提交代码
This commit is contained in:
parent
9777059eee
commit
7bd8f1d02f
|
|
@ -0,0 +1,44 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询镜像管理列表
|
||||
export function listManager(query) {
|
||||
return request({
|
||||
url: '/ai/mirror/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询镜像管理详细
|
||||
export function getManager(id) {
|
||||
return request({
|
||||
url: '/ai/mirror/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增镜像管理
|
||||
export function addManager(data) {
|
||||
return request({
|
||||
url: '/ai/mirror/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改镜像管理
|
||||
export function updateManager(data) {
|
||||
return request({
|
||||
url: '/ai/mirror/edit',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除镜像管理
|
||||
export function delManager(id) {
|
||||
return request({
|
||||
url: '/ai/mirror/delete/' + id,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import request from '@/utils/request'
|
|||
// 查询模型管理列表
|
||||
export function listManager(query) {
|
||||
return request({
|
||||
url: '/ai/manager/list',
|
||||
url: '/ai/model/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
|
|
@ -12,7 +12,7 @@ export function listManager(query) {
|
|||
// 查询模型管理详细
|
||||
export function getManager(id) {
|
||||
return request({
|
||||
url: '/ai/manager/' + id,
|
||||
url: '/ai/model/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ export function getManager(id) {
|
|||
// 新增模型管理
|
||||
export function addManager(data) {
|
||||
return request({
|
||||
url: '/ai/manager/add',
|
||||
url: '/ai/model/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
|
|
@ -29,7 +29,7 @@ export function addManager(data) {
|
|||
// 修改模型管理
|
||||
export function updateManager(data) {
|
||||
return request({
|
||||
url: '/ai/manager/edit',
|
||||
url: '/ai/model/edit',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
|
|
@ -38,7 +38,19 @@ export function updateManager(data) {
|
|||
// 删除模型管理
|
||||
export function delManager(id) {
|
||||
return request({
|
||||
url: '/ai/manager/delete/' + id,
|
||||
url: '/ai/model/delete/' + id,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function uploadFile(formData) {
|
||||
return request({
|
||||
url: '/ai/manager/uploadFile',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
timeout: 600000, // 设置超时时间为 10 秒(10000 毫秒)
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export default {
|
|||
rules: {
|
||||
versionName: [
|
||||
{ required: true, message: '版本名称不能为空', trigger: 'blur' },
|
||||
{ pattern: /^V\d{1,3}(\.\d{1,3}){2}$/, message: '版本名称式不正确,应为 vX.Y.Z 格式', trigger: 'blur' }
|
||||
{ pattern: /^V\d{1,3}(\.\d{1,3}){2}$/, message: '版本名称式不正确,应为 VX.Y.Z 格式', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,6 @@ export default {
|
|||
const totalChunks = Math.ceil(file.size / this.chunkSize);
|
||||
let currentChunk = 0;
|
||||
const uploadNextChunk = () => {
|
||||
console.log(this.queue.length)
|
||||
if (!this.drawer) return; // 检查是否需要继续上传
|
||||
const start = currentChunk * this.chunkSize;
|
||||
const end = Math.min(file.size, start + this.chunkSize);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,229 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="镜像名称" prop="mirrorName">
|
||||
<el-input v-model="form.mirrorName" placeholder="请输入镜像名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属模型" prop="ownModel">
|
||||
<el-input v-model="form.ownModel" placeholder="请输入所属模型"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="运行环境" prop="runEnvironment">
|
||||
<el-input v-model="form.runEnvironment" type="textarea" placeholder="请输入内容"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型框架" prop="modelFrame">
|
||||
<el-input v-model="form.modelFrame" placeholder="请输入模型框架"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="语言" prop="language">
|
||||
<el-input v-model="form.language" placeholder="请输入语言"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="镜像文件path" prop="mirrorPath">
|
||||
<el-input v-model="form.mirrorPath" placeholder="请输入镜像文件path"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否删除" prop="isActive">
|
||||
<el-input v-model="form.isActive" placeholder="请输入是否删除"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据集id" prop="datasetId">
|
||||
<el-input v-model="form.datasetId" placeholder="请输入数据集id"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型文件名" prop="modelFileName">
|
||||
<el-input v-model="form.modelFileName" placeholder="请输入模型文件名"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="使用手册文件名" prop="manualFileName">
|
||||
<el-input v-model="form.manualFileName" placeholder="请输入使用手册文件名"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getManager, addManager, updateManager, uploadFile } from '@/api/dataCenter/model'
|
||||
import { datasetList } from '@/api/dataCenter/dataSet'
|
||||
import { generateUUID } from '../../../../utils/configure'
|
||||
|
||||
export default {
|
||||
dicts: ['ai_annotate_type', 'ai_frame_type'],
|
||||
props: {
|
||||
open: { type: Boolean, required: true },
|
||||
getList: { type: Function, required: true },
|
||||
title: { type: String, required: true },
|
||||
mirrorId: { type: [Number, null], default: 0 }
|
||||
},
|
||||
computed: {
|
||||
isOpen: {
|
||||
get() {
|
||||
return this.open
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('dialog-cancel')
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
datasetList: [],
|
||||
uploadProgress: { modelFile: 0, manualFile: 0 },
|
||||
uploading: false,
|
||||
chunkSize: 1024 * 1024 * 20, // 20MB
|
||||
form: { modelFile: null, manualFile: null, modelFileName: '', manualFileName: '' },
|
||||
rules: {
|
||||
modelName: [{ required: true, message: '模型名称不能为空', trigger: 'blur' }],
|
||||
modelVersion: [{
|
||||
required: true,
|
||||
message: '版本号不能为空',
|
||||
trigger: 'blur'
|
||||
}, { pattern: /^V\d{1,3}(\.\d{1,3}){2}$/, message: '版本名称式不正确,应为 VX.Y.Z 格式', trigger: 'blur' }],
|
||||
modelType: [{ required: true, message: '请选择模型类型', trigger: 'blur' }],
|
||||
modelFrame: [{ required: true, message: '请选择模型框架', trigger: 'blur' }],
|
||||
modelFileName: [{ required: true, message: '请上传模型文件', trigger: 'blur' }],
|
||||
manualFileName: [{ required: true, message: '请上传使用手册', trigger: 'blur' }]
|
||||
},
|
||||
currentUpload: { modelFile: null, manualFile: null } // 为每个文件设置独立的上传任务
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isOpen(newVal) {
|
||||
if (newVal) {
|
||||
datasetList({}).then(response => {
|
||||
this.datasetList = response.data
|
||||
})
|
||||
if (this.mirrorId) {
|
||||
getManager(this.mirrorId).then(response => {
|
||||
this.form = response.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
triggerFileInput(refName) {
|
||||
this.$refs[refName].click()
|
||||
},
|
||||
handleFileChange(field, event) {
|
||||
const file = event.target.files[0]
|
||||
if (file) {
|
||||
this.form[field] = file
|
||||
this.form[`${field}Name`] = file.name
|
||||
}
|
||||
},
|
||||
resetField(field) {
|
||||
this.form[field] = null
|
||||
this.form[`${field}Name`] = ''
|
||||
this.$refs[field].value = '' // 重置文件选择
|
||||
},
|
||||
uploadChunks(file, field, uuid) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 文件为空时直接返回
|
||||
if (!file || file.size === 0) {
|
||||
resolve()
|
||||
}
|
||||
const totalChunks = Math.ceil(file.size / this.chunkSize)
|
||||
let currentChunk = 0
|
||||
this.uploading = true // 标记正在上传
|
||||
const controller = new AbortController() // 新增AbortController
|
||||
const signal = controller.signal // 获取 signal 用于中止请求
|
||||
|
||||
const uploadNextChunk = () => {
|
||||
if (this.uploading === false) { // 如果上传已经取消,退出
|
||||
reject('Upload canceled')
|
||||
return
|
||||
}
|
||||
const start = currentChunk * this.chunkSize
|
||||
const end = Math.min(file.size, start + this.chunkSize)
|
||||
const chunk = file.slice(start, end)
|
||||
const formData = new FormData()
|
||||
formData.append('file', chunk)
|
||||
formData.append('fileName', uuid + `.${file.name.split('.').pop().toLowerCase()}`)
|
||||
formData.append('chunk', currentChunk + 1)
|
||||
formData.append('totalChunks', totalChunks)
|
||||
uploadFile(formData, { signal }) // 在上传请求中传入 signal 用于中止
|
||||
.then(res => {
|
||||
currentChunk++
|
||||
// 更新进度
|
||||
this.uploadProgress[field] = Math.floor((currentChunk / totalChunks) * 100)
|
||||
if (currentChunk < totalChunks) {
|
||||
uploadNextChunk()
|
||||
} else {
|
||||
if (field === 'modelFile') {
|
||||
this.form.modelPath = res.data.fileUrl
|
||||
}
|
||||
if (field === 'manualFile') {
|
||||
this.form.modelManual = res.data.fileUrl
|
||||
}
|
||||
resolve() // 上传完成,返回成功
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.name === 'AbortError') {
|
||||
console.log('Upload was aborted')
|
||||
}
|
||||
reject(error) // 上传失败,返回失败
|
||||
this.$message.error('上传失败')
|
||||
})
|
||||
}
|
||||
|
||||
uploadNextChunk()
|
||||
// 将当前上传任务保存到 currentUpload 中
|
||||
this.currentUpload[field] = {
|
||||
abort: () => controller.abort() // 提供 abort 方法来中止上传
|
||||
}
|
||||
|
||||
return this.currentUpload[field] // 返回当前上传任务
|
||||
})
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
// 上传文件后再调用新增或修改操作
|
||||
// 独立上传每个文件,并确保只有所有文件上传成功后才提交表单
|
||||
Promise.all([
|
||||
this.uploadChunks(this.form.modelFile, 'modelFile', generateUUID()),
|
||||
this.uploadChunks(this.form.manualFile, 'manualFile', generateUUID())
|
||||
]).then(() => {
|
||||
this.uploading = false
|
||||
const action = this.form.id ? updateManager : addManager
|
||||
action(this.form)
|
||||
.then(() => {
|
||||
this.$message.success(this.form.id ? '修改成功' : '新增成功')
|
||||
this.cancel()
|
||||
this.getList()
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message.error('文件上传失败,无法提交')
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
cancel() {
|
||||
this.isOpen = false
|
||||
this.uploading = false
|
||||
// 取消当前文件的上传任务
|
||||
if (this.currentUpload.modelFile && this.currentUpload.modelFile.abort) {
|
||||
this.currentUpload.modelFile.abort()
|
||||
}
|
||||
if (this.currentUpload.manualFile && this.currentUpload.manualFile.abort) {
|
||||
this.currentUpload.manualFile.abort()
|
||||
}
|
||||
this.resetForm()
|
||||
},
|
||||
resetForm() {
|
||||
this.resetField('modelInput')
|
||||
this.resetField('manualInput')
|
||||
this.uploading = false
|
||||
this.uploadProgress = { modelFile: 0, manualFile: 0 }
|
||||
this.form = { modelFile: null, manualFile: null, modelFileName: '', manualFileName: '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="镜像名称" prop="mirrorName">
|
||||
<el-input
|
||||
v-model="queryParams.mirrorName"
|
||||
placeholder="请输入镜像名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['system:manager:add']"
|
||||
>新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="el-icon-edit"
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['system:manager:edit']"
|
||||
>修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['system:manager:remove']"
|
||||
>删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="managerList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center"/>
|
||||
<el-table-column label="镜像名称" align="center" prop="mirrorName"/>
|
||||
<el-table-column label="所属模型" align="center" prop="ownModel"/>
|
||||
<el-table-column label="运行环境" align="center" prop="runEnvironment"/>
|
||||
<el-table-column label="模型框架" align="center" prop="modelFrame"/>
|
||||
<el-table-column label="语言" align="center" prop="language"/>
|
||||
<el-table-column label="镜像文件path" align="center" prop="mirrorPath"/>
|
||||
<el-table-column label="备注" align="center" prop="remark"/>
|
||||
<el-table-column label="是否删除" align="center" prop="isActive"/>
|
||||
<el-table-column label="数据集id" align="center" prop="datasetId"/>
|
||||
<el-table-column label="模型文件名" align="center" prop="modelFileName"/>
|
||||
<el-table-column label="使用手册文件名" align="center" prop="manualFileName"/>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:manager:edit']"
|
||||
>修改
|
||||
</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:manager:remove']"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改镜像管理对话框 -->
|
||||
<custom-dialog :title="title" :get-list="getList" :open="open" :model-id="mirrorId" @dialog-cancel="handleCancel"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listManager, delManager } from '@/api/dataCenter/mirror'
|
||||
import customDialog from '../model/child/customDialog.vue'
|
||||
|
||||
export default {
|
||||
name: 'Mirror',
|
||||
components: { customDialog },
|
||||
data() {
|
||||
return {
|
||||
mirrorId: null,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 镜像管理表格数据
|
||||
managerList: [],
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
mirrorName: null
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 查询镜像管理列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
listManager(this.queryParams).then(response => {
|
||||
this.managerList = response.rows
|
||||
this.total = response.total
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
this.handleQuery()
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.title = '添加'
|
||||
this.mirrorId = null
|
||||
this.open = true
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.mirrorId = row.id || this.ids[0]
|
||||
this.title = '添加'
|
||||
this.open = true
|
||||
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const ids = row.id || this.ids
|
||||
this.$modal.confirm('是否确认删除数据项?').then(function() {
|
||||
return delManager(ids)
|
||||
}).then(() => {
|
||||
this.getList()
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -4,45 +4,79 @@
|
|||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="名称" prop="groupName">
|
||||
<el-input v-model="form.groupName" maxlength="20" placeholder="请输入名称"/>
|
||||
<el-form-item label="模型名称" prop="modelName">
|
||||
<el-input v-model="form.modelName" maxlength="100" placeholder="请输入模型名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="description">
|
||||
<el-input v-model="form.description" type="textarea" maxlength="200"
|
||||
show-word-limit placeholder="请输入内容"
|
||||
/>
|
||||
<el-form-item label="版本号" prop="modelVersion">
|
||||
<el-input v-model="form.modelVersion" maxlength="100" placeholder="请输入版本号"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据集" prop="datasetId">
|
||||
<el-select v-model="form.datasetId" placeholder="请选择数据集" clearable style="width: 100%">
|
||||
<el-option v-for="dict in datasetList" :key="dict.datasetId" :label="dict.datasetName"
|
||||
:value="dict.datasetId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型类型" prop="modelType">
|
||||
<el-select v-model="form.modelType" placeholder="请选择模型类型" clearable style="width: 100%">
|
||||
<el-option v-for="dict in dict.type.ai_annotate_type" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型框架" prop="modelFrame">
|
||||
<el-select v-model="form.modelFrame" placeholder="请选择模型框架" clearable style="width: 100%">
|
||||
<el-option v-for="dict in dict.type.ai_frame_type" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型上传" prop="modelFileName">
|
||||
<div class="custom-upload">
|
||||
<input type="file" ref="modelInput" style="display: none" @change="handleFileChange('modelFile', $event)"
|
||||
accept=".zip"
|
||||
/>
|
||||
<el-button size="mini" type="text" v-if="!form.modelFileName" @click="triggerFileInput('modelInput')">选择文件
|
||||
</el-button>
|
||||
<span style="cursor: pointer;" @click="triggerFileInput('modelInput')" v-if="form.modelFileName"
|
||||
>{{ form.modelFileName }}</span>
|
||||
<el-progress v-if="uploading" :percentage="uploadProgress.modelFile"/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="使用手册" prop="manualFileName">
|
||||
<div class="custom-upload">
|
||||
<input type="file" ref="manualInput" style="display: none"
|
||||
@change="handleFileChange('manualFile', $event)" accept=".doc,.docx,.pdf"
|
||||
/>
|
||||
<el-button size="mini" type="text" v-if="!form.manualFileName" @click="triggerFileInput('manualInput')">
|
||||
选择文件
|
||||
</el-button>
|
||||
<span style="cursor: pointer;" @click="triggerFileInput('manualInput')" v-if="form.manualFileName"
|
||||
>{{ form.manualFileName }}<i></i></span>
|
||||
<el-progress v-if="uploading" :percentage="uploadProgress.manualFile"/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" :disabled="uploading" @click="submitForm">确定</el-button>
|
||||
<el-button @click="cancel">取消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getManager, addManager, updateManager } from '@/api/dataCenter/model'
|
||||
import { getManager, addManager, updateManager } from '@/api/dataCenter/mirror'
|
||||
import { datasetList } from '@/api/dataCenter/dataSet'
|
||||
import { generateUUID } from '../../../../utils/configure'
|
||||
|
||||
export default {
|
||||
dicts: ['ai_annotate_type', 'ai_frame_type'],
|
||||
props: {
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
},
|
||||
getList: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
groupId: {
|
||||
type: [Number, null],
|
||||
default: 0 // 如果没有传递值,默认为 0
|
||||
}
|
||||
open: { type: Boolean, required: true },
|
||||
getList: { type: Function, required: true },
|
||||
title: { type: String, required: true },
|
||||
modelId: { type: [Number, null], default: 0 }
|
||||
},
|
||||
computed: {
|
||||
isOpen: {
|
||||
|
|
@ -50,68 +84,167 @@ export default {
|
|||
return this.open
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('dialog-cancel') // 通知父组件
|
||||
this.$emit('dialog-cancel')
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
datasetList: [],
|
||||
uploadProgress: { modelFile: 0, manualFile: 0 },
|
||||
uploading: false,
|
||||
chunkSize: 1024 * 1024 * 20, // 20MB
|
||||
form: { modelFile: null, manualFile: null, modelFileName: '', manualFileName: '' },
|
||||
rules: {
|
||||
groupName: [
|
||||
{ required: true, message: '名称不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
modelName: [{ required: true, message: '模型名称不能为空', trigger: 'blur' }],
|
||||
modelVersion: [{
|
||||
required: true,
|
||||
message: '版本号不能为空',
|
||||
trigger: 'blur'
|
||||
}, { pattern: /^V\d{1,3}(\.\d{1,3}){2}$/, message: '版本名称式不正确,应为 VX.Y.Z 格式', trigger: 'blur' }],
|
||||
modelType: [{ required: true, message: '请选择模型类型', trigger: 'blur' }],
|
||||
modelFrame: [{ required: true, message: '请选择模型框架', trigger: 'blur' }],
|
||||
modelFileName: [{ required: true, message: '请上传模型文件', trigger: 'blur' }],
|
||||
manualFileName: [{ required: true, message: '请上传使用手册', trigger: 'blur' }]
|
||||
},
|
||||
currentUpload: { modelFile: null, manualFile: null } // 为每个文件设置独立的上传任务
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isOpen(newVal, oldVal) {
|
||||
if (this.groupId && newVal) {
|
||||
getGroups(this.groupId).then(response => {
|
||||
this.form = response.data
|
||||
isOpen(newVal) {
|
||||
if (newVal) {
|
||||
datasetList({}).then(response => {
|
||||
this.datasetList = response.data
|
||||
})
|
||||
if (this.modelId) {
|
||||
getManager(this.modelId).then(response => {
|
||||
this.form = response.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
if (this.form.groupId != null) {
|
||||
updateGroups(this.form).then(response => {
|
||||
this.$modal.msgSuccess('修改成功')
|
||||
this.isOpen = false
|
||||
this.getList()
|
||||
})
|
||||
} else {
|
||||
addGroups(this.form).then(response => {
|
||||
this.$modal.msgSuccess('新增成功')
|
||||
this.isOpen = false
|
||||
this.getList()
|
||||
})
|
||||
triggerFileInput(refName) {
|
||||
this.$refs[refName].click()
|
||||
},
|
||||
handleFileChange(field, event) {
|
||||
const file = event.target.files[0]
|
||||
if (file) {
|
||||
this.form[field] = file
|
||||
this.form[`${field}Name`] = file.name
|
||||
}
|
||||
},
|
||||
resetField(field) {
|
||||
this.form[field] = null
|
||||
this.form[`${field}Name`] = ''
|
||||
this.$refs[field].value = '' // 重置文件选择
|
||||
},
|
||||
uploadChunks(file, field, uuid) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 文件为空时直接返回
|
||||
if (!file || file.size === 0) {
|
||||
resolve()
|
||||
}
|
||||
const totalChunks = Math.ceil(file.size / this.chunkSize)
|
||||
let currentChunk = 0
|
||||
this.uploading = true // 标记正在上传
|
||||
const controller = new AbortController() // 新增AbortController
|
||||
const signal = controller.signal // 获取 signal 用于中止请求
|
||||
|
||||
const uploadNextChunk = () => {
|
||||
if (this.uploading === false) { // 如果上传已经取消,退出
|
||||
reject('Upload canceled')
|
||||
return
|
||||
}
|
||||
const start = currentChunk * this.chunkSize
|
||||
const end = Math.min(file.size, start + this.chunkSize)
|
||||
const chunk = file.slice(start, end)
|
||||
const formData = new FormData()
|
||||
formData.append('file', chunk)
|
||||
formData.append('fileName', uuid + `.${file.name.split('.').pop().toLowerCase()}`)
|
||||
formData.append('chunk', currentChunk + 1)
|
||||
formData.append('totalChunks', totalChunks)
|
||||
uploadFile(formData, { signal }) // 在上传请求中传入 signal 用于中止
|
||||
.then(res => {
|
||||
currentChunk++
|
||||
// 更新进度
|
||||
this.uploadProgress[field] = Math.floor((currentChunk / totalChunks) * 100)
|
||||
if (currentChunk < totalChunks) {
|
||||
uploadNextChunk()
|
||||
} else {
|
||||
if (field === 'modelFile') {
|
||||
this.form.modelPath = res.data.fileUrl
|
||||
}
|
||||
if (field === 'manualFile') {
|
||||
this.form.modelManual = res.data.fileUrl
|
||||
}
|
||||
resolve() // 上传完成,返回成功
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.name === 'AbortError') {
|
||||
console.log('Upload was aborted')
|
||||
}
|
||||
reject(error) // 上传失败,返回失败
|
||||
this.$message.error('上传失败')
|
||||
})
|
||||
}
|
||||
|
||||
uploadNextChunk()
|
||||
// 将当前上传任务保存到 currentUpload 中
|
||||
this.currentUpload[field] = {
|
||||
abort: () => controller.abort() // 提供 abort 方法来中止上传
|
||||
}
|
||||
|
||||
return this.currentUpload[field] // 返回当前上传任务
|
||||
})
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
// 上传文件后再调用新增或修改操作
|
||||
// 独立上传每个文件,并确保只有所有文件上传成功后才提交表单
|
||||
Promise.all([
|
||||
this.uploadChunks(this.form.modelFile, 'modelFile', generateUUID()),
|
||||
this.uploadChunks(this.form.manualFile, 'manualFile', generateUUID())
|
||||
]).then(() => {
|
||||
this.uploading = false
|
||||
const action = this.form.id ? updateManager : addManager
|
||||
action(this.form)
|
||||
.then(() => {
|
||||
this.$message.success(this.form.id ? '修改成功' : '新增成功')
|
||||
this.cancel()
|
||||
this.getList()
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message.error('文件上传失败,无法提交')
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.isOpen = false
|
||||
this.reset()
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
groupName: null,
|
||||
description: null
|
||||
this.uploading = false
|
||||
// 取消当前文件的上传任务
|
||||
if (this.currentUpload.modelFile && this.currentUpload.modelFile.abort) {
|
||||
this.currentUpload.modelFile.abort()
|
||||
}
|
||||
this.resetForm('form')
|
||||
if (this.currentUpload.manualFile && this.currentUpload.manualFile.abort) {
|
||||
this.currentUpload.manualFile.abort()
|
||||
}
|
||||
this.resetForm()
|
||||
},
|
||||
resetForm() {
|
||||
this.resetField('modelInput')
|
||||
this.resetField('manualInput')
|
||||
this.uploading = false
|
||||
this.uploadProgress = { modelFile: 0, manualFile: 0 }
|
||||
this.form = { modelFile: null, manualFile: null, modelFileName: '', manualFileName: '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
|
|||
|
|
@ -10,12 +10,11 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型类型" prop="modelType">
|
||||
<el-input
|
||||
v-model="queryParams.modelType"
|
||||
placeholder="请选择模型类型"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
<el-select v-model="queryParams.modelType" placeholder="请选择模型类型" clearable style="width: 100%">
|
||||
<el-option v-for="dict in dict.type.ai_annotate_type" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
|
|
@ -31,7 +30,7 @@
|
|||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['system:manager:add']"
|
||||
v-hasPermi="['model:manager:add']"
|
||||
>新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
|
@ -43,7 +42,7 @@
|
|||
size="mini"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['system:manager:edit']"
|
||||
v-hasPermi="['model:manager:edit']"
|
||||
>修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
|
@ -55,34 +54,60 @@
|
|||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['system:manager:remove']"
|
||||
v-hasPermi="['model:manager:remove']"
|
||||
>删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-download"
|
||||
size="mini"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['system:manager:export']"
|
||||
>导出
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="managerList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center"/>
|
||||
<el-table-column type="index" label="序号" align="center" :index="indexMethod" width="50"/>
|
||||
<el-table-column label="模型名称" align="center" prop="modelName"/>
|
||||
<el-table-column label="版本号" align="center" prop="modelVersion"/>
|
||||
<el-table-column label="模型类型" align="center" prop="modelType"/>
|
||||
<el-table-column label="模型框架" align="center" prop="modelFrame"/>
|
||||
<el-table-column label="模型上传path" align="center" prop="modelPath"/>
|
||||
<el-table-column label="使用手册path" align="center" prop="modelManual"/>
|
||||
<el-table-column label="备注" align="center" prop="remark"/>
|
||||
<el-table-column label="是否删除" align="center" prop="isActive"/>
|
||||
<el-table-column label="数据集" align="center" prop="datasetName"/>
|
||||
<el-table-column label="模型类型" align="center" prop="modelType">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.ai_annotate_type" :value="scope.row.modelType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="模型框架" align="center" prop="modelFrame">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.ai_frame_type" :value="scope.row.modelFrame"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建人" align="center" prop="createBy"/>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime"/>
|
||||
<el-table-column label="模型文件" align="center" prop="modelFrame">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="handleDownload(scope.row.modelPath,scope.row.modelFileName)"
|
||||
v-hasPermi="['model:manager:edit']"
|
||||
>下载
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="使用手册" align="center" prop="modelFrame">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="handleDownload(scope.row.modelManual,scope.row.manualFileName)"
|
||||
v-hasPermi="['model:manager:edit']"
|
||||
>下载
|
||||
</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="handleSee(scope.row.modelManual)"
|
||||
v-hasPermi="['model:manager:remove']"
|
||||
>查看
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
|
|
@ -90,7 +115,7 @@
|
|||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:manager:edit']"
|
||||
v-hasPermi="['model:manager:edit']"
|
||||
>修改
|
||||
</el-button>
|
||||
<el-button
|
||||
|
|
@ -98,7 +123,7 @@
|
|||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:manager:remove']"
|
||||
v-hasPermi="['model:manager:remove']"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
|
|
@ -113,49 +138,23 @@
|
|||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改模型管理对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="${comment}" prop="id">
|
||||
<el-input v-model="form.id" placeholder="请输入${comment}"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型名称" prop="modelName">
|
||||
<el-input v-model="form.modelName" placeholder="请输入模型名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="版本号" prop="modelVersion">
|
||||
<el-input v-model="form.modelVersion" placeholder="请输入版本号"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型框架" prop="modelFrame">
|
||||
<el-input v-model="form.modelFrame" placeholder="请输入模型框架"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型上传path" prop="modelPath">
|
||||
<el-input v-model="form.modelPath" placeholder="请输入模型上传path"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="使用手册path" prop="modelManual">
|
||||
<el-input v-model="form.modelManual" placeholder="请输入使用手册path"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否删除" prop="isActive">
|
||||
<el-input v-model="form.isActive" placeholder="请输入是否删除"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<custom-dialog :title="title" :get-list="getList" :open="open" :model-id="modelId" @dialog-cancel="handleCancel"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listManager, delManager } from '@/api/dataCenter/model'
|
||||
import customDialog from './child/customDialog.vue'
|
||||
import { Base64 } from 'js-base64'
|
||||
import { encryptCBC } from '../../../utils/aescbc'
|
||||
|
||||
export default {
|
||||
name: 'Manager',
|
||||
dicts: ['ai_annotate_type', 'ai_frame_type'],
|
||||
components: { customDialog },
|
||||
data() {
|
||||
return {
|
||||
modelId: null,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
|
|
@ -187,6 +186,9 @@ export default {
|
|||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
indexMethod(index) {
|
||||
return (this.queryParams.pageNum - 1) * this.queryParams.pageSize + index + 1
|
||||
},
|
||||
/** 查询模型管理列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
|
|
@ -214,25 +216,72 @@ export default {
|
|||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset()
|
||||
this.title = '添加'
|
||||
this.modelId = null
|
||||
this.open = true
|
||||
this.title = '添加模型管理'
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset()
|
||||
const id = row.id || this.ids
|
||||
this.modelId = row.id || this.ids[0]
|
||||
this.title = '修改'
|
||||
this.open = true
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const ids = row.id || this.ids
|
||||
this.$modal.confirm('是否确认删除模型管理编号为"' + ids + '"的数据项?').then(function() {
|
||||
this.$modal.confirm('是否确认删除数据项?').then(function() {
|
||||
return delManager(ids)
|
||||
}).then(() => {
|
||||
this.getList()
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
// 处理取消事件
|
||||
handleCancel() {
|
||||
this.open = false
|
||||
},
|
||||
handleDownload(url, fileName) {
|
||||
// 创建一个 XMLHttpRequest 对象
|
||||
const xhr = new XMLHttpRequest()
|
||||
// 设置响应类型为 blob,这样能处理二进制数据等各种类型的文件内容
|
||||
xhr.responseType = 'blob'
|
||||
// 监听请求的加载完成事件
|
||||
xhr.onload = function() {
|
||||
if (this.status === 200) {
|
||||
// 获取响应内容(以 blob 形式)
|
||||
const blob = this.response
|
||||
// 创建一个用于下载的链接元素
|
||||
const downloadLink = document.createElement('a')
|
||||
// 为链接元素生成一个临时的 URL,指向 blob 数据
|
||||
downloadLink.href = URL.createObjectURL(blob)
|
||||
// 设置下载链接的下载属性(指定文件名)
|
||||
downloadLink.download = fileName
|
||||
// 模拟点击链接来触发下载
|
||||
downloadLink.click()
|
||||
// 释放创建的临时 URL 资源
|
||||
URL.revokeObjectURL(downloadLink.href)
|
||||
}
|
||||
}
|
||||
// 监听请求的错误事件
|
||||
xhr.onerror = function() {
|
||||
console.error('下载请求出现错误')
|
||||
}
|
||||
// 打开请求,使用 GET 方法去获取资源
|
||||
xhr.open('GET', `${JSON.parse(localStorage.getItem('minIoUrl')).minioUrl}${url}`, true)
|
||||
// 发送请求
|
||||
xhr.send()
|
||||
},
|
||||
handleSee(url) {
|
||||
let b64Encoded = Base64.encode(`${JSON.parse(localStorage.getItem('minIoUrl')).minioUrl}${url}`)
|
||||
window.open(`http://192.168.0.14:8012/onlinePreview?url=${b64Encoded}&token=${this.generateToken()}`, '_blank')
|
||||
},
|
||||
generateToken() {
|
||||
// 获取当前时间戳(以秒为单位)
|
||||
const currentTimeStamp = Math.floor(Date.now())
|
||||
// 将时间戳转换为字符串
|
||||
const timeStampString = currentTimeStamp.toString()
|
||||
return encryptCBC(timeStampString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue