AI算法画面对接接口

main
zhufu 3 weeks ago
parent a46ff070b2
commit 8e0ad3dd51

@ -0,0 +1,23 @@
import { defHttp } from '@/utils/http/axios';
enum Api {
// 获取算法列表
GetAlgorithmsRepositoryList = '/api/AlgorithmsRepository/GetAlgorithmsRepositoryList',
// 添加算法
AddAlgorithmsRepository = '/api/AlgorithmsRepository/AddAlgorithmsRepository',
// 修改算法
UpdateAlgorithmsRepository = '/api/AlgorithmsRepository/UpdateAlgorithmsRepository',
}
export const GetAlgorithmsRepositoryList = (params) => defHttp.get({
url: Api.GetAlgorithmsRepositoryList,
params,
});
export const AddAlgorithmsRepository = (params) => defHttp.post({
url: Api.AddAlgorithmsRepository,
params,
});
export const UpdateAlgorithmsRepository = (params) => defHttp.post({
url: Api.UpdateAlgorithmsRepository,
params,
});

@ -1,127 +1,156 @@
<template>
<div class="title-div">
<div class="title-span-div">
<div class="title-icon"></div>
<div class="title-span">创建算法</div>
<div class="title-span">{{props.modalType == 'insert'? '创建算法': '编辑算法'}}</div>
</div>
<div class="close-button" @click="emits('changeAddModal',false)"></div>
</div>
<div class="interval"></div>
<div class="modal-content">
<div class="left-content">
<div class="item-title">模型名称</div>
<a-input class="name-input" v-model:value="modelName" placeholder="请输入相关名称" />
<div class="item-title">模型描述</div>
<a-textarea class="item-desc" v-model:value="modelDescription" placeholder="请输入相关描述" :rows="4" />
<div class="item-title">模型封面</div>
<div class="item-image">
<div class="empty-image" @click="changeSelectImageModal(true)">
<div class="empty-image-content">
<div class="empty-image-icon"></div>
<div class="empty-image-span">请选择文件上传</div>
<a-spin tip="加载中..." :spinning="loading">
<div class="modal-content">
<div class="left-content">
<div class="item-title">模型名称</div>
<a-input class="name-input" v-model:value="name" placeholder="请输入相关名称" />
<div class="item-title">模型描述</div>
<a-textarea class="item-desc" v-model:value="describe" placeholder="请输入相关描述" :rows="4" />
<div class="item-title">模型封面</div>
<div class="item-image">
<img v-if="pic" class="show-image" :src="`${VITE_GLOB_API_URL}/${pic}`" @click="changeSelectImageModal(true)"/>
<div v-else class="empty-image" @click="changeSelectImageModal(true)">
<div class="empty-image-content">
<div class="empty-image-icon"></div>
<div class="empty-image-span">请选择文件上传</div>
</div>
</div>
</div>
</div>
</div>
<div class="right-content">
<div class="item-title">模型类型</div>
<a-segmented class="item-type" v-model:value="modelType" :options="modelTypeOptions"></a-segmented>
<template v-if="modelType == '0'">
<div class="item-title">模型规模</div>
<a-segmented class="item-type" v-model:value="modelScale" :options="modelScaleOptions"></a-segmented>
<div class="item-title">模型文件</div>
<a-upload
v-model:file-list="modelFile"
accept=".zip"
:maxCount="1"
:custom-request="customRequest"
>
<a-button class="select-model-file-button" type="primary" :icon="h(PlusOutlined)">选择模型文件</a-button>
<!-- <template #itemRender="{ file, actions }">
<a-space>
<span>{{ file.name }}</span>
</a-space>
</template> -->
</a-upload>
<div class="upload-hint">*请上传模型压缩文件支持pt格式</div>
<div class="item-title">模型标签</div>
<div class="item-label-div">
<div class="label-title-div">
<div class="label-title-span">请根据模型实际情况填写信息</div>
<div class="add-button" @click="addLabelRow">
<div class="button-icon"></div>
<div class="button-span">添加行</div>
</div>
</div>
<div class="label-interval"></div>
<div class="label-table-title">
<div class="table-title-item" style="padding-left: 6px;">枚举值</div>
<div class="table-title-item">名称</div>
<div class="table-title-item">推荐置信度</div>
<div class="table-title-item" style="width: 55px;justify-content: center;">操作</div>
</div>
<div class="label-table-content">
<div class="table-row" v-for="(item,index) in modelLabelList" :key="index">
<div class="table-item">
<a-input class="table-item-input" v-model:value="item.test"/>
<div class="right-content">
<div class="item-title">模型类型</div>
<a-segmented class="item-type" v-model:value="type" :options="modelTypeOptions"></a-segmented>
<template v-if="type == 0">
<div class="item-title">模型规模</div>
<a-segmented class="item-type" v-model:value="size" :options="modelScaleOptions"></a-segmented>
<div class="item-title">模型文件</div>
<a-upload
:file-list="modelFile"
accept=".zip,.pt"
:max-count="1"
:custom-request="customRequest"
>
<a-button class="select-model-file-button" type="primary" :icon="h(PlusOutlined)">选择模型文件</a-button>
</a-upload>
<div class="upload-hint">*请上传模型压缩文件支持pt格式</div>
<div class="item-title">模型标签</div>
<div class="item-label-div">
<div class="label-title-div">
<div class="label-title-span">请根据模型实际情况填写信息</div>
<div class="add-button" @click="addLabelRow">
<div class="button-icon"></div>
<div class="button-span">添加行</div>
</div>
<div class="table-item">
<a-input class="table-item-input" />
</div>
<div class="table-item">
<a-input class="table-item-input" />
</div>
<div class="table-item" style="width: 55px; justify-content: center">
<div class="del-table-row" @click="delLabelRow(index)"></div>
</div>
<div class="label-interval"></div>
<div class="label-table-title">
<div class="table-title-item" style="padding-left: 6px;">枚举值</div>
<div class="table-title-item">名称</div>
<div class="table-title-item">推荐置信度</div>
<div class="table-title-item" style="width: 55px;justify-content: center;">操作</div>
</div>
<div class="label-table-content">
<div class="table-row" v-for="(item,index) in modelLabels" :key="index">
<div class="table-item">
<a-input class="table-item-input" v-model:value="item.enumValue"/>
</div>
<div class="table-item">
<a-input class="table-item-input" v-model:value="item.name"/>
</div>
<div class="table-item">
<a-input class="table-item-input" v-model:value="item.reliability"/>
</div>
<div class="table-item" style="width: 55px; justify-content: center">
<div class="del-table-row" @click="delLabelRow(index)"></div>
</div>
</div>
</div>
</div>
</div>
</template>
<template v-else>
<div class="item-title">推流地址</div>
<a-input class="name-input" v-model:value="modelPushAddress" placeholder="请输入地址" />
<div class="item-title">拉流地址</div>
<a-input class="name-input" v-model:value="modelPullAddress" placeholder="请输入地址" />
<div class="item-title">服务地址</div>
<a-input class="name-input" v-model:value="modelServeAddress" placeholder="请输入地址" />
<div class="item-title">密钥</div>
<a-textarea class="item-key" v-model:value="modelKey" placeholder="请输入密钥" :rows="4" />
</template>
</template>
<template v-else>
<div class="item-title">推流地址</div>
<a-input class="name-input" v-model:value="pushUrl" placeholder="请输入地址" />
<div class="item-title">拉流地址</div>
<a-input class="name-input" v-model:value="pullUrl" placeholder="请输入地址" />
<div class="item-title">服务地址</div>
<a-input class="name-input" v-model:value="serviceUrl" placeholder="请输入地址" />
<div class="item-title">密钥</div>
<a-textarea class="item-key" v-model:value="secretKey" placeholder="请输入密钥" :rows="4" />
</template>
</div>
</div>
</div>
<div class="interval"></div>
<div class="footer">
<a-button class="cancel-button" @click="emits('changeAddModal',false)"></a-button>
<a-button class="save-button" type="primary">确定</a-button>
</div>
<div class="interval"></div>
<div class="footer">
<a-button class="cancel-button" @click="emits('changeAddModal',false)"></a-button>
<a-button class="save-button" type="primary" @click="submit"></a-button>
</div>
</a-spin>
<a-modal v-model:open="selectImageModal" title="选择封面" width="1000px" :footer="null" :destroyOnClose="true">
<SelectImageModal @changeSelectImageModal="changeSelectImageModal"/>
<SelectImageModal @changeSelectImageModal="changeSelectImageModal" @getResultImage="getResultImage"/>
</a-modal>
</template>
<script setup lang="ts">
import { ref, h, defineEmits, defineProps } from "vue"
import { ref, h, defineEmits, defineProps, onMounted } from "vue"
import { uploadFile } from '@/api/formrender/index';
import { PlusOutlined, } from '@ant-design/icons-vue';
import SelectImageModal from "./SelectImageModal.vue";
const props = defineProps([''])
const emits = defineEmits(['changeAddModal'])
const modelName = ref('')
const modelDescription = ref('')
const modelType = ref('0')
const modelScale = ref('N')
const modelPullAddress = ref('')
const modelPushAddress = ref('')
const modelServeAddress = ref('')
const modelKey = ref('')
const modelFile = ref([])
const modelFilePath = ref()
const modelLabelList = ref([])
import { ModelLabelsType } from './utils'
import { getAppEnvConfig } from '@/utils/env';
import { AddAlgorithmsRepository, UpdateAlgorithmsRepository } from '@/api/demo/ailist'
import { message } from "ant-design-vue";
const { VITE_GLOB_API_URL } = getAppEnvConfig();
const props = defineProps(['modalType', 'algorithmInfo'])
const emits = defineEmits(['changeAddModal', 'query'])
onMounted(() => {
if(props.modalType == 'update'){
id.value = props.algorithmInfo.id
name.value = props.algorithmInfo.name
describe.value = props.algorithmInfo.describe
pic.value = props.algorithmInfo.pic
type.value = props.algorithmInfo.type
size.value = props.algorithmInfo.size
pullUrl.value = props.algorithmInfo.pullUrl
pushUrl.value = props.algorithmInfo.pushUrl
serviceUrl.value = props.algorithmInfo.serviceUrl
secretKey.value = props.algorithmInfo.secretKey
path.value = props.algorithmInfo.path
modelLabels.value = props.algorithmInfo.modelLabels
modelFile.value?.push({
uid: '-1',
name: props.algorithmInfo.path,
status: 'done',
url: VITE_GLOB_API_URL + '/' + props.algorithmInfo.path,
});
}
})
const id = ref('')
const name = ref('')
const describe = ref('')
const pic = ref('')
const type = ref(0)
const size = ref('N')
const pullUrl = ref('')
const pushUrl = ref('')
const serviceUrl = ref('')
const secretKey = ref('')
const modelFile = ref<any>([])
const path = ref()
const modelLabels = ref<ModelLabelsType[]>([])
const selectImageModal = ref(false)
const loading = ref(false)
const modelTypeOptions = ref([
{ label: '本地模型', value: '0' },
{ label: '远程模型', value: '1' }
{ label: '本地模型', value: 0 },
{ label: '远程模型', value: 1 }
])
const modelScaleOptions = ref([
{ label: 'N', value: 'N' },
@ -131,25 +160,72 @@ const modelScaleOptions = ref([
{ label: 'XI', value: 'XI' },
])
const customRequest = (file) => {
modelFile.value = []
const formData = new FormData()
formData.append('files', file.file)
uploadFile(formData).then(res => {
modelFilePath.value = res[0].filePath
// emits('update:shppath',res[0].filePath)
modelFile.value?.push({
uid: '-1',
name: res[0].filePath,
status: 'done',
url: VITE_GLOB_API_URL + '/' + res[0].filePath,
});
path.value = res[0].filePath
})
}
const addLabelRow = () => {
// TODO
modelLabelList.value.push({
test:''
modelLabels.value.push({
id: '',
name: '',
enumValue: '',
reliability: '',
pId: ''
})
}
const delLabelRow = (index) => {
modelLabelList.value.splice(index,1)
modelLabels.value.splice(index,1)
}
const changeSelectImageModal = (type: boolean) => {
selectImageModal.value = type
}
const getResultImage = (url: string) => {
pic.value = url
}
const submit = () => {
loading.value = true
let params = {
id: id.value,
name: name.value,
describe: describe.value,
pic: pic.value,
size: size.value,
type: type.value,
path: path.value,
pushUrl: pushUrl.value,
pullUrl: pullUrl.value,
serviceUrl: serviceUrl.value,
secretKey: secretKey.value,
modelLabels: modelLabels.value
}
console.log('params',params)
if(props.modalType == 'insert'){
AddAlgorithmsRepository(params).then(res => {
message.success('算法创建成功')
emits('query')
emits('changeAddModal',false)
}).finally(() => {
loading.value = false
})
}else{
UpdateAlgorithmsRepository(params).then(res => {
message.success('算法修改成功')
emits('query')
emits('changeAddModal',false)
}).finally(() => {
loading.value = false
})
}
}
</script>
<style lang="scss" scoped>
@ -235,6 +311,11 @@ const changeSelectImageModal = (type: boolean) => {
background: #F5F5F5;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border: 2px dashed rgba(28,29,34,0.08);
.show-image{
width: 249px;
height: 151px;
cursor: pointer;
}
.empty-image{
position: absolute;
top: 50%;

@ -45,7 +45,7 @@ import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
import { dataURLtoBlob } from '@/utils/file/base64Conver';
import { uploadFile } from '@/api/formrender/index';
const emits = defineEmits(['changeSelectImageModal'])
const emits = defineEmits(['changeSelectImageModal', 'getResultImage'])
const sourceImg = ref(null);
const cropper = ref(null);
const flipH = ref(1);
@ -54,7 +54,6 @@ const zoomValue = ref(1);
const croppedUrl = ref('');
const onFileChange = (e) => {
console.log(e)
const file = e
if (!file) return;
const url = URL.createObjectURL(file);
@ -113,7 +112,6 @@ const getCropped = () => {
const canvas = cropper.value.getCroppedCanvas({ width: 300, height: 200 });
if (!canvas) return;
croppedUrl.value = canvas.toDataURL('image/png');
console.log('croppedUrl.value',croppedUrl.value)
};
const clear = () => {
@ -125,12 +123,6 @@ const clear = () => {
croppedUrl.value = '';
};
const submitImage = (format='image/jpeg', quality = 0.9) => {
// const blob = dataURLtoBlob(croppedUrl.value);
// const formData = new FormData();
// formData.append('files', blob);
// uploadFile(formData).then(res => {
// console.log(res)
// })
if (!cropper.value) return;
cropper.value.getCroppedCanvas({ width: 300, height: 200 })
.toBlob(async (blob) => {
@ -141,7 +133,9 @@ const submitImage = (format='image/jpeg', quality = 0.9) => {
formData.append('files', blob, `cropped.jpeg`);
uploadFile(formData).then(res => {
console.log(res)
let url = res[0].filePath
emits('getResultImage',url)
emits('changeSelectImageModal',false)
})
}, format, quality);
}

@ -1,43 +0,0 @@
import { BasicColumn, FormSchema } from '@/components/Table';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
import Icon from '@/components/Icon/Icon.vue';
export const columns: BasicColumn[] = [
{
title: '按钮名称',
dataIndex: 'name',
},
{
title: 'DOMID',
dataIndex: 'domId',
},
{
title: '排序',
dataIndex: 'sort',
},
{
title: '样式',
dataIndex: 'class',
},
{
title: '状态',
dataIndex: 'enabledMark',
width: 80,
customRender: ({ record }) => {
const color = record.status == 1 ? 'blue' : 'red';
const text = record.status == 1 ? '启用' : '停用';
return h(Tag, { color: color }, () => text);
},
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'key',
label: '关键字',
component: 'Input',
colProps: { span: 8 },
},
];

@ -1,4 +1,5 @@
<template>
<a-spin tip="加载中..." :spinning="loading">
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<div class="w-1/4 xl:w-1/5 leftbox">
<div class="left-title-div">
@ -6,7 +7,7 @@
<div class="span-title">算法库</div>
<div class="span-subtitle">行业生态算法厂商</div>
</div>
<a-button class="add-algorithm" type="primary" :icon="h(PlusOutlined)" @click="changeAddModal(true)"></a-button>
<a-button class="add-algorithm" type="primary" :icon="h(PlusOutlined)" @click="openChangeAddModal(true)"></a-button>
</div>
<div class="left-interval"></div>
<div class="show-algorithm-list-div">
@ -19,7 +20,7 @@
<div class="empty-icon-image"></div>
<div class="empty-span-subtitle" style="display: inline;">说明书 </div>
</div>
<a-button class="empty-add-algorithm" type="primary" :icon="h(PlusOutlined)" @click="changeAddModal(true)"></a-button>
<a-button class="empty-add-algorithm" type="primary" :icon="h(PlusOutlined)" @click="openChangeAddModal(true)"></a-button>
</div>
</div>
</div>
@ -46,53 +47,105 @@
</div>
<div class="show-list-div">
<div class="list-div">
<div class="item" v-for="value in 8">
<div class="item" v-for="item in dataList">
<div class="image-div">
<img class="image-item" src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">
<img class="image-item" :src="`${VITE_GLOB_API_URL}/${item.pic}`">
<div class="image-span">
<div class="image-span-icon"></div>
<div class="image-span-info">#排水沟盖板缺</div>
<div class="image-span-info">{{ getModelLabels(item.modelLabels) }}</div>
</div>
</div>
<div class="info-div">
<div class="info-icon"></div>
<div class="info-span-div">
<div class="info-span-title">高速排水沟盖板缺失检测算法</div>
<div class="info-span-subtitle">识别高速公路两侧的排水沟盖板缺</div>
<div class="info-span-title">{{ item.name }}</div>
<div class="info-span-subtitle">{{ item.describe }}</div>
</div>
<a-button class="show-info-button" type="primary">查看</a-button>
<a-button class="show-info-button" type="primary" @click="openChangeAddModal(false,item)"></a-button>
</div>
</div>
</div>
<div class="pagination-div">
<a-pagination
size="small"
:total="50"
:total="total"
v-model:current="page"
v-model:page-size="limit"
show-size-changer
show-quick-jumper
:page-size-options="pageSizeOptions"
:show-total="total => `共 ${total} 条数据`"
@change="changePagination"
/>
</div>
</div>
</div>
<a-modal width="631px" v-model:open="addAlgorithmModal" :footer="null" :closable="false" :destroyOnClose="true" :maskClosable="false" :keyboard="false">
<AddAlgorithmModal @changeAddModal="changeAddModal"/>
<AddAlgorithmModal :modalType="modalType" :algorithmInfo="algorithmInfo" @changeAddModal="changeAddModal" @query="query"/>
</a-modal>
</PageWrapper>
</a-spin>
</template>
<script lang="ts" setup>
import { ref,h } from 'vue';
import { ref,h, onMounted, watch } from 'vue';
import { PlusOutlined, } from '@ant-design/icons-vue';
import { PageWrapper } from '@/components/Page';
import AddAlgorithmModal from './AddAlgorithmModal.vue';
import { GetAlgorithmsRepositoryList } from '@/api/demo/ailist'
import { getAppEnvConfig } from '@/utils/env';
import { DataListType } from './utils'
const { VITE_GLOB_API_URL } = getAppEnvConfig();
onMounted(() => {
query()
})
const query = () => {
loading.value = true
let params = {
page: page.value,
limit: limit.value,
key: searchValue.value
}
GetAlgorithmsRepositoryList(params).then(res => {
console.log('res',res)
total.value = res.total
dataList.value = res.items
}).finally(() => {
loading.value = false
})
}
const searchValue = ref('');
const addAlgorithmModal = ref(false)
const page = ref('1')
const limit = ref('8')
const total = ref('0')
const dataList = ref<DataListType[]>([])
const pageSizeOptions = ref<string[]>(['8', '16', '32', '64']);
const loading = ref(false)
const modalType = ref('')
const algorithmInfo = ref()
const onSearch = () => {
query()
}
const changeAddModal = (type: boolean) => {
addAlgorithmModal.value = type
}
const openChangeAddModal = (type: boolean, item?) => {
// True False
if(type){
modalType.value = 'insert'
}else{
modalType.value = 'update'
algorithmInfo.value = item
}
changeAddModal(true)
}
const changePagination = () => {
query()
}
const getModelLabels = (data) => {
return data.map( item => `#${item.name}`).join(' ')
}
</script>
<style lang="scss" scoped>
@ -279,6 +332,7 @@
display: flex;
flex-wrap: wrap;
margin-bottom: 90px;
min-height: 576px;
.item{
width: 300px;
height: 268px;
@ -321,6 +375,9 @@
font-size: 11px;
color: #FFFFFF;
line-height: 15px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}

@ -0,0 +1,23 @@
export type ModelLabelsType = {
id?: string,
name: string,
enumValue: string,
reliability: string,
pId?: string,
}
export type DataListType = {
createTime: string,
describe: string,
id: string,
modelLabels: ModelLabelsType[],
name: string,
path: string,
pic: string,
pullUrl: string,
pushUrl: string,
secretKey: string,
serviceUrl: string,
size: string,
type: number
}
Loading…
Cancel
Save