仪评指标联分析模块
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
export * from './iva';
|
||||
export * from './sca';
|
||||
export * from './ticket';
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { pyRequestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 获取已分析的图片列表
|
||||
*/
|
||||
export async function refreshTicketImageList() {
|
||||
return pyRequestClient.get('/llm/getTicketImageList');
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片分析任务
|
||||
*/
|
||||
export async function createTicketImageTask(formData: FormData) {
|
||||
return pyRequestClient.post('/llm/createTicketImageTask', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -8,7 +8,7 @@ const routes: RouteRecordRaw[] = [
|
||||
meta: {
|
||||
icon: 'ic:round-remove-red-eye',
|
||||
authority: ['iva', 'sca', 'ysa'],
|
||||
keepAlive: false,
|
||||
keepAlive: true,
|
||||
order: 2,
|
||||
title: $t('计算机视觉'),
|
||||
},
|
||||
@@ -48,6 +48,17 @@ const routes: RouteRecordRaw[] = [
|
||||
},
|
||||
component: () => import('#/views/cv/ysa/index.vue'),
|
||||
},
|
||||
{
|
||||
name: 'TICKET',
|
||||
path: '/cv/ticket',
|
||||
meta: {
|
||||
authority: ['ticket'],
|
||||
icon: 'mdi:ticket-confirmation',
|
||||
title: $t('仪评指标联分析'),
|
||||
keepAlive: true,
|
||||
},
|
||||
component: () => import('#/views/cv/ticket/index.vue'),
|
||||
},
|
||||
{
|
||||
name: 'CVAT',
|
||||
path: '/cv/cvat',
|
||||
|
||||
@@ -27,6 +27,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
onConfirm() {
|
||||
if (!projectName.value || !selectedFile.value) {
|
||||
message.warning('请填写项目名并选择视频文件');
|
||||
return; // 阻止继续执行
|
||||
}
|
||||
uploadFile();
|
||||
},
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Form, Input, message } from 'ant-design-vue';
|
||||
|
||||
import { createVideoTask } from '#/api/cv/iva';
|
||||
|
||||
const projectName = ref('');
|
||||
const year = ref<null | number>(null);
|
||||
const month = ref<null | number>(null);
|
||||
const day = ref<null | number>(null);
|
||||
const hours = ref<null | number>(null);
|
||||
const minutes = ref<null | number>(null);
|
||||
const seconds = ref<null | number>(null);
|
||||
const fileName = ref('');
|
||||
const selectedFile = ref<File | null>(null);
|
||||
const fileInputRef = ref<HTMLInputElement | null>(null);
|
||||
@@ -27,6 +18,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
onConfirm() {
|
||||
if (!projectName.value || !selectedFile.value) {
|
||||
message.warning('请填写项目名并选择蚕茧图片');
|
||||
return;
|
||||
}
|
||||
uploadFile();
|
||||
},
|
||||
@@ -41,12 +33,6 @@ async function uploadFile() {
|
||||
modalApi.close();
|
||||
// 清空表单
|
||||
projectName.value = '';
|
||||
year.value = null;
|
||||
month.value = null;
|
||||
day.value = null;
|
||||
hours.value = null;
|
||||
minutes.value = null;
|
||||
seconds.value = null;
|
||||
fileName.value = '';
|
||||
selectedFile.value = null;
|
||||
});
|
||||
@@ -60,7 +46,7 @@ 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;
|
||||
fileName.value = files[0].name;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
<script setup lang="ts">
|
||||
import { onDeactivated, onMounted, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Form, Input, message } from 'ant-design-vue';
|
||||
|
||||
import * as api from '#/api';
|
||||
|
||||
const list = ref<any[]>([]);
|
||||
const error = ref<null | string>(null);
|
||||
const selectedItem = ref<any>(null);
|
||||
|
||||
async function loadList() {
|
||||
error.value = null;
|
||||
list.value = (await api.refreshTicketImageList()) || [];
|
||||
}
|
||||
|
||||
function createTask() {
|
||||
modalApi.open();
|
||||
}
|
||||
|
||||
async function selectItem(item: any) {
|
||||
selectedItem.value = item;
|
||||
refreshLineChart();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadList();
|
||||
});
|
||||
|
||||
const showInfoStr = ref<Record<string, number | string>>({});
|
||||
const showInfoStr2 = ref<Record<string, number | string>>({});
|
||||
|
||||
function refreshLineChart() {
|
||||
const data = selectedItem.value;
|
||||
showInfoStr.value = {
|
||||
项目名: data.name,
|
||||
上传时间: data.created_at,
|
||||
文件名: data.file_name,
|
||||
文件大小: `${data.size} MB`,
|
||||
分辨率: data.resolution,
|
||||
};
|
||||
showInfoStr2.value = {
|
||||
含水量: `${data.moisture_content} %`,
|
||||
下足茧重: `${data.cocoon_weight} g`,
|
||||
'非蛹/僵蚕': `${data.defective_pupa_count} 粒`,
|
||||
鲜壳量: `${data.fresh_shell_weight} g`,
|
||||
小样数: `${data.sample_count} 粒`,
|
||||
净重合计: `${data.net_weight_total} kg`,
|
||||
仪评人: data.evaluator,
|
||||
复核人: data.reviewer,
|
||||
条码: data.barcode,
|
||||
};
|
||||
}
|
||||
|
||||
onDeactivated(() => {
|
||||
// 离开路由时清理状态
|
||||
selectedItem.value = null;
|
||||
showInfoStr.value = {};
|
||||
});
|
||||
|
||||
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() {
|
||||
if (!selectedFile.value) {
|
||||
message.warning('请选择图片');
|
||||
return;
|
||||
}
|
||||
|
||||
// 先关闭弹窗
|
||||
modalApi.close();
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', selectedFile.value);
|
||||
formData.append('projectName', projectName.value);
|
||||
await api.createTicketImageTask(formData);
|
||||
|
||||
// 接口完成后再触发事件
|
||||
message.success('分析任务完成');
|
||||
loadList();
|
||||
|
||||
// 清空表单
|
||||
selectedFile.value = null;
|
||||
projectName.value = '';
|
||||
fileName.value = '';
|
||||
} catch {
|
||||
message.error('上传失败');
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-[90dvh] w-full flex-col">
|
||||
<Modal>
|
||||
<Form layout="vertical">
|
||||
<Form.Item label="任务名称">
|
||||
<Input v-model:value="projectName" placeholder="可为空,将取随机值" />
|
||||
</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>
|
||||
<BaseModal />
|
||||
<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>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<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.created_at }}</div>
|
||||
</div>
|
||||
</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-28 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">
|
||||
<div
|
||||
v-for="(value, key) in showInfoStr2"
|
||||
: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>
|
||||
|
||||
<!-- 右侧 -->
|
||||
<div class="flex flex-1 flex-col gap-4">
|
||||
<!-- 上:左右两个图片显示 -->
|
||||
<!-- 左图 -->
|
||||
<div
|
||||
class="flex h-full w-full items-center justify-center rounded border bg-white p-4"
|
||||
>
|
||||
<img
|
||||
:src="selectedItem?.oss_url"
|
||||
alt="左图"
|
||||
class="h-[80dvh] w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -2,97 +2,105 @@
|
||||
import type {
|
||||
WorkbenchProjectItem,
|
||||
WorkbenchQuickNavItem,
|
||||
} from "@vben/common-ui";
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import { useRouter } from "vue-router";
|
||||
import { WorkbenchHeader, WorkbenchQuickNav } from "@vben/common-ui";
|
||||
import { preferences } from "@vben/preferences";
|
||||
import { useUserStore } from "@vben/stores";
|
||||
import { openWindow } from "@vben/utils";
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { WorkbenchHeader, WorkbenchQuickNav } from '@vben/common-ui';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { useUserStore } from '@vben/stores';
|
||||
import { openWindow } from '@vben/utils';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 同样,这里的 url 也可以使用以 http 开头的外部链接
|
||||
const cv: WorkbenchQuickNavItem[] = [
|
||||
{
|
||||
color: "#3fb27f",
|
||||
color: '#3fb27f',
|
||||
authority: ['iva'],
|
||||
icon: "mdi:video",
|
||||
title: "视频智能分析",
|
||||
url: "/cv/iva"
|
||||
icon: 'mdi:video',
|
||||
title: '视频智能分析',
|
||||
url: '/cv/iva',
|
||||
},
|
||||
{
|
||||
color: "#3fb27f",
|
||||
color: '#3fb27f',
|
||||
authority: ['sca'],
|
||||
icon: "mdi:ice-cream",
|
||||
title: "蚕茧仪评分析",
|
||||
url: "/cv/sca"
|
||||
icon: 'mdi:ice-cream',
|
||||
title: '蚕茧仪评分析',
|
||||
url: '/cv/sca',
|
||||
},
|
||||
{
|
||||
color: "#3fb27f",
|
||||
color: '#3fb27f',
|
||||
authority: ['ysa'],
|
||||
icon: "mdi:waveform",
|
||||
title: "催青阶段分析",
|
||||
url: "/cv/ysa"
|
||||
icon: 'mdi:waveform',
|
||||
title: '催青阶段分析',
|
||||
url: '/cv/ysa',
|
||||
},
|
||||
{
|
||||
color: "#3fb27f",
|
||||
icon: "ion:bar-chart-outline",
|
||||
title: "标注平台入口",
|
||||
color: '#3fb27f',
|
||||
authority: ['ticket'],
|
||||
icon: 'mdi:ticket-confirmation',
|
||||
title: '仪评指标联分析',
|
||||
url: '/cv/ticket',
|
||||
},
|
||||
{
|
||||
color: '#3fb27f',
|
||||
icon: 'ion:bar-chart-outline',
|
||||
title: '标注平台入口',
|
||||
authority: ['user'],
|
||||
url: "http://171.212.101.199:13013/"
|
||||
url: 'http://171.212.101.199:13013/',
|
||||
},
|
||||
];
|
||||
const llm: WorkbenchQuickNavItem[] = [
|
||||
{
|
||||
color: "#1fdaca",
|
||||
color: '#1fdaca',
|
||||
authority: ['bot'],
|
||||
icon: "mdi:face-agent",
|
||||
title: "通用智能体",
|
||||
url: "/llm/bot"
|
||||
icon: 'mdi:face-agent',
|
||||
title: '通用智能体',
|
||||
url: '/llm/bot',
|
||||
},
|
||||
{
|
||||
color: "#1fdaca",
|
||||
color: '#1fdaca',
|
||||
authority: ['report'],
|
||||
icon: 'mdi:set-center',
|
||||
title: "智农观数阁",
|
||||
url: "/llm/report/report-chat"
|
||||
title: '智农观数阁',
|
||||
url: '/llm/report/report-chat',
|
||||
},
|
||||
{
|
||||
color: "#1fdaca",
|
||||
color: '#1fdaca',
|
||||
authority: ['service'],
|
||||
icon: 'mdi:android-head',
|
||||
title: "灵思智服阁",
|
||||
url: "/llm/service/service-chat"
|
||||
title: '灵思智服阁',
|
||||
url: '/llm/service/service-chat',
|
||||
},
|
||||
];
|
||||
const common: WorkbenchQuickNavItem[] = [
|
||||
{
|
||||
color: "#bf0c2c",
|
||||
color: '#bf0c2c',
|
||||
authority: ['remote'],
|
||||
icon: "carbon:workspace",
|
||||
title: "设备远程控制",
|
||||
url: "/remote"
|
||||
icon: 'carbon:workspace',
|
||||
title: '设备远程控制',
|
||||
url: '/remote',
|
||||
},
|
||||
{
|
||||
color: "#bf0c2c",
|
||||
color: '#bf0c2c',
|
||||
icon: 'ion:grid-outline',
|
||||
title: "RAGFlow",
|
||||
title: 'RAGFlow',
|
||||
authority: ['user'],
|
||||
url: "/out/rag"
|
||||
url: '/out/rag',
|
||||
},
|
||||
];
|
||||
const router = useRouter();
|
||||
|
||||
// 这是一个示例方法,实际项目中需要根据实际情况进行调整
|
||||
function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
|
||||
if (nav.url?.startsWith("http")) {
|
||||
if (nav.url?.startsWith('http')) {
|
||||
openWindow(nav.url);
|
||||
return;
|
||||
}
|
||||
if (nav.url?.startsWith("/")) {
|
||||
if (nav.url?.startsWith('/')) {
|
||||
router.push(nav.url).catch((error) => {
|
||||
console.error("Navigation failed:", error);
|
||||
console.error('Navigation failed:', error);
|
||||
});
|
||||
} else {
|
||||
console.warn(`Unknown URL for navigation item: ${nav.title} -> ${nav.url}`);
|
||||
@@ -101,10 +109,10 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
|
||||
|
||||
function getGreeting() {
|
||||
const hour = new Date().getHours();
|
||||
if (hour < 6) return "凌晨好";
|
||||
if (hour < 12) return "早安";
|
||||
if (hour < 18) return "下午好";
|
||||
return "晚上好";
|
||||
if (hour < 6) return '凌晨好';
|
||||
if (hour < 12) return '早安';
|
||||
if (hour < 18) return '下午好';
|
||||
return '晚上好';
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user