添加定时清理相关
This commit is contained in:
parent
8540f76fcf
commit
9cfb302777
Binary file not shown.
|
Before Width: | Height: | Size: 647 KiB After Width: | Height: | Size: 63 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 90 KiB |
@ -30,7 +30,7 @@ func WireApp(bc *conf.Bootstrap, log *slog.Logger) (http.Handler, func(), error)
|
||||
vqdTaskCore := api.NewVqdTaskCore(db)
|
||||
hostCore := host.NewCore(bc)
|
||||
mediaCore := media.NewCore(hostCore)
|
||||
vqdSdkCore := vqdsdk.NewCore(hostCore, vqdTaskCore)
|
||||
vqdSdkCore := vqdsdk.NewCore(hostCore, vqdTaskCore, bc)
|
||||
vqdTaskAPI := api.NewVqdTaskAPI(vqdTaskCore, mediaCore,vqdSdkCore,hostCore, bc)
|
||||
|
||||
usecase := &api.Usecase{
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"easyvqd/internal/core/vqd"
|
||||
"git.lnton.com/lnton/pkg/orm"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ vqd.VqdAlarmStorer = VqdAlarm{}
|
||||
@ -48,6 +49,15 @@ func (d VqdAlarm) Del(ctx context.Context, model *vqd.VqdAlarm, opts ...orm.Quer
|
||||
return orm.DeleteWithContext(ctx, d.db, model, opts...)
|
||||
}
|
||||
|
||||
// DelAll implements vqd.VqdTaskTemplateStorer.
|
||||
func (d VqdAlarm) DelAll(expireTime time.Time, batchSize int) (int, error) {
|
||||
result := d.db.Where(`created_at < ?`, expireTime).Limit(batchSize).Delete(&vqd.VqdAlarm{})
|
||||
if result.Error != nil {
|
||||
return 0, result.Error
|
||||
}
|
||||
return int(result.RowsAffected), nil
|
||||
}
|
||||
|
||||
// EditStatus implements vqd.VqdAlarmStorer.
|
||||
func (d VqdAlarm) EditStatus(status int, id int) error {
|
||||
if err := d.db.Model(&vqd.VqdAlarm{}).Where(`id = ?`, id).Update("task_status", status).Error; err != nil {
|
||||
|
||||
@ -55,3 +55,6 @@ func (d VqdTaskTemplate) EditStatus(status vqd.EncodeStatus, id int) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (d VqdTaskTemplate) FirstOrCreate(b any) error {
|
||||
return d.db.FirstOrCreate(b).Error
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"git.lnton.com/lnton/pkg/reason"
|
||||
"github.com/jinzhu/copier"
|
||||
"log/slog"
|
||||
"time"
|
||||
)
|
||||
|
||||
// VqdAlarmStorer Instantiation interface
|
||||
@ -17,6 +18,7 @@ type VqdAlarmStorer interface {
|
||||
Add(context.Context, *VqdAlarm) error
|
||||
Edit(context.Context, *VqdAlarm, func(*VqdAlarm), ...orm.QueryOption) error
|
||||
Del(context.Context, *VqdAlarm, ...orm.QueryOption) error
|
||||
DelAll(expireTime time.Time, batchSize int) (int, error)
|
||||
}
|
||||
|
||||
// FindVqdAlarmAll Paginated search
|
||||
@ -113,3 +115,8 @@ func (c Core) DelVqdAlarmAll(ctx context.Context, ids []int) (*VqdAlarm, error)
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// DelVqdTaskAlarmAll Delete object
|
||||
func (c Core) DelVqdTaskAlarmAll(expireTime time.Time, batchSize int) (int, error) {
|
||||
return c.store.VqdAlarm().DelAll(expireTime, batchSize)
|
||||
}
|
||||
|
||||
@ -52,6 +52,18 @@ func (c Core) FindVqdTask(ctx context.Context, in *FindVqdTaskInput) ([]*VqdTask
|
||||
}
|
||||
}
|
||||
|
||||
// FindVqdTaskTemplateID Query a single object
|
||||
func (c Core) FindVqdTaskTemplateID(ctx context.Context, id int) (*VqdTask, error) {
|
||||
var out VqdTask
|
||||
if err := c.store.VqdTask().Get(ctx, &out, orm.Where("task_template_id=?", id)); err != nil {
|
||||
if orm.IsErrRecordNotFound(err) {
|
||||
return nil, reason.ErrNotFound.Withf(`Get err[%s]`, err.Error())
|
||||
}
|
||||
return nil, reason.ErrDB.Withf(`Get err[%s]`, err.Error())
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// GetVqdTask Query a single object
|
||||
func (c Core) GetVqdTask(ctx context.Context, id int) (*VqdTask, error) {
|
||||
var out VqdTask
|
||||
@ -63,6 +75,7 @@ func (c Core) GetVqdTask(ctx context.Context, id int) (*VqdTask, error) {
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func (c Core) GetNameVqdTask(ctx context.Context, name string) (*VqdTask, error) {
|
||||
var out VqdTask
|
||||
if err := c.store.VqdTask().Get(ctx, &out, orm.Where("name=?", name)); err != nil {
|
||||
|
||||
@ -14,7 +14,7 @@ type EditVqdTaskInput struct {
|
||||
Name string `json:"name"` // 名称
|
||||
ChannelID string `json:"channel_id"` // 关联通道
|
||||
ChannelName string `json:"channel_name"` // 通道名称
|
||||
TaskTemplateID string `json:"task_template_id"` // 关联模板
|
||||
TaskTemplateID int `json:"task_template_id"` // 关联模板
|
||||
TaskTemplateName string `json:"task_template_name"` // 模板名称
|
||||
Enable bool `form:"enable"` // 启用
|
||||
Des string `json:"des"` // 描述
|
||||
@ -24,7 +24,7 @@ type AddVqdTaskInput struct {
|
||||
Name string `json:"name"` // 名称
|
||||
ChannelID string `json:"channel_id"` // 关联通道
|
||||
ChannelName string `json:"channel_name"` // 通道名称
|
||||
TaskTemplateID string `json:"task_template_id"` // 关联模板
|
||||
TaskTemplateID int `json:"task_template_id"` // 关联模板
|
||||
TaskTemplateName string `json:"task_template_name"` // 模板名称
|
||||
Enable bool `form:"enable"` // 启用
|
||||
Des string `json:"des"` // 描述
|
||||
|
||||
@ -17,6 +17,8 @@ type VqdTaskTemplateStorer interface {
|
||||
Add(context.Context, *VqdTaskTemplate) error
|
||||
Edit(context.Context, *VqdTaskTemplate, func(*VqdTaskTemplate), ...orm.QueryOption) error
|
||||
Del(context.Context, *VqdTaskTemplate, ...orm.QueryOption) error
|
||||
|
||||
FirstOrCreate(b any) error
|
||||
}
|
||||
|
||||
// FindVqdTaskTemplateAll Paginated search
|
||||
@ -33,16 +35,14 @@ func (c Core) FindVqdTaskTemplateAll() ([]*VqdTaskTemplate, int64, error) {
|
||||
func (c Core) FindVqdTaskTemplate(ctx context.Context, in *FindVqdTaskTemplateInput) ([]*VqdTaskTemplate, int64, error) {
|
||||
items := make([]*VqdTaskTemplate, 0)
|
||||
if in.Name != "" {
|
||||
query := orm.NewQuery(8).
|
||||
Where("name like ? ", "%"+in.Name+"%").OrderBy("created_at DESC")
|
||||
query := orm.NewQuery(8).Where("name like ? ", "%"+in.Name+"%")
|
||||
total, err := c.store.VqdTaskTemplate().Find(ctx, &items, in, query.Encode()...)
|
||||
if err != nil {
|
||||
return nil, 0, reason.ErrDB.Withf(`Find err[%s]`, err.Error())
|
||||
}
|
||||
return items, total, nil
|
||||
} else {
|
||||
query := orm.NewQuery(2).OrderBy("created_at DESC")
|
||||
total, err := c.store.VqdTaskTemplate().Find(ctx, &items, in, query.Encode()...)
|
||||
total, err := c.store.VqdTaskTemplate().Find(ctx, &items, in)
|
||||
if err != nil {
|
||||
return nil, 0, reason.ErrDB.Withf(`Find err[%s]`, err.Error())
|
||||
}
|
||||
@ -61,6 +61,16 @@ func (c Core) GetVqdTaskTemplate(ctx context.Context, id int) (*VqdTaskTemplate,
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
func (c Core) GetIDVqdTaskTemplate(ctx context.Context, ID int64) (*VqdTaskTemplate, error) {
|
||||
var out VqdTaskTemplate
|
||||
if err := c.store.VqdTaskTemplate().Get(ctx, &out, orm.Where("id=?", ID)); err != nil {
|
||||
if orm.IsErrRecordNotFound(err) {
|
||||
return nil, reason.ErrNotFound.Withf(`Get err[%s]`, err.Error())
|
||||
}
|
||||
return nil, reason.ErrDB.Withf(`Get err[%s]`, err.Error())
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
func (c Core) GetNameVqdTaskTemplate(ctx context.Context, name string) (*VqdTaskTemplate, error) {
|
||||
var out VqdTaskTemplate
|
||||
if err := c.store.VqdTaskTemplate().Get(ctx, &out, orm.Where("name=?", name)); err != nil {
|
||||
@ -72,6 +82,11 @@ func (c Core) GetNameVqdTaskTemplate(ctx context.Context, name string) (*VqdTask
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// FirstOrCreateTemplate Insert into database
|
||||
func (c Core) FirstOrCreateTemplate(b any) error {
|
||||
return c.store.VqdTaskTemplate().FirstOrCreate(b)
|
||||
}
|
||||
|
||||
// AddVqdTaskTemplate Insert into database
|
||||
func (c Core) AddVqdTaskTemplate(ctx context.Context, in *AddVqdTaskTemplateInput) (*VqdTaskTemplate, error) {
|
||||
var out VqdTaskTemplate
|
||||
|
||||
@ -11,39 +11,39 @@ type FindVqdTaskTemplateInput struct {
|
||||
}
|
||||
|
||||
type EditVqdTaskTemplateInput struct {
|
||||
Name string `json:"name"`
|
||||
Plans string `json:"plans"`
|
||||
Enable bool `json:"enable"`
|
||||
//VqdConfig VqdConfig `json:"vqd_config"` // 诊断基础配置
|
||||
//VqdLgtDark VqdLgtDark `json:"vqd_lgt_dark"` // 亮度检测
|
||||
//VqdBlue VqdBlue `json:"vqd_blue"` // 蓝屏检查
|
||||
//VqdClarity VqdClarity `json:"vqd_clarity"` // 清晰度检查
|
||||
//VqdShark VqdShark `json:"vqd_shark"` // 抖动检查
|
||||
//VqdFreeze VqdFreeze `json:"vqd_freeze"` // 冻结检测
|
||||
//VqdColor VqdColor `json:"vqd_color"` // 偏色检测
|
||||
//VqdOcclusion VqdOcclusion `json:"vqd_occlusion"` // 遮挡检测
|
||||
//VqdNoise VqdNoise `json:"vqd_noise"` // 噪声检测
|
||||
//VqdContrast VqdContrast `json:"vqd_contrast"` // 对比度检测
|
||||
//VqdMosaic VqdMosaic `json:"vqd_mosaic"` // 马赛克检测
|
||||
//VqdFlower VqdFlower `json:"vqd_flower"` // 花屏检测
|
||||
Des string ` json:"des"` // 描述
|
||||
Name string `json:"name"`
|
||||
Plans string `json:"plans"`
|
||||
Enable bool `json:"enable"`
|
||||
VqdConfig VqdConfig `json:"vqd_config"` // 诊断基础配置
|
||||
VqdLgtDark VqdLgtDark `json:"vqd_lgt_dark"` // 亮度检测
|
||||
VqdBlue VqdBlue `json:"vqd_blue"` // 蓝屏检查
|
||||
VqdClarity VqdClarity `json:"vqd_clarity"` // 清晰度检查
|
||||
VqdShark VqdShark `json:"vqd_shark"` // 抖动检查
|
||||
VqdFreeze VqdFreeze `json:"vqd_freeze"` // 冻结检测
|
||||
VqdColor VqdColor `json:"vqd_color"` // 偏色检测
|
||||
VqdOcclusion VqdOcclusion `json:"vqd_occlusion"` // 遮挡检测
|
||||
VqdNoise VqdNoise `json:"vqd_noise"` // 噪声检测
|
||||
VqdContrast VqdContrast `json:"vqd_contrast"` // 对比度检测
|
||||
VqdMosaic VqdMosaic `json:"vqd_mosaic"` // 马赛克检测
|
||||
VqdFlower VqdFlower `json:"vqd_flower"` // 花屏检测
|
||||
Des string ` json:"des"` // 描述
|
||||
}
|
||||
|
||||
type AddVqdTaskTemplateInput struct {
|
||||
Name string `json:"name"`
|
||||
Plans string `json:"plans"`
|
||||
Enable bool `json:"enable"`
|
||||
//VqdConfig VqdConfig `json:"vqd_config"` // 诊断基础配置
|
||||
//VqdLgtDark VqdLgtDark `json:"vqd_lgt_dark"` // 亮度检测
|
||||
//VqdBlue VqdBlue `json:"vqd_blue"` // 蓝屏检查
|
||||
//VqdClarity VqdClarity `json:"vqd_clarity"` // 清晰度检查
|
||||
//VqdShark VqdShark `json:"vqd_shark"` // 抖动检查
|
||||
//VqdFreeze VqdFreeze `json:"vqd_freeze"` // 冻结检测
|
||||
//VqdColor VqdColor `json:"vqd_color"` // 偏色检测
|
||||
//VqdOcclusion VqdOcclusion `json:"vqd_occlusion"` // 遮挡检测
|
||||
//VqdNoise VqdNoise `json:"vqd_noise"` // 噪声检测
|
||||
//VqdContrast VqdContrast `json:"vqd_contrast"` // 对比度检测
|
||||
//VqdMosaic VqdMosaic `json:"vqd_mosaic"` // 马赛克检测
|
||||
//VqdFlower VqdFlower `json:"vqd_flower"` // 花屏检测
|
||||
Des string ` json:"des"` // 描述
|
||||
Name string `json:"name"`
|
||||
Plans string `json:"plans"`
|
||||
Enable bool `json:"enable"`
|
||||
VqdConfig VqdConfig `json:"vqd_config"` // 诊断基础配置
|
||||
VqdLgtDark VqdLgtDark `json:"vqd_lgt_dark"` // 亮度检测
|
||||
VqdBlue VqdBlue `json:"vqd_blue"` // 蓝屏检查
|
||||
VqdClarity VqdClarity `json:"vqd_clarity"` // 清晰度检查
|
||||
VqdShark VqdShark `json:"vqd_shark"` // 抖动检查
|
||||
VqdFreeze VqdFreeze `json:"vqd_freeze"` // 冻结检测
|
||||
VqdColor VqdColor `json:"vqd_color"` // 偏色检测
|
||||
VqdOcclusion VqdOcclusion `json:"vqd_occlusion"` // 遮挡检测
|
||||
VqdNoise VqdNoise `json:"vqd_noise"` // 噪声检测
|
||||
VqdContrast VqdContrast `json:"vqd_contrast"` // 对比度检测
|
||||
VqdMosaic VqdMosaic `json:"vqd_mosaic"` // 马赛克检测
|
||||
VqdFlower VqdFlower `json:"vqd_flower"` // 花屏检测
|
||||
Des string ` json:"des"` // 描述
|
||||
}
|
||||
|
||||
227
internal/core/vqdsdk/clean.go
Normal file
227
internal/core/vqdsdk/clean.go
Normal file
@ -0,0 +1,227 @@
|
||||
package vqdsdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 配置参数
|
||||
const (
|
||||
// 要清理的目标目录(请替换为你实际的目录路径)
|
||||
cleanDir = "/snap"
|
||||
// 定时任务执行间隔(每天执行一次)
|
||||
interval = 24 * time.Hour
|
||||
// 批量删除大小(避免单次删除过多锁表)
|
||||
batchSize = 1000
|
||||
// 日期目录的格式(如 20260117)
|
||||
dateDirLayout = "20060102"
|
||||
// 定时任务首次执行时间(每天凌晨1点)
|
||||
executeHour = 1
|
||||
)
|
||||
|
||||
// scheduleCleanFile 定时执行清理任务
|
||||
func (c Core) scheduleCleanTask() {
|
||||
// 计算首次执行时间(今天/明天的凌晨1点)
|
||||
now := time.Now()
|
||||
nextExec := time.Date(now.Year(), now.Month(), now.Day(), executeHour, 0, 0, 0, now.Location())
|
||||
if nextExec.Before(now) {
|
||||
nextExec = nextExec.Add(24 * time.Hour)
|
||||
}
|
||||
|
||||
// 计算首次执行的等待时间
|
||||
initialDelay := nextExec.Sub(now)
|
||||
slog.Info(fmt.Sprintf("定时任务已启动,首次执行时间:%s(等待 %v)", nextExec.Format(time.RFC3339), initialDelay))
|
||||
|
||||
// 首次执行等待
|
||||
time.Sleep(initialDelay)
|
||||
|
||||
// 执行首次清理
|
||||
if err := c.cleanExpiredFiles(); err != nil {
|
||||
slog.Error("首次清理任务执行失败 Files", "err", err)
|
||||
}
|
||||
time.Sleep(20 * time.Minute)
|
||||
if err := c.cleanExpiredDbs(); err != nil {
|
||||
slog.Error("首次清理任务执行失败 Dbs", "err", err)
|
||||
}
|
||||
|
||||
// 循环执行定时任务
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
if err := c.cleanExpiredFiles(); err != nil {
|
||||
slog.Error("定时清理任务执行失败 Files", "err", err)
|
||||
}
|
||||
time.Sleep(20 * time.Minute)
|
||||
if err := c.cleanExpiredDbs(); err != nil {
|
||||
slog.Error("定时清理任务执行失败 Dbs", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanExpiredDbs 清理超过expireDays天的数据
|
||||
func (c Core) cleanExpiredDbs() error {
|
||||
// 计算过期时间阈值
|
||||
expireDays := time.Duration(c.Cfg.VqdConfig.SaveDay)
|
||||
if expireDays < 1 {
|
||||
expireDays = 1
|
||||
}
|
||||
expireTime := time.Now().Add(-expireDays * 24 * time.Hour)
|
||||
slog.Info(fmt.Sprintf("开始清理中超过 %d 天的数据,过期时间阈值:%s", expireDays, expireTime.Format(time.RFC3339)))
|
||||
|
||||
totalDeleted := 0
|
||||
|
||||
for {
|
||||
deletedCount, err := c.VqdTaskCore.DelVqdTaskAlarmAll(expireTime, batchSize)
|
||||
if err != nil {
|
||||
slog.Error("数据清理失败", "err", err)
|
||||
return err
|
||||
}
|
||||
// 获取本次删除的行数
|
||||
totalDeleted += deletedCount
|
||||
|
||||
// 无更多数据则退出循环
|
||||
if deletedCount < batchSize {
|
||||
break
|
||||
}
|
||||
|
||||
// 批量删除间隔,降低数据库压力
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
slog.Info("本次数据清理任务执行完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteDirContents 删除目录下的所有文件(保留目录结构,仅删文件)
|
||||
func deleteTaskDirContents(dir string, expireTime time.Time) error {
|
||||
// 遍历目录
|
||||
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||
// 处理遍历过程中的错误(如权限问题)
|
||||
if err != nil {
|
||||
slog.Error("访问路径失败", "path", path, "err", err)
|
||||
return nil
|
||||
}
|
||||
// 只处理目录(跳过文件)
|
||||
if !d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
// 获取当前目录的名称(如 20260117)
|
||||
dirName := filepath.Base(path)
|
||||
dirDate, err := time.Parse(dateDirLayout, dirName)
|
||||
if err != nil {
|
||||
slog.Error("解析目录日期失败", "path", path, "err", err)
|
||||
return nil
|
||||
}
|
||||
// 判断日期目录是否早于阈值(即过期)
|
||||
isExpired := dirDate.Before(expireTime)
|
||||
|
||||
// 非日期目录,继续遍历子目录
|
||||
if dirDate.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 日期目录未过期,跳过
|
||||
if !isExpired {
|
||||
slog.Error("目录日期未过期跳过", "path", path, "dirDate", dirDate.Format(dateDirLayout))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 过期日期目录:先删除目录内所有文件
|
||||
slog.Error("目录已过期开始清理其中文件", "path", path, "dirDate", dirDate.Format(dateDirLayout))
|
||||
if err := deleteDirContents(path, expireTime); err != nil {
|
||||
slog.Error("清理目录内容失败", "path", path, "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除空的日期目录
|
||||
if err := os.Remove(path); err != nil {
|
||||
slog.Error("删除空目录失败(可能非空)", "path", path, "err", err)
|
||||
} else {
|
||||
slog.Info("成功删除过期目录", "path", path)
|
||||
}
|
||||
|
||||
// 跳过已删除目录的子目录遍历(避免无效操作)
|
||||
return fs.SkipDir
|
||||
})
|
||||
if err != nil {
|
||||
slog.Error("遍历目录失败", "dir", dir, "err", err)
|
||||
return fmt.Errorf("遍历目录 [%s] 失败:%w", dir, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func deleteDirContents(dir string, expireTime time.Time) error {
|
||||
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
slog.Error("访问路径失败", "path", path, "err", err)
|
||||
return nil // 跳过错误路径,继续处理
|
||||
}
|
||||
|
||||
// 只删除文件,跳过目录
|
||||
if !d.IsDir() {
|
||||
// 获取文件信息(包含修改时间)
|
||||
fileInfo, err := d.Info()
|
||||
if err != nil {
|
||||
slog.Error("获取文件失败", "path", path, "err", err)
|
||||
return nil
|
||||
}
|
||||
// 判断文件是否过期
|
||||
if fileInfo.ModTime().Before(expireTime) {
|
||||
// 删除过期文件
|
||||
if errs := os.Remove(path); errs != nil {
|
||||
slog.Error("删除文件失败", "path", path, "err", err)
|
||||
} else {
|
||||
slog.Info("成功删除文件", "path", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("遍历目录 [%s] 失败:%w", dir, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanExpiredFiles 清理指定目录下超过expireDays天未修改的文件
|
||||
func (c Core) cleanExpiredFiles() error {
|
||||
// 计算过期时间阈值
|
||||
expireDays := time.Duration(c.Cfg.VqdConfig.SaveDay)
|
||||
if expireDays < 1 {
|
||||
expireDays = 1
|
||||
}
|
||||
expireTime := time.Now().Add(-expireDays * 24 * time.Hour)
|
||||
slog.Info(fmt.Sprintf("开始清理目录 [%s] 中超过 %d 天的文件,过期时间阈值:%s", cleanDir, expireDays, expireTime.Format(time.RFC3339)))
|
||||
|
||||
dir, _ := os.Getwd()
|
||||
rootDir := filepath.Join(dir, cleanDir)
|
||||
dateDirs, err := os.ReadDir(rootDir)
|
||||
if err != nil {
|
||||
slog.Error("访问根目录路径失败", "path", rootDir, "err", err)
|
||||
return nil
|
||||
}
|
||||
for _, d := range dateDirs {
|
||||
// 只处理目录(跳过文件)
|
||||
if !d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
path := filepath.Join(rootDir, d.Name())
|
||||
if err := deleteTaskDirContents(path, expireTime); err != nil {
|
||||
slog.Error("清理目录内容失败", "path", path, "err", err)
|
||||
return nil
|
||||
}
|
||||
// 删除空的日期目录
|
||||
if err := os.Remove(path); err != nil {
|
||||
slog.Error("删除空目录失败(可能非空)", "path", path, "err", err)
|
||||
} else {
|
||||
slog.Info("成功删除过期目录", "path", path)
|
||||
}
|
||||
}
|
||||
|
||||
slog.Info("本次文件清理任务执行完成")
|
||||
return nil
|
||||
}
|
||||
@ -2,6 +2,7 @@ package vqdsdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"easyvqd/internal/conf"
|
||||
"easyvqd/internal/core/host"
|
||||
"easyvqd/internal/core/vqd"
|
||||
"time"
|
||||
@ -10,14 +11,14 @@ import (
|
||||
type Core struct {
|
||||
HostCore *host.Core
|
||||
VqdTaskCore *vqd.Core
|
||||
//WorkflowCore *Workflow
|
||||
Cfg *conf.Bootstrap
|
||||
}
|
||||
|
||||
func NewCore(HostCore *host.Core, VqdTaskCore *vqd.Core) *Core {
|
||||
func NewCore(HostCore *host.Core, VqdTaskCore *vqd.Core, Cfg *conf.Bootstrap) *Core {
|
||||
core := &Core{
|
||||
HostCore: HostCore,
|
||||
VqdTaskCore: VqdTaskCore,
|
||||
//WorkflowCore: OpenVqdTask(VqdTaskCore),
|
||||
Cfg: Cfg,
|
||||
}
|
||||
time.AfterFunc(time.Duration(5)*time.Second, func() {
|
||||
in := &vqd.AddVqdAlarmInput{
|
||||
@ -31,10 +32,13 @@ func NewCore(HostCore *host.Core, VqdTaskCore *vqd.Core) *Core {
|
||||
TaskName: "",
|
||||
FilePath: "",
|
||||
}
|
||||
core.VqdTaskCore.AddVqdAlarm(context.TODO(), in)
|
||||
core.VqdTaskCore.AddVqdAlarm(context.TODO(), in)
|
||||
for i := 0; i < 40; i++ {
|
||||
core.VqdTaskCore.AddVqdAlarm(context.TODO(), in)
|
||||
}
|
||||
|
||||
})
|
||||
// 启用定时清理任务
|
||||
core.scheduleCleanTask()
|
||||
// 启用任务管理器
|
||||
return core
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"easyvqd/internal/core/vqd"
|
||||
"easyvqd/internal/web/api/static"
|
||||
"expvar"
|
||||
"git.lnton.com/lnton/pkg/orm"
|
||||
statics "github.com/gin-contrib/static"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
@ -24,6 +26,12 @@ import (
|
||||
|
||||
var startRuntime = time.Now()
|
||||
|
||||
// recordErr 记录错误
|
||||
func recordErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
func setupRouter(r *gin.Engine, uc *Usecase) {
|
||||
r.Use(
|
||||
// 格式化输出到控制台,然后记录到日志
|
||||
@ -55,7 +63,9 @@ func setupRouter(r *gin.Engine, uc *Usecase) {
|
||||
registerConfig(r, ConfigAPI{uc: uc, cfg: uc.Conf})
|
||||
RegisterHostAPI(r, uc)
|
||||
RegisterVqdTask(r, uc.VqdTaskAPI)
|
||||
|
||||
if !orm.GetEnabledAutoMigrate() {
|
||||
recordErr(InitTemplate(uc))
|
||||
}
|
||||
r.NoRoute(func(ctx *gin.Context) {
|
||||
p := ctx.Request.URL.Path
|
||||
if strings.HasPrefix(p, "/web/") {
|
||||
@ -151,6 +161,105 @@ type KV struct {
|
||||
Value int64
|
||||
}
|
||||
|
||||
func InitTemplate(uc *Usecase) error {
|
||||
cfg := uc.Conf
|
||||
in := vqd.VqdTaskTemplate{
|
||||
Enable: true,
|
||||
IsDefault: true,
|
||||
VqdConfig: vqd.VqdConfig{
|
||||
Enable: true,
|
||||
FrmNum: cfg.VqdConfig.FrmNum,
|
||||
IsDeepLearn: cfg.VqdConfig.IsDeepLearn,
|
||||
},
|
||||
VqdLgtDark: vqd.VqdLgtDark{
|
||||
Enable: true,
|
||||
DarkThr: cfg.VqdLgtDark.DarkThr,
|
||||
LgtThr: cfg.VqdLgtDark.LgtThr,
|
||||
LgtDarkAbnNumRatio: cfg.VqdLgtDark.LgtDarkAbnNumRatio,
|
||||
},
|
||||
VqdBlue: vqd.VqdBlue{
|
||||
Enable: true,
|
||||
BlueThr: cfg.VqdBlue.BlueThr,
|
||||
BlueAbnNumRatio: cfg.VqdBlue.BlueAbnNumRatio,
|
||||
},
|
||||
VqdClarity: vqd.VqdClarity{
|
||||
Enable: true,
|
||||
ClarityThr: cfg.VqdClarity.ClarityThr,
|
||||
ClarityAbnNumRatio: cfg.VqdClarity.ClarityAbnNumRatio,
|
||||
},
|
||||
VqdShark: vqd.VqdShark{
|
||||
Enable: true,
|
||||
SharkThr: cfg.VqdShark.SharkThr,
|
||||
SharkAbnNumRatio: cfg.VqdShark.SharkAbnNumRatio,
|
||||
},
|
||||
VqdFreeze: vqd.VqdFreeze{
|
||||
Enable: true,
|
||||
FreezeThr: cfg.VqdFreeze.FreezeThr,
|
||||
FreezeAbnNumRatio: cfg.VqdFreeze.FreezeAbnNumRatio,
|
||||
},
|
||||
VqdColor: vqd.VqdColor{
|
||||
Enable: true,
|
||||
ColorThr: cfg.VqdColor.ColorThr,
|
||||
ColorAbnNumRatio: cfg.VqdColor.ColorAbnNumRatio,
|
||||
},
|
||||
VqdOcclusion: vqd.VqdOcclusion{
|
||||
Enable: true,
|
||||
OcclusionThr: cfg.VqdOcclusion.OcclusionThr,
|
||||
OcclusionAbnNumRatio: cfg.VqdOcclusion.OcclusionAbnNumRatio,
|
||||
},
|
||||
VqdNoise: vqd.VqdNoise{
|
||||
Enable: true,
|
||||
NoiseThr: cfg.VqdNoise.NoiseThr,
|
||||
NoiseAbnNumRatio: cfg.VqdNoise.NoiseAbnNumRatio,
|
||||
},
|
||||
VqdContrast: vqd.VqdContrast{
|
||||
Enable: true,
|
||||
CtraLowThr: cfg.VqdContrast.CtraLowThr,
|
||||
CtraHighThr: cfg.VqdContrast.CtraHighThr,
|
||||
CtraAbnNumRatio: cfg.VqdContrast.CtraAbnNumRatio,
|
||||
},
|
||||
VqdMosaic: vqd.VqdMosaic{
|
||||
Enable: true,
|
||||
MosaicThr: cfg.VqdMosaic.MosaicThr,
|
||||
MosaicAbnNumRatio: cfg.VqdMosaic.MosaicAbnNumRatio,
|
||||
},
|
||||
VqdFlower: vqd.VqdFlower{
|
||||
Enable: true,
|
||||
FlowerThr: cfg.VqdFlower.FlowerThr,
|
||||
FlowerAbnNumRatio: cfg.VqdFlower.FlowerAbnNumRatio,
|
||||
MosaicThr: cfg.VqdFlower.MosaicThr,
|
||||
},
|
||||
}
|
||||
|
||||
in.Name = "每天"
|
||||
in.Model.ID = 1
|
||||
in.Model.CreatedAt = orm.Time{Time: time.Now()}
|
||||
in.Plans = "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
|
||||
in.Des = "每天分析,启用全部分析模块。"
|
||||
if err := uc.VqdTaskCore.FirstOrCreateTemplate(&in); err != nil {
|
||||
slog.Error("FirstOrCreateTemplate", "err", err)
|
||||
return err
|
||||
}
|
||||
in.Name = "工作日"
|
||||
in.Model.ID = 2
|
||||
in.Model.CreatedAt = orm.Time{Time: time.Now()}
|
||||
in.Plans = "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000000"
|
||||
in.Des = "工作日分析,启用全部分析模块。"
|
||||
if err := uc.VqdTaskCore.FirstOrCreateTemplate(&in); err != nil {
|
||||
slog.Error("FirstOrCreateTemplate", "err", err)
|
||||
return err
|
||||
}
|
||||
in.Name = "双休日"
|
||||
in.Model.ID = 3
|
||||
in.Model.CreatedAt = orm.Time{Time: time.Now()}
|
||||
in.Plans = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111"
|
||||
in.Des = "休息日分析,启用全部分析模块。"
|
||||
if err := uc.VqdTaskCore.FirstOrCreateTemplate(&in); err != nil {
|
||||
slog.Error("FirstOrCreateTemplate", "err", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func sortExpvarMap(data *expvar.Map, top int) []KV {
|
||||
kvs := make([]KV, 0, 8)
|
||||
data.Do(func(kv expvar.KeyValue) {
|
||||
|
||||
@ -27,27 +27,60 @@ func registerConfig(g gin.IRouter, api ConfigAPI, handler ...gin.HandlerFunc) {
|
||||
|
||||
group.GET("/base", web.WarpH(api.getBase))
|
||||
group.PUT("/base", web.WarpH(api.editBase))
|
||||
group.GET("/default", web.WarpH(api.getDefaultConfig))
|
||||
|
||||
}
|
||||
|
||||
type getBaseOutput conf.VqdConfig
|
||||
type editBaseInput conf.VqdConfig
|
||||
type getBaseOutput struct {
|
||||
SaveDay int32 `json:"save_day"`
|
||||
}
|
||||
type editBaseInput struct {
|
||||
SaveDay int32 `json:"save_day"`
|
||||
}
|
||||
type getBaseConfigOutput struct {
|
||||
FrmNum int32 `json:"frm_num"`
|
||||
IsDeepLearn bool `json:"is_deep_learn"`
|
||||
VqdLgtDark conf.VqdLgtDark `json:"vqd_lgt_dark"` // 亮度检测
|
||||
VqdBlue conf.VqdBlue `json:"vqd_blue"` // 蓝屏检查
|
||||
VqdClarity conf.VqdClarity `json:"vqd_clarity"` // 清晰度检查
|
||||
VqdShark conf.VqdShark `json:"vqd_shark"` // 抖动检查
|
||||
VqdFreeze conf.VqdFreeze `json:"vqd_freeze"` // 冻结检测
|
||||
VqdColor conf.VqdColor `json:"vqd_color"` // 偏色检测
|
||||
VqdOcclusion conf.VqdOcclusion `json:"vqd_occlusion"` // 遮挡检测
|
||||
VqdNoise conf.VqdNoise `json:"vqd_noise"` // 噪声检测
|
||||
VqdContrast conf.VqdContrast `json:"vqd_contrast"` // 对比度检测
|
||||
VqdMosaic conf.VqdMosaic `json:"vqd_mosaic"` // 马赛克检测
|
||||
VqdFlower conf.VqdFlower `json:"vqd_flower"` // 花屏检测
|
||||
}
|
||||
|
||||
func (uc *ConfigAPI) editBase(c *gin.Context, in *editBaseInput) (any, error) {
|
||||
uc.cfg.VqdConfig.FrmNum = in.FrmNum
|
||||
uc.cfg.VqdConfig.SaveDay = in.SaveDay
|
||||
uc.cfg.VqdConfig.IsDeepLearn = in.IsDeepLearn
|
||||
conf.WriteConfig(uc.cfg, uc.cfg.ConfigDirPath())
|
||||
return in, nil
|
||||
}
|
||||
func (uc *ConfigAPI) getDefaultConfig(_ *gin.Context, _ *struct{}) (getBaseConfigOutput, error) {
|
||||
return getBaseConfigOutput{
|
||||
FrmNum: uc.cfg.VqdConfig.FrmNum,
|
||||
IsDeepLearn: uc.cfg.VqdConfig.IsDeepLearn,
|
||||
VqdLgtDark: uc.cfg.VqdLgtDark,
|
||||
VqdBlue: uc.cfg.VqdBlue,
|
||||
VqdClarity: uc.cfg.VqdClarity,
|
||||
VqdShark: uc.cfg.VqdShark,
|
||||
VqdFreeze: uc.cfg.VqdFreeze,
|
||||
VqdColor: uc.cfg.VqdColor,
|
||||
VqdOcclusion: uc.cfg.VqdOcclusion,
|
||||
VqdNoise: uc.cfg.VqdNoise,
|
||||
VqdContrast: uc.cfg.VqdContrast,
|
||||
VqdMosaic: uc.cfg.VqdMosaic,
|
||||
VqdFlower: uc.cfg.VqdFlower,
|
||||
}, nil
|
||||
}
|
||||
func (uc *ConfigAPI) getBase(_ *gin.Context, _ *struct{}) (getBaseOutput, error) {
|
||||
confMutex.Lock()
|
||||
defer confMutex.Unlock()
|
||||
|
||||
return getBaseOutput{
|
||||
FrmNum: uc.cfg.VqdConfig.FrmNum,
|
||||
IsDeepLearn: uc.cfg.VqdConfig.IsDeepLearn,
|
||||
SaveDay: uc.cfg.VqdConfig.SaveDay,
|
||||
SaveDay: uc.cfg.VqdConfig.SaveDay,
|
||||
}, nil
|
||||
}
|
||||
func (uc *ConfigAPI) getToml(c *gin.Context) {
|
||||
|
||||
@ -64,6 +64,10 @@ func (a VqdTaskAPI) findVqdTask(c *gin.Context, in *vqd.FindVqdTaskInput) (any,
|
||||
row["channel_name"] = item.ChannelName
|
||||
row["task_template_id"] = item.TaskTemplateID
|
||||
row["task_template_name"] = item.TaskTemplateName
|
||||
template, errs := a.core.GetIDVqdTaskTemplate(c.Request.Context(), item.TaskTemplateID)
|
||||
if errs == nil && template != nil {
|
||||
row["task_template_name"] = template.Name
|
||||
}
|
||||
row["created_at"] = item.CreatedAt
|
||||
row["updated_at"] = item.UpdatedAt
|
||||
|
||||
|
||||
@ -20,6 +20,19 @@ func (a VqdTaskAPI) findVqdTaskTemplate(c *gin.Context, in *vqd.FindVqdTaskTempl
|
||||
row["des"] = item.Des
|
||||
row["plans"] = item.Plans
|
||||
row["enable"] = item.Enable
|
||||
row["is_default"] = item.IsDefault
|
||||
row["vqd_config"] = item.VqdConfig
|
||||
row["vqd_lgt_dark"] = item.VqdLgtDark
|
||||
row["vqd_blue"] = item.VqdBlue
|
||||
row["vqd_clarity"] = item.VqdClarity
|
||||
row["vqd_shark"] = item.VqdShark
|
||||
row["vqd_freeze"] = item.VqdFreeze
|
||||
row["vqd_color"] = item.VqdColor
|
||||
row["vqd_occlusion"] = item.VqdOcclusion
|
||||
row["vqd_noise"] = item.VqdNoise
|
||||
row["vqd_contrast"] = item.VqdContrast
|
||||
row["vqd_mosaic"] = item.VqdMosaic
|
||||
row["vqd_flower"] = item.VqdFlower
|
||||
row["created_at"] = item.CreatedAt
|
||||
row["updated_at"] = item.UpdatedAt
|
||||
|
||||
@ -38,6 +51,19 @@ func (a VqdTaskAPI) getVqdTaskTemplate(c *gin.Context, _ *struct{}) (any, error)
|
||||
|
||||
row["id"] = item.ID
|
||||
row["name"] = item.Name
|
||||
row["is_default"] = item.IsDefault
|
||||
row["vqd_config"] = item.VqdConfig
|
||||
row["vqd_lgt_dark"] = item.VqdLgtDark
|
||||
row["vqd_blue"] = item.VqdBlue
|
||||
row["vqd_clarity"] = item.VqdClarity
|
||||
row["vqd_shark"] = item.VqdShark
|
||||
row["vqd_freeze"] = item.VqdFreeze
|
||||
row["vqd_color"] = item.VqdColor
|
||||
row["vqd_occlusion"] = item.VqdOcclusion
|
||||
row["vqd_noise"] = item.VqdNoise
|
||||
row["vqd_contrast"] = item.VqdContrast
|
||||
row["vqd_mosaic"] = item.VqdMosaic
|
||||
row["vqd_flower"] = item.VqdFlower
|
||||
row["des"] = item.Des
|
||||
row["plans"] = item.Plans
|
||||
row["enable"] = item.Enable
|
||||
@ -71,7 +97,20 @@ func (a VqdTaskAPI) editVqdTaskTemplate(c *gin.Context, in *vqd.EditVqdTaskTempl
|
||||
|
||||
func (a VqdTaskAPI) delVqdTaskTemplate(c *gin.Context, _ *struct{}) (any, error) {
|
||||
ID, _ := strconv.Atoi(c.Param("id"))
|
||||
_, err := a.core.DelVqdTaskTemplate(c.Request.Context(), ID)
|
||||
info, err := a.core.GetVqdTaskTemplate(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`find vqd [%d] err [%s]`, ID, err.Error()))
|
||||
}
|
||||
if info.IsDefault {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`默认模板不支持删除 [%s] `, info.Name))
|
||||
}
|
||||
templateInfo, err := a.core.FindVqdTaskTemplateID(c.Request.Context(), ID)
|
||||
if err == nil {
|
||||
if templateInfo != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`模板关联【%s】任务,需先清理【%s】任务后才能删除模板!`, templateInfo.Name, templateInfo.Name))
|
||||
}
|
||||
}
|
||||
_, err = a.core.DelVqdTaskTemplate(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`del vqd [%d] err [%s]`, ID, err.Error()))
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { GET, PUT } from "./http";
|
||||
import type { UpdateConfigBaseReq, VqdConfigBaseDetailRes } from "../types/config";
|
||||
import type { UpdateConfigBaseReq, VqdConfigBaseDetailRes, VqdConfigDefaultDetailRes } from "../types/config";
|
||||
|
||||
/**
|
||||
* 获取详情
|
||||
@ -7,6 +7,12 @@ import type { UpdateConfigBaseReq, VqdConfigBaseDetailRes } from "../types/confi
|
||||
export async function GetVqdConfigBase() {
|
||||
return await GET<VqdConfigBaseDetailRes>(`/configs/base`);
|
||||
}
|
||||
/**
|
||||
* 获取默认详情
|
||||
*/
|
||||
export async function GetVqdConfigDefault() {
|
||||
return await GET<VqdConfigDefaultDetailRes>(`/configs/default`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
|
||||
@ -31,6 +31,8 @@ export async function GetVqdTaskTemplateById(id: string) {
|
||||
*/
|
||||
export async function UpdateVqdTaskTemplate(data: UpdateVqdTaskTemplateReq) {
|
||||
const { id, ...payload } = data;
|
||||
console.log(data);
|
||||
|
||||
return await PUT<VqdTaskTemplateBaseRes>(`/template/${id}`, payload);
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { forwardRef, useImperativeHandle, useState, useRef } from "react";
|
||||
import { Modal, Form, Input, Radio, Button, message, Space } from "antd";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { Modal, Form, Input, Select, Button, message, Space } from "antd";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { CreateVqdTask, UpdateVqdTask } from "../api/vqdtask";
|
||||
import { GetVqdTaskTemplate } from "../api/vqdtasktemplate";
|
||||
import type { CreateVqdTaskReq, VqdTaskItem } from "../types/vqdtask";
|
||||
import { useGlobal } from "../Context";
|
||||
|
||||
import ChannelModel, { IChannelModelFunc } from "./channel/Channel";
|
||||
|
||||
interface AddVqdTaskProps {
|
||||
title: string;
|
||||
@ -19,12 +20,15 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
|
||||
({ title, onSuccess }, ref) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<boolean>(false);
|
||||
const [channelId, setChannelId] = useState<string>("");
|
||||
const channelRef = useRef<IChannelModelFunc>(null);
|
||||
const [form] = Form.useForm();
|
||||
const { ErrorHandle } = useGlobal();
|
||||
useImperativeHandle(ref, () => ({
|
||||
open: (task?: VqdTaskItem) => {
|
||||
if (task) {
|
||||
setEditing(true);
|
||||
setChannelId(task.channel_id)
|
||||
const formValues = {
|
||||
name: task.name,
|
||||
id: task.id,
|
||||
@ -45,7 +49,29 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
|
||||
setOpen(true);
|
||||
},
|
||||
}));
|
||||
const [pagination, setPagination] = useState({
|
||||
page: 1,
|
||||
size: 999,
|
||||
name: ""
|
||||
});
|
||||
|
||||
// 获取任务列表
|
||||
const {
|
||||
data: storageResponse,
|
||||
isLoading,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ["storage", pagination],
|
||||
queryFn: () =>
|
||||
GetVqdTaskTemplate({ ...pagination })
|
||||
.then((res) => res.data)
|
||||
.catch((err) => {
|
||||
ErrorHandle(err);
|
||||
throw err;
|
||||
}),
|
||||
// refetchInterval: 4000,
|
||||
retry: 1,
|
||||
});
|
||||
const { mutate: createMutate, isPending: creating } = useMutation({
|
||||
mutationFn: CreateVqdTask,
|
||||
onSuccess: () => {
|
||||
@ -69,6 +95,7 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
setEditing(false);
|
||||
setChannelId("");
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
@ -76,7 +103,7 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
|
||||
<Modal
|
||||
style={{ top: '-180px' }} // 距离顶部 80px(可改为 10% 等百分比)
|
||||
title={title}
|
||||
|
||||
destroyOnHidden={true}
|
||||
open={open}
|
||||
onCancel={handleClose}
|
||||
centered
|
||||
@ -134,7 +161,34 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
|
||||
>
|
||||
<Input placeholder="请输入名称" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="channel_id"
|
||||
label="关联通道"
|
||||
rules={[{ required: true, message: "请选择通道" }]}
|
||||
>
|
||||
<Space.Compact style={{ width: '100%' }}>
|
||||
<Input defaultValue="请输入通道" disabled value={channelId} />
|
||||
|
||||
<Button type="primary" onClick={() => {
|
||||
channelRef.current?.openModal(channelId)
|
||||
}}>选择通道</Button>
|
||||
</Space.Compact>
|
||||
</Form.Item>
|
||||
<Form.Item name="task_template_id" label="选择模板" rules={[{ required: true, message: "请选择模板" }]}>
|
||||
<Select
|
||||
placeholder="请选择模板"
|
||||
onChange={(res, item: any) => {
|
||||
form.setFieldsValue({ task_template_name: item?.label });
|
||||
}}
|
||||
options={storageResponse?.items
|
||||
.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))
|
||||
.filter((item) => item.value !== 0)}
|
||||
></Select>
|
||||
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="des"
|
||||
label="描述"
|
||||
@ -146,7 +200,17 @@ const AddVqdTask = forwardRef<AddVqdTaskRef, AddVqdTaskProps>(
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item name="channel_name" hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="task_template_name" hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<ChannelModel ref={channelRef} onCallback={(id: any, name: any) => {
|
||||
form.setFieldsValue({ channel_id: id, channel_name: name });
|
||||
setChannelId(id)
|
||||
}} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,16 +1,77 @@
|
||||
import { forwardRef, useImperativeHandle, useState, useRef } from "react";
|
||||
import { Modal, Form, Input, Radio, Button, message, Space } from "antd";
|
||||
import { forwardRef, useImperativeHandle, useState, useRef, createContext, useContext, useEffect } from "react";
|
||||
import { Modal, Form, Input, InputNumber, Button, message, Row, Col, Card, Flex, Switch, Tabs, FormInstance } from "antd";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { CreateVqdTaskTemplate, UpdateVqdTaskTemplate } from "../api/vqdtasktemplate";
|
||||
import { GetVqdConfigDefault } from "../api/config";
|
||||
import { CreateVqdTaskTemplate, UpdateVqdTaskTemplate, } from "../api/vqdtasktemplate";
|
||||
import type { CreateVqdTaskTemplateReq, VqdTaskTemplateItem } from "../types/vqdtasktemplate";
|
||||
import { useGlobal } from "../Context";
|
||||
|
||||
import type { TabsProps } from 'antd';
|
||||
const week = [
|
||||
'星期一',
|
||||
'星期二',
|
||||
'星期三',
|
||||
'星期四',
|
||||
'星期五',
|
||||
'星期六',
|
||||
'星期日',
|
||||
];
|
||||
|
||||
const hour = [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
||||
22, 23,
|
||||
];
|
||||
|
||||
const gridStyle: React.CSSProperties = {
|
||||
border: '0.1px solid #ccc',
|
||||
textAlign: 'center',
|
||||
lineHeight: '40px',
|
||||
userSelect: 'none',
|
||||
};
|
||||
const titleStyle: React.CSSProperties = {
|
||||
border: '0.1px solid #ccc',
|
||||
width: '90px',
|
||||
textAlign: 'center',
|
||||
lineHeight: '40px',
|
||||
userSelect: 'none',
|
||||
};
|
||||
type PlansSpan = {
|
||||
start: string;
|
||||
end: string;
|
||||
};
|
||||
|
||||
export const emptyList = [
|
||||
Array(24).fill(0),
|
||||
Array(24).fill(0),
|
||||
Array(24).fill(0),
|
||||
Array(24).fill(0),
|
||||
Array(24).fill(0),
|
||||
Array(24).fill(0),
|
||||
Array(24).fill(0),
|
||||
];
|
||||
interface AddVqdTaskTemplateProps {
|
||||
title: string;
|
||||
onSuccess: () => void;
|
||||
}
|
||||
|
||||
interface IAddTemplateContext {
|
||||
editing: boolean;
|
||||
checkList: number[][];
|
||||
form: FormInstance<CreateVqdTaskTemplateReq>;
|
||||
setCheckList: React.Dispatch<React.SetStateAction<number[][]>>;
|
||||
onDelPullDeviceData: (index: number) => void;
|
||||
}
|
||||
const layout = {
|
||||
labelCol: { span: 10 },
|
||||
wrapperCol: { span: 14 },
|
||||
};
|
||||
const AddTemplateContext = createContext<IAddTemplateContext | null>(null);
|
||||
const useAddTemplate = () => {
|
||||
const context = useContext(AddTemplateContext);
|
||||
if (!context) {
|
||||
throw new Error('useAddTemplate must be used within a AddTemplateProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
export interface AddVqdTaskTemplateRef {
|
||||
open: (task?: VqdTaskTemplateItem) => void;
|
||||
}
|
||||
@ -19,8 +80,81 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
|
||||
({ title, onSuccess }, ref) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<boolean>(false);
|
||||
const [checkList, setCheckList] = useState<number[][]>([...emptyList.map((list) => [...list])]);
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const { ErrorHandle } = useGlobal();
|
||||
|
||||
const arrayToString = (arr: number[][]): string => {
|
||||
return arr.map((subArr) => subArr.join('')).join('');
|
||||
}
|
||||
const parsePlans = (value: string | undefined): number[][] => {
|
||||
const result: number[][] = [];
|
||||
if (!value) return result;
|
||||
const binaryArray: number[] = value.split('').map(Number);
|
||||
while (binaryArray.length) {
|
||||
result.push(binaryArray.splice(0, 24));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const onChange = (key: string) => {
|
||||
console.log(key);
|
||||
};
|
||||
|
||||
const itemsTabs: TabsProps['items'] = [
|
||||
{
|
||||
key: '1',
|
||||
forceRender: true,
|
||||
label: '诊断参数',
|
||||
children: <TemplateConfig />
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
forceRender: true,
|
||||
label: '诊断时间',
|
||||
children: <TemplatePlans />
|
||||
},
|
||||
];
|
||||
const { mutate: getVqdConfigDefault } = useMutation({
|
||||
mutationFn: GetVqdConfigDefault,
|
||||
onSuccess: (res) => {
|
||||
const formValues = {
|
||||
vqd_config: {
|
||||
enable: false,
|
||||
frm_num: res.data.frm_num,
|
||||
is_deep_learn: res.data.is_deep_learn,
|
||||
},
|
||||
enable: true,
|
||||
plans: '',
|
||||
des: '',
|
||||
name: '',
|
||||
vqd_lgt_dark: res.data.vqd_lgt_dark,
|
||||
vqd_blue: res.data.vqd_blue,
|
||||
vqd_clarity: res.data.vqd_clarity,
|
||||
vqd_shark: res.data.vqd_shark,
|
||||
vqd_freeze: res.data.vqd_freeze,
|
||||
vqd_color: res.data.vqd_color,
|
||||
vqd_occlusion: res.data.vqd_occlusion,
|
||||
vqd_noise: res.data.vqd_noise,
|
||||
vqd_contrast: res.data.vqd_contrast,
|
||||
vqd_mosaic: res.data.vqd_mosaic,
|
||||
vqd_flower: res.data.vqd_flower,
|
||||
};
|
||||
formValues.vqd_lgt_dark.enable = false
|
||||
formValues.vqd_blue.enable = false
|
||||
formValues.vqd_clarity.enable = false
|
||||
formValues.vqd_shark.enable = false
|
||||
formValues.vqd_freeze.enable = false
|
||||
formValues.vqd_color.enable = false
|
||||
formValues.vqd_occlusion.enable = false
|
||||
formValues.vqd_noise.enable = false
|
||||
formValues.vqd_contrast.enable = false
|
||||
formValues.vqd_mosaic.enable = false
|
||||
formValues.vqd_flower.enable = false
|
||||
form.setFieldsValue(formValues);
|
||||
},
|
||||
onError: ErrorHandle,
|
||||
});
|
||||
useImperativeHandle(ref, () => ({
|
||||
open: (task?: VqdTaskTemplateItem) => {
|
||||
if (task) {
|
||||
@ -30,14 +164,29 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
|
||||
id: task.id,
|
||||
plans: task.plans,
|
||||
enable: task.enable,
|
||||
des: task.des,
|
||||
vqd_config: task.vqd_config,
|
||||
vqd_lgt_dark: task.vqd_lgt_dark,
|
||||
vqd_blue: task.vqd_blue,
|
||||
vqd_clarity: task.vqd_clarity,
|
||||
vqd_shark: task.vqd_shark,
|
||||
vqd_freeze: task.vqd_freeze,
|
||||
vqd_color: task.vqd_color,
|
||||
vqd_occlusion: task.vqd_occlusion,
|
||||
vqd_noise: task.vqd_noise,
|
||||
vqd_contrast: task.vqd_contrast,
|
||||
vqd_mosaic: task.vqd_mosaic,
|
||||
vqd_flower: task.vqd_flower,
|
||||
};
|
||||
setCheckList(parsePlans(task.plans))
|
||||
form.setFieldsValue(formValues);
|
||||
} else {
|
||||
getVqdConfigDefault()
|
||||
setEditing(false);
|
||||
form.resetFields();
|
||||
// form.setFieldsValue({
|
||||
// bid: "2",
|
||||
// });
|
||||
form.setFieldsValue({
|
||||
|
||||
});
|
||||
}
|
||||
setOpen(true);
|
||||
},
|
||||
@ -68,77 +217,471 @@ const AddVqdTaskTemplate = forwardRef<AddVqdTaskTemplateRef, AddVqdTaskTemplateP
|
||||
setEditing(false);
|
||||
form.resetFields();
|
||||
};
|
||||
const onDelPullDeviceData = (index: number) => {
|
||||
|
||||
}
|
||||
return (
|
||||
<Modal
|
||||
style={{ top: '-180px' }} // 距离顶部 80px(可改为 10% 等百分比)
|
||||
title={title}
|
||||
|
||||
open={open}
|
||||
onCancel={handleClose}
|
||||
centered
|
||||
onOk={() => form.submit()}
|
||||
confirmLoading={creating || updating}
|
||||
<AddTemplateContext.Provider
|
||||
value={{
|
||||
form,
|
||||
checkList,
|
||||
editing,
|
||||
setCheckList,
|
||||
onDelPullDeviceData
|
||||
}}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={(values) => {
|
||||
if (creating || updating) return
|
||||
console.log(values);
|
||||
|
||||
const {
|
||||
name,
|
||||
des,
|
||||
plans,
|
||||
enable } = values as {
|
||||
name: string;
|
||||
des: string;
|
||||
id?: number;
|
||||
plans: string;
|
||||
|
||||
enable: boolean;
|
||||
};
|
||||
const payload: CreateVqdTaskTemplateReq = {
|
||||
name,
|
||||
des,
|
||||
plans,
|
||||
enable,
|
||||
};
|
||||
|
||||
if (editing) {
|
||||
const id = (values as any).id;
|
||||
updateMutate({ id: String(id), ...payload });
|
||||
} else {
|
||||
createMutate(payload);
|
||||
}
|
||||
}}
|
||||
<Modal
|
||||
// style={{ top: '-180px' }} // 距离顶部 80px(可改为 10% 等百分比)
|
||||
title={title}
|
||||
width={"64%"}
|
||||
open={open}
|
||||
onCancel={handleClose}
|
||||
destroyOnHidden={true}
|
||||
centered
|
||||
onOk={() => form.submit()}
|
||||
confirmLoading={creating || updating}
|
||||
>
|
||||
|
||||
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="名称"
|
||||
rules={[{ required: true, message: "请输入名称" }]}
|
||||
<Form
|
||||
form={form}
|
||||
{...layout}
|
||||
onFinish={(values: CreateVqdTaskTemplateReq) => {
|
||||
if (creating || updating) return
|
||||
const payload = values as CreateVqdTaskTemplateReq;
|
||||
payload.plans = arrayToString(checkList)
|
||||
console.log(payload);
|
||||
if (editing) {
|
||||
const id = (values as any).id;
|
||||
updateMutate({ id: String(id), ...payload });
|
||||
} else {
|
||||
createMutate(payload);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Input placeholder="请输入名称" />
|
||||
</Form.Item>
|
||||
<br />
|
||||
<Flex gap="large" >
|
||||
|
||||
<Form.Item
|
||||
name="des"
|
||||
label="描述"
|
||||
>
|
||||
<Input placeholder="请输入描述" />
|
||||
</Form.Item>
|
||||
{editing && (
|
||||
<Form.Item name="id" hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
</Modal>
|
||||
<Form.Item
|
||||
style={{ width: '30%' }}
|
||||
name="name"
|
||||
label="模板名称"
|
||||
rules={[{ required: true, message: "请输入模板名称" }]}
|
||||
>
|
||||
<Input placeholder="请输入名称" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
style={{ width: '30%' }}
|
||||
name="des"
|
||||
label="描述"
|
||||
>
|
||||
<Input placeholder="请输入描述" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
style={{ width: '20%' }}
|
||||
name="enable"
|
||||
label="启用"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
|
||||
<Tabs defaultActiveKey="1" items={itemsTabs} onChange={onChange} />
|
||||
|
||||
|
||||
{editing && (
|
||||
<Form.Item name="id" hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
</Modal>
|
||||
</AddTemplateContext.Provider>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default AddVqdTaskTemplate;
|
||||
|
||||
|
||||
|
||||
|
||||
const TemplatePlans: React.FC = () => {
|
||||
const {
|
||||
checkList,
|
||||
editing,
|
||||
setCheckList,
|
||||
form,
|
||||
} = useAddTemplate();
|
||||
// 开始滑动选择
|
||||
const [config, setConfig] = useState<{
|
||||
start: boolean;
|
||||
}>({ start: false });
|
||||
|
||||
const selector = useRef<boolean>(true);
|
||||
|
||||
const [coping, setCoping] = useState<
|
||||
{ index: number; value: number[] } | undefined
|
||||
>();
|
||||
|
||||
const handler = (list: number[]) => {
|
||||
let out: PlansSpan[] = [];
|
||||
let start = false;
|
||||
let timeParam: PlansSpan = { start: '', end: '' };
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const v = list[i];
|
||||
|
||||
let s = i.toString().padStart(2, '0');
|
||||
if (i == 23 && v == 1) {
|
||||
s = '24';
|
||||
}
|
||||
|
||||
if (!start && v == 1) {
|
||||
start = true;
|
||||
timeParam = { start: `${i}:00`, end: '' };
|
||||
}
|
||||
|
||||
if (start && (v == 0 || i == list.length - 1)) {
|
||||
start = false;
|
||||
timeParam.end = `${s}:00`;
|
||||
out.push(timeParam);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
return <>
|
||||
<div style={{ border: '1px solid #999' }}>
|
||||
<Row wrap={false}>
|
||||
<div
|
||||
style={{
|
||||
...titleStyle,
|
||||
}}
|
||||
>
|
||||
Week/Time
|
||||
</div>
|
||||
<Row wrap={false} style={{ width: '86.5%' }}>
|
||||
{[...hour].map((v, idx) => {
|
||||
return (
|
||||
<Col key={v} span={1} style={gridStyle}>
|
||||
{v}
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</Row>
|
||||
{week.map((v, weekIdx) => {
|
||||
return (
|
||||
<Row
|
||||
key={v}
|
||||
onMouseDown={() => setConfig({ ...config, start: true })}
|
||||
onMouseUp={() => setConfig({ ...config, start: false })}
|
||||
wrap={false}
|
||||
>
|
||||
<div style={titleStyle}>{v}</div>
|
||||
<Row key={v + '1'} wrap={false} style={{ width: '86.5%' }}>
|
||||
{[...hour].map((v, hourIdx) => {
|
||||
// 时间选择
|
||||
return (
|
||||
<Col
|
||||
onMouseOver={() => {
|
||||
if (!config.start) return;
|
||||
setCheckList((v) => {
|
||||
let list = [...v];
|
||||
list[weekIdx][hourIdx] = selector.current ? 1 : 0;
|
||||
return list;
|
||||
});
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
selector.current = checkList[weekIdx][hourIdx] == 0;
|
||||
setCheckList((v) => {
|
||||
let list = [...v];
|
||||
list[weekIdx][hourIdx] = selector.current ? 1 : 0;
|
||||
return list;
|
||||
});
|
||||
}}
|
||||
key={v}
|
||||
span={1}
|
||||
style={{
|
||||
...gridStyle,
|
||||
backgroundColor:
|
||||
checkList[weekIdx][hourIdx] == 1
|
||||
? '#658EE0'
|
||||
: 'white',
|
||||
}}
|
||||
></Col>
|
||||
);
|
||||
})}
|
||||
<div
|
||||
style={{
|
||||
margin: 'auto',
|
||||
marginLeft: '2px',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type={coping?.index == weekIdx ? 'primary' : 'default'}
|
||||
size="small"
|
||||
style={{ padding: '1px 5px', fontSize: '12px' }}
|
||||
onClick={() => {
|
||||
if (!coping) {
|
||||
setCoping({
|
||||
index: weekIdx,
|
||||
value: checkList[weekIdx],
|
||||
});
|
||||
return;
|
||||
}
|
||||
setCheckList((v) => {
|
||||
let list = [...v];
|
||||
list[weekIdx] = [...coping.value];
|
||||
return list;
|
||||
});
|
||||
setCoping(undefined);
|
||||
}}
|
||||
>
|
||||
{!coping || coping?.index == weekIdx ? "复制" : "粘贴"}
|
||||
</Button>
|
||||
</div>
|
||||
</Row>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div style={{ height: '20px' }}></div>
|
||||
|
||||
<Card>
|
||||
<div>
|
||||
<span>时间段</span>
|
||||
</div>
|
||||
<div>
|
||||
{week.map((v, weekIdx) => {
|
||||
return (
|
||||
// 周
|
||||
<Row wrap={false} key={v}>
|
||||
<div style={{ color: '#9A9A9A', paddingRight: '5px' }}>
|
||||
<span>{v}</span> :
|
||||
</div>
|
||||
<Row wrap={false}>
|
||||
{handler(checkList[weekIdx]).map((v, idx, arr) => {
|
||||
// 已选时间段
|
||||
return (
|
||||
<Col key={`${v.start}~${v.end}_${idx}_${weekIdx}`}>
|
||||
{`${v.start}~${v.end}` +
|
||||
(idx < arr.length - 1 ? ',' : '')}
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
}
|
||||
const BoxInputNumber: React.FC<{
|
||||
parent: string;
|
||||
children: string;
|
||||
defaultValue: number
|
||||
labelName: string;
|
||||
}> = ({ parent, children, defaultValue, labelName }) => {
|
||||
return (
|
||||
<Form.Item
|
||||
className="mb-1"
|
||||
name={[parent, children]}
|
||||
label={labelName}
|
||||
normalize={(value) => {
|
||||
return value ? parseFloat(value) : defaultValue;
|
||||
}}
|
||||
>
|
||||
<InputNumber
|
||||
size="small"
|
||||
defaultValue={defaultValue}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.1}
|
||||
stringMode
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
const TemplateConfig: React.FC = () => {
|
||||
const {
|
||||
editing,
|
||||
form,
|
||||
} = useAddTemplate();
|
||||
|
||||
const handleValuesChange = (value: string) => {
|
||||
if (value) {
|
||||
// form.setFieldsValue({ });
|
||||
}
|
||||
};
|
||||
return <>
|
||||
<Flex gap="large" wrap justify="space-between">
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade" >
|
||||
<h4>基础配置</h4>
|
||||
<Form.Item
|
||||
className="mb-0"
|
||||
name={['vqd_config', 'enable']}
|
||||
>
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<Form.Item
|
||||
className="mb-1"
|
||||
name={['vqd_config', 'is_deep_learn']}
|
||||
label="深度学习"
|
||||
>
|
||||
<Switch size="small" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
className="mb-1"
|
||||
name={['vqd_config', 'frm_num']}
|
||||
label="连续分析帧数"
|
||||
>
|
||||
<InputNumber min={2} max={64} defaultValue={10} size="small" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Flex>
|
||||
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>蓝屏检查</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_blue', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_blue" children="blue_thr" defaultValue={0.6} labelName="判断阈值" />
|
||||
<BoxInputNumber parent="vqd_blue" children="blue_abn_num_ratio" defaultValue={0.5} labelName="次数比例" />
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>清晰度检查</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_clarity', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_clarity" children="clarity_thr" defaultValue={0.4} labelName="判断阈值" />
|
||||
<BoxInputNumber parent="vqd_clarity" children="clarity_abn_num_ratio" defaultValue={0.5} labelName="次数比例" />
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>抖动检查</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_shark', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_shark" children="shark_thr" defaultValue={0.2} labelName="阈值参数" />
|
||||
<BoxInputNumber parent="vqd_shark" children="shark_abn_num_ratio" defaultValue={0.2} labelName="次数比例" />
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>冻结检测</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_freeze', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_freeze" children="freeze_thr" defaultValue={0.4} labelName="阈值参数" />
|
||||
<BoxInputNumber parent="vqd_freeze" children="freeze_abn_num_ratio" defaultValue={0.9} labelName="次数比例" />
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>偏色检测</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_color', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_color" children="color_thr" defaultValue={0.2} labelName="偏色判断值" />
|
||||
<BoxInputNumber parent="vqd_color" children="color_abn_num_ratio" defaultValue={0.5} labelName="次数比例" />
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>遮挡检测</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_occlusion', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_occlusion" children="occlusion_thr" defaultValue={0.1} labelName="判断阈值" />
|
||||
<BoxInputNumber parent="vqd_occlusion" children="occlusion_abn_num_ratio" defaultValue={0.5} labelName="次数比例" />
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>马赛克检测</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_mosaic', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_mosaic" children="mosaic_thr" defaultValue={0.1} labelName="阈值参数" />
|
||||
<BoxInputNumber parent="vqd_mosaic" children="mosaic_abn_num_ratio" defaultValue={0.5} labelName="次数比例" />
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>噪声检测</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_noise', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_noise" children="noise_thr" defaultValue={0.3} labelName="过暗阈值" />
|
||||
<BoxInputNumber parent="vqd_noise" children="noise_abn_num_ratio" defaultValue={0.6} labelName="次数比例" />
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>对比度检测</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_contrast', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_contrast" children="ctra_low_thr" defaultValue={0.2} labelName="低判断阈值" />
|
||||
<BoxInputNumber parent="vqd_contrast" children="ctra_high_thr" defaultValue={0.8} labelName="高判断阈值" />
|
||||
<BoxInputNumber parent="vqd_contrast" children="ctra_abn_num_ratio" defaultValue={0.5} labelName="次数比例" />
|
||||
</div>
|
||||
</Flex>
|
||||
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>亮度检测</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_lgt_dark', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_lgt_dark" children="dark_thr" defaultValue={0.4} labelName="过暗阈值" />
|
||||
<BoxInputNumber parent="vqd_lgt_dark" children="lgt_thr" defaultValue={0.1} labelName="过亮阈值" />
|
||||
<BoxInputNumber parent="vqd_lgt_dark" children="lgt_dark_abn_num_ratio" defaultValue={0.5} labelName="次数比例" />
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex vertical className="config-box">
|
||||
<Flex justify="space-between" align="center" className="config-box-hade">
|
||||
<h4>花屏检测</h4>
|
||||
<Form.Item className="mb-0" name={['vqd_flower', 'enable']} >
|
||||
<Switch className="ml-1" size="small" />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<div className="config-box-item">
|
||||
<BoxInputNumber parent="vqd_flower" children="flower_thr" defaultValue={0.3} labelName="阈值参数" />
|
||||
<BoxInputNumber parent="vqd_flower" children="flower_abn_num_ratio" defaultValue={0.6} labelName="过亮阈值" />
|
||||
<BoxInputNumber parent="vqd_flower" children="mosaic_thr" defaultValue={0.3} labelName="马赛克阈值参数" />
|
||||
</div>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
</>
|
||||
}
|
||||
34
web/src/components/Snap.tsx
Normal file
34
web/src/components/Snap.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
// import {
|
||||
// FindAlarmSnapshot,
|
||||
// } from '@/service/http/aiserver';
|
||||
import { Image } from 'antd';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
interface ISnapshot {
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
const Snapshot: React.FC<ISnapshot> = ({
|
||||
filePath,
|
||||
}) => {
|
||||
// const [base64, setBase64] = useState("");
|
||||
// useEffect(() => {
|
||||
// if (filePath != "") {
|
||||
// FindAlarmSnapshot(filePath).then(data => {
|
||||
// setBase64(data.data.data)
|
||||
// })
|
||||
// }
|
||||
// }, [filePath]);
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
width="100%"
|
||||
height="130px"
|
||||
fallback={'./assets/img/noImg.png'}
|
||||
src={filePath || './assets/img/noImg.png'}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Snapshot;
|
||||
@ -1,15 +1,16 @@
|
||||
import { useRef, useState, useMemo } from "react";
|
||||
import { Table, Button, Space, Popconfirm, Flex, message, Tooltip } from "antd";
|
||||
import { Table, Button, Space, Popconfirm, Flex, message, Tooltip, Select, Row, Col, Empty, Pagination, Tag } from "antd";
|
||||
import { EditOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { GetVqdAlarm, DeleteVqdAlarm, DeleteVqdAlarmAll } from "../api/vqdalarm";
|
||||
import type { VqdAlarmItem } from "../types/vqdalarm";
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
import ChannelModel, { IChannelModelFunc } from "./channel/Channel";
|
||||
import { useGlobal } from "../Context";
|
||||
import { FormatFileSizeToString } from "../utils/rate";
|
||||
import { formatSecondsToHMS } from "../utils/time";
|
||||
import Filter from "./Filter";
|
||||
import AlarmSnap from './snap';
|
||||
const variants = ['filled'] as const;
|
||||
export default function VqdAlarmPage() {
|
||||
const { ErrorHandle } = useGlobal();
|
||||
const [pagination, setPagination] = useState({
|
||||
@ -17,7 +18,8 @@ export default function VqdAlarmPage() {
|
||||
size: 10,
|
||||
name: ""
|
||||
});
|
||||
|
||||
const [arrList, setArrList] = useState<any>([{ name: "全部类型", id: 0 }]);
|
||||
const [templateData, setTemplateData] = useState<VqdAlarmItem[]>([]);
|
||||
// 获取任务列表
|
||||
const {
|
||||
data: storageResponse,
|
||||
@ -55,6 +57,13 @@ export default function VqdAlarmPage() {
|
||||
});
|
||||
|
||||
// 处理分页变化
|
||||
const onAlarmPageChange = (page: number, pageSize?: number) => {
|
||||
setPagination((prev) => ({
|
||||
...prev,
|
||||
page: page,
|
||||
size: pageSize || prev.size,
|
||||
}));
|
||||
}
|
||||
const handleTableChange = (page: number, pageSize?: number) => {
|
||||
setPagination((prev) => ({
|
||||
...prev,
|
||||
@ -80,7 +89,7 @@ export default function VqdAlarmPage() {
|
||||
setSelectedRowKeys([...newSelectedRowKeys]);
|
||||
},
|
||||
};
|
||||
// 批量删除任务
|
||||
// 批量删除任务
|
||||
const { mutate: deleteMutationAll, isPending: delAllLoadings } = useMutation({
|
||||
mutationFn: DeleteVqdAlarmAll,
|
||||
onSuccess: () => {
|
||||
@ -129,7 +138,7 @@ export default function VqdAlarmPage() {
|
||||
|
||||
|
||||
<Popconfirm
|
||||
title="确定要删除这个文件吗?"
|
||||
title="确定要删除这个告警吗?"
|
||||
onConfirm={() => {
|
||||
if (record.id) {
|
||||
deleteMutation(record.id);
|
||||
@ -151,7 +160,7 @@ export default function VqdAlarmPage() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Flex justify="space-between" align="center" className="mb-4">
|
||||
{/* <Flex justify="space-between" align="center" className="mb-4">
|
||||
<Space>
|
||||
|
||||
<Popconfirm
|
||||
@ -176,9 +185,9 @@ export default function VqdAlarmPage() {
|
||||
setPagination({ ...pagination, name: value });
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex> */}
|
||||
{/* 表格 */}
|
||||
<Table
|
||||
{/* <Table
|
||||
columns={columns}
|
||||
rowSelection={rowSelection}
|
||||
dataSource={storageResponse?.items}
|
||||
@ -195,7 +204,109 @@ export default function VqdAlarmPage() {
|
||||
onChange: handleTableChange,
|
||||
onShowSizeChange: handleTableChange,
|
||||
}}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<Flex justify="flex-end" className="mr-2" align="center">
|
||||
{/* <Space className="pl-2">
|
||||
|
||||
<Popconfirm
|
||||
title="确定要批量删除文件吗?"
|
||||
|
||||
onConfirm={() => {
|
||||
deleteMutationAll({ ids: selectedRowKeys as number[] })
|
||||
}}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<Button color="danger" variant="solid" loading={delAllLoadings} disabled={selectedRowKeys.length == 0} >
|
||||
批量删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
|
||||
</Space> */}
|
||||
{/* <DatePicker
|
||||
defaultDate={moment().format('YYYY-MM-DD')}
|
||||
moonData={moonReq}
|
||||
onChangePanel={handleSelectMoon}
|
||||
onSelectDate={handleSelectDate}
|
||||
loading={moonLoading}
|
||||
/> */}
|
||||
<div className="w-[150px] ml-[20px]">
|
||||
<Select
|
||||
defaultValue={0}
|
||||
className="w-[100%] mr-2"
|
||||
placeholder="选择类型"
|
||||
onChange={(v) => {
|
||||
console.log("类型", v);
|
||||
|
||||
}
|
||||
}
|
||||
options={arrList.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
})}
|
||||
optionRender={(option) => (
|
||||
<Space>
|
||||
<span>
|
||||
{option.data.label}
|
||||
</span>
|
||||
</Space>
|
||||
)}
|
||||
/>
|
||||
<Filter
|
||||
|
||||
searchLoading={isLoading}
|
||||
onSearchChange={(value: string) => {
|
||||
setPagination({ ...pagination, name: value });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Flex>
|
||||
<br />
|
||||
<Row gutter={[16, 16]} className="pl-2 pr-2">
|
||||
{storageResponse?.items.map((item) => {
|
||||
return (
|
||||
<Col xxl={6} xl={8} lg={12} md={12} sm={12} xs={24} key={item.id} >
|
||||
<div className="overflow-hidden relative rounded-lg h-full w-full cursor-pointer shadow">
|
||||
<AlarmSnap filePath={item.file_path} />
|
||||
<div className="pl-3 pr-2 pb-1">
|
||||
|
||||
<Flex justify="space-between" align="center">
|
||||
<Tag bordered={false} color="#87d068" className="m-0 mt-2">{item.alarm_name}</Tag>
|
||||
<Space className="pr-2"> 通道:{item.channel_name||"2222"}</Space>
|
||||
</Flex>
|
||||
<Flex justify="space-between" align="center">
|
||||
<p className="m-0"> {item.created_at}</p>
|
||||
<Tooltip title="删除">
|
||||
<Popconfirm
|
||||
title="删除告警"
|
||||
description="确定要删除此告警吗?"
|
||||
onConfirm={() => {
|
||||
// delAlarmSnapshot([item.id])
|
||||
}}
|
||||
>
|
||||
{/* loading={isDelLoading} */}
|
||||
<Button type="text" danger icon={<DeleteOutlined />}></Button>
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
{storageResponse?.items.length === 0 && (
|
||||
<Col span={24}>
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
<Flex justify="flex-end" className="mr-[20px] mt-6 mb-3" align="center">
|
||||
<Pagination onChange={onAlarmPageChange} defaultCurrent={pagination.page} defaultPageSize={pagination.size} total={storageResponse?.total} />
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -25,10 +25,7 @@ export default function VqdTaskPage() {
|
||||
queryFn: () =>
|
||||
GetVqdConfigBase()
|
||||
.then((res) => {
|
||||
console.log(res.data);
|
||||
const formValues = {
|
||||
frm_num: res.data.frm_num,
|
||||
is_deep_learn: res.data.is_deep_learn,
|
||||
save_day: res.data.save_day,
|
||||
};
|
||||
form.setFieldsValue(formValues);
|
||||
@ -51,17 +48,11 @@ export default function VqdTaskPage() {
|
||||
|
||||
const {
|
||||
save_day,
|
||||
frm_num,
|
||||
is_deep_learn
|
||||
} = values as {
|
||||
save_day: number;
|
||||
frm_num: number;
|
||||
is_deep_learn: boolean;
|
||||
};
|
||||
const payload: UpdateConfigBaseReq = {
|
||||
save_day,
|
||||
frm_num,
|
||||
is_deep_learn,
|
||||
};
|
||||
|
||||
const id = (values as any).id;
|
||||
@ -71,12 +62,12 @@ export default function VqdTaskPage() {
|
||||
<Form.Item name="save_day" label="数据保存天数" rules={[{ required: true, message: "请输入数据保存天数" }]}>
|
||||
<InputNumber min={1} defaultValue={3} />
|
||||
</Form.Item>
|
||||
<Form.Item name="frm_num" label="连续分析帧数(2-64), 默认为10, 最大为 64" rules={[{ required: true, message: "请输入连续分析帧数" }]}>
|
||||
{/* <Form.Item name="frm_num" label="连续分析帧数(2-64), 默认为10, 最大为 64" rules={[{ required: true, message: "请输入连续分析帧数" }]}>
|
||||
<InputNumber max={64} min={2} defaultValue={10} />
|
||||
</Form.Item>
|
||||
<Form.Item label="是否使用深度学习版本" name="is_deep_learn">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Form.Item> */}
|
||||
<Button type="primary" onClick={handleSave}>
|
||||
保存
|
||||
</Button>
|
||||
|
||||
@ -6,7 +6,6 @@ import { GetVqdTask, DeleteVqdTask } from "../api/vqdtask";
|
||||
import type { VqdTaskItem } from "../types/vqdtask";
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
import AddVqdTask, { AddVqdTaskRef } from "./AddVqdTask";
|
||||
import ChannelModel, { IChannelModelFunc } from "./channel/Channel";
|
||||
import { useGlobal } from "../Context";
|
||||
import { FormatFileSizeToString } from "../utils/rate";
|
||||
import { formatSecondsToHMS } from "../utils/time";
|
||||
@ -100,8 +99,18 @@ export default function VqdTaskPage() {
|
||||
align: "center",
|
||||
},
|
||||
{
|
||||
title: "文件名称",
|
||||
dataIndex: "file_name",
|
||||
title: "关联通道",
|
||||
dataIndex: "channel_name",
|
||||
align: "center",
|
||||
render: (text, record) => (
|
||||
<Space>
|
||||
{text}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "模板",
|
||||
dataIndex: "task_template_name",
|
||||
align: "center",
|
||||
render: (text, record) => (
|
||||
<Space>
|
||||
@ -132,7 +141,7 @@ export default function VqdTaskPage() {
|
||||
<Button icon={<EditOutlined />} onClick={() => handleEdit(record)} />
|
||||
</Tooltip>
|
||||
<Popconfirm
|
||||
title="确定要删除这个文件吗?"
|
||||
title="确定要删除这个任务吗?"
|
||||
onConfirm={() => {
|
||||
if (record.id) {
|
||||
deleteMutation(record.id);
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { useRef, useState, useMemo } from "react";
|
||||
import { Table, Button, Space, Popconfirm, Flex, message, Tooltip } from "antd";
|
||||
import { Table, Button, Space, Popconfirm, Flex, message, Tooltip, Switch, Tag } from "antd";
|
||||
import { EditOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { GetVqdTaskTemplate, DeleteVqdTaskTemplate } from "../api/vqdtasktemplate";
|
||||
import type { VqdTaskTemplateItem } from "../types/vqdtasktemplate";
|
||||
import { GetVqdTaskTemplate, DeleteVqdTaskTemplate, UpdateVqdTaskTemplate } from "../api/vqdtasktemplate";
|
||||
import type { VqdTaskTemplateItem, CreateVqdTaskTemplateReq } from "../types/vqdtasktemplate";
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
import AddVqdTaskTemplate, { AddVqdTaskTemplateRef } from "./AddVqdTaskTemplate";
|
||||
import { useGlobal } from "../Context";
|
||||
@ -64,6 +64,11 @@ export default function VqdTaskTemplatePage() {
|
||||
const handleEdit = (disk: VqdTaskTemplateItem) => {
|
||||
dialogRef.current?.open(disk);
|
||||
};
|
||||
const saveTemplate = (disk: VqdTaskTemplateItem, t: boolean) => {
|
||||
const payload = disk as CreateVqdTaskTemplateReq;
|
||||
payload.enable = t
|
||||
updateMutate({ id: String(disk.id), ...payload })
|
||||
};
|
||||
|
||||
// 处理分页变化
|
||||
const handleTableChange = (page: number, pageSize?: number) => {
|
||||
@ -91,6 +96,14 @@ export default function VqdTaskTemplatePage() {
|
||||
setSelectedRowKeys([...newSelectedRowKeys]);
|
||||
},
|
||||
};
|
||||
const { mutate: updateMutate } = useMutation({
|
||||
mutationFn: UpdateVqdTaskTemplate,
|
||||
onSuccess: () => {
|
||||
message.success("更新成功");
|
||||
refetch()
|
||||
},
|
||||
onError: ErrorHandle,
|
||||
});
|
||||
// 表格列定义
|
||||
const columns: ColumnsType<VqdTaskTemplateItem> = [
|
||||
{
|
||||
@ -99,26 +112,54 @@ export default function VqdTaskTemplatePage() {
|
||||
align: "center",
|
||||
},
|
||||
{
|
||||
title: "文件名称",
|
||||
dataIndex: "file_name",
|
||||
title: "启用",
|
||||
dataIndex: "enable",
|
||||
align: "center",
|
||||
render: (text, record) => (
|
||||
<Space>
|
||||
{text}
|
||||
<Switch value={text} defaultChecked onChange={(t) => {
|
||||
saveTemplate(record, t)
|
||||
}} />
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "诊断模块",
|
||||
align: "center",
|
||||
width: 550,
|
||||
dataIndex: "vqd_lgt_dark",
|
||||
render: (text, record) => (
|
||||
<Flex gap="small" align="center" wrap>
|
||||
{record?.vqd_lgt_dark?.enable && <Tag bordered={false} color="green" >亮度检测</Tag>}
|
||||
{record?.vqd_blue?.enable && <Tag bordered={false} color="green" >蓝屏检查</Tag>}
|
||||
{record?.vqd_clarity?.enable && <Tag bordered={false} color="green" >清晰度检查</Tag>}
|
||||
{record?.vqd_shark?.enable && <Tag bordered={false} color="green" >抖动检查</Tag>}
|
||||
{record?.vqd_freeze?.enable && <Tag bordered={false} color="green" >冻结检测</Tag>}
|
||||
{record?.vqd_color?.enable && <Tag bordered={false} color="green" >偏色检测</Tag>}
|
||||
{record?.vqd_occlusion?.enable && <Tag bordered={false} color="green" >遮挡检测</Tag>}
|
||||
{record?.vqd_noise?.enable && <Tag bordered={false} color="green" >噪声检测</Tag>}
|
||||
{record?.vqd_contrast?.enable && <Tag bordered={false} color="green" >对比度检测</Tag>}
|
||||
{record?.vqd_mosaic?.enable && <Tag bordered={false} color="green" >马赛克检测</Tag>}
|
||||
{record?.vqd_flower?.enable && <Tag bordered={false} color="green" >花屏检测</Tag>}
|
||||
</Flex>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "描述",
|
||||
dataIndex: "des",
|
||||
align: "center",
|
||||
},
|
||||
{
|
||||
title: "创建日期",
|
||||
dataIndex: "created_at",
|
||||
align: "center",
|
||||
render: (text: string) => (text ? new Date(text).toLocaleString() : "-"),
|
||||
},
|
||||
// {
|
||||
// title: "日期",
|
||||
// dataIndex: "created_at",
|
||||
// align: "center",
|
||||
// render: (text: string, record) => (
|
||||
// <>
|
||||
// <div>创建:{(text ? new Date(text).toLocaleString() : "-")}</div>
|
||||
// 更新:{(record?.updated_at ? new Date(record?.updated_at).toLocaleString() : "-")}
|
||||
// </>
|
||||
// ),
|
||||
// },
|
||||
{
|
||||
title: "操作",
|
||||
align: "center",
|
||||
@ -126,12 +167,12 @@ export default function VqdTaskTemplatePage() {
|
||||
fixed: "right",
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
|
||||
|
||||
<Tooltip placement="top" title="编辑" color="#fff">
|
||||
<Button icon={<EditOutlined />} onClick={() => handleEdit(record)} />
|
||||
</Tooltip>
|
||||
<Popconfirm
|
||||
title="确定要删除这个文件吗?"
|
||||
{!record.is_default&&<Popconfirm
|
||||
title="确定要删除这个模板吗?"
|
||||
onConfirm={() => {
|
||||
if (record.id) {
|
||||
deleteMutation(record.id);
|
||||
@ -145,7 +186,8 @@ export default function VqdTaskTemplatePage() {
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
/>
|
||||
</Popconfirm>
|
||||
</Popconfirm>}
|
||||
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@ -203,7 +245,7 @@ export default function VqdTaskTemplatePage() {
|
||||
{/* 编辑模态框 */}
|
||||
<AddVqdTaskTemplate
|
||||
ref={dialogRef}
|
||||
title="编辑"
|
||||
title="添加/编辑模板"
|
||||
onSuccess={() => refetch()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { Space, ConfigProvider, Modal, Tag, Tooltip, Button } from "antd";
|
||||
import { DeliveredProcedureOutlined } from "@ant-design/icons";
|
||||
import { Alert, ConfigProvider, Modal, Tag, message } from "antd";
|
||||
import Table, { ColumnsType } from "antd/es/table";
|
||||
import React, {
|
||||
forwardRef,
|
||||
@ -13,30 +12,28 @@ import { ChannelItem, ChannelReq } from "../../types/device";
|
||||
import Filter from "./Filter";
|
||||
import { GetChannels } from "../../api/devices";
|
||||
import { useGlobal } from "../../Context";
|
||||
import type { AddTaskItem } from "../../types/audiotask";
|
||||
|
||||
export interface IChannelModelFunc {
|
||||
openModal: (id: number, name: string) => void;
|
||||
openModal: (id: string) => void;
|
||||
}
|
||||
|
||||
interface IChannelModel {
|
||||
ref: any;
|
||||
onCallback: (data: AddTaskItem[]) => void;
|
||||
onCallback: (id: any, name:any) => void;
|
||||
}
|
||||
|
||||
const ChannelModel: React.FC<IChannelModel> = forwardRef(({ onCallback }, ref) => {
|
||||
const ChannelModel: React.FC<IChannelModel> = forwardRef(({onCallback},ref) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
openModal: (id: number, name: string) => {
|
||||
openModal: (id: string) => {
|
||||
setOpen(true);
|
||||
// if (id != 0) {
|
||||
// setSelectedRowKeys([id])
|
||||
// }
|
||||
audioName.current = name
|
||||
if (id != "") {
|
||||
setSelectedRowKeys([id])
|
||||
}
|
||||
pid.current = id;
|
||||
},
|
||||
}));
|
||||
const [open, setOpen] = useState(false);
|
||||
const pid = useRef<number>(0);
|
||||
const audioName = useRef<string>('');
|
||||
const pid = useRef<string>(undefined);
|
||||
const { ErrorHandle } = useGlobal();
|
||||
|
||||
const columns: ColumnsType<ChannelItem> = [
|
||||
@ -77,25 +74,6 @@ const ChannelModel: React.FC<IChannelModel> = forwardRef(({ onCallback }, ref) =
|
||||
dataIndex: "protocol",
|
||||
render: (text: string) => text || "-",
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
align: "center",
|
||||
width: 120,
|
||||
fixed: "right",
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Tooltip placement="top" title="下发广播任务" color="#fff">
|
||||
<Button icon={<DeliveredProcedureOutlined />} onClick={() => {
|
||||
onCallback([{
|
||||
audio_id: pid.current,
|
||||
channel_id: record.id,
|
||||
channel_name: record.name,
|
||||
}])
|
||||
}} />
|
||||
</Tooltip>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// 获取通道列表
|
||||
@ -104,10 +82,9 @@ const ChannelModel: React.FC<IChannelModel> = forwardRef(({ onCallback }, ref) =
|
||||
size: 10, // 通道一般 < 10 个,客户端不做分页,一次性全查
|
||||
device_id: "",
|
||||
pid: "ROOT",
|
||||
status: true,
|
||||
status: "",
|
||||
name: "",
|
||||
bid: "",
|
||||
protocol: "GB28181"
|
||||
});
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
@ -122,7 +99,7 @@ const ChannelModel: React.FC<IChannelModel> = forwardRef(({ onCallback }, ref) =
|
||||
enabled: open,
|
||||
});
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [selectedRowsAll, setSelectedRows] = useState<ChannelItem[]>([]);
|
||||
const [selectedRows, setSelectedRows] = useState<ChannelItem[]>([]);
|
||||
const rowSelection = {
|
||||
selectedRowKeys,
|
||||
getCheckboxProps: (record: ChannelItem) => ({
|
||||
@ -132,34 +109,18 @@ const ChannelModel: React.FC<IChannelModel> = forwardRef(({ onCallback }, ref) =
|
||||
newSelectedRowKeys: React.Key[],
|
||||
selectedRows: ChannelItem[]
|
||||
) => {
|
||||
setSelectedRowKeys(newSelectedRowKeys);
|
||||
setSelectedRows(selectedRows)
|
||||
// if (newSelectedRowKeys.length > 0) {
|
||||
// setSelectedRowKeys([newSelectedRowKeys[newSelectedRowKeys.length - 1]]);
|
||||
// }
|
||||
if (newSelectedRowKeys.length > 0) {
|
||||
setSelectedRowKeys([newSelectedRowKeys[newSelectedRowKeys.length - 1]]);
|
||||
setSelectedRows([selectedRows[selectedRows.length - 1]]);
|
||||
}
|
||||
},
|
||||
};
|
||||
const onAll = () => {
|
||||
let dataItem: AddTaskItem[] = []
|
||||
selectedRowsAll.forEach(record => {
|
||||
let list: AddTaskItem = {
|
||||
audio_id: pid.current,
|
||||
channel_id: record.id,
|
||||
channel_name: record.name,
|
||||
}
|
||||
dataItem.push(list)
|
||||
});
|
||||
onCallback(dataItem)
|
||||
setOpen(false);
|
||||
setSelectedRows([])
|
||||
setSelectedRowKeys([])
|
||||
}
|
||||
|
||||
const onCancel = () => {
|
||||
setOpen(false);
|
||||
// if (selectedRowKeys.length>0) {
|
||||
// onCallback(selectedRowKeys[0], pid.current)
|
||||
// }
|
||||
setSelectedRows([])
|
||||
if (selectedRowKeys.length>0) {
|
||||
onCallback(selectedRows[0].id, selectedRows[0].name||selectedRows[0].id)
|
||||
}
|
||||
setSelectedRowKeys([])
|
||||
};
|
||||
|
||||
@ -184,17 +145,11 @@ const ChannelModel: React.FC<IChannelModel> = forwardRef(({ onCallback }, ref) =
|
||||
>
|
||||
<Modal
|
||||
open={open}
|
||||
title={`选择通道广播【${audioName.current}】音频`}
|
||||
title="绑定通道"
|
||||
style={{ top: "5%" }}
|
||||
width={"1000px"}
|
||||
footer={
|
||||
<>
|
||||
<Button onClick={() => onCancel()} >关 闭</Button>
|
||||
<Button disabled={selectedRowKeys.length == 0} type="primary" onClick={() => onAll()} className="mr-6">批量下发</Button>
|
||||
</>
|
||||
}
|
||||
onCancel={onCancel}
|
||||
// onOk={onCancel}
|
||||
onCancel={onCancel}
|
||||
onOk={onCancel}
|
||||
>
|
||||
<div>
|
||||
<div className="mb-2 flex justify-end">
|
||||
@ -204,7 +159,7 @@ const ChannelModel: React.FC<IChannelModel> = forwardRef(({ onCallback }, ref) =
|
||||
onSearchChange={(value: string) => {
|
||||
setPagination({ ...pagination, name: value, bid: value });
|
||||
}}
|
||||
onSelectChange={(value: any) => {
|
||||
onSelectChange={(value: string) => {
|
||||
setPagination({ ...pagination, status: value });
|
||||
}}
|
||||
/>
|
||||
|
||||
@ -145,6 +145,14 @@
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
.m-0 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mb-0 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
@ -169,10 +177,26 @@
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.mr-4 {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.mt-6 {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
@ -438,6 +462,10 @@
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.pb-1 {
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.pb-2 {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
@ -446,6 +474,18 @@
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.pl-2 {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
.pl-3 {
|
||||
padding-left: 0.75rem;
|
||||
}
|
||||
|
||||
.pr-2 {
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.pr-4 {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
@ -506,6 +546,12 @@
|
||||
color: rgb(17 24 39 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.shadow {
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)
|
||||
}
|
||||
|
||||
.filter {
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
@ -553,3 +599,22 @@ body {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.config-box {
|
||||
width: 260px;
|
||||
border: 1px solid rgb(222,222,222,0.8);
|
||||
border-radius: 0.5rem;
|
||||
|
||||
}
|
||||
.config-box-hade {
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 0.5rem 0.5rem 0 0;
|
||||
background-color: rgb(222,222,222,0.8);
|
||||
}
|
||||
.config-box-hade h4 {
|
||||
margin: 0;
|
||||
}
|
||||
.config-box-item {
|
||||
padding: 0.3rem;
|
||||
}
|
||||
@ -1,4 +1,16 @@
|
||||
|
||||
import type {
|
||||
VqdLgtDark,
|
||||
VqdBlue,
|
||||
VqdClarity,
|
||||
VqdShark,
|
||||
VqdFreeze,
|
||||
VqdColor,
|
||||
VqdOcclusion,
|
||||
VqdNoise,
|
||||
VqdContrast,
|
||||
VqdMosaic,
|
||||
VqdFlower,
|
||||
} from "./vqdtasktemplate"
|
||||
/**
|
||||
* 基础
|
||||
*/
|
||||
@ -8,12 +20,24 @@ export type VqdConfigBaseRes = {
|
||||
|
||||
export type UpdateConfigBaseReq = {
|
||||
save_day: number;
|
||||
frm_num: number;
|
||||
is_deep_learn: boolean;
|
||||
};
|
||||
|
||||
export type VqdConfigBaseDetailRes = {
|
||||
save_day: number;
|
||||
}
|
||||
export type VqdConfigDefaultDetailRes = {
|
||||
frm_num: number;
|
||||
is_deep_learn: boolean;
|
||||
}
|
||||
vqd_lgt_dark: VqdLgtDark;
|
||||
vqd_blue: VqdBlue;
|
||||
vqd_clarity: VqdClarity;
|
||||
vqd_shark: VqdShark;
|
||||
vqd_freeze: VqdFreeze;
|
||||
vqd_color: VqdColor;
|
||||
vqd_occlusion: VqdOcclusion;
|
||||
vqd_noise: VqdNoise;
|
||||
vqd_contrast: VqdContrast;
|
||||
vqd_mosaic: VqdMosaic;
|
||||
vqd_flower: VqdFlower;
|
||||
}
|
||||
|
||||
|
||||
2
web/src/types/device.d.ts
vendored
2
web/src/types/device.d.ts
vendored
@ -189,7 +189,7 @@ type ChannelReq = {
|
||||
/**
|
||||
* true:在线; false:离线;
|
||||
*/
|
||||
status?: boolean;
|
||||
status?: string;
|
||||
};
|
||||
|
||||
export type ChannelRes = {
|
||||
|
||||
@ -26,6 +26,19 @@ export type VqdTaskTemplateItem = {
|
||||
name: string;
|
||||
plans: string;
|
||||
enable: boolean;
|
||||
is_default: boolean;
|
||||
vqd_config: VqdConfig;
|
||||
vqd_lgt_dark: VqdLgtDark;
|
||||
vqd_blue: VqdBlue;
|
||||
vqd_clarity: VqdClarity;
|
||||
vqd_shark: VqdShark;
|
||||
vqd_freeze: VqdFreeze;
|
||||
vqd_color: VqdColor;
|
||||
vqd_occlusion: VqdOcclusion;
|
||||
vqd_noise: VqdNoise;
|
||||
vqd_contrast: VqdContrast;
|
||||
vqd_mosaic: VqdMosaic;
|
||||
vqd_flower: VqdFlower;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
des: string;
|
||||
@ -49,6 +62,70 @@ export type VqdTaskTemplateReq = {
|
||||
size: number;
|
||||
|
||||
}
|
||||
export type VqdConfig = {
|
||||
enable: boolean;
|
||||
frm_num: number;
|
||||
is_deep_learn: boolean;
|
||||
}
|
||||
export type VqdLgtDark = {
|
||||
enable: boolean;
|
||||
dark_thr: number;
|
||||
lgt_thr: number;
|
||||
lgt_dark_abn_num_ratio: number;
|
||||
}
|
||||
export type VqdBlue = {
|
||||
enable: boolean;
|
||||
blue_thr: number;
|
||||
blue_abn_num_ratio: number;
|
||||
}
|
||||
export type VqdClarity = {
|
||||
enable: boolean;
|
||||
clarity_thr: number;
|
||||
clarity_abn_num_ratio: number;
|
||||
}
|
||||
export type VqdShark = {
|
||||
enable: boolean;
|
||||
shark_thr: number;
|
||||
shark_abn_num_ratio: number;
|
||||
}
|
||||
export type VqdFreeze = {
|
||||
enable: boolean;
|
||||
freeze_thr: number;
|
||||
freeze_abn_num_ratio: number;
|
||||
}
|
||||
export type VqdColor = {
|
||||
enable: boolean;
|
||||
color_thr: number;
|
||||
color_abn_num_ratio: number;
|
||||
}
|
||||
export type VqdOcclusion = {
|
||||
enable: boolean;
|
||||
occlusion_thr: number;
|
||||
occlusion_abn_num_ratio: number;
|
||||
}
|
||||
export type VqdNoise = {
|
||||
enable: boolean;
|
||||
noise_thr: number;
|
||||
noise_abn_num_ratio: number;
|
||||
}
|
||||
export type VqdContrast = {
|
||||
enable: boolean;
|
||||
ctra_low_thr: number;
|
||||
ctra_high_thr: number;
|
||||
ctra_abn_num_ratio: number;
|
||||
}
|
||||
export type VqdMosaic = {
|
||||
enable: boolean;
|
||||
mosaic_thr: number;
|
||||
mosaic_abn_num_ratio: number;
|
||||
}
|
||||
export type VqdFlower = {
|
||||
enable: boolean;
|
||||
flower_thr: number;
|
||||
flower_abn_num_ratio: number;
|
||||
mosaic_thr: number;
|
||||
}
|
||||
|
||||
export type CreateVqdTaskTemplateReq = {
|
||||
/**
|
||||
* 名称
|
||||
@ -62,6 +139,18 @@ export type CreateVqdTaskTemplateReq = {
|
||||
* 启用
|
||||
*/
|
||||
enable: boolean;
|
||||
vqd_config: VqdConfig;
|
||||
vqd_lgt_dark: VqdLgtDark;
|
||||
vqd_blue: VqdBlue;
|
||||
vqd_clarity: VqdClarity;
|
||||
vqd_shark: VqdShark;
|
||||
vqd_freeze: VqdFreeze;
|
||||
vqd_color: VqdColor;
|
||||
vqd_occlusion: VqdOcclusion;
|
||||
vqd_noise: VqdNoise;
|
||||
vqd_contrast: VqdContrast;
|
||||
vqd_mosaic: VqdMosaic;
|
||||
vqd_flower: VqdFlower;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
@ -86,6 +175,19 @@ export type VqdTaskTemplateDetailRes = {
|
||||
name: string;
|
||||
plans: string;
|
||||
enable: boolean;
|
||||
is_default: boolean;
|
||||
vqd_config: VqdConfig;
|
||||
vqd_lgt_dark: VqdLgtDark;
|
||||
vqd_blue: VqdBlue;
|
||||
vqd_clarity: VqdClarity;
|
||||
vqd_shark: VqdShark;
|
||||
vqd_freeze: VqdFreeze;
|
||||
vqd_color: VqdColor;
|
||||
vqd_occlusion: VqdOcclusion;
|
||||
vqd_noise: VqdNoise;
|
||||
vqd_contrast: VqdContrast;
|
||||
vqd_mosaic: VqdMosaic;
|
||||
vqd_flower: VqdFlower;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
des: string;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user