lots change
This commit is contained in:
@@ -23,6 +23,7 @@ env/
|
|||||||
|
|
||||||
uploads/*
|
uploads/*
|
||||||
|
|
||||||
|
core/dataset/*.zip
|
||||||
core/dataset/toy/*
|
core/dataset/toy/*
|
||||||
core/dataset/benchmark/*
|
core/dataset/benchmark/*
|
||||||
core/models/*.pth
|
core/models/*.pth
|
||||||
@@ -33,3 +34,5 @@ build/bin
|
|||||||
frontend/node_modules
|
frontend/node_modules
|
||||||
frontend/dist
|
frontend/dist
|
||||||
frontend/public
|
frontend/public
|
||||||
|
|
||||||
|
static/images/*
|
||||||
|
|||||||
+59
-52
@@ -3,6 +3,7 @@ package backend
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
@@ -17,23 +18,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
publicImagePath = "./frontend/public/images"
|
staticImagesPath = "./static/images"
|
||||||
modelPath = "resnet_epoch_100.onnx"
|
modelPath = "resnet18_epoch_50.onnx"
|
||||||
|
|
||||||
// ImageNet 标准化参数
|
// ImageNet 标准化参数
|
||||||
mean = []float32{0.485, 0.456, 0.406}
|
mean = []float32{0.485, 0.456, 0.406}
|
||||||
std = []float32{0.229, 0.224, 0.225}
|
std = []float32{0.229, 0.224, 0.225}
|
||||||
|
|
||||||
// 猫品种标签
|
|
||||||
labelName = []string{
|
|
||||||
"american_shorthair",
|
|
||||||
"bengal",
|
|
||||||
"british_shorthair",
|
|
||||||
"exotic_shorthair",
|
|
||||||
"maine_coon",
|
|
||||||
"ragdoll",
|
|
||||||
"sphynx",
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewApp() *App {
|
func NewApp() *App {
|
||||||
@@ -90,16 +80,14 @@ func preprocessImage(imgData []byte) ([]float32, error) {
|
|||||||
return input, nil
|
return input, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runInference(input []float32) (string, float64, error) {
|
func runInference(input []float32, labels []string) (string, float64, error) {
|
||||||
// 创建输入张量 [1, 3, 224, 224]
|
|
||||||
inputTensor, err := ort.NewTensor(ort.Shape{1, 3, 224, 224}, input)
|
inputTensor, err := ort.NewTensor(ort.Shape{1, 3, 224, 224}, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, fmt.Errorf("create input tensor: %w", err)
|
return "", 0, fmt.Errorf("create input tensor: %w", err)
|
||||||
}
|
}
|
||||||
defer inputTensor.Destroy()
|
defer inputTensor.Destroy()
|
||||||
|
|
||||||
// 创建输出张量 [1, 7]
|
outputTensor, err := ort.NewTensor(ort.Shape{1, 12}, make([]float32, 12))
|
||||||
outputTensor, err := ort.NewTensor(ort.Shape{1, 7}, make([]float32, 7))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, fmt.Errorf("create output tensor: %w", err)
|
return "", 0, fmt.Errorf("create output tensor: %w", err)
|
||||||
}
|
}
|
||||||
@@ -147,7 +135,7 @@ func runInference(input []float32) (string, float64, error) {
|
|||||||
}
|
}
|
||||||
confidence := math.Exp(float64(maxVal)) / sum
|
confidence := math.Exp(float64(maxVal)) / sum
|
||||||
|
|
||||||
return labelName[maxIdx], confidence, nil
|
return labels[maxIdx], confidence, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GormDB() (*gorm.DB, error) {
|
func (a *App) GormDB() (*gorm.DB, error) {
|
||||||
@@ -159,7 +147,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 := publicImagePath
|
uploadsDir := staticImagesPath
|
||||||
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()}
|
||||||
@@ -174,32 +162,12 @@ func (a *App) UploadImage(data []byte, filename string) Response {
|
|||||||
return Response{Code: 1, Message: "failed", Data: err.Error()}
|
return Response{Code: 1, Message: "failed", Data: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
println("333")
|
imageResult := map[string]any{
|
||||||
|
"filename": newFilename,
|
||||||
return Response{Code: 0, Message: "success", Data: newFilename}
|
"data": base64.StdEncoding.EncodeToString(data),
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) GetHistory(page int, pageSize int) Response {
|
|
||||||
db, err := a.GormDB()
|
|
||||||
if err != nil {
|
|
||||||
return Response{Code: 1, Message: err.Error()}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var total int64
|
return Response{Code: 0, Message: "success", Data: imageResult}
|
||||||
db.Table("history_test").Count(&total)
|
|
||||||
|
|
||||||
var historyList []HistoryWithBreed
|
|
||||||
err = db.Table("history_test").Select("history_test.*, breeds_test.brief, breeds_test.name").
|
|
||||||
Joins("LEFT JOIN breeds_test ON history_test.breed = breeds_test.id").
|
|
||||||
Order("history_test.id DESC").Limit(pageSize).Offset((page - 1) * pageSize).Find(&historyList).Error
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return Response{Code: 1, Message: err.Error()}
|
|
||||||
}
|
|
||||||
|
|
||||||
historyData := HistoryData{Page: page, PageSize: pageSize, Total: total, List: historyList}
|
|
||||||
|
|
||||||
return Response{Code: 0, Message: "success", Data: historyData}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Detect(filename string) Response {
|
func (a *App) Detect(filename string) Response {
|
||||||
@@ -208,7 +176,7 @@ func (a *App) Detect(filename string) Response {
|
|||||||
return Response{Code: 1, Message: err.Error()}
|
return Response{Code: 1, Message: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
filePath := filepath.Join(publicImagePath, filename)
|
filePath := filepath.Join(staticImagesPath, filename)
|
||||||
imgData, err := os.ReadFile(filePath)
|
imgData, err := os.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Response{Code: 1, Message: "failed to read image: " + err.Error()}
|
return Response{Code: 1, Message: "failed to read image: " + err.Error()}
|
||||||
@@ -219,22 +187,31 @@ func (a *App) Detect(filename string) Response {
|
|||||||
return Response{Code: 1, Message: "failed to preprocess: " + err.Error()}
|
return Response{Code: 1, Message: "failed to preprocess: " + err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询breeds表的Code列,组成切片
|
||||||
|
var labels []string
|
||||||
|
err = db.Table("breeds").Pluck("code", &labels).Error
|
||||||
|
if err != nil {
|
||||||
|
return Response{Code: 1, Message: "failed to query breed codes: " + err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
// 推理
|
// 推理
|
||||||
detectRet, confidence, err := runInference(input)
|
detectRet, confidence, err := runInference(input, labels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Response{Code: 1, Message: "model inference failed: " + err.Error()}
|
return Response{Code: 1, Message: "model inference failed: " + err.Error()}
|
||||||
}
|
}
|
||||||
|
confidence = math.Round(confidence*10000) / 10000
|
||||||
|
|
||||||
var breed Breed
|
var breed Breed
|
||||||
err = db.Table("breeds_test").Where("code = ?", detectRet).First(&breed).Error
|
err = db.Table("breeds").Where("code = ?", detectRet).First(&breed).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Response{Code: 1, Message: err.Error()}
|
return Response{Code: 1, Message: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
now := int(time.Now().Unix())
|
now := int(time.Now().Unix())
|
||||||
|
print("confidence", confidence)
|
||||||
|
|
||||||
one := HistoryItem{Img: filename, Breed: int(breed.Id), Date: now}
|
one := HistoryItem{Img: filename, Breed: int(breed.Id), Confidence: confidence, Date: now}
|
||||||
result := db.Table("history_test").Create(&one)
|
result := db.Table("history").Create(&one)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return Response{Code: 1, Message: result.Error.Error()}
|
return Response{Code: 1, Message: result.Error.Error()}
|
||||||
}
|
}
|
||||||
@@ -250,13 +227,43 @@ func (a *App) Detect(filename string) Response {
|
|||||||
return Response{Code: 0, Message: "success", Data: detectData}
|
return Response{Code: 0, Message: "success", Data: detectData}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) GetHistory(page int, pageSize int) Response {
|
||||||
|
db, err := a.GormDB()
|
||||||
|
if err != nil {
|
||||||
|
return Response{Code: 1, Message: err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
var total int64
|
||||||
|
db.Table("history").Count(&total)
|
||||||
|
|
||||||
|
var historyList []HistoryWithBreed
|
||||||
|
err = db.Table("history").Select("history.*, breeds.brief, breeds.name").
|
||||||
|
Joins("LEFT JOIN breeds ON history.breed = breeds.id").
|
||||||
|
Order("history.id DESC").Limit(pageSize).Offset((page - 1) * pageSize).Find(&historyList).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return Response{Code: 1, Message: err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range historyList {
|
||||||
|
imgPath := filepath.Join(staticImagesPath, historyList[i].Img)
|
||||||
|
if data, err := os.ReadFile(imgPath); err == nil {
|
||||||
|
historyList[i].ImgData = base64.StdEncoding.EncodeToString(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
historyData := HistoryData{Page: page, PageSize: pageSize, Total: total, List: historyList}
|
||||||
|
|
||||||
|
return Response{Code: 0, Message: "success", Data: historyData}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) DeleteOneHistory(id uint) Response {
|
func (a *App) DeleteOneHistory(id uint) Response {
|
||||||
db, err := a.GormDB()
|
db, err := a.GormDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Response{Code: 1, Message: err.Error()}
|
return Response{Code: 1, Message: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := db.Table("history_test").Delete(&HistoryItem{}, id)
|
result := db.Table("history").Delete(&HistoryItem{}, id)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return Response{Code: 1, Message: result.Error.Error()}
|
return Response{Code: 1, Message: result.Error.Error()}
|
||||||
}
|
}
|
||||||
@@ -270,15 +277,15 @@ func (a *App) ClearHistory() Response {
|
|||||||
return Response{Code: 1, Message: err.Error()}
|
return Response{Code: 1, Message: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := db.Exec("DELETE FROM history_test")
|
result := db.Exec("DELETE FROM history")
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return Response{Code: 1, Message: result.Error.Error()}
|
return Response{Code: 1, Message: result.Error.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.RemoveAll(publicImagePath); err != nil {
|
if err := os.RemoveAll(staticImagesPath); err != nil {
|
||||||
return Response{Code: 1, Message: fmt.Sprintf("failed to remove images: %v", err)}
|
return Response{Code: 1, Message: fmt.Sprintf("failed to remove images: %v", err)}
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(publicImagePath, 0755); err != nil {
|
if err := os.MkdirAll(staticImagesPath, 0755); err != nil {
|
||||||
return Response{Code: 1, Message: fmt.Sprintf("failed to recreate images dir: %v", err)}
|
return Response{Code: 1, Message: fmt.Sprintf("failed to recreate images dir: %v", err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,12 +19,14 @@ type (
|
|||||||
Id uint `gorm:"primaryKey"`
|
Id uint `gorm:"primaryKey"`
|
||||||
Img string `gorm:"column:img"`
|
Img string `gorm:"column:img"`
|
||||||
Breed int `gorm:"column:breed"`
|
Breed int `gorm:"column:breed"`
|
||||||
|
Confidence float64 `gorm:"column:confidence"`
|
||||||
Date int `gorm:"column:date"`
|
Date int `gorm:"column:date"`
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryWithBreed struct {
|
HistoryWithBreed struct {
|
||||||
Id uint `gorm:"column:id" json:"id"`
|
Id uint `gorm:"column:id" json:"id"`
|
||||||
Img string `gorm:"column:img" json:"img"`
|
Img string `gorm:"column:img" json:"img"`
|
||||||
|
ImgData string `gorm:"-" json:"img_data"`
|
||||||
Breed int `gorm:"column:breed" json:"breed"`
|
Breed int `gorm:"column:breed" json:"breed"`
|
||||||
Date int `gorm:"column:date" json:"date"`
|
Date int `gorm:"column:date" json:"date"`
|
||||||
Name string `gorm:"column:name" json:"name"`
|
Name string `gorm:"column:name" json:"name"`
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import torch
|
import torch
|
||||||
import sys
|
|
||||||
# from core.nets.resnet import resnet
|
|
||||||
from core.nets.resnet18 import resnet18
|
from core.nets.resnet18 import resnet18
|
||||||
|
|
||||||
# 加载 pth
|
# 加载 pth
|
||||||
# net = resnet() # 实例化你的模型
|
|
||||||
net = resnet18()
|
net = resnet18()
|
||||||
|
|
||||||
|
|
||||||
@@ -14,7 +11,7 @@ core_dir = os.path.dirname(script_dir)
|
|||||||
model_dir = os.path.join(core_dir, "models")
|
model_dir = os.path.join(core_dir, "models")
|
||||||
|
|
||||||
|
|
||||||
net.load_state_dict(torch.load(os.path.join(model_dir, "resnet18_epoch_50_bak2.pth"), map_location="cpu"))
|
net.load_state_dict(torch.load(os.path.join(model_dir, "resnet18_epoch_50.pth"), map_location="cpu"))
|
||||||
net.eval()
|
net.eval()
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +20,7 @@ dummy_input = torch.randn(1, 3, 224, 224)
|
|||||||
torch.onnx.export(
|
torch.onnx.export(
|
||||||
net,
|
net,
|
||||||
dummy_input,
|
dummy_input,
|
||||||
os.path.join(model_dir, "resnet18_epoch_50_bak2.onnx"),
|
os.path.join(model_dir, "resnet18_epoch_50.onnx"),
|
||||||
export_params=True,
|
export_params=True,
|
||||||
opset_version=11,
|
opset_version=11,
|
||||||
input_names=["input"],
|
input_names=["input"],
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export const Footer = () => (
|
|||||||
<footer class="app-footer">
|
<footer class="app-footer">
|
||||||
<span>📩 xiadongliang88@163.com</span>
|
<span>📩 xiadongliang88@163.com</span>
|
||||||
<span>|</span>
|
<span>|</span>
|
||||||
<span>📦 https://git.leonstack.com/owner</span>
|
<span>📦 https://git.leonstack.com/xiadongliang</span>
|
||||||
<span>|</span>
|
<span>|</span>
|
||||||
<span>🌏 https://www.leonstack.com/</span>
|
<span>🌏 https://www.leonstack.com/</span>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import Modal from './Modal'
|
|||||||
import { message } from '../utils/toast'
|
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', {
|
||||||
@@ -64,7 +65,6 @@ const History = () => {
|
|||||||
message.error('currentId为空')
|
message.error('currentId为空')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// dfdfdf
|
|
||||||
const result = await (window as any).go.backend.App.DeleteOneHistory(currentId)
|
const result = await (window as any).go.backend.App.DeleteOneHistory(currentId)
|
||||||
if (result.code === 0) {
|
if (result.code === 0) {
|
||||||
message.success('删除成功')
|
message.success('删除成功')
|
||||||
@@ -109,7 +109,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/${item.img}`} onClick={() => handleShowItem(item)} />
|
<img src={`data:image/jpeg;base64,${item.img_data}`} onClick={() => handleShowItem(item)} />
|
||||||
</div>
|
</div>
|
||||||
<div class="card-right">
|
<div class="card-right">
|
||||||
<div class="right-top">
|
<div class="right-top">
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ import { useState, useRef } from 'preact/hooks'
|
|||||||
import type { DetectResult } from '../preact'
|
import type { DetectResult } from '../preact'
|
||||||
import { message } from '../utils/toast'
|
import { message } from '../utils/toast'
|
||||||
|
|
||||||
|
|
||||||
const Main = () => {
|
const Main = () => {
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||||
const [fileSrc, setFileSrc] = useState<string>('')
|
const [fileSrc, setFileSrc] = useState<string>('')
|
||||||
|
const [filename, setFilename] = useState<string>('')
|
||||||
const [step, setStep] = useState<number>(0)
|
const [step, setStep] = useState<number>(0)
|
||||||
const [detectResult, setDetectResult] = useState<DetectResult | null>(null)
|
const [detectResult, setDetectResult] = useState<DetectResult | null>(null)
|
||||||
|
|
||||||
@@ -46,7 +48,6 @@ const Main = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -64,8 +65,8 @@ const Main = () => {
|
|||||||
file.name
|
file.name
|
||||||
)
|
)
|
||||||
if (result.code === 0) {
|
if (result.code === 0) {
|
||||||
console.log("rrr", result)
|
setFileSrc(result.data.data)
|
||||||
setFileSrc(result.data)
|
setFilename(result.data.filename)
|
||||||
setStep(1)
|
setStep(1)
|
||||||
} else if (result.code === 1) {
|
} else if (result.code === 1) {
|
||||||
message.error(result.message)
|
message.error(result.message)
|
||||||
@@ -92,14 +93,16 @@ const Main = () => {
|
|||||||
setStep(2)
|
setStep(2)
|
||||||
|
|
||||||
setTimeout(async() => {
|
setTimeout(async() => {
|
||||||
const result = await (window as any).go.backend.App.Detect(fileSrc)
|
if (filename) {
|
||||||
|
const result = await (window as any).go.backend.App.Detect(filename)
|
||||||
if (result.code === 0) {
|
if (result.code === 0) {
|
||||||
setDetectResult(result.data)
|
setDetectResult(result.data)
|
||||||
setStep(3)
|
setStep(3)
|
||||||
} else if (result.code === 1) {
|
} else if (result.code === 1) {
|
||||||
message.error(result.message)
|
message.error(result.message)
|
||||||
}
|
}
|
||||||
}, 1000)
|
}
|
||||||
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTryOther = () => {
|
const handleTryOther = () => {
|
||||||
@@ -108,8 +111,6 @@ const Main = () => {
|
|||||||
resetUpload()
|
resetUpload()
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("fff", fileSrc)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main class="app-main">
|
<main class="app-main">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
@@ -135,7 +136,7 @@ const Main = () => {
|
|||||||
>
|
>
|
||||||
{fileSrc.length > 0 && step == 1 ?
|
{fileSrc.length > 0 && step == 1 ?
|
||||||
<div id="previewContent">
|
<div id="previewContent">
|
||||||
<img src={`/images/${fileSrc}`} />
|
<img src={`data:image/jpeg;base64,${fileSrc}`} />
|
||||||
<button onClick={handleRemovePhoto}>
|
<button onClick={handleRemovePhoto}>
|
||||||
❌ 移除照片
|
❌ 移除照片
|
||||||
</button>
|
</button>
|
||||||
@@ -194,7 +195,7 @@ const Main = () => {
|
|||||||
<h2>完成!</h2>
|
<h2>完成!</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="result-main">
|
<div class="result-main">
|
||||||
<img src={`/images/${fileSrc}`} />
|
<img src={`data:image/jpeg;base64,${fileSrc}`} />
|
||||||
<div class="result-word">
|
<div class="result-word">
|
||||||
<div>
|
<div>
|
||||||
<h3>{detectResult?.name}</h3>
|
<h3>{detectResult?.name}</h3>
|
||||||
@@ -202,7 +203,7 @@ const Main = () => {
|
|||||||
<div class="probability-bar">
|
<div class="probability-bar">
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
<span>置信度 {detectResult ? detectResult.confidence_level * 100 + '%' : ''}</span>
|
<span>置信度 {detectResult ? (detectResult.confidence_level * 100).toFixed(2) + '%' : ''}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{detectResult?.brief}</p>
|
<p>{detectResult?.brief}</p>
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import type { ModalProps } from '../preact'
|
import type { ModalProps } from '../preact'
|
||||||
|
|
||||||
|
|
||||||
const Modal = ({ open, title, onClick, onClose, children }: ModalProps) => {
|
const Modal = ({ open, title, onClick, onClose, children }: ModalProps) => {
|
||||||
const handleClose = () => {
|
const handleClose = () => onClose?.()
|
||||||
onClose?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
onClick?.()
|
onClick?.()
|
||||||
@@ -11,9 +10,7 @@ const Modal = ({ open, title, onClick, onClose, children }: ModalProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleMaskClick = (e: MouseEvent) => {
|
const handleMaskClick = (e: MouseEvent) => {
|
||||||
if (e.target === e.currentTarget) {
|
if (e.target === e.currentTarget) handleClose()
|
||||||
handleClose()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Vendored
+1
@@ -1,6 +1,7 @@
|
|||||||
export interface HistoryItem {
|
export interface HistoryItem {
|
||||||
id: number
|
id: number
|
||||||
img: string
|
img: string
|
||||||
|
img_data: string
|
||||||
breed: number
|
breed: number
|
||||||
date: number
|
date: number
|
||||||
name: string
|
name: string
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ export const message: MessageAPI = {
|
|||||||
div.appendChild(subDiv)
|
div.appendChild(subDiv)
|
||||||
document.body.appendChild(div)
|
document.body.appendChild(div)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => div.remove(), 3000)
|
||||||
div.remove()
|
|
||||||
}, 3000)
|
|
||||||
},
|
},
|
||||||
error: (text: string) => {
|
error: (text: string) => {
|
||||||
const div = document.createElement('div')
|
const div = document.createElement('div')
|
||||||
@@ -46,8 +44,6 @@ export const message: MessageAPI = {
|
|||||||
div.appendChild(subDiv)
|
div.appendChild(subDiv)
|
||||||
document.body.appendChild(div)
|
document.body.appendChild(div)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => div.remove(), 3000)
|
||||||
div.remove()
|
|
||||||
}, 3000)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user