AI算法画面

main
zhufu 2025-08-09 15:36:36 +08:00
parent f3a2821d71
commit 3d87834969
12 changed files with 1027 additions and 292 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 B

View File

@ -0,0 +1,486 @@
<template>
<div class="title-div">
<div class="title-span-div">
<div class="title-icon"></div>
<div class="title-span">创建算法</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>
</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>
<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>
</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>
</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>
<a-modal v-model:open="selectImageModal" title="选择封面" width="1000px" :footer="null" :destroyOnClose="true">
<SelectImageModal @changeSelectImageModal="changeSelectImageModal"/>
</a-modal>
</template>
<script setup lang="ts">
import { ref, h, defineEmits, defineProps } 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([])
const selectImageModal = ref(false)
const modelTypeOptions = ref([
{ label: '本地模型', value: '0' },
{ label: '远程模型', value: '1' }
])
const modelScaleOptions = ref([
{ label: 'N', value: 'N' },
{ label: 'S', value: 'S' },
{ label: 'M', value: 'M' },
{ label: 'L', value: 'L' },
{ label: 'XI', value: 'XI' },
])
const customRequest = (file) => {
const formData = new FormData()
formData.append('files', file.file)
uploadFile(formData).then(res => {
modelFilePath.value = res[0].filePath
// emits('update:shppath',res[0].filePath)
})
}
const addLabelRow = () => {
// TODO
modelLabelList.value.push({
test:''
})
}
const delLabelRow = (index) => {
modelLabelList.value.splice(index,1)
}
const changeSelectImageModal = (type: boolean) => {
selectImageModal.value = type
}
</script>
<style lang="scss" scoped>
.title-div{
width: 100%;
height: 64px;
padding-top: 15px;
padding-left: 26px;
padding-right: 35px;
padding-bottom: 14px;
display: flex;
justify-content: space-between;
align-items: center;
.title-span-div{
display: flex;
align-items: center;
.title-icon{
width: 3px;
height: 18px;
background: #0B60BD;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
margin-right: 9px;
}
.title-span{
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 20px;
color: #11131B;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
}
.close-button{
width: 18px;
height: 18px;
background-image: url('/public/ailist/add_algorithm_close_button.png');
cursor: pointer;
}
}
.interval{
width: 631px;
height: 1px;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border: 1px solid #DBDBDB;
opacity: 0.66;
}
.modal-content{
display: flex;
padding-top: 14px;
padding-left: 27px;
padding-right: 35px;
padding-bottom: 28px;
.item-title{
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #222738;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
margin-bottom: 9px;
}
.name-input{
height: 40px;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 2px;
border: 1px solid #CCCCCC;
margin-bottom: 6px;
}
.left-content{
width: 253px;
margin-right: 46px;
.item-desc{
height: 151px;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 2px;
border: 1px solid #CCCCCC;
margin-bottom: 6px;
}
.item-image{
position: relative;
width: 100%;
height: 155px;
background: #F5F5F5;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border: 2px dashed rgba(28,29,34,0.08);
.empty-image{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 209px;
height: 119px;
background: #FFFFFF;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15), 0px 8px 6px 0px rgba(28,29,34,0.06);
border-radius: 2px;
border: 1px solid #CCCCCC;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.empty-image-content{
width: 98px;
height: 63px;
display: flex;
flex-direction: column;
align-items: center;
.empty-image-icon{
width: 26px;
height: 28px;
background-image: url('/public/ailist/algorithm_upload_icon.png');
margin-bottom: 1px;
}
.empty-image-span{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #222738;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
}
}
}
}
.right-content{
width: 269px;
.item-type{
width: 262px;
height: 40px;
background: rgba(28,29,34,0.04);
border-radius: 20px;
margin-bottom: 6px;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
:deep(.ant-segmented-group){
height: 36px;
}
:deep(.ant-segmented-item){
width: 50%;
display: flex;
align-items: center;
justify-content: center;
border-radius: 18px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: rgba(28,29,34,0.5);
line-height: 20px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
:deep(.ant-segmented-item-selected){
font-weight: 500;
color: #1C1D22;
}
}
.select-model-file-button{
width: 151px;
height: 40px;
background: #0D68CB;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 2px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 16px;
color: #FFFFFF;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
.upload-hint{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #333333;
line-height: 28px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
.item-label-div{
width: 100%;
height: 155px;
background: #F5F5F5;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 2px;
padding-top: 3px;
padding-left: 8px;
padding-right: 10px;
.label-title-div{
width: 100%;
height: 34px;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 4px;
.label-title-span{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #222738;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
.add-button{
width: 66px;
height: 26px;
background: #FFFFFF;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15), 0px 8px 6px 0px rgba(28,29,34,0.06);
border: 1px solid rgba(28,29,34,0.06);
border-radius: 13px;
display: flex;
align-items: center;
padding-left: 8px;
cursor: pointer;
user-select: none;
.button-icon{
width: 8px;
height: 8px;
background-image: url('/public/ailist/algorithm_add_row_icon.png');
margin-right: 4px;
}
.button-span{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #0D68CB;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
}
}
.label-interval{
height: 1px;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
background:#DBDBDB;
opacity: 0.66;
margin-left: -8px;
margin-right: -10px;
}
.label-table-title{
height: 30px;
display: flex;
margin-bottom: 4px;
.table-title-item{
display: flex;
width: 66px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #222738;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
}
.label-table-content{
height: 75px;
overflow: auto;
scrollbar-width: none;
::-webkit-scrollbar {
width: 0;
height: 0;
}
.table-row{
width: 100%;
height: 26px;
display: flex;
margin-bottom: 1px;
.table-item{
width: 65px;
height: 26px;
background: #FFFFFF;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15), 0px 8px 6px 0px rgba(28,29,34,0.06);
border-radius: 2px 0px 0px 2px;
margin-right: 1px;
display: flex;
align-items: center;
// padding-left: 9px;
.table-item-input{
height: 26px;
border: 0px;
}
.table-item-input:focus{
box-shadow: none
}
.del-table-row{
width: 14px;
height: 14px;
background-image: url('/public/ailist/del_table_row.png');
cursor: pointer;
}
}
}
}
}
.item-key{
height: 88px;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 2px;
border: 1px solid #CCCCCC;
}
}
}
.footer{
height: 85px;
padding-right: 34px;
display: flex;
align-items: center;
justify-content: end;
.cancel-button{
width: 101px;
height: 40px;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 4px;
border: 1px solid #979797;
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #222738;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
margin-right: 14px;
}
.save-button{
width: 146px;
height: 40px;
background: #0D68CB;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 4px;
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #FFFFFF;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
}
</style>

View File

@ -0,0 +1,166 @@
<template>
<div class="cropper-tool">
<div class="container">
<div class="left">
<div style="padding-left: 20px;">
<a-upload :fileList="[]" accept="image/*" :beforeUpload="onFileChange">
<a-button size="small" type="primary">选择图片</a-button>
</a-upload>
</div>
<div style="margin-top:8px">
<img style="width: 640px;height: 266px;" ref="sourceImg" crossorigin="anonymous" />
</div>
<div class="controls">
<button @click="zoomIn"></button>
<button @click="zoomOut"></button>
<button @click="rotateLeft"></button>
<button @click="rotateRight"></button>
<button @click="flipHorizontal"></button>
<button @click="flipVertical"></button>
<button @click="reset"></button>
</div>
</div>
<div class="right">
<div style="margin-bottom:10px">裁切预览固定 300×200</div>
<div class="preview" ref="previewBox" style="width:300px;height:200px;">
<span v-if="!croppedUrl"></span>
<img v-if="croppedUrl" :src="croppedUrl" alt="预览" />
</div>
<div style="margin-top:8px; display:flex; gap:8px;">
<a v-if="croppedUrl" :href="croppedUrl" download="cropped.png"></a>
<button @click="clear"></button>
</div>
</div>
</div>
<div class="footer">
<a-button style="margin-right: 15px;" @click="emits('changeSelectImageModal',false)"></a-button>
<a-button type="primary" @click="submitImage"></a-button>
</div>
</div>
</template>
<script setup>
import { ref, defineEmits } from 'vue';
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 sourceImg = ref(null);
const cropper = ref(null);
const flipH = ref(1);
const flipV = ref(1);
const zoomValue = ref(1);
const croppedUrl = ref('');
const onFileChange = (e) => {
console.log(e)
const file = e
if (!file) return;
const url = URL.createObjectURL(file);
sourceImg.value.src = url;
sourceImg.value.onload = () => {
if (cropper.value) {
cropper.value.destroy();
}
cropper.value = new Cropper(sourceImg.value, {
viewMode: 1,
aspectRatio: 3 / 2, // 300:200
autoCropArea: 1,
movable: true,
zoomable: true,
rotatable: true,
scalable: true,
responsive: true,
background: true,
cropBoxResizable: false,
cropBoxMovable: false,
dragMode: 'move',
ready() {
zoomValue.value = 1;
flipH.value = 1;
flipV.value = 1;
croppedUrl.value = '';
getCropped()
},
crop() {
getCropped()
}
});
};
};
const zoomIn = () => cropper.value?.zoom(0.1);
const zoomOut = () => cropper.value?.zoom(-0.1);
const rotateLeft = () => cropper.value?.rotate(-90);
const rotateRight = () => cropper.value?.rotate(90);
const flipHorizontal = () => {
flipH.value = -flipH.value;
cropper.value?.scaleX(flipH.value);
};
const flipVertical = () => {
flipV.value = -flipV.value;
cropper.value?.scaleY(flipV.value);
};
const reset = () => {
cropper.value?.reset();
zoomValue.value = 1;
flipH.value = 1;
flipV.value = 1;
};
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 = () => {
if (cropper.value) {
cropper.value.destroy();
cropper.value = null;
}
sourceImg.value.src = '';
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) => {
if (!blob) return;
// FormData
const formData = new FormData();
formData.append('files', blob, `cropped.jpeg`);
uploadFile(formData).then(res => {
console.log(res)
})
}, format, quality);
}
</script>
<style scoped>
.container { display: flex; gap: 20px; }
.left { width: 640px; }
.right { width: 320px; }
img { max-width: 100%; display: block; max-height: 480px; background: #f3f3f3; }
.controls { margin-top: 10px; display:flex; flex-wrap:wrap; gap:8px; }
.preview { border:1px solid #ddd; display:flex; align-items:center; justify-content:center; overflow:hidden; background:#fafafa }
.footer{
margin-top: 12px;
height: 53px;
border-top: 1px solid #eee;
display: flex;
align-items: center;
justify-content: end;
padding-right: 40px;
}
</style>

View File

@ -1,136 +1,403 @@
<template>
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<div class="w-1/4 xl:w-1/5 leftbox flex column">
<div class="flex ai-c jc-sb">
<div class="textbox">
<p>算法库</p>
<span>行业生态算法厂商</span>
<div class="w-1/4 xl:w-1/5 leftbox">
<div class="left-title-div">
<div class="span-div">
<div class="span-title">算法库</div>
<div class="span-subtitle">行业生态算法厂商</div>
</div>
<a-button type="primary" size="small" @click="createAddClick">
<template #icon><PlusOutlined /></template>
创建
</a-button>
<a-button class="add-algorithm" type="primary" :icon="h(PlusOutlined)" @click="changeAddModal(true)"></a-button>
</div>
<div class="flex ai-c jc-c column flex-1">
<div class="descbox">
<p>请先创建自定义算法基于算法新增实例<br />了解更多<BookOutlined :style="{fontSize: '14px', color: '#0A99EB'}" /> 说明书</p>
<div class="left-interval"></div>
<div class="show-algorithm-list-div">
<div class="empty-list">
<div class="empty-image"></div>
<div class="empty-span-title">暂无数据</div>
<div class="empty-span-subtitle">请先创建自定义算法基于算法新增实例</div>
<div style="display: flex;align-items: center;margin-bottom: 28px;">
<div class="empty-span-subtitle" style="display: inline;">了解更多</div>
<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>
</div>
<a-button type="primary" @click="createAddClick">
<template #icon><PlusOutlined /></template>
创建自定义算法
</a-button>
</div>
</div>
<BasicTable
class="w-3/4 xl:w-4/5"
@register="registerTable"
@fetch-success="onFetchSuccess"
:searchInfo="searchInfo"
>
</BasicTable>
<Modal
@submitsuccess="submitsuccess"
@register="registerModal"
@submit-reload="submitReload"
/>
<div class="w-3/4 xl:w-4/5 right-out-div">
<div class="search-div">
<div class="search-span-icon"></div>
<div class="search-span">算法库</div>
<div class="search-input-div">
<a-input-search
class="search-input"
v-model:value="searchValue"
placeholder="按照实例搜索"
enter-button="搜索"
@search="onSearch"
>
<template #prefix>
<div class="search-input-icon"></div>
</template>
</a-input-search>
</div>
</div>
<div class="search-interval">
<div class="interval-icon"></div>
</div>
<div class="show-list-div">
<div class="list-div">
<div class="item" v-for="value in 8">
<div class="image-div">
<img class="image-item" src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">
<div class="image-span">
<div class="image-span-icon"></div>
<div class="image-span-info">#排水沟盖板缺</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>
<a-button class="show-info-button" type="primary">查看</a-button>
</div>
</div>
</div>
<div class="pagination-div">
<a-pagination
size="small"
:total="50"
show-size-changer
show-quick-jumper
:show-total="total => `共 ${total} 条数据`"
/>
</div>
</div>
</div>
<a-modal width="631px" v-model:open="addAlgorithmModal" :footer="null" :closable="false" :destroyOnClose="true" :maskClosable="false" :keyboard="false">
<AddAlgorithmModal @changeAddModal="changeAddModal"/>
</a-modal>
</PageWrapper>
</template>
<script lang="ts" setup>
import { reactive, nextTick, ref } from 'vue';
import { PlusOutlined, BookOutlined } from '@ant-design/icons-vue';
import { BasicTable, useTable } from '@/components/Table';
import { getButtonList, getMenuDetail, deleteButton } from '@/api/demo/system';
import { ref,h } from 'vue';
import { PlusOutlined, } from '@ant-design/icons-vue';
import { PageWrapper } from '@/components/Page';
import { columns, searchFormSchema } from './index.data';
import { useMessage } from '@/hooks/web/useMessage';
import Modal from './modal.vue';
import { useModal } from '@/components/Modal';
import AddAlgorithmModal from './AddAlgorithmModal.vue';
const { createConfirm, createMessage } = useMessage();
defineOptions({ name: 'MenuManagement' });
const searchInfo = reactive<Recordable>({});
const [registerTable, { reload, expandAll, getSelectRows, clearSelectedRowKeys }] = useTable({
title: '算法列表',
// api: getButtonList,
columns,
rowKey: 'id',
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
isTreeTable: true,
striped: false,
useSearchForm: true,
showTableSetting: true,
bordered: true,
showIndexColumn: false,
rowSelection: {
//
// type: 'checkbox',
type: 'radio',
},
handleSearchInfoFn(info) {
return info;
},
});
const [registerModal, { openModal }] = useModal();
function createAddClick(){
openModal(true, {
});
const searchValue = ref('');
const addAlgorithmModal = ref(false)
const onSearch = () => {
}
const changeAddModal = (type: boolean) => {
addAlgorithmModal.value = type
}
function onFetchSuccess() {
//
nextTick(expandAll);
}
</script>
<style scoped>
.flex{
display: flex;
}
.flex-1{
flex: 1;
}
.column{
flex-direction: column;
}
.ai-c{
align-items: center;
}
.jc-sb{
justify-content: space-between;
}
.jc-c{
justify-content: center;
}
<style lang="scss" scoped>
.leftbox{
background: #fff;
margin: 16px 0 16px 16px;
padding: 16px;
.textbox{
p{
.left-title-div{
width: 100%;
height: 82px;
display: flex;
align-items: center;
padding-left: 16px;
padding-right: 33px;
justify-content: space-between;
.span-div{
width: 104px;
height: 42px;
.span-title{
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #060606;
line-height: 22px;
}
.span-subtitle{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 13px;
color: #393939;
line-height: 18px;
}
}
.add-algorithm{
width: 80px;
height: 30px;
background: #0B60BD;
border-radius: 0px;
}
}
.left-interval{
width: 100%;
height: 1px;
border: 1px solid rgba(28,29,34,0.08);
opacity: 0.46;
}
.show-algorithm-list-div{
position: relative;
width: 100%;
height: calc(100% - 84px );
.empty-list{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 280px;
height: 311px;
background: #FFFFFF;
box-shadow: 5px 18px 32px 0px rgba(0,0,0,0.1);
border: 2px dashed rgba(28,29,34,0.08);
border-radius: 10px;
padding-top: 28px;
display: flex;
flex-direction: column;
align-items: center;
.empty-image{
width: 114px;
height: 120px;
background-image: url('/public/ailist/empty_image.png');
margin-bottom: 8px;
}
.empty-span-title{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 13px;
color: #000000;
line-height: 18px;
margin-bottom: 12px;
}
.empty-span-subtitle{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 13px;
color: #393939;
line-height: 18px;
}
.empty-icon-image{
width: 16px;
height: 14px;
background-image: url('/public/ailist/empty_icon_image.png');
line-height: 18px;
display: inline;
cursor: pointer;
}
.empty-add-algorithm{
width: 167px;
height: 30px;
background: #0D68CB;
border-radius: 0px;
}
}
}
}
.right-out-div{
padding: 16px 16px 16px 33px;
.search-div{
position: relative;
height: 57px;
display: flex;
align-items: center;
.search-span-icon{
width: 20px;
height: 18px;
background-image: url('/public/ailist/search_span_icon.png');
margin-right: 10px;
}
.search-span{
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #060606;
line-height: 22px;
margin-bottom: 0;
}
span{
font-weight: 400;
font-size: 13px;
color: #393939;
line-height: 18px;
.search-input-div{
position: absolute;
top: 0px;
right: 0px;
width: 304px;
height: 38px;
.search-input{
:deep(.ant-input-affix-wrapper){
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
line-height: 28px;
height: 38px;
border-top-left-radius: 19px;
border-bottom-left-radius: 19px;
}
:deep(.ant-input-search-button,){
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #FFFFFF;
line-height: 28px;
height: 38px;
border-top-right-radius: 19px;
border-bottom-right-radius: 19px;
}
:deep(.ant-input){
padding-left: 20px;
}
.search-input-icon{
width: 18px;
height: 18px;
background-image: url('/public/ailist/search_input_icon.png');
}
}
}
}
.descbox{
font-weight: 400;
font-size: 13px;
color: #393939;
line-height: 18px;
text-align: center;
.search-interval{
position: relative;
width: 100%;
height: 2px;
background:rgba(28,29,34,0.08);
margin-bottom: 22px;
.interval-icon{
position: absolute;
top: 0px;
left: 0px;
width: 137px;
height: 2px;
background: #0B60BD;
}
}
.show-list-div{
width: 100%;
height: calc( 100% - 81px );
background: #FFFFFF;
border: 2px dashed rgba(28,29,34,0.08);
border-radius: 10px;
padding: 32px;
.list-div{
width: 100%;
display: flex;
flex-wrap: wrap;
margin-bottom: 90px;
.item{
width: 300px;
height: 268px;
background: #FFFFFF;
box-shadow: 5px 18px 32px 0px rgba(28,29,34,0.1);
border-radius: 10px;
margin-right: 10px;
margin-bottom: 20px;
.image-div{
position: relative;
width: 300px;
height: 200px;
margin-bottom: 12px;
.image-item{
width: 100%;
height: 100%;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.image-span{
position: absolute;
left: 0px;
bottom: 0px;
width: 205px;
height: 20px;
background: linear-gradient( 270deg, rgba(7,7,7,0) 0%, #111111 100%);
display: flex;
align-items: center;
padding-left: 9px;
.image-span-icon{
width: 4px;
height: 4px;
background: #FFA048;
border-radius: 50%;
margin-right: 5px;
}
.image-span-info{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 11px;
color: #FFFFFF;
line-height: 15px;
}
}
}
.info-div{
width: 100%;
height: 40px;
display: flex;
align-items: center;
padding-left: 7px;
.info-icon{
width: 3px;
height: 34px;
background: #0B60BD;
margin-right: 5px;
}
.info-span-div{
width: 187px;
margin-right: 22px;
.info-span-title{
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 14px;
color: #1C1D22;
line-height: 20px;
margin-bottom: 5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.info-span-subtitle{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 11px;
color: rgba(28,29,34,0.5);
line-height: 15px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.show-info-button{
width: 54px;
height: 23px;
border-radius: 12px;
background: #0B60BD;
:deep(span){
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 11px;
color: #FFFFFF;
line-height: 15px;
}
}
}
}
.item:nth-child(4n){
margin-right: 0px;
}
}
.pagination-div{
padding-right: 32px;
width: 100%;
height: 66px;
display: flex;
align-items: center;
justify-content: end;
}
}
.right-box{
width: 100%;
height: 100%;
background-color: #fff;
}
}
</style>

View File

@ -1,184 +0,0 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:canFullscreen="false"
:defaultFullscreen="false"
:showCancelBtn="true"
:showOkBtn="true"
:draggable="false"
title="创建算法"
@ok="submitClick"
>
<div class="flex titlesbox">
<div class="li" v-for="(item,index) in tabList">
<span :class="index==tabIndex?'active':''" @click="tabsClick(index)">{{item}}</span>
</div>
</div>
<!-- 视频算法 -->
<div class="formsbox" v-if="tabIndex == 0">
<a-form
:model="formState"
ref="formRef"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
autocomplete="off"
>
<a-form-item
label="算法名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="概要描述"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-textarea v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="算力名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="推流地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="拉流地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="服务地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="秘钥"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
</a-form>
</div>
<!-- 图片算法 -->
<div class="formsbox" v-if="tabIndex == 1">
<a-form
:model="formState"
ref="formRef"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
autocomplete="off"
>
<a-form-item
label="算法名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="概要描述"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-textarea v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="算力名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="服务地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="秘钥"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
</a-form>
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, defineEmits, reactive, toRaw } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
const tabIndex = ref(0);
const tabList = ["视频算法","图片算法"];
const tabsClick = (index) =>{
tabIndex.value = index
}
const formRef = ref();
interface FormState {
username: string;
password: string;
remember: boolean;
}
const formState = reactive<FormState>({
username: '',
password: '',
remember: true,
});
const submitClick = ()=>{
console.log('submit')
formRef.value.validate().then(() => {
console.log('values', formState, toRaw(formState));
})
.catch(error => {
console.log('error', error);
});
}
const [registerModal, { closeModal }] = useModalInner((data: any) => {
});
</script>
<style scoped>
.titlesbox{
width: 92%;
margin-left: 4%;
line-height: 40px;
border-bottom: 1px solid #F5F6FA;
.li{
width: 50%;
text-align: center;
span{
color: #222738;
cursor: pointer;
line-height: 40px;
display: inline-block;
}
.active{
border-bottom: 3px solid #3A57E8;
}
}
}
.formsbox{
width: 92%;
margin-left: 4%;
padding-top: 20px;
}
</style>