调整数据库表名,增加催青阶段分析演示模块
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
from .aimessage import *
|
||||
from .aiprofile import *
|
||||
from .aisession import *
|
||||
from .image_ysa import *
|
||||
from .iot import *
|
||||
from .knowledge import *
|
||||
from .license import *
|
||||
|
||||
@@ -41,7 +41,7 @@ def get_all_ai_bot(user_id: str, module: str) -> List[Dict]:
|
||||
with pg_pool.getConn() as conn:
|
||||
with conn.cursor() as cur:
|
||||
# 查询用户角色
|
||||
cur.execute("SELECT roles FROM users WHERE id = %s", (user_id,))
|
||||
cur.execute("SELECT roles FROM sys_users WHERE id = %s", (user_id,))
|
||||
role_row = cur.fetchone()
|
||||
if not role_row:
|
||||
return [] # 用户不存在
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import json
|
||||
|
||||
from config.minIO import get_temp_url
|
||||
from config.pgDb import pg_pool
|
||||
from utils import MyUtils
|
||||
|
||||
|
||||
def insert_ysa_image(
|
||||
file_name,
|
||||
resolution,
|
||||
size,
|
||||
cocoon_count,
|
||||
max_confidence,
|
||||
min_confidence,
|
||||
average_confidence,
|
||||
other_info,
|
||||
preprocess_time_ms,
|
||||
inference_time_ms,
|
||||
postprocess_time_ms,
|
||||
name,
|
||||
image_pre,
|
||||
image_after,
|
||||
created_by,
|
||||
):
|
||||
with pg_pool.getConn() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
other_info = json.dumps(other_info)
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO image_sca (
|
||||
upload_datetime, file_name, resolution, size, cocoon_count, max_confidence, min_confidence,
|
||||
average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms, name, image_pre, image_after, created_by
|
||||
)
|
||||
VALUES (NOW(), %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )
|
||||
RETURNING id
|
||||
""",
|
||||
(
|
||||
file_name,
|
||||
resolution,
|
||||
size,
|
||||
cocoon_count,
|
||||
max_confidence,
|
||||
min_confidence,
|
||||
average_confidence,
|
||||
other_info,
|
||||
preprocess_time_ms,
|
||||
inference_time_ms,
|
||||
postprocess_time_ms,
|
||||
name,
|
||||
image_pre,
|
||||
image_after,
|
||||
created_by,
|
||||
),
|
||||
)
|
||||
new_id = cursor.fetchone()[0]
|
||||
conn.commit()
|
||||
return new_id
|
||||
|
||||
|
||||
def get_ysa_image_list(user_id, name, page=1, page_size=10):
|
||||
"""
|
||||
获取用户已分析图片列表,带分页
|
||||
"""
|
||||
offset = (page - 1) * page_size
|
||||
|
||||
with pg_pool.getConn() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
# 1️⃣ 查询总条数
|
||||
# ✅ 改进版:支持 name 为空时统计全部,不为空时模糊统计
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT COUNT(*)
|
||||
FROM image_ysa
|
||||
WHERE created_by = %s
|
||||
AND (%s = '' OR name LIKE '%%' || %s || '%%')
|
||||
""",
|
||||
(user_id, name, name),
|
||||
)
|
||||
|
||||
total = cursor.fetchone()[0]
|
||||
|
||||
# 2️⃣ 查询当前页数据
|
||||
# ✅ 改进版
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT id, name, upload_datetime, file_name, image_pre, image_after, resolution,
|
||||
size, silkworm_count, max_confidence, min_confidence, average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms
|
||||
FROM image_ysa
|
||||
WHERE created_by = %s
|
||||
AND (%s = '' OR name LIKE '%%' || %s || '%%')
|
||||
ORDER BY upload_datetime DESC
|
||||
LIMIT %s OFFSET %s
|
||||
""",
|
||||
(user_id, name, name, page_size, offset),
|
||||
)
|
||||
|
||||
rows = cursor.fetchall()
|
||||
|
||||
result = []
|
||||
for row in rows:
|
||||
result.append(
|
||||
{
|
||||
"id": row[0],
|
||||
"name": row[1],
|
||||
"upload_datetime": MyUtils.format_datetime(row[2]),
|
||||
"file_name": row[3],
|
||||
"image_pre": get_temp_url("image-ysa", "raw/" + row[4]),
|
||||
"image_after": get_temp_url("image-ysa", "ai/" + row[5]),
|
||||
"resolution": row[6],
|
||||
"size": MyUtils.safe_round(row[7] / 1024, 2),
|
||||
"silkworm_count": row[8],
|
||||
"max_confidence": row[9],
|
||||
"min_confidence": row[10],
|
||||
"average_confidence": row[11],
|
||||
"other_info": row[12],
|
||||
"preprocess_time_ms": MyUtils.safe_round(row[13], 4),
|
||||
"inference_time_ms": MyUtils.safe_round(row[14], 4),
|
||||
"postprocess_time_ms": MyUtils.safe_round(row[15], 4),
|
||||
}
|
||||
)
|
||||
|
||||
return total, result
|
||||
@@ -10,7 +10,7 @@ def insert_license_image(
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO license_images (
|
||||
INSERT INTO image_license (
|
||||
created_by, created_at, file_name, resolution, size, name, oss,
|
||||
type, content
|
||||
)
|
||||
@@ -36,7 +36,7 @@ def get_license_image_list(user_id, page=1, page_size=10):
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT COUNT(*)
|
||||
FROM license_images
|
||||
FROM image_license
|
||||
WHERE created_by = %s
|
||||
""",
|
||||
(user_id,),
|
||||
@@ -47,7 +47,7 @@ def get_license_image_list(user_id, page=1, page_size=10):
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT created_at, file_name, resolution, size, name, oss, id, type, content
|
||||
FROM license_images
|
||||
FROM image_license
|
||||
WHERE created_by = %s
|
||||
ORDER BY created_at DESC
|
||||
LIMIT %s OFFSET %s
|
||||
|
||||
@@ -27,7 +27,7 @@ def insert_sca_image(
|
||||
other_info = json.dumps(other_info)
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO sca_images (
|
||||
INSERT INTO image_sca (
|
||||
upload_datetime, file_name, resolution, size, cocoon_count, max_confidence, min_confidence,
|
||||
average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms, name, image_pre, image_after, created_by
|
||||
)
|
||||
@@ -70,7 +70,7 @@ def get_sca_image_list(user_id, name, page=1, page_size=10):
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT COUNT(*)
|
||||
FROM sca_images
|
||||
FROM image_sca
|
||||
WHERE created_by = %s
|
||||
AND (%s = '' OR name LIKE '%%' || %s || '%%')
|
||||
""",
|
||||
@@ -85,7 +85,7 @@ def get_sca_image_list(user_id, name, page=1, page_size=10):
|
||||
"""
|
||||
SELECT id, name, upload_datetime, file_name, image_pre, image_after, resolution,
|
||||
size, cocoon_count, max_confidence, min_confidence, average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms
|
||||
FROM sca_images
|
||||
FROM image_sca
|
||||
WHERE created_by = %s
|
||||
AND (%s = '' OR name LIKE '%%' || %s || '%%')
|
||||
ORDER BY upload_datetime DESC
|
||||
|
||||
@@ -17,7 +17,7 @@ def get_sca_video_list(name, page=1, page_size=10):
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT COUNT(*)
|
||||
FROM sca_videos
|
||||
FROM video_sca
|
||||
WHERE (%s = '' OR name LIKE '%%' || %s || '%%')
|
||||
""",
|
||||
(name, name),
|
||||
@@ -30,7 +30,7 @@ def get_sca_video_list(name, page=1, page_size=10):
|
||||
SELECT id, name, raw_object_name, ai_object_name, duration, size, video_codec, audio_codec,
|
||||
overall_bit_rate, resolution, sc_analysis_time, sc_analysis_total_count, sc_analysis_max_count,
|
||||
sc_analysis_primary_type, sc_analysis_secondary_type, other_info, created_at
|
||||
FROM sca_videos
|
||||
FROM video_sca
|
||||
WHERE (%s = '' OR name LIKE '%%' || %s || '%%')
|
||||
ORDER BY created_at DESC
|
||||
LIMIT %s OFFSET %s
|
||||
@@ -67,7 +67,7 @@ def get_sca_video_list(name, page=1, page_size=10):
|
||||
return total, result
|
||||
|
||||
|
||||
def get_sca_video_details(v_id):
|
||||
def get_video_sca_details(v_id):
|
||||
"""
|
||||
获取指定视频的分析明细列表
|
||||
"""
|
||||
@@ -76,7 +76,7 @@ def get_sca_video_details(v_id):
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT id, v_id, time_stamp, other_info
|
||||
FROM sca_video_details
|
||||
FROM video_sca_details
|
||||
WHERE v_id = %s
|
||||
ORDER BY time_stamp ASC
|
||||
""",
|
||||
|
||||
@@ -446,7 +446,7 @@ def get_user_list_db_page(
|
||||
with conn.cursor() as cursor:
|
||||
|
||||
# ---- 统计总数 ----
|
||||
count_sql = f"SELECT COUNT(*) FROM users u {where_clause};"
|
||||
count_sql = f"SELECT COUNT(*) FROM sys_users u {where_clause};"
|
||||
cursor.execute(count_sql, params)
|
||||
total = cursor.fetchone()[0]
|
||||
|
||||
@@ -461,7 +461,7 @@ def get_user_list_db_page(
|
||||
u.dept_id,
|
||||
d.name AS dept_name,
|
||||
u.created_at
|
||||
FROM users u
|
||||
FROM sys_users u
|
||||
LEFT JOIN sys_dept d ON u.dept_id = d.id
|
||||
{where_clause}
|
||||
ORDER BY u.created_at DESC
|
||||
@@ -511,7 +511,9 @@ def get_user_list_db_page(
|
||||
def db_user_name_exists(username: str) -> bool:
|
||||
with pg_pool.getConn() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute("SELECT COUNT(*) FROM users WHERE email = %s;", (username,))
|
||||
cursor.execute(
|
||||
"SELECT COUNT(*) FROM sys_users WHERE email = %s;", (username,)
|
||||
)
|
||||
return cursor.fetchone()[0] > 0
|
||||
|
||||
|
||||
@@ -521,7 +523,7 @@ def insert_user(data: dict) -> str:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO users (username, email, phone, is_active, dept_id, password_hash)
|
||||
INSERT INTO sys_users (username, email, phone, is_active, dept_id, password_hash)
|
||||
VALUES (%s, %s, %s, %s, %s, %s) RETURNING id;
|
||||
""",
|
||||
(
|
||||
@@ -578,7 +580,7 @@ def patch_user_db(id: str, data: dict) -> int:
|
||||
|
||||
# 如果有需要更新的字段
|
||||
if fields:
|
||||
sql = f"UPDATE users SET {', '.join(fields)} WHERE id = %s"
|
||||
sql = f"UPDATE sys_users SET {', '.join(fields)} WHERE id = %s"
|
||||
params.append(id)
|
||||
cursor.execute(sql, tuple(params))
|
||||
|
||||
@@ -603,7 +605,7 @@ def delete_user_db(id: str) -> int:
|
||||
with pg_pool.getConn() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute("DELETE FROM sys_user_role WHERE user_id=%s;", (id,))
|
||||
cursor.execute("DELETE FROM users WHERE id=%s;", (id,))
|
||||
cursor.execute("DELETE FROM sys_users WHERE id=%s;", (id,))
|
||||
conn.commit()
|
||||
return cursor.rowcount
|
||||
|
||||
@@ -882,7 +884,7 @@ def get_dept_ids_by_user_id(user_id: UUID) -> list:
|
||||
# 第一步:通过 user_id 查找其所属的 dept_id
|
||||
with pg_pool.getConn() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute("SELECT dept_id FROM users WHERE id = %s", (user_id,))
|
||||
cursor.execute("SELECT dept_id FROM sys_users WHERE id = %s", (user_id,))
|
||||
dept_id = cursor.fetchone()
|
||||
|
||||
if not dept_id:
|
||||
@@ -914,7 +916,7 @@ def get_dept_ids_by_user_id(user_id: UUID) -> list:
|
||||
# 第一步:通过 user_id 查找其所属的 dept_id
|
||||
with pg_pool.getConn() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute("SELECT dept_id FROM users WHERE id = %s", (user_id,))
|
||||
cursor.execute("SELECT dept_id FROM sys_users WHERE id = %s", (user_id,))
|
||||
dept_id = cursor.fetchone()
|
||||
|
||||
if not dept_id:
|
||||
@@ -946,7 +948,7 @@ def get_dept_id_by_user_id(user_id: str) -> str:
|
||||
# 通过 user_id 查找其所属的 dept_id
|
||||
with pg_pool.getConn() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute("SELECT dept_id FROM users WHERE id = %s", (user_id,))
|
||||
cursor.execute("SELECT dept_id FROM sys_users WHERE id = %s", (user_id,))
|
||||
dept_id = cursor.fetchone()
|
||||
dept_id = dept_id[0]
|
||||
return str(dept_id)
|
||||
|
||||
@@ -12,7 +12,7 @@ def get_ticket_image_list(user_id):
|
||||
moisture_content, cocoon_weight, defective_pupa_count,
|
||||
fresh_shell_weight, sample_count, barcode, oss,
|
||||
net_weight_total, evaluator, reviewer,id ,dead_pupa_count
|
||||
FROM ticket_images
|
||||
FROM image_ticket
|
||||
WHERE created_by = %s
|
||||
""",
|
||||
(user_id,),
|
||||
@@ -66,7 +66,7 @@ def insert_ticket_image(
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO ticket_images (
|
||||
INSERT INTO image_ticket (
|
||||
created_by, file_name, resolution, size, name,
|
||||
moisture_content, cocoon_weight, defective_pupa_count, dead_pupa_count,
|
||||
fresh_shell_weight, sample_count, barcode, oss,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from sqlalchemy import text
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy import text
|
||||
from config.pgDb import pg_pool
|
||||
from config.ssDb import mssql_pool
|
||||
from sqlalchemy import text
|
||||
|
||||
|
||||
def executeSQL(sql: str):
|
||||
"""
|
||||
@@ -14,11 +13,14 @@ def executeSQL(sql: str):
|
||||
# SQLAlchemy 2.x 返回 Row 对象,转成 dict
|
||||
return [dict(row._mapping) for row in result]
|
||||
|
||||
|
||||
def get_company_list(user_id: str):
|
||||
# 1️⃣ 从 PostgreSQL 获取 tenant_id
|
||||
with pg_pool.getConn() as pg_conn:
|
||||
with pg_conn.cursor() as cur:
|
||||
cur.execute("SELECT bbit_tenant_id FROM users WHERE id = %s", (user_id,))
|
||||
cur.execute(
|
||||
"SELECT bbit_tenant_id FROM sys_users WHERE id = %s", (user_id,)
|
||||
)
|
||||
row = cur.fetchone()
|
||||
tenant_id = row[0] if row else None
|
||||
|
||||
@@ -33,5 +35,3 @@ def get_company_list(user_id: str):
|
||||
with mssql_pool.getConn() as mssql_conn:
|
||||
result = mssql_conn.execute(query, params)
|
||||
return [{"id": str(row[0]), "name": row[1]} for row in result.fetchall()]
|
||||
|
||||
|
||||
@@ -153,6 +153,25 @@ def getSilkwormCocoonAnalysisTasks(
|
||||
)
|
||||
|
||||
|
||||
# ————————————————————————————————催青阶段分析————————————————————————————————————————————————
|
||||
@visionRouter.get("/getPromoteStageAnalysisTasks")
|
||||
def getPromoteStageAnalysisTasks(
|
||||
user_id: UUID = Depends(get_user_id_from_token),
|
||||
name: str = "",
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(10, ge=1, le=100),
|
||||
):
|
||||
if not user_id:
|
||||
return {"error": "userId is required"}
|
||||
total, items = pg.get_ysa_image_list(user_id, name, page=page, page_size=page_size)
|
||||
return BaseResponse(
|
||||
data={
|
||||
"total": total,
|
||||
"items": items,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# ————————————————————————————————蚕茧视频识别任务————————————————————————————————————————————————
|
||||
|
||||
|
||||
@@ -190,4 +209,4 @@ def getAnalyticsDetailBySCVideoId(
|
||||
):
|
||||
if not user_id:
|
||||
return {"error": "userId is required"}
|
||||
return BaseResponse(data=pg.get_sca_video_details(vId))
|
||||
return BaseResponse(data=pg.get_video_sca_details(vId))
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
import org.jetbrains.exposed.v1.json.json
|
||||
|
||||
object UsersTable : UUIDTable("users") {
|
||||
object UsersTable : UUIDTable("sys_users") {
|
||||
// 用户名
|
||||
val username = varchar("username", length = 50).nullable()
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
||||
|
||||
|
||||
object VideoAnalyticsDetailsTable : UUIDTable("video_analytics_details") {
|
||||
object VideoAnalyticsDetailsTable : UUIDTable("video_act_details") {
|
||||
val vId = uuid("v_id").references(VideosTable.id)
|
||||
val aTimeStamp = integer("time_stamp")
|
||||
val aTotalPeople = integer("total_people")
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.v1.datetime.datetime
|
||||
|
||||
|
||||
object VideosTable : UUIDTable("videos") {
|
||||
object VideosTable : UUIDTable("video_act") {
|
||||
val vName = varchar("name", 255)
|
||||
val vObjectName = varchar("object_name", 255)
|
||||
val vFileName = varchar("file_name", 255)
|
||||
|
||||
@@ -4,3 +4,4 @@ export * from './license';
|
||||
export * from './sca';
|
||||
export * from './sca2';
|
||||
export * from './ticket';
|
||||
export * from './ysa';
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { pyRequestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 获取已分析的图片列表
|
||||
*/
|
||||
export async function getSilkwormCocoonAnalysisTasks(
|
||||
name = '',
|
||||
page = 1,
|
||||
pageSize = 9,
|
||||
) {
|
||||
return pyRequestClient.get('/cv/getPromoteStageAnalysisTasks', {
|
||||
params: { name, page, page_size: pageSize },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片分析任务
|
||||
*/
|
||||
export async function createSilkwormCocoonAnalysisTask(formData: FormData) {
|
||||
return pyRequestClient.post('/cv/createPromoteStageAnalysisTask', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,390 @@
|
||||
<script setup lang="ts">
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
|
||||
import {
|
||||
computed,
|
||||
onActivated,
|
||||
onDeactivated,
|
||||
onMounted,
|
||||
ref,
|
||||
watch,
|
||||
} from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
|
||||
import { Button, Form, Input, message } from 'ant-design-vue';
|
||||
|
||||
import * as api from '#/api';
|
||||
import { createSilkwormCocoonAnalysisTask } from '#/api';
|
||||
|
||||
const list = ref<any[]>([]);
|
||||
const error = ref<null | string>(null);
|
||||
const filterKeyword = ref('');
|
||||
const selectedItem = ref<any>(null);
|
||||
async function loadList() {
|
||||
error.value = null;
|
||||
const res = await api.getSilkwormCocoonAnalysisTasks(
|
||||
filterKeyword.value,
|
||||
page.value,
|
||||
pageSize.value,
|
||||
);
|
||||
list.value = res.items || [];
|
||||
total.value = res.total;
|
||||
}
|
||||
function refreshList() {
|
||||
filterKeyword.value = '';
|
||||
loadList();
|
||||
message.success('列表加载完成');
|
||||
}
|
||||
function createTask() {
|
||||
modalApi.open();
|
||||
}
|
||||
|
||||
async function selectItem(item: any) {
|
||||
selectedItem.value = item;
|
||||
refreshLineChart();
|
||||
}
|
||||
// 监听关键词变化,调用防抖接口
|
||||
watch(filterKeyword, () => {
|
||||
loadList();
|
||||
});
|
||||
watch(selectedItem, () => {});
|
||||
onMounted(() => {
|
||||
loadList();
|
||||
});
|
||||
|
||||
const showInfoStr = ref<Record<string, number | string>>({});
|
||||
|
||||
const chartRef1 = ref<EchartsUIType>();
|
||||
const { renderEcharts: renderEcharts1 } = useEcharts(chartRef1);
|
||||
|
||||
const chartRef2 = ref<EchartsUIType>();
|
||||
const { renderEcharts: renderEcharts2 } = useEcharts(chartRef2);
|
||||
|
||||
function refreshLineChart() {
|
||||
const data = selectedItem.value;
|
||||
showInfoStr.value = {
|
||||
项目名: data.name,
|
||||
项目上传时间: data.upload_datetime,
|
||||
文件名: data.file_name,
|
||||
文件大小: `${data.size} MB`,
|
||||
分辨率: data.resolution,
|
||||
最高置信度: data.max_confidence,
|
||||
最低置信度: data.min_confidence,
|
||||
平均置信度: data.average_confidence,
|
||||
预处理时间: `${data.preprocess_time_ms} ms`,
|
||||
推理时间: `${data.inference_time_ms} ms`,
|
||||
后处理时间: `${data.postprocess_time_ms} ms`,
|
||||
};
|
||||
|
||||
renderEcharts1({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: Object.keys(data.other_info),
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '数量',
|
||||
type: 'bar',
|
||||
barWidth: '30%',
|
||||
data: Object.values(data.other_info).map(Number),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
renderEcharts2({
|
||||
legend: { top: '5%', left: 'center' },
|
||||
series: [
|
||||
{
|
||||
animationDelay() {
|
||||
return Math.random() * 100;
|
||||
},
|
||||
animationEasing: 'exponentialInOut',
|
||||
animationType: 'scale',
|
||||
avoidLabelOverlap: false,
|
||||
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
|
||||
data: Object.entries(data.other_info).map(([key, val]) => ({
|
||||
name: key,
|
||||
value: Number(val),
|
||||
})),
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderWidth: 3,
|
||||
},
|
||||
label: {
|
||||
position: 'center',
|
||||
show: false,
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
padAngle: 5,
|
||||
name: '蚕茧分类',
|
||||
radius: ['10%', '70%'],
|
||||
type: 'pie',
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onDeactivated(() => {
|
||||
// 离开路由时清理状态
|
||||
selectedItem.value = null;
|
||||
showInfoStr.value = {};
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
// 回来的时候重新刷新一次列表
|
||||
loadList();
|
||||
});
|
||||
|
||||
const projectName = ref('');
|
||||
const fileName = ref('');
|
||||
const selectedFile = ref<File | null>(null);
|
||||
const fileInputRef = ref<HTMLInputElement | null>(null);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
title: '新建幼蚕分析任务',
|
||||
class: 'w-[600px]',
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm() {
|
||||
if (!selectedFile.value) {
|
||||
message.warning('请选择幼蚕图片');
|
||||
return;
|
||||
}
|
||||
uploadFile();
|
||||
},
|
||||
});
|
||||
|
||||
async function uploadFile() {
|
||||
// 先关闭弹窗
|
||||
modalApi.close();
|
||||
const formData = new FormData();
|
||||
formData.append('file', selectedFile.value!);
|
||||
formData.append('projectName', projectName.value);
|
||||
await createSilkwormCocoonAnalysisTask(formData).then(() => {
|
||||
// 清空表单
|
||||
projectName.value = '';
|
||||
fileName.value = '';
|
||||
selectedFile.value = null;
|
||||
});
|
||||
|
||||
// 接口完成后再触发事件
|
||||
message.success('分析完成');
|
||||
loadList();
|
||||
}
|
||||
|
||||
function selectFile() {
|
||||
fileInputRef.value?.click();
|
||||
}
|
||||
|
||||
function handleFileChange(event: Event) {
|
||||
const files = (event.target as HTMLInputElement).files;
|
||||
if (files && files.length > 0) {
|
||||
selectedFile.value = files[0];
|
||||
fileName.value = files[0].name;
|
||||
}
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
const page = ref(1);
|
||||
const pageSize = ref(9);
|
||||
const total = ref(0); // 总条数
|
||||
// 计算总页数
|
||||
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
|
||||
|
||||
function changePage(newPage) {
|
||||
page.value = newPage;
|
||||
loadList();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>正在开发中,敬请期待</h1>
|
||||
<div class="flex h-[90dvh] w-full flex-col">
|
||||
<Modal>
|
||||
<Form layout="vertical">
|
||||
<Form.Item label="任务名称">
|
||||
<Input v-model:value="projectName" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="上传幼蚕图片*" required>
|
||||
<div
|
||||
@click="selectFile"
|
||||
style="
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border: 1px dashed #d9d9d9;
|
||||
"
|
||||
>
|
||||
{{ fileName || '点击选择文件' }}
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
ref="fileInputRef"
|
||||
@change="handleFileChange"
|
||||
style="display: none"
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<div class="flex h-full w-full bg-gray-50">
|
||||
<!-- 左侧:筛选 + 列表 -->
|
||||
<div class="flex w-64 flex-col border-r bg-white p-4">
|
||||
<!-- 按钮组 -->
|
||||
<div class="mb-4 flex justify-between space-x-2">
|
||||
<Button
|
||||
type="primary"
|
||||
@click="createTask"
|
||||
class="flex-1"
|
||||
>
|
||||
新建任务
|
||||
</Button>
|
||||
<Button @click="refreshList" class="flex-1"> 刷新列表 </Button>
|
||||
</div>
|
||||
<!-- 筛选框 -->
|
||||
<input
|
||||
v-model="filterKeyword"
|
||||
placeholder="筛选分析任务"
|
||||
class="mb-4 rounded-md border px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
/>
|
||||
<!-- 列表 -->
|
||||
<div class="flex-1 space-y-2 overflow-auto">
|
||||
<div
|
||||
v-for="item in list"
|
||||
:key="item.v_id"
|
||||
@click="selectItem(item)"
|
||||
class="cursor-pointer rounded border p-3 hover:bg-gray-100"
|
||||
:class="{ 'bg-gray-100': item.id === selectedItem?.id }"
|
||||
>
|
||||
<div class="text-base font-medium">{{ item.name }}</div>
|
||||
<div class="text-sm text-gray-400">{{ item.upload_datetime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-2 flex justify-center space-x-2">
|
||||
<button
|
||||
:disabled="page === 1"
|
||||
@click="changePage(page - 1)"
|
||||
class="rounded border px-1"
|
||||
>
|
||||
上一页
|
||||
</button>
|
||||
|
||||
<span>第 {{ page }} 页 / 共 {{ totalPages }} 页</span>
|
||||
|
||||
<button
|
||||
:disabled="page === totalPages"
|
||||
@click="changePage(page + 1)"
|
||||
class="rounded border px-1"
|
||||
>
|
||||
下一页
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:Tab 内容区 -->
|
||||
<div class="flex flex-1 flex-col overflow-hidden p-6">
|
||||
<div
|
||||
v-if="!selectedItem"
|
||||
class="flex h-full items-center justify-center text-gray-400"
|
||||
>
|
||||
请先选择左侧列表中的分析任务
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="flex h-full flex-col gap-4">
|
||||
<!-- 主内容区域:左右结构 -->
|
||||
<div class="flex flex-1 gap-4">
|
||||
<!-- 左侧 -->
|
||||
<div class="flex w-72 flex-col gap-4">
|
||||
<!-- 视频基础信息展示 -->
|
||||
<div
|
||||
class="w-full rounded border bg-white p-4"
|
||||
id="video_base_info"
|
||||
>
|
||||
<div
|
||||
v-for="(value, key) in showInfoStr"
|
||||
:key="key"
|
||||
class="mb-2 flex text-sm text-gray-700"
|
||||
>
|
||||
<div class="w-32 font-medium text-gray-900">
|
||||
{{ key }}:
|
||||
</div>
|
||||
<div class="flex-1 break-all text-gray-600">
|
||||
{{ value || '—' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下:空白卡片 -->
|
||||
<div class="flex-1 rounded border bg-white p-4">
|
||||
<EchartsUI ref="chartRef2" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧 -->
|
||||
<div class="flex flex-1 flex-col gap-4">
|
||||
<!-- 上:左右两个图片显示 -->
|
||||
<div class="flex flex-1 gap-4">
|
||||
<!-- 左图 -->
|
||||
<div
|
||||
class="flex flex-1 items-center justify-center rounded border bg-white p-4"
|
||||
>
|
||||
<img
|
||||
:src="selectedItem?.image_pre"
|
||||
alt=""
|
||||
class="h-[30dvh] w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
<!-- 右图 -->
|
||||
<div
|
||||
class="flex flex-1 items-center justify-center rounded border bg-white p-4"
|
||||
>
|
||||
<img
|
||||
:src="selectedItem?.image_after"
|
||||
alt=""
|
||||
class="h-[30dvh] w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 下:柱状图区域 -->
|
||||
<div class="flex flex-1 flex-col rounded border bg-white p-4">
|
||||
<EchartsUI ref="chartRef1" class="flex-1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user