Merge commit '062242a63ccb2d57868033730d9edbbf68c44896' into zh

This commit is contained in:
2026-03-16 15:49:34 +08:00
24 changed files with 4448 additions and 4 deletions

View File

@@ -0,0 +1,4 @@
export const BASE_LABEL_API =
process.env.NEXT_PUBLIC_ENV === "production"
? "https://label.cowarobot.com"
: "https://label.softtest.cowarobot.com"

View File

@@ -0,0 +1,49 @@
import httpFetch from "@/api/fetch"
import { Daily } from "./typing"
import { BASE_LABEL_API } from "../const"
export const getSelfDailyWorkList = (data: Daily.Request) => {
return httpFetch<Daily.Response>({
url: BASE_LABEL_API + "/api/v1/label_server/daily_work/list/self",
method: "POST",
body: JSON.stringify(data),
})
}
export const getTeamDailyWorkList = (data: Daily.Request) => {
return httpFetch<Daily.Response>({
url: BASE_LABEL_API + "/api/v1/label_server/daily_work/list/team",
method: "POST",
body: JSON.stringify(data),
})
}
export const addDailyWorkData = (data: Daily.CreateProps) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/daily_work/add",
method: "POST",
body: JSON.stringify(data),
})
}
export const editDailyWorkData = (data: Daily.CreateProps, id: number) => {
return httpFetch({
url: BASE_LABEL_API + `/api/v1/label_server/daily_work/edit/${id}`,
method: "put",
body: JSON.stringify(data),
})
}
export const deleteDailyWorkData = (id: number) => {
return httpFetch({
url: BASE_LABEL_API + `/api/v1/label_server/daily_work/delete/${id}`,
method: "delete",
})
}
export const lockDailyWorkData = (lock_status: boolean, id: number) => {
return httpFetch({
url: BASE_LABEL_API + `/api/v1/label_server/daily_work/lock/${id}`,
method: "put",
body: JSON.stringify({ lock_status }),
})
}

View File

@@ -0,0 +1,84 @@
export namespace Daily {
export interface Request {
date_end?: string
date_start?: string
/**
* 0-个人日报 1-团队日报
*/
flag?: number
name?: string
order_by?: string
page_number?: number
page_size?: number
[property: string]: any
}
export interface Summary {
total_work_time: number
total_standard_size: number
total_completed_size: number
total_residual_size: number
total_residual_work_time: number
}
export interface Response {
daily_work_list: DailyWorkList[]
total_items: number
total_pages: number
total_summary: Summary
[property: string]: any
}
export interface DailyWorkList {
accounting_type?: string | null
actual_type?: string
completed_size?: number
date?: string
end_time?: string
id?: number
leader_name?: string | null
leader_uid?: number | null
lock_status?: boolean
obj_type?: number | null
performance_accounting?: boolean | null
project_id?: number | null
project_name?: string | null
rate?: number
remarks?: string | null
residual_pm?: boolean | null
residual_size?: number
residual_work_time?: number
set_type?: string
standard_size?: number
start_time?: string
uid?: number
username?: string
work_time?: number
work_type?: string | null
[property: string]: any
}
export interface CreateProps {
accounting_type?: string
actual_type: string
completed_size?: number
date: string
end_time?: string
leader_uid?: number
obj_type?: number
performance_accounting?: boolean
project_id?: number
rate?: number
remarks?: string
residual_pm?: boolean
residual_size?: number
residual_work_time?: number
set_type: string
standard_size?: number
start_time?: string
uid: number
work_time?: number
work_type?: string
[property: string]: any
}
}

View File

@@ -0,0 +1,171 @@
export interface LabelResult {
data_name: string
image_objects?: ImageObjects
video_objects?: {
[key: string]: ImageObjects
}
pc_box_objects?: { [key: string]: any }[]
pc_segment_objects?: { [key: string]: any }[]
pc_vegetation_objects?: { [key: string]: any }[]
}
export interface ImageObjects {
annotations: Annotation[]
categories: Category[]
desc?: Desc
image_groups: ImageGroups
images: Images
key_frame?: boolean
label1_ts?: number
label1_uid?: number
review1_ts?: number
review1_uid?: number
review2_ts?: number
review2_uid?: number
}
export interface Annotation {
bbox?: number[]
category_id: number
comment?: Comment[]
create_timestamp: number
first_modified_timestamp: number
first_modified_uid: number
hollow_segmentation?: Array<Array<number[]>>
id: number
image_id: number
key_points?: KeyPoint[]
last_modified_timestamp: number
last_modified_uid: number
line_segment?: Array<Array<number[]>>
prelabel: boolean
rect?: number[]
segmentation?: Array<Array<number[]>>
sub_attributes?: { [key: string]: any }
uid: number
}
export interface Comment {
check_box_comments: string[]
comment_type: number
date: number
scene_info: SceneInfo
text_comments: string[]
times: number
turn: number
uid: number
}
export interface SceneInfo {
phi?: number
point_idx?: number[]
theta?: number
type: number
vfov_factor?: number
x?: number
x_offset?: number
y?: number
y_offset?: number
z?: number
zoom?: number
}
export interface KeyPoint {
attributes?: { [key: string]: any }
point: [number, number]
}
export interface Category {
id: number
name: string
supercategory: string
}
export interface Desc {
meta_operation: string[]
other_desc: string
}
export interface ImageGroups {
// "01J4GZMQ2G8KKQVF52S6EXBGRA": string;
}
export interface Images {
file_name: string
height: number
id: number
width: number
}
export interface RequestLabelResult {
label_object_size: { [key: string]: any }
results: LabelResult[]
task_id: number
uid: number
work_load: WorkLoad
work_time: { [key: string]: any }
}
export interface WorkLoad {
comment_size: number
dynamic_size: { [key: number]: number }
key_frame_size: { [key: number]: number }
object_size: { [key: number]: number }
prelabel_modify_size: { [key: number]: number }
question_size: { [key: number]: number }
review_dynamic_size: { [key: number]: number }
review_key_frame_size: { [key: number]: number }
review_question_size: { [key: number]: number }
review_size: number
review_static_size: { [key: number]: number }
static_size: { [key: number]: number }
work_time: number
}
export interface ServerResponse {
data_list?: DataList[]
proxy?: string
[property: string]: any
}
export interface DataList {
data_name: string
data_type: string
image_data?: string
video_data?: string
pointcloud_data?: PointcloudDatum[]
[property: string]: any
}
export interface PointcloudDatum {
intensity: number
time_offset: number
x: number
y: number
z: number
[property: string]: any
}
export interface AuxiliaryParams {
project_id: number
file_name: string
image_hw: [number, number]
points_and_box: Array<[number, number]>
tags: number[]
clear_previous_state?: boolean
}
interface UserInfo {
uid: number
name: string
all_projects: { [x: number]: number[] }
collect_projects: number[]
group_name: string
work_status: number
base_city: string
}
export interface LoginInfo extends UserInfo {
token: string
refresh_token: string
}

View File

@@ -0,0 +1,198 @@
import httpFetch from "@/api/fetch"
import { Project, ProjectDetail, ReviewUsers } from "./typing"
import { BASE_LABEL_API } from "../const"
// 获取项目列表
export const getProjectList = (data: Project.ListRequest) => {
return httpFetch<Project.ListResponse>({
url: BASE_LABEL_API + "/api/v1/label_server/project/list",
method: "POST",
body: JSON.stringify(data),
})
}
// 获取项目详情
export const getProjectDetailById = (id: number) => {
return httpFetch<ProjectDetail.DataProps>({
url: BASE_LABEL_API + `/api/v1/label_server/project/detail/${id}`,
method: "GET",
})
}
// 校验项目源数据
export const checkLabelDataSource = (data: Project.CheckRequest) => {
return httpFetch<Project.CheckResponse>({
url: BASE_LABEL_API + "/api/v1/label_server/project/check_data",
method: "POST",
body: JSON.stringify(data),
})
}
// 创建项目
export const projectAdd = (data: Project.CreateProps) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/project/add",
method: "POST",
body: JSON.stringify(data),
})
}
// 审核项目
export const projectReview = (data: Project.CreateProps) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/project/review",
method: "POST",
body: JSON.stringify(data),
})
}
// 配置项目
export const projectConfig = (data: Project.ConfigProps) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/project/config",
method: "POST",
body: JSON.stringify(data),
})
}
// 编辑项目
export const projectEditById = (data: any, id: number) => {
return httpFetch({
url: BASE_LABEL_API + `/api/v1/label_server/project/edit/${id}`,
method: "POST",
body: JSON.stringify(data),
})
}
// 审核试标项目
export const projectPreLabelReview = (data: Project.PreLabelProps) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/project/prototype_review",
method: "POST",
body: JSON.stringify(data),
})
}
// 提交验收
export const projectDeliver = (data: { project_id: number }) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/project/deliver",
method: "POST",
body: JSON.stringify(data),
})
}
// 完成项目
export const projectAccept = (data: { project_id: number; pass: boolean }) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/project/accept",
method: "POST",
body: JSON.stringify(data),
})
}
// 收藏项目
export const projectCollect = (data: {
project_id: number
is_collect: boolean
}) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/project/collect",
method: "POST",
body: JSON.stringify(data),
})
}
// 获取项目抽检比例
export const getReviewScaleList = (params: { project_id: number }) => {
return httpFetch<any[]>({
url: BASE_LABEL_API + "/api/v1/label_server/review_scale/list",
method: "GET",
data: params,
})
}
// 保存抽检比例
export const saveUserReviewScale = (data: Project.ReviewScaleProps) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/review_scale/save",
method: "POST",
body: JSON.stringify(data),
})
}
export const getProjectDetail = (id: number) => {
return httpFetch<Project.DetailResponse>({
url: BASE_LABEL_API + `/api/v1/label_server/project/detail/${id}`,
method: "GET",
})
}
// 重新生成Embedding
export const regenerateProjectEmbedding = (data: { project_id: number }) => {
return httpFetch({
url: BASE_LABEL_API + `/api/v1/label_server/project/regenerate_embedding`,
method: "POST",
body: JSON.stringify(data),
})
}
// 获取个人参与项目
export const getPersonProjectDashBoard = () => {
return httpFetch<Array<Project.PersonProjectDashboardResponseItem>>({
url:
BASE_LABEL_API + `/api/v1/label_server/data_statistics/personal_project`,
method: "GET",
})
}
// 获取个人看板数据
export const getCurrentPersonData = (params: { date: string }) => {
return httpFetch<{
label_data_size: number
label_work_time: number
review1_data_size: number
review1_work_time: number
review2_data_size: number
review2_work_time: number
}>({
url:
BASE_LABEL_API +
"/api/v1/label_server/data_statistics/personal_dashboard",
method: "GET",
data: params,
})
}
/* ****************************** Options ****************************** */
// 获取项目所属业务列表
export const getProjectTypeList = () => {
return httpFetch<string[]>({
url: BASE_LABEL_API + "/api/v1/label_server/project/type_list",
method: "GET",
})
}
// 获取项目管理员列表
export const getProjectAdminList = () => {
return httpFetch<Array<{ label: string; value: number }>>({
url: BASE_LABEL_API + "/api/v1/label_server/project/admin_list",
method: "GET",
})
}
// 获取项目审核员
export const getProjectReviewers = () => {
return httpFetch<ReviewUsers>({
url: BASE_LABEL_API + "/api/v1/label_server/project/reviewer_map",
method: "GET",
})
}
// 获取用户有权限的项目
export const getSelfProjectList = () => {
return httpFetch<Array<{ label: string; value: number }>>({
url: BASE_LABEL_API + "/api/v1/label_server/project/self",
method: "GET",
})
}

View File

@@ -0,0 +1,474 @@
export namespace Project {
export interface DataProps {
key: React.Key
id: number // 项目ID
name: string // 项目名称
is_collect: boolean // 是否收藏
project_type?: string // 所属业务
label_schema_name?: string // 标注方案
label_schema_version?: string
status: number // 项目状态
labeled_scale?: number // 标注完成比例
reviewed_scale?: number // 审核完成比例
data_size?: number // 总数据量
rate_speed?: number // 额定倍速
create_user: string // 创建人
owner: string // 项目负责人
label_process?: string[] // 工序
admin_user: number[] // 标注管理员id
data_source?: string // 数据源
remarks?: string // 备注
// reviewer_user?: string; // 审核人
// reviewer_comment?: string; // 审核意见
create_at?: string // 创建时间
update_at?: string // 更新时间
modify?: number //0=可以发起变更,无变更 1=正在变更,无变更 2=可以发起变更,有变更 3=正在变更,有变更
}
export interface CheckRequest {
project_type: string
nfs_path: string
flag: number
}
export interface CheckResponse {
data_size: number
error_code: number
}
export interface CreateProps {
admin_user: string[]
create_user: string
data_size: number
data_source: string
label_schema_name: string
label_schema_version: string
project_name: string
project_type: string
related_task?: number[]
remarks?: string
[property: string]: any
}
export interface ReviewProps {
pass: boolean
project_id: number
project_name: string
review_comment?: string
review_user: string
test_proportion?: number
}
export interface ListRequest {
admin_user?: string
create_user?: string
end_date?: string
page_number: number
page_size: number
project_id?: number
project_name?: string
project_type?: string
review_user?: string
start_date?: string
status?: number[]
[property: string]: any
}
export interface ListResponse {
project_list: DataProps[]
total_pages: number
total_items: number
}
export interface LabelConfig {
acquire_mode: number
acquire_size: number
avg_remind_threshold: number
dynamic_label_speed: number
filter_pre_label: boolean
fix_remind_threshold: number
label_accuracy_rate: number
label_update_month: number
max_size: number
pre_label_confirm: boolean
static_label_speed: number
}
export interface Review1Config {
acquire_mode: number
acquire_size: number
dynamic_review1_speed: number
end_date?: string
filter_label_first_sub: boolean
filter_objs: boolean
filter_rework: boolean
filter_task_batch: boolean
filter_track_user: boolean
is_rework?: boolean
label_first_sub_end_date?: string
label_first_sub_start_date?: string
max_objs?: number
max_size: number
min_objs?: number
review1_accuracy_rate: number
review1_update_month: number
start_date?: string
static_review1_speed: number
track_user_list?: number[]
}
export interface Review2Config {
acquire_mode: number
acquire_size: number
dynamic_review2_speed: number
end_date?: string
filter_objs: boolean
filter_review1_first_sub: boolean
filter_review1_user: boolean
filter_rework: boolean
filter_task_batch: boolean
filter_track_user: boolean
is_rework?: boolean
max_objs?: number
max_size: number
min_objs?: number
review1_first_sub_end_date?: string
review1_first_sub_start_date?: string
review1_user_list?: number[]
review2_update_month: number
start_date?: string
static_review2_speed: number
track_user_list?: number[]
}
export interface ConfigProps {
admin_user?: number[]
check_frame?: boolean
check_task?: boolean
comment_rule?: { [key: string]: any }
default_pose?: string
is_lock_object?: boolean
label_config?: LabelConfig
label_users?: number[]
modifiable_types?: string[]
project_id: number
project_name?: string
project_remark?: string
review1_config?: Review1Config
review1_users?: number[]
review2_config?: Review2Config
review2_users?: number[]
user_grade?: { [key: string]: any }
}
// 试标审核
export interface PreLabelProps {
id: number
review_user: string
action: number // 1-试标完成2-试标通过3-试标不通过
detail?: string
}
export interface ReviewScaleProps {
frame_scale?: number
grade?: string
project_id: number
task_scale?: number
uids: number[]
}
/**
* DetailResponse
*/
export interface DetailResponse {
admin_user: string[]
alarm_threshold: AlarmThreshold
check_frame: boolean
check_task: boolean
comment_rule: CommentRule
create_at: string
create_user: string
data_size: number
default_pose: null
id: number
is_lock_object: boolean
label_process: LabelProcess
label_schema_list: LabelSchemaList[]
label_type: number
labeled_multiplier: LabeledMultiplier
modifiable_types: string[]
name: string
project_progress: ProjectProgress
remarks: string
rpath: string
status: number
task_size: number
user_grade: UserGrade
}
export interface AlarmThreshold {
avg_remind_threshold: number
fix_remind_threshold: number
}
export interface CommentRule {
check_box_rules: {
create_date: string
create_user: string
text: string
}[]
text_rules: string[]
}
export interface LabelProcess {
filter_pre_label: boolean
filter_task_info: string[]
pre_label_confirm: boolean
step_detail: StepDetail[]
}
export interface StepDetail {
acquire_mode: number
acquire_size: number
action: number
max_size: number
user_list: number[]
}
export interface LabelSchemaList {
bbox_range: number[]
bbox_size: number[]
bbox_type: number
category_id: number
color: [number, number, number]
label_class: string
label_type: number
radius: number
sensor_list: string[]
sub_attributes: string[]
sub_attributes_describe: SubAttributesDescribe[]
super_category: string
}
export interface SubAttributesDescribe {
chinese_name: string
english_name: string
isrequired: number
optional_item: string[]
round_id: number
select_mode: number
}
export interface LabeledMultiplier {
dynamic_label_speed: null
dynamic_review1_speed: null
dynamic_review2_speed: null
label_accuracy_rate: null
label_update_month: null
review1_accuracy_rate: null
review1_update_month: null
review2_update_month: null
static_label_speed: null
static_review1_speed: null
static_review2_speed: null
}
export interface ProjectProgress {
normal_labeled_task_size: number
normal_reviewed1_task_size: number
normal_reviewed2_task_size: number
rework_labeled_task_size: number
rework_reviewed1_task_size: number
rework_reviewed2_task_size: number
unlabeled_task_size: number
}
export interface UserGrade {
A: {
frame: number
task: number
}
B: {
frame: number
task: number
}
C: {
frame: number
task: number
}
}
/**
* DetailResponse
*/
export interface PersonProjectDashboardResponseItem {
project_id: number
project_label_type: string
project_name: string
project_type: string
work_time: number
}
}
export interface ReviewUsers {
reviewer1: string[]
reviewer2: string[]
reviewer3: string[]
}
export namespace ProjectDetail {
export interface DataProps {
admin_user: number[]
alarm_threshold: AlarmThreshold
check_frame: boolean
check_task: boolean
comment_rule: CommentRule
create_at: string
create_user: string
data_size: number
default_pose: null
id: number
is_lock_object: boolean
label_process: LabelProcess
label_schema_list: LabelSchemaList[]
label_type: number
labeled_multiplier: LabeledMultiplier
modifiable_types: string[]
name: string
project_progress: ProjectProgress
remarks: string
rpath: string
status: number
task_size: number
user_grade: UserGrade
[property: string]: any
}
export interface AlarmThreshold {
avg_remind_threshold: number
fix_remind_threshold: number
[property: string]: any
}
export interface CommentRule {
check_box_rules: string[]
text_rules: string[]
[property: string]: any
}
export interface LabelProcess {
filter_pre_label: boolean
filter_task_info: FilterTaskInfo[]
pre_label_confirm: boolean
step_detail: StepDetail[]
[property: string]: any
}
export interface FilterTaskInfo {
action: number
end_date: string
filter_label_first_sub: boolean
filter_objs: boolean
filter_review1_first_sub: boolean
filter_review1_user: boolean
filter_rework: boolean
filter_task_batch: boolean
filter_track_user: boolean
is_rework: boolean
label_first_sub_end_date: string
label_first_sub_start_date: string
max_objs: number
min_objs: number
review1_first_sub_end_date: string
review1_first_sub_start_date: string
review1_user_list: number[]
start_date: string
track_user_list: number[]
[property: string]: any
}
export interface StepDetail {
acquire_mode: number
acquire_size: number
action: number
max_size: number
user_list: number[]
[property: string]: any
}
export interface LabelSchemaList {
bbox_range: number[]
bbox_size: number[]
bbox_type: number
category_id: number
color: number[]
label_class: string
label_type: number
radius: number
sensor_list: string[]
sub_attributes: string[]
sub_attributes_describe: SubAttributesDescribe[]
super_category: string
[property: string]: any
}
export interface SubAttributesDescribe {
chinese_name: string
english_name: string
isrequired: number
optional_item: string[]
round_id: number
select_mode: number
[property: string]: any
}
export interface LabeledMultiplier {
dynamic_label_speed: number
dynamic_review1_speed: number
dynamic_review2_speed: number
label_accuracy_rate: number
label_update_month: number
review1_accuracy_rate: number
review1_update_month: number
review2_update_month: number
static_label_speed: number
static_review1_speed: number
static_review2_speed: number
[property: string]: any
}
export interface ProjectProgress {
normal_labeled_task_size: number
normal_reviewed1_task_size: number
normal_reviewed2_task_size: number
rework_labeled_task_size: number
rework_reviewed1_task_size: number
rework_reviewed2_task_size: number
unlabeled_task_size: number
[property: string]: any
}
export interface UserGrade {
A: A
B: B
C: C
[property: string]: any
}
export interface A {
frame: number
task: number
[property: string]: any
}
export interface B {
frame: number
task: number
[property: string]: any
}
export interface C {
frame: number
task: number
[property: string]: any
}
}

View File

@@ -0,0 +1,77 @@
import httpFetch from "@/api/fetch"
import { Task } from "./typing"
import { BASE_LABEL_API } from "../const"
// 获取任务列表
export const getTaskList = (data: Task.ListRequest) => {
return httpFetch<Task.ListResponse>({
url: BASE_LABEL_API + "/api/v1/label_server/task/list",
method: "POST",
body: JSON.stringify(data),
})
}
// 领取任务
export const taskAcquire = (data: Task.AcquireReqProps) => {
return httpFetch<Task.ListResponse>({
url: BASE_LABEL_API + "/api/v1/label_server/task/acquire",
method: "POST",
body: JSON.stringify(data),
})
}
// 调度任务
export const taskDispatch = (data: Task.DispatchReqProps) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/task/dispatch",
method: "POST",
body: JSON.stringify(data),
})
}
// 释放任务
export const taskRelease = (data: Task.ReleaseReqProps) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/task/release",
method: "POST",
body: JSON.stringify(data),
})
}
// 获取下一个任务
export const getNextTaskId = (data: Task.NextReqProps) => {
return httpFetch<Task.NextResProps>({
url: BASE_LABEL_API + "/api/v1/label_server/task/next",
method: "POST",
body: JSON.stringify(data),
})
}
// 提交任务
export const taskCommit = (data: Task.CommitReqProps) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/task/commit",
method: "POST",
body: JSON.stringify(data),
})
}
// 获取任务的工作流程
export const getTaskWorkFlow = (id: number) => {
return httpFetch<any[]>({
url: BASE_LABEL_API + `/api/v1/label_server/task/workflow/${id}`,
method: "GET",
})
}
// 获取个人任务列表
export const getUserTaskList = (params: {
page_size: number
page_number: number
}) => {
return httpFetch<Task.ResGetUserTaskList>({
url: BASE_LABEL_API + `/api/v1/label_server/task/list_by_user`,
method: "GET",
data: params,
})
}

View File

@@ -0,0 +1,179 @@
export namespace Task {
export interface DataProps {
id: number
create_date: string
data_type?: number
data_name: {
name: [string]
related_images: []
}[]
project_id: number
project_name: string
project_uuid: string
label_status: number
current_uid?: number
current_username?: string
label_user?: number
label_username?: string
review_user1?: number
review1_username?: string
review_user2?: number
review2_username?: string
rejected: boolean
work_time: {
label_work_time: number
first_review_work_time: number
second_review_work_time: number
}
label_object_size: {
object_size: number
valid_size: number
first_comment_size: number
second_comment_size: number
new_size: number
prelabel_size: number
prelabel_modify_size: number
dynamic_size: number
static_size: number
review_size: number
review_dynamic_size: number
review_static_size: number
key_frame_size: number
question_size: number
total_qa_group_size: number
single_qa_group_size: number
multiple_qa_group_size: number
new_qa_group_size: number
new_single_qa_group_size: number
new_multiple_qa_group_size: number
review_question_size: number
comment_question_size: number
}
first_commit_label_time?: string
first_commit_review1_time?: string
reject_times: number
review_rate: {
review1_rate: number
review2_rate: number
}
label_first_work_time: number
review1_first_work_time: number
review2_first_work_time: number
first_reject_label_time?: string
}
export interface ListRequest {
data_type?: number
current_uid?: number[]
data_name?: string
first_commit_label_time_end?: string
first_commit_label_time_start?: string
first_commit_review1_time_end?: string
first_commit_review1_time_start?: string
get_data: boolean
id?: number[]
label_status?: number[]
label_user?: number[]
order_by?: string
page_number: number
page_size: number
project_id?: number[]
rejected?: number
review_user1?: number[]
review_user2?: number[]
}
export interface SizeProps {
object_size: number
valid_size: number
first_comment_size: number
second_comment_size: number
new_size: number
prelabel_size: number
prelabel_modify_size: number
dynamic_size: number
static_size: number
review_size: number
review_dynamic_size: number
review_static_size: number
key_frame_size: number
question_size: number
total_qa_group_size: number
single_qa_group_size: number
multiple_qa_group_size: number
new_qa_group_size: number
new_single_qa_group_size: number
new_multiple_qa_group_size: number
review_question_size: number
comment_question_size: number
}
export interface ListResponse {
task_list: DataProps[]
total_items: number
total_pages: number
total_size: SizeProps
}
export interface AcquireReqProps {
uid: number
project_id: number
label_action: 1 | 2 | 3 // 1-领取标注任务 2-领取审核任务 3-领取复审任务
}
export interface DispatchReqProps {
operation_uid: number
reject: boolean
task_ids: number[]
task_status_dst: number
task_status_src: number
uid_dst: number | null
uid_src: number | null
}
export interface ReleaseReqProps {
operation_uid: number
reject: boolean
task_ids: number[]
task_status: number
uid: number
}
export interface NextReqProps {
project_id: number
task_status: number
rejected: boolean
last_task_id: number
}
export interface NextResProps {
project_id: number
task_id: number | null
}
export interface CommitReqProps {
task_id: number
uid: number
commit_action: number //1-提交标注 2-提交审核1 3-提交审核2 4-驳回 5-无法标注
work_time: number
comment_question_size?: number
}
export interface ResGetUserTaskList {
simple_task_list: SimpleTaskItem[]
total_items: number
total_pages: number
}
export interface SimpleTaskItem {
id: number
label_status: number
label_type: number
label_username?: string
project_id: number
project_name: string
rejected: boolean
review1_username?: string
review2_username?: string
}
}

605
components/label/store.ts Normal file
View File

@@ -0,0 +1,605 @@
import { del, get, set } from "idb-keyval"
import superjson from "superjson"
import { create } from "zustand"
import { persist, PersistStorage, StorageValue } from "zustand/middleware"
import { Comment } from "./api/label/typing"
import { safeClone } from "./utils/clone"
type LabelData = Map<string, Map<string, [number, any[], any, any[]][]>>
type LabelPathTuple = [number, any[], any, any[]]
type LabelTime = {
label: number
review1: number
review2: number
}
const isPaperPointLike = (value: any) => {
return (
value &&
typeof value === "object" &&
!Array.isArray(value) &&
typeof value.x === "number" &&
typeof value.y === "number"
)
}
const isPaperSegmentLike = (value: any) => {
return (
value &&
typeof value === "object" &&
!Array.isArray(value) &&
value.point &&
isPaperPointLike(value.point)
)
}
const normalizePointCollection = (value: any): any => {
if (Array.isArray(value)) {
return value.map((item) => normalizePointCollection(item))
}
if (isPaperSegmentLike(value)) {
return [value.point.x, value.point.y]
}
if (isPaperPointLike(value)) {
return [value.x, value.y]
}
return value
}
const normalizeLabelPathTuple = (path: LabelPathTuple): LabelPathTuple => {
return [
path[0],
normalizePointCollection(path[1]),
path[2],
normalizePointCollection(path[3]),
]
}
interface LabelState {
// pathId pathPoints detail hollowPathPoints
label: LabelData
labelTime: LabelTime
labelDefaultComments: Comment[]
setLabel: (label: LabelData) => void
stateStack: LabelData[]
setStateStack: (data: LabelData[]) => void
popStateStack: () => LabelData | null
pushStateStack: (newData: LabelData) => void
getLabelDetailDataByKeys: (
imageId: string,
categoryId: string,
id: number
) => any
updateLabelDetailDataByKeys: (
imageId: string,
categoryId: string,
id: number,
newDetail: any
) => void
addDetail: (
imageId: string,
categoryId: string,
id: number,
obj: {
[keys: string]: any
}
) => void
setLabelTime: (
labelTime: LabelTime | ((prev: LabelTime) => LabelTime)
) => void
setLabelDefaultComments: (arr: Comment[]) => void
setImage: (
imageId: string,
operation: Map<string, [number, any[], any, any[]][]>
) => void
setOperation: (
imageId: string,
operationId: string,
path: [number, any[], any, any[]]
) => void
deleteOperation: (
imageId: string,
operationId: string,
pathId: number
) => void
resetLabel: () => void
}
interface ObjectState {
pathStatus: {
// key = imageId + pathId
[key: string]: {
["HIDE"]?: boolean
}
}
selectedPath: {
// key = imageId
[key: string]: number[]
}
operationStatus: {
// key = imageId + operationId
[key: string]: {
["HIDE"]?: boolean
["INPUT"]?: string
["COLLAPSE"]?: boolean
}
}
groupStatus: {
// key = imageId + groupId
[key: string]: {
["COLLAPSE"]?: boolean
}
}
imageStatus: {
// key = imageId
[key: string]: {
["INPUT"]?: string
["COLLAPSE"]?: boolean
["FILTERCOLOR"]?: {
[key: string]: boolean
}
}
}
updatePathStatus: (
id: string,
statusKey: "HIDE",
statusValue: boolean
) => void
updateSelectedPath: (
id: string,
type: "ADD" | "DELETE" | "ADDSOME",
pathValue?: number
) => void
resetPathAndOperationStatus: () => void
updateOperationStatus: (
id: string,
statusKey: "HIDE" | "COLLAPSE" | "INPUT",
statusValue: string | boolean
) => void
updateGroupStatus: (
id: string,
statusKey: "COLLAPSE",
statusValue: string | boolean
) => void
updateImageStatus: (
id: string,
statusKey: "INPUT" | "COLLAPSE" | "FILTERCOLOR",
statusValue:
| string
| boolean
| {
[key: string]: boolean
}
) => void
}
interface KeyEventState {
shift: boolean
focusInput: boolean
setShift: (value: boolean) => void
setFocusInput: (value: boolean) => void
}
const initialLabelState = {
label: new Map(),
labelTime: {
label: 0,
review1: 0,
review2: 0,
},
}
export const initialDetail = {
comment: [],
create_timestamp: 0,
first_modified_timestamp: 0,
first_modified_uid: 0,
last_modified_timestamp: 0,
last_modified_uid: 0,
sub_attributes: {},
prelabel: false,
}
// 自定义 storage 对象
export const storage: PersistStorage<any> = {
getItem: async (name: string) => {
// console.log(name, "has been retrieved");
let str = await get(name)
if (!str) return null
return superjson.parse(str) || null
},
setItem: async (name: string, value: StorageValue<any>): Promise<void> => {
// console.log(name, "with value", value, "has been saved");
await set(name, superjson.stringify(value))
},
removeItem: async (name: string): Promise<void> => {
console.log(name, "has been deleted")
await del(name)
},
}
// store path data
export const useLabelStore = create(
persist<LabelState>(
(storeSet) => ({
label: initialLabelState.label,
labelTime: initialLabelState.labelTime,
labelDefaultComments: [],
stateStack: [],
setLabel: (label) =>
storeSet((state) => {
return {
...state,
label,
}
}),
setStateStack: (data) =>
storeSet((state) => {
return {
...state,
stateStack: data,
}
}),
popStateStack: () => {
const arr: LabelData[] = useLabelStore.getState().stateStack
console.log("弹出栈", arr)
if (!arr.length) return null
if (arr.length === 1) {
return arr[0]
} else {
arr.pop()
storeSet((state) => {
return {
...state,
stateStack: arr,
}
})
return arr[arr.length - 1]
}
},
pushStateStack: (newData) => {
const arr: LabelData[] = useLabelStore.getState().stateStack
if (arr.length === 10) {
arr.shift()
}
arr.push(safeClone(newData))
console.log(arr)
return storeSet((state) => {
return {
...state,
stateStack: arr,
}
})
},
getLabelDetailDataByKeys: (
imageId: string,
categoryId: string,
id: number
) => {
const data = useLabelStore.getState().label
if (!data.has(imageId)) return null
const image = data.get(imageId)
if (!image?.has(categoryId)) return null
const category = image.get(categoryId)
let detail = null
category?.map((item) => {
if (item[0] === id) detail = item[2]
})
return detail
},
updateLabelDetailDataByKeys: (imageId, categoryId, id, newData) => {
const labelData = useLabelStore.getState().label
if (!labelData.has(imageId)) return
const image = labelData.get(imageId)
if (!image?.has(categoryId)) return
const category = image.get(categoryId)
category?.map((item) => {
if (item[0] === id) item[2] = newData
})
},
addDetail: (imageId, categoryId, id, obj) => {
const labelData = useLabelStore.getState().label
if (!labelData.has(imageId)) return
const image = labelData.get(imageId)
if (!image?.has(categoryId)) return
const category = image.get(categoryId)
category?.map((item) => {
if (item[0] === id) item[2] = Object.assign(item[2], obj)
})
},
setLabelTime: (labelTime) =>
storeSet((state) => {
return {
...state,
labelTime:
typeof labelTime === "function"
? labelTime(state.labelTime)
: labelTime,
}
}),
setLabelDefaultComments: (labelDefaultComments) =>
storeSet((state) => {
return {
...state,
labelDefaultComments,
}
}),
setImage: (imageId, operation) =>
storeSet((state) => {
state.label.set(imageId, operation)
return state
}),
setOperation: (imageId, operationId, path) =>
storeSet((state) => {
const normalizedPath = normalizeLabelPathTuple(path)
const labelData = useLabelStore.getState().label
if (!labelData.has(imageId)) {
labelData.set(imageId, new Map())
}
if (labelData.get(imageId)?.get(operationId)?.length) {
let result = [
...labelData.get(imageId)?.get(operationId)!,
normalizedPath,
]
.reduce((acc, curr) => {
const key = curr[0]
acc.set(key, curr)
return acc
}, new Map())
.values()
labelData.get(imageId)?.set(operationId, Array.from(result))
} else {
labelData.get(imageId)?.set(operationId, [normalizedPath])
}
console.log("绘制时", labelData)
useLabelStore.getState().pushStateStack(safeClone(labelData))
return { ...state, label: safeClone(labelData) }
}),
// 删除path
deleteOperation: (imageId, operationId, pathId) =>
storeSet((state) => {
if (state.label.get(imageId)?.get(operationId)?.length) {
let result =
state.label
.get(imageId)
?.get(operationId)
?.filter((item) => item[0] !== pathId) || []
state.label.get(imageId)?.set(operationId, result)
}
return state
}),
resetLabel: () =>
storeSet((state) => {
return {
...state,
label: initialLabelState.label,
labelTime: initialLabelState.labelTime,
}
}),
}),
{
name: "label-data-storage",
storage: storage,
}
)
)
const initialObjectState = {
pathStatus: {},
selectedPath: {},
operationStatus: {},
groupStatus: {},
imageStatus: {},
}
// store object list status
export const useObjectStore = create(
persist<ObjectState>(
(storeSet) => ({
pathStatus: initialObjectState.pathStatus,
selectedPath: initialObjectState.selectedPath,
operationStatus: initialObjectState.operationStatus,
groupStatus: initialObjectState.groupStatus,
imageStatus: initialObjectState.imageStatus,
updatePathStatus: (id: string, statusKey: "HIDE", statusValue: boolean) =>
storeSet((state) => {
let idStatus = state.pathStatus[id] || {}
idStatus = Object.assign(idStatus, {
[statusKey]: statusValue,
})
return {
...state,
pathStatus: {
...state.pathStatus,
[id]: idStatus,
},
}
}),
updateSelectedPath: (
id: string,
type: "ADD" | "DELETE" | "ADDSOME",
pathValue?: number
) =>
storeSet((state) => {
let idSelectedPath = state.selectedPath[id] || []
if (type === "ADD") {
if (useKeyEventStore.getState().shift) {
idSelectedPath = pathValue
? [...new Set([...idSelectedPath, pathValue])]
: idSelectedPath
} else {
idSelectedPath = pathValue ? [pathValue] : []
}
} else if (type === "ADDSOME") {
idSelectedPath = pathValue
? [...new Set([...idSelectedPath, pathValue])]
: idSelectedPath
} else if (type === "DELETE") {
idSelectedPath =
idSelectedPath?.filter((id) => id !== pathValue) || []
}
return {
...state,
selectedPath: {
...state.selectedPath,
[id]: idSelectedPath,
},
}
}),
resetPathAndOperationStatus: () =>
storeSet((state) => {
return {
...state,
selectedPath: {},
pathStatus: {},
operationStatus: {},
}
}),
updateOperationStatus: (
id: string,
statusKey: "HIDE" | "COLLAPSE" | "INPUT",
statusValue: string | boolean
) =>
storeSet((state) => {
let idStatus = state.operationStatus[id] || {}
idStatus = Object.assign(idStatus, {
[statusKey]: statusValue,
})
return {
...state,
operationStatus: {
...state.operationStatus,
[id]: idStatus,
},
}
}),
updateGroupStatus: (
id: string,
statusKey: "COLLAPSE",
statusValue: string | boolean
) =>
storeSet((state) => {
let idStatus = state.groupStatus[id] || {}
idStatus = Object.assign(idStatus, {
[statusKey]: statusValue,
})
return {
...state,
groupStatus: {
...state.groupStatus,
[id]: idStatus,
},
}
}),
updateImageStatus: (
id: string,
statusKey: "INPUT" | "COLLAPSE" | "FILTERCOLOR",
statusValue:
| string
| boolean
| {
[key: string]: boolean
}
) =>
storeSet((state) => {
let idStatus = state.imageStatus[id] || {}
idStatus = Object.assign(idStatus, {
[statusKey]: statusValue,
})
return {
...state,
imageStatus: {
...state.imageStatus,
[id]: idStatus,
},
}
}),
}),
{
name: "object-data-storage",
storage: storage,
partialize: (state: ObjectState) =>
Object.fromEntries(
Object.entries(state).filter(
([key]) => !["selectedPath"].includes(key)
)
) as any,
}
)
)
const initialKeyEventState = {
shift: false,
focusInput: false,
}
// store keyboard and mouse status
export const useKeyEventStore = create<KeyEventState>((set) => ({
shift: initialKeyEventState.shift,
setShift: (value: boolean) =>
set((state: KeyEventState) => ({ ...state, shift: value })),
focusInput: initialKeyEventState.focusInput,
setFocusInput: (value: boolean) =>
set((state: KeyEventState) => ({ ...state, focusInput: value })),
}))
interface ImageState {
images: Map<string, string>
setImages: (value: any) => void
}
interface VideoFrameState {
videoFrames: Map<string, string[]>
setVideoFrames: (key: string, value: string[]) => void
removeVideoFrames: (key: string) => void
}
export const useImagesStore = create(
persist<ImageState>(
(storeSet) => ({
images: new Map(),
setImages: (value: any) =>
storeSet((state) => {
return {
...state,
images: value,
}
}),
}),
{
name: "image-data-storage",
storage: storage,
}
)
)
export const useVideoFrameStore = create(
persist<VideoFrameState>(
(storeSet) => ({
videoFrames: new Map(),
setVideoFrames: (key: string, value: string[]) =>
storeSet((state) => {
const nextMap = new Map(state.videoFrames)
nextMap.set(key, value)
return {
...state,
videoFrames: nextMap,
}
}),
removeVideoFrames: (key: string) =>
storeSet((state) => {
const nextMap = new Map(state.videoFrames)
nextMap.delete(key)
return {
...state,
videoFrames: nextMap,
}
}),
}),
{
name: "video-frame-storage",
storage: storage,
}
)
)

View File

@@ -0,0 +1,49 @@
const deepCloneFallback = <T>(value: T, cache = new WeakMap()): T => {
if (value === null || typeof value !== "object") return value
if (cache.has(value as object)) return cache.get(value as object)
if (value instanceof Date) return new Date(value.getTime()) as T
if (value instanceof Map) {
const clonedMap = new Map()
cache.set(value as object, clonedMap)
value.forEach((mapValue, mapKey) => {
clonedMap.set(
deepCloneFallback(mapKey, cache),
deepCloneFallback(mapValue, cache)
)
})
return clonedMap as T
}
if (value instanceof Set) {
const clonedSet = new Set()
cache.set(value as object, clonedSet)
value.forEach((setValue) => {
clonedSet.add(deepCloneFallback(setValue, cache))
})
return clonedSet as T
}
if (Array.isArray(value)) {
const clonedArr: any[] = []
cache.set(value as object, clonedArr)
value.forEach((item, index) => {
clonedArr[index] = deepCloneFallback(item, cache)
})
return clonedArr as T
}
const clonedObj: Record<string, any> = {}
cache.set(value as object, clonedObj)
Object.keys(value as object).forEach((key) => {
clonedObj[key] = deepCloneFallback((value as any)[key], cache)
})
return clonedObj as T
}
export const safeClone = <T>(value: T): T => {
try {
return structuredClone(value)
} catch (error) {
console.warn("structuredClone failed, fallback to deep clone", error)
return deepCloneFallback(value)
}
}

View File

@@ -0,0 +1,248 @@
"use client"
export const dateFormat = "YYYY-MM-DD HH:mm:ss"
export const weekDayText = (data: number) => {
switch (data) {
case 0:
return "星期日"
case 1:
return "星期一"
case 2:
return "星期二"
case 3:
return "星期三"
case 4:
return "星期四"
case 5:
return "星期五"
case 6:
return "星期六"
default:
return "-"
}
}
// 标注任务工序
export const processOpts = [
{ label: "标注", value: "标注" },
{ label: "审核1", value: "审核1" },
{ label: "审核2", value: "审核2" },
]
// 状态码
export const STATUS_CODE = new Map([
[1, "已创建"],
[101, "已创建"], // 审核
[102, "已创建"], // 复审
[103, "已创建"], // 审核3
[180, "数据同步中"],
[190, "Embedding生成中"],
[191, "Embedding生成失败"],
[199, "审核失败"],
[201, "试标阶段"], // 试标中
[202, "试标阶段"], // 试标完成
[203, "试标阶段"], // 试标确认
[500, "标注中"],
[580, "待验收"],
[600, "已完成"],
[700, "已终止"],
])
// 状态码
export const projectStatusOpts = [
{ label: "试标阶段", value: "201,202,203" },
{ label: "标注中", value: 500 },
{ label: "待验收", value: 580 },
{ label: "已完成", value: 600 },
// { label: "审核失败", value: 199 },
{ label: "已终止", value: 700 },
]
export const CREATED_CODE = [1, 101, 102, 103, 190, 191, 199] // 已创建的状态码数组
export const ALL_CODE = [201, 202, 203, 500, 580, 600, 700] // 其他的状态码数组
export const PRELABEL_CODE = [201, 202, 203]
export const LABEL_CODE = [500, 580]
export const projectLabelTypeOpts = [
{ label: "点云框", value: 1 },
{ label: "点云分割", value: 2 },
{ label: "点云分类", value: 3 },
{ label: "图片", value: 4 },
{ label: "大语言基础", value: 5 },
{ label: "大语言QA", value: 6 },
{ label: "视频", value: 7 },
]
// 项目列表中状态码对应值及style
export const statusStyles = {
1: {
label: "已创建",
styleText: "text-[#1874FF] bg-[#E8F0FC]",
},
101: {
label: "已创建",
styleText: "text-[#1874FF] bg-[#E8F0FC]",
},
102: {
label: "已创建",
styleText: "text-[#1874FF] bg-[#E8F0FC]",
},
103: {
label: "已创建",
styleText: "text-[#1874FF] bg-[#E8F0FC]",
},
190: {
label: "Embedding生成中",
styleText: "text-[#1874FF] bg-[#E8F0FC]",
},
191: {
label: "Embedding生成失败",
styleText: "text-[#D9333E] bg-[#FBEBEC]",
},
199: {
label: "审核失败",
styleText: "text-[#D9333E] bg-[#FBEBEC]",
},
201: {
label: "试标阶段",
styleText: "text-[#21D3ED] bg-[#EEFDFF]",
},
202: {
label: "试标阶段",
styleText: "text-[#21D3ED] bg-[#EEFDFF]",
},
203: {
label: "试标阶段",
styleText: "text-[#21D3ED] bg-[#EEFDFF]",
},
500: {
label: "标注中",
styleText: "text-[#1874FF] bg-[#E8F0FC]",
},
580: {
label: "待验收",
styleText: "text-[#FFB84D] bg-[#FEF5E7]",
},
600: {
label: "已完成",
styleText: "text-[#4FD796] bg-[#E7F9F0]",
},
700: {
label: "已终止",
styleText: "text-[#34425F] bg-[#F7F8F9]",
},
}
export const staticsColor = [
{ "8_151_156": 1 },
{ "9_109_217": 2 },
{ "15_15_118": 3 },
{ "28_27_175": 4 },
{ "35_84_116": 5 },
{ "45_69_235": 6 },
{ "53_127_127": 7 },
{ "54_124_171": 8 },
{ "54_207_201": 9 },
{ "56_158_13": 10 },
{ "63_18_119": 11 },
{ "69_151_223": 12 },
{ "81_188_187": 13 },
{ "83_29_171": 14 },
{ "84_119_48": 15 },
{ "95_33_176": 16 },
{ "100_16_23": 17 },
{ "113_249_251": 18 },
{ "116_78_41": 19 },
{ "125_179_75": 20 },
{ "127_48_236": 21 },
{ "127_127_54": 22 },
{ "132_133_239": 23 },
{ "147_210_239": 24 },
{ "151_29_36": 25 },
{ "155_125_62": 26 },
{ "156_239_102": 27 },
{ "161_98_48": 28 },
{ "178_253_253": 29 },
{ "188_136_242": 30 },
{ "189_191_84": 31 },
{ "191_49_47": 32 },
{ "196_29_127": 33 },
{ "202_247_138": 34 },
{ "203_164_81": 35 },
{ "207_19_34": 36 },
{ "212_107_8": 37 },
{ "212_177_6": 38 },
{ "223_134_144": 39 },
{ "233_157_84": 40 },
{ "244_206_159": 41 },
{ "252_202_0": 42 },
{ "252_250_147": 43 },
{ "253_247_87": 44 },
{ "255_191_120": 45 },
{ "255_236_61": 46 },
]
export const labelTypeOpts = [
{ label: "点云框", value: 1 },
{ label: "点云分割", value: 2 },
// { label: '点云分类', value: '点云分类' },
{ label: "2D框", value: 101 },
{ label: "多边形", value: 102 },
{ label: "关键点", value: 103 },
{ label: "多线段", value: 104 },
{ label: "文本描述", value: 201 },
]
export const labelTypeMap = new Map([
[1, "点云框"],
[2, "点云分割"],
[101, "2D框"],
[102, "多边形"],
[103, "关键点"],
[104, "多线段"],
[201, "文本描述"],
])
export const selectModeOpts = [
{ label: "输入", value: 0 },
{ label: "单选", value: 1 },
{ label: "多选", value: 2 },
]
export const selectModeMap = new Map([
[0, "输入"],
[1, "单选"],
[2, "多选"],
])
// 标注任务状态码
export const TaskStatusEnum = new Map([
// [0, "None"],
[1, "待领取"],
[2, "标注中"],
[3, "标注完成"],
[4, "审核中"],
[5, "审核完成"],
[6, "复审中"],
[7, "复审完成"],
[8, "无法标注"],
])
export const OwnTaskStatusOpts = {
0: "全部",
2: "标注中",
4: "审核中",
6: "复审中",
}
export const TaskStatusOpts = {
0: "全部",
1: "待领取",
2: "标注中",
3: "标注完成",
4: "审核中",
5: "审核完成",
6: "复审中",
7: "复审完成",
8: "无法标注",
}