Browse Source

update

pull/13/head
陈钰楠 2 years ago
parent
commit
7fbd135501
20 changed files with 132 additions and 294 deletions
  1. +3
    -3
      webapp/.env.pre
  2. +2
    -1
      webapp/.eslintignore
  3. +4
    -7
      webapp/babel.config.js
  4. +12
    -0
      webapp/package.json
  5. +2
    -10
      webapp/src/api/trainingImage/index.js
  6. +4
    -0
      webapp/src/assets/styles/atomic.scss
  7. +3
    -0
      webapp/src/components/BaseTooltip/index.vue
  8. +3
    -2
      webapp/src/components/Training/jobForm.vue
  9. +1
    -1
      webapp/src/utils/VisualUtils/download.js
  10. +18
    -0
      webapp/src/utils/constant.js
  11. +1
    -1
      webapp/src/utils/download.js
  12. +2
    -2
      webapp/src/views/algorithm/components/algorithmForm.vue
  13. +2
    -3
      webapp/src/views/cloudServing/components/forms/batchServingForm.vue
  14. +3
    -4
      webapp/src/views/cloudServing/components/forms/servingModelConfig.vue
  15. +7
    -3
      webapp/src/views/dataset/modelService/CreateModelService.vue
  16. +7
    -3
      webapp/src/views/dataset/modelService/ModifyModelService.vue
  17. +3
    -3
      webapp/src/views/dataset/pointCloud/components/auto-annotate-modal.vue
  18. +3
    -4
      webapp/src/views/development/components/CreateDialog.vue
  19. +52
    -238
      webapp/src/views/trainingImage/index.vue
  20. +0
    -9
      webapp/src/views/trainingJob/utils.js

+ 3
- 3
webapp/.env.pre View File

@@ -14,16 +14,16 @@ VUE_APP_DATA_API = '/'
VUE_APP_VISUAL_API = '/'

# minio
VUE_APP_MINIO_API = 'http://127.0.0.1:9000/minio'
VUE_APP_MINIO_API = 'http://10.105.1.132:9000/minio'

# atlas
VUE_APP_ATLAS_HOST = 'http://127.0.0.1'
VUE_APP_ATLAS_HOST = 'http://183.129.174.186'

# DCM4CHEE
VUE_APP_DCM_API = 'http://pre.dubhe.club/dcm4chee/dcm4chee-arc/aets/DCM4CHEE_ADMIN'

# minIO 服务 IP
VUE_APP_MINIO_ENDPOINT = '127.0.0.1'
VUE_APP_MINIO_ENDPOINT = '10.105.1.132'
# minIO 服务 端口
VUE_APP_MINIO_PORT = '9000'
# 是否开启 SSL


+ 2
- 1
webapp/.eslintignore View File

@@ -5,4 +5,5 @@ dist
src/components/Crud
mock
src/views/visual
src/store/modules/Visual
src/store/modules/Visual
src/utils/VisualUtils

+ 4
- 7
webapp/babel.config.js View File

@@ -1,12 +1,9 @@
const plugins = [
"@vue/babel-plugin-transform-vue-jsx",
"@babel/plugin-proposal-optional-chaining",
];
const plugins = ['@vue/babel-plugin-transform-vue-jsx', '@babel/plugin-proposal-optional-chaining'];
// 生产环境移除console
if (process.env.NODE_ENV === "production") {
plugins.push("transform-remove-console");
if (process.env.NODE_ENV === 'production') {
plugins.push('transform-remove-console');
}
module.exports = {
plugins,
presets: [["@vue/app",{ useBuiltIns: "entry" }]],
presets: [['@vue/app', { useBuiltIns: 'entry' }]],
};

+ 12
- 0
webapp/package.json View File

@@ -24,6 +24,18 @@
"lint:style": "stylelint src/**/*.{html,vue,css,sass,scss}",
"fix:style": "stylelint src/**/*.{html,vue,css,sass,scss} --fix"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
]
},
"repository": {
"type": "git",
"url": "git@codeup.teambition.com:zhejianglab/dubhe-web.git"


+ 2
- 10
webapp/src/api/trainingImage/index.js View File

@@ -19,7 +19,7 @@ import { API_MODULE_NAME } from '@/config';

export function list(params) {
return request({
url: `/${API_MODULE_NAME.IMAGE}/ptImage/info`,
url: `/${API_MODULE_NAME.IMAGE}/ptImage/list`,
method: 'get',
params,
});
@@ -27,7 +27,7 @@ export function list(params) {

export function add(data) {
return request({
url: `/${API_MODULE_NAME.IMAGE}/ptImage/uploadImage`,
url: `/${API_MODULE_NAME.IMAGE}/ptImage`,
method: 'post',
data,
});
@@ -65,14 +65,6 @@ export function getImageTagList(params) {
});
}

export function setPrecast(params) {
return request({
url: `/${API_MODULE_NAME.IMAGE}/ptImage/imageResource`,
method: 'put',
params,
});
}

// 设置notebook默认镜像-获取默认镜像
export function getDefaultImage(params) {
return request({


+ 4
- 0
webapp/src/assets/styles/atomic.scss View File

@@ -332,6 +332,10 @@
width: 320px;
}

.w-400 {
width: 400px;
}

.w-500 {
width: 500px;
}


+ 3
- 0
webapp/src/components/BaseTooltip/index.vue View File

@@ -9,6 +9,9 @@ the License. * ============================================================= */
<template>
<el-tooltip v-bind="mergedAttrs">
<i class="primary f18 vm" :class="[icon]" />
<template #content>
<slot name="content" />
</template>
</el-tooltip>
</template>



+ 3
- 2
webapp/src/components/Training/jobForm.vue View File

@@ -541,6 +541,7 @@ import {
RESOURCES_MODULE_ENUM,
RESOURCES_POOL_TYPE_ENUM,
RESOURCES_POOL_TYPE_MAP,
IMAGE_TYPE_ENUM,
} from '@/utils';
import { list as getAlgorithmList, getAlgorithmInfo } from '@/api/algorithm/algorithm';
import { getModelByResource } from '@/api/model/model';
@@ -551,7 +552,7 @@ import { list as getSpecsNames } from '@/api/system/resources';
import { list as getNotebooks } from '@/api/development/notebook';
import { trainConfig } from '@/config';
import { NOTEBOOK_STATUS_ENUM } from '@/views/development/utils';
import { IMAGE_TYPE, TRAINING_TYPE_ENUM } from '@/views/trainingJob/utils';
import { TRAINING_TYPE_ENUM } from '@/views/trainingJob/utils';
import BaseTooltip from '@/components/BaseTooltip';
import DataSourceSelector from './dataSourceSelector';

@@ -929,7 +930,7 @@ export default {
},
async getHarborProjects() {
this.harborProjectList = await getImageNameList({
imageTypes: [IMAGE_TYPE.TRAIN],
imageTypes: [IMAGE_TYPE_ENUM.TRAIN],
});
if (
this.form.imageName &&


+ 1
- 1
webapp/src/utils/VisualUtils/download.js View File

@@ -39,7 +39,7 @@ const covertSVG2Image = (node, name, width, height, type = 'png') => {
const context = canvas.getContext('2d');
context.fillStyle = '#fff';
context.fillRect(0, 0, 10000, 10000);
image.onload = function() {
image.onload = () => {
context.drawImage(image, 0, 0);
const a = document.createElement('a');
a.download = `${name}.${type}`;


+ 18
- 0
webapp/src/utils/constant.js View File

@@ -133,6 +133,24 @@ export const K8S_BUSINESS_LABEL_MAP = {
[K8S_BUSINESS_LABEL_ENUM.POINT_CLOUD]: '点云数据集',
};

// 镜像分类枚举
export const IMAGE_TYPE_ENUM = {
NOTEBOOK: 0, // notebook镜像类型
TRAIN: 1, // 训练镜像和预置镜像类型
SERVING: 2, // serving镜像
TERMINAL: 3, // 终端镜像
POINTCLOUD: 4, // 点云镜像
DATASETMARKED: 5, // 数据标注镜像
};
export const IMAGE_TYPE_MAP = {
[IMAGE_TYPE_ENUM.NOTEBOOK]: 'Notebook镜像',
[IMAGE_TYPE_ENUM.TRAIN]: '训练镜像',
[IMAGE_TYPE_ENUM.SERVING]: 'Serving镜像',
[IMAGE_TYPE_ENUM.TERMINAL]: '终端镜像',
[IMAGE_TYPE_ENUM.POINTCLOUD]: '点云镜像',
[IMAGE_TYPE_ENUM.DATASETMARKED]: '数据标注镜像',
};

// 默认进度条颜色
export const defaultProcessColors = [
{ color: '#909399', percentage: 40 },


+ 1
- 1
webapp/src/utils/download.js View File

@@ -20,7 +20,7 @@ import ZIP from './zip';

const pMap = require('p-map');

//streamSaver.mitm = 'https://static.zhejianglab.com/mitm.html';
streamSaver.mitm = 'https://static.zhejianglab.com/mitm.html';

// 默认名字解析
const defaultName = (file) => file.name;


+ 2
- 2
webapp/src/views/algorithm/components/algorithmForm.vue View File

@@ -171,13 +171,13 @@ import {
invalidFileNameChar,
defaultProcessColors,
ALGORITHM_RESOURCE_ENUM,
IMAGE_TYPE_ENUM,
} from '@/utils';
import UploadInline from '@/components/UploadForm/inline';
import UploadProgress from '@/components/UploadProgress';
import { useMapGetters } from '@/hooks';
import { algorithmConfig } from '@/config';
import { getImageNameList, getImageTagList } from '@/api/trainingImage';
import { IMAGE_TYPE } from '@/views/trainingJob/utils';

const defaultForm = {
id: null,
@@ -289,7 +289,7 @@ export default {
};
// 获取镜像名列表
const getImageNames = async (keepValue = false) => {
state.imageNameList = await getImageNameList({ imageTypes: [IMAGE_TYPE.TRAIN] });
state.imageNameList = await getImageNameList({ imageTypes: [IMAGE_TYPE_ENUM.TRAIN] });
if (!keepValue || !form.imageName) {
form.imageTag = null;
} else if (!state.imageNameList.includes(form.imageName)) {


+ 2
- 3
webapp/src/views/cloudServing/components/forms/batchServingForm.vue View File

@@ -199,14 +199,13 @@
<script>
import { mapGetters } from 'vuex';

import { getUniqueId, invalidFileNameChar, RESOURCES_MODULE_ENUM } from '@/utils';
import { getUniqueId, invalidFileNameChar, RESOURCES_MODULE_ENUM, IMAGE_TYPE_ENUM } from '@/utils';
import { getServingModel } from '@/api/model/model';
import { list as getModelVersions } from '@/api/model/modelVersion';
import { getImageNameList, getImageTagList } from '@/api/trainingImage';
import { getInferenceAlgorithm, add as addAlgorithm } from '@/api/algorithm/algorithm';
import { list as getSpecsNames } from '@/api/system/resources';
import { validateNameWithHyphen } from '@/utils/validate';
import { IMAGE_TYPE } from '@/views/trainingJob/utils';
import RunParamForm from '@/components/Training/runParamForm';
import BaseModal from '@/components/BaseModal';
import AlgorithmForm from '@/views/algorithm/components/algorithmForm';
@@ -541,7 +540,7 @@ export default {

// 镜像选择
async getImageNames(keepValue = false) {
this.imageNameList = await getImageNameList({ imageTypes: [IMAGE_TYPE.SERVING] });
this.imageNameList = await getImageNameList({ imageTypes: [IMAGE_TYPE_ENUM.SERVING] });
if (!keepValue || !this.form.imageName) {
this.form.imageTag = null;
} else if (!this.imageNameList.includes(this.form.imageName)) {


+ 3
- 4
webapp/src/views/cloudServing/components/forms/servingModelConfig.vue View File

@@ -204,8 +204,7 @@ import { getImageNameList, getImageTagList } from '@/api/trainingImage';
import { getInferenceAlgorithm, add as addAlgorithm } from '@/api/algorithm/algorithm';
import { list as getSpecsNames } from '@/api/system/resources';
import { servingConfig } from '@/config';
import { RESOURCES_MODULE_ENUM } from '@/utils';
import { IMAGE_TYPE } from '@/views/trainingJob/utils';
import { RESOURCES_MODULE_ENUM, IMAGE_TYPE_ENUM } from '@/utils';
import RunParamForm from '@/components/Training/runParamForm';
import BaseModal from '@/components/BaseModal';
import AlgorithmForm from '@/views/algorithm/components/algorithmForm';
@@ -540,7 +539,7 @@ export default {
// 镜像选择
// 获取镜像名称列表
async getImageNames(keepValue = false) {
this.imageNameList = await getImageNameList({ imageTypes: [IMAGE_TYPE.SERVING] });
this.imageNameList = await getImageNameList({ imageTypes: [IMAGE_TYPE_ENUM.SERVING] });
if (!keepValue || !this.form.imageName) {
this.form.imageTag = null;
} else if (!this.imageNameList.includes(this.form.imageName)) {
@@ -554,7 +553,7 @@ export default {
async getImageTags(imageName, keepValue = false) {
this.imageTagList = await getImageTagList({
imageName,
imageTypes: [IMAGE_TYPE.SERVING],
imageTypes: [IMAGE_TYPE_ENUM.SERVING],
});
if (keepValue && this.form.imageTag) {
if (!this.imageTagList.some((image) => image.imageTag === this.form.imageTag)) {


+ 7
- 3
webapp/src/views/dataset/modelService/CreateModelService.vue View File

@@ -134,8 +134,12 @@ import { list as listBranchModel } from '@/api/model/modelVersion';
import { getImageNameList, getImageTagList } from '@/api/trainingImage';
import { list as listResourceSpec } from '@/api/system/resources';
import { types } from '@/utils/validate';
import { RESOURCES_POOL_TYPE_ENUM, RESOURCES_MODULE_ENUM, modelTypeSymbol } from '@/utils/constant';
import { IMAGE_TYPE } from '@/views/trainingJob/utils';
import {
RESOURCES_POOL_TYPE_ENUM,
RESOURCES_MODULE_ENUM,
modelTypeSymbol,
IMAGE_TYPE_ENUM,
} from '@/utils/constant';

const initialForm = {
name: undefined,
@@ -246,7 +250,7 @@ export default {
};

const queryImages = () => {
return getImageNameList({ imageTypes: [IMAGE_TYPE.DATASETMARKED] });
return getImageNameList({ imageTypes: [IMAGE_TYPE_ENUM.DATASETMARKED] });
};

const handleModelParentChange = () => {


+ 7
- 3
webapp/src/views/dataset/modelService/ModifyModelService.vue View File

@@ -143,8 +143,12 @@ import { list as listBranchModel } from '@/api/model/modelVersion';
import { getImageNameList, getImageTagList } from '@/api/trainingImage';
import { list as listResourceSpec } from '@/api/system/resources';
import { types } from '@/utils/validate';
import { RESOURCES_POOL_TYPE_ENUM, RESOURCES_MODULE_ENUM, modelTypeSymbol } from '@/utils/constant';
import { IMAGE_TYPE } from '@/views/trainingJob/utils';
import {
RESOURCES_POOL_TYPE_ENUM,
RESOURCES_MODULE_ENUM,
modelTypeSymbol,
IMAGE_TYPE_ENUM,
} from '@/utils/constant';

const initialForm = {
name: undefined,
@@ -283,7 +287,7 @@ export default {

// 查询镜像列表
const queryImages = () => {
return getImageNameList({ imageTypes: [IMAGE_TYPE.DATASETMARKED] });
return getImageNameList({ imageTypes: [IMAGE_TYPE_ENUM.DATASETMARKED] });
};

// 查询镜像版本列表


+ 3
- 3
webapp/src/views/dataset/pointCloud/components/auto-annotate-modal.vue View File

@@ -163,8 +163,8 @@ import {
ALGORITHM_RESOURCE_ENUM,
MODEL_RESOURCE_ENUM,
RESOURCES_POOL_TYPE_ENUM,
IMAGE_TYPE_ENUM,
} from '@/utils';
import { IMAGE_TYPE } from '@/views/trainingJob/utils';

const initFormState = {
modelId: null,
@@ -285,7 +285,7 @@ export default {
return Promise.reject();
}
return getImageTagList({
imageTypes: IMAGE_TYPE.POINTCLOUD,
imageTypes: IMAGE_TYPE_ENUM.POINTCLOUD,
imageName: modelState.imageName,
}).then((res) => {
state.imageTagList = res;
@@ -294,7 +294,7 @@ export default {

// 镜像选择
const getImageNames = async () => {
state.imageNameList = await getImageNameList({ imageTypes: [IMAGE_TYPE.POINTCLOUD] });
state.imageNameList = await getImageNameList({ imageTypes: [IMAGE_TYPE_ENUM.POINTCLOUD] });
if (
modelState.imageName &&
!state.imageNameList.some((image) => image === modelState.imageName)


+ 3
- 4
webapp/src/views/development/components/CreateDialog.vue View File

@@ -122,14 +122,13 @@
</template>

<script>
import { validateNameWithHyphen, RESOURCES_MODULE_ENUM } from '@/utils';
import { validateNameWithHyphen, RESOURCES_MODULE_ENUM, IMAGE_TYPE_ENUM } from '@/utils';
import BaseModal from '@/components/BaseModal';
import InfoSelect from '@/components/InfoSelect';
import { getPublishedDatasets, getDatasetVersions } from '@/api/preparation/dataset';
import { add as addNotebook } from '@/api/development/notebook';
import { list as getSpecsNames } from '@/api/system/resources';
import { getImageNameList, getImageTagList } from '@/api/trainingImage/index';
import { IMAGE_TYPE } from '@/views/trainingJob/utils';

const defaultForm = {
noteBookName: null,
@@ -208,7 +207,7 @@ export default {
}
},
async getHarborProjects() {
this.harborProjectList = await getImageNameList({ imageTypes: [IMAGE_TYPE.NOTEBOOK] });
this.harborProjectList = await getImageNameList({ imageTypes: [IMAGE_TYPE_ENUM.NOTEBOOK] });
if (
this.form.imageName &&
!this.harborProjectList.some((project) => project === this.form.imageName)
@@ -237,7 +236,7 @@ export default {
}
return getImageTagList({
imageName: this.form.imageName,
imageTypes: [IMAGE_TYPE.NOTEBOOK],
imageTypes: [IMAGE_TYPE_ENUM.NOTEBOOK],
}).then((res) => {
this.harborImageList = res;
});


+ 52
- 238
webapp/src/views/trainingImage/index.vue View File

@@ -48,7 +48,7 @@
<el-table
v-if="prefabricate"
ref="table"
v-loading="crud.loading || disableEdit"
v-loading="crud.loading"
:data="crud.data"
highlight-current-row
@selection-change="crud.selectionChangeHandler"
@@ -57,21 +57,6 @@
<el-table-column v-if="!isPreset" prop="id" label="ID" sortable="custom" width="80px" />
<el-table-column prop="imageName" label="镜像名称" sortable="custom" />
<el-table-column prop="imageTag" label="镜像版本号" sortable="custom" />
<el-table-column prop="imageStatus" label="状态" width="130px">
<template #header>
<dropdown-header
title="状态"
:list="imageStatusList"
:filtered="Boolean(localQuery.imageStatus)"
@command="filterStatus"
/>
</template>
<template slot-scope="scope">
<el-tag effect="plain" :type="map[scope.row.imageStatus]">
{{ statusMap[scope.row.imageStatus] }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="imageTypes" label="镜像用途" width="180px">
<template #header>
<dropdown-header
@@ -91,31 +76,8 @@
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column v-if="isNotebook" prop="imageResource" label="是否为默认" align="center">
<template slot-scope="scope">
<i
:class="resourceObj(scope.row.imageResource).icon"
:style="{ color: resourceObj(scope.row.imageResource).color, fontSize: '20px' }"
></i>
</template>
</el-table-column>
<el-table-column v-if="isAdmin || isCustom" label="操作" width="200px" fixed="right">
<template slot-scope="scope">
<el-tooltip
v-if="isAdmin && isNotebook"
effect="dark"
content="设为在线编辑算法时创建nootbook的默认镜像"
placement="top"
>
<el-button
:id="`doPrecast_` + scope.$index"
:disabled="Boolean(scope.row.imageResource)"
type="text"
@click.stop="doPrecast(scope.row.id)"
>
{{ resourceObj(scope.row.imageResource).butText }}
</el-button>
</el-tooltip>
<el-button
v-if="(hasPermission('training:image:edit') && isCustom) || (isPreset && isAdmin)"
:id="`doEdit_` + scope.$index"
@@ -143,15 +105,13 @@
:visible="crud.status.cu > 0"
:title="crud.status.title"
:loading="crud.status.cu === 2"
:disabled="loading"
width="600px"
@open="onDialogOpen"
@close="onDialogClose"
@cancel="crud.cancelCU"
@ok="crud.submitCU"
>
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-form-item v-if="isEdit && isAdmin" label="镜像类别" prop="imageResource">
<el-form-item v-if="isFormAdd && isAdmin" label="镜像类别" prop="imageResource">
<el-radio-group v-model="form.imageResource" @change="imageResourceChange">
<el-radio :label="Number(IMAGE_RESOURCE_ENUM.CUSTOM)" border class="mr-0"
>我的镜像</el-radio
@@ -159,21 +119,20 @@
<el-radio :label="Number(IMAGE_RESOURCE_ENUM.PRESET)" border>预置镜像</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="isEdit" label="镜像名称" prop="imageName">
<el-form-item label="镜像名称" prop="imageName">
<el-autocomplete
ref="imageName"
v-model="form.imageName"
class="inline-input"
class="inline-input w-400"
:fetch-suggestions="querySearchAsync"
placeholder="请选择或输入镜像名称"
style="width: 400px;"
></el-autocomplete>
</el-form-item>
<el-form-item label="镜像用途">
<el-select
v-model="form.imageTypes"
placeholder="请选择或输入镜像用途"
style="width: 400px;"
class="w-400"
multiple
filterable
allow-create
@@ -187,36 +146,18 @@
/>
</el-select>
</el-form-item>
<el-form-item v-if="isEdit" ref="imagePath" label="镜像文件路径" prop="imagePath">
<upload-inline
v-if="crud.status.cu > 0"
ref="upload"
action="fakeApi"
accept=".zip,.tar,.rar,.gz"
list-type="text"
:acceptSize="imageConfig.uploadFileAcceptSize"
:acceptSizeFormat="uploadSizeFomatter"
:params="uploadParams"
:show-file-count="false"
:auto-upload="true"
:filters="uploadFilters"
:limit="1"
:on-remove="onFileRemove"
@uploadStart="uploadStart"
@uploadSuccess="uploadSuccess"
@uploadError="uploadError"
/>
<upload-progress
v-if="loading"
:progress="progress"
:color="customColors"
:status="status"
:size="size"
@onSetProgress="onSetProgress"
/>
<el-form-item ref="imageUrl" label="镜像地址" prop="imageUrl">
<el-input v-model="form.imageUrl" placeholder="请输入镜像地址" class="w-400" />
<BaseTooltip icon="el-icon-warning" class="c-info">
<template #content>
<div>
镜像地址标准格式:镜像仓库域名/命名空间/镜像名:镜像版本号<br />示例:registry.cn-hangzhou.aliyuncs.com/enlin/notebook:v1
</div>
</template>
</BaseTooltip>
</el-form-item>
<el-form-item v-if="isEdit" label="镜像版本号" prop="imageTag">
<el-input id="imageTag" v-model="form.imageTag" style="width: 400px;" />
<el-form-item label="镜像版本号" prop="imageTag">
<el-input id="imageTag" v-model="form.imageTag" class="w-400" />
</el-form-item>
<el-form-item label="描述" prop="remark">
<el-input
@@ -227,19 +168,19 @@
maxlength="1024"
show-word-limit
placeholder
style="width: 400px;"
class="w-400"
/>
</el-form-item>
</el-form>
</BaseModal>
<!--Notebook默认镜像设置表单-->
<BaseModal
:visible.sync="formVisible"
:visible.sync="notebookFormVisible"
title="Notebook默认镜像设置"
:loading="formSubmitting"
:loading="notebookFormSubmitting"
width="800px"
@cancel="formVisible = false"
@ok="onSubmitForm"
@cancel="notebookFormVisible = false"
@ok="onSubmitNotebookForm"
>
<el-form
ref="noteBookFormRef"
@@ -253,7 +194,7 @@
id="defaultImage"
v-model="noteBookForm.defaultImage"
placeholder="请选择镜像"
style="width: 400px;"
class="w-400"
filterable
allow-create
default-first-option
@@ -283,8 +224,6 @@

<script>
import { mapGetters } from 'vuex';
// eslint-disable-next-line import/no-extraneous-dependencies
import { debounce } from 'throttle-debounce';

import cdOperation from '@crud/CD.operation';
import rrOperation from '@crud/RR.operation';
@@ -292,40 +231,36 @@ import pagination from '@crud/Pagination';
import CRUD, { presenter, header, form, crud } from '@crud/crud';
import trainingImageApi, {
del,
setPrecast,
setDefaultImage,
getDefaultImage,
getImageNameList,
getImageTagList,
} from '@/api/trainingImage/index';
import {
getUniqueId,
uploadSizeFomatter,
invalidFileNameChar,
ADMIN_ROLE_ID,
hasPermission,
validateImageName,
validateImageTag,
IMAGE_TYPE_ENUM,
IMAGE_TYPE_MAP,
} from '@/utils';
import BaseModal from '@/components/BaseModal';
import UploadInline from '@/components/UploadForm/inline';
import BaseTooltip from '@/components/BaseTooltip';
import DropdownHeader from '@/components/DropdownHeader';
import UploadProgress from '@/components/UploadProgress';
import { imageConfig } from '@/config';

import { IMAGE_RESOURCE_ENUM, IMAGE_TYPE } from '../trainingJob/utils';
import { IMAGE_RESOURCE_ENUM } from '../trainingJob/utils';

const defaultForm = {
imagePath: null,
imageUrl: null,
imageTag: null,
remark: null,
imageTypes: Number(IMAGE_TYPE),
imageTypes: Number(IMAGE_TYPE_ENUM),
imageResource: Number(IMAGE_RESOURCE_ENUM.CUSTOM),
imageName: null,
};

const defaultQuery = {
imageStatus: null,
imageNameOrId: null,
imageTypes: null,
};
@@ -334,28 +269,27 @@ export default {
name: 'TrainingImage',
components: {
BaseModal,
BaseTooltip,
pagination,
cdOperation,
rrOperation,
UploadInline,
DropdownHeader,
UploadProgress,
},
cruds() {
return CRUD({
title: '镜像',
crudMethod: { ...trainingImageApi },
optShow: {
add: imageConfig.allowUploadImage && hasPermission('training:image:upload'),
add: imageConfig.allowUploadImage && hasPermission('training:image:save'),
del: false,
},
queryOnPresenterCreated: false,
props: {
optText: {
add: '上传镜像',
add: '创建镜像',
},
optTitle: {
add: '上传',
add: '创建',
},
},
});
@@ -365,24 +299,6 @@ export default {
return {
active: IMAGE_RESOURCE_ENUM.CUSTOM,
localQuery: { ...defaultQuery },
map: {
0: 'info',
1: 'success',
2: 'danger',
},
statusMap: {
0: '制作中',
1: '制作成功',
2: '制作失败',
},
imageTypesMap: {
0: 'Notebook镜像',
1: '训练镜像',
2: 'Serving镜像',
3: '终端镜像',
4: '点云镜像',
5: '数据标注镜像',
},
rules: {
imageTypes: [{ required: true, message: '请选择镜像类型', trigger: 'change' }],
imageResource: [{ required: true, message: '请选择镜像来源', trigger: 'change' }],
@@ -390,7 +306,7 @@ export default {
{ required: true, message: '请选择项目名称', trigger: 'change' },
{ validator: validateImageName, trigger: ['blur', 'change'] },
],
imagePath: [{ required: true, message: '请输入镜像路径', trigger: ['blur', 'manual'] }],
imageUrl: [{ required: true, message: '请输入镜像地址', trigger: ['blur', 'manual'] }],
imageTag: [
{ required: true, message: '请输入镜像版本号', trigger: 'blur' },
{ validator: validateImageTag, trigger: ['blur', 'change'] },
@@ -401,32 +317,17 @@ export default {
id: [{ required: true, message: '请选择默认镜像版本', trigger: 'blur' }],
},
harborProjectList: [],
drawer: false,
uploadParams: {
objectPath: null, // 对象存储路径
},
progress: 0,
size: 0,
customColors: [
{ color: '#909399', percentage: 40 },
{ color: '#e6a23c', percentage: 80 },
{ color: '#67c23a', percentage: 100 },
],
disableEdit: false,
loading: false,
isEdit: false,
prefabricate: true,
// 以下为配置参数及常量参数
imageConfig,
IMAGE_RESOURCE_ENUM,
IMAGE_TYPE,
uploadFilters: [invalidFileNameChar],
// 设置notebook默认镜像相关参数
noteBookImages: [],
noteBookTags: [],
noteBookForm: { defaultImage: '', defaultTag: '', id: '' },
formVisible: false,
formSubmitting: false,

formType: 'add',
notebookFormVisible: false,
notebookFormSubmitting: false,
};
},
computed: {
@@ -441,14 +342,8 @@ export default {
isPreset() {
return this.active === IMAGE_RESOURCE_ENUM.PRESET;
},
isNotebook() {
return this.active === IMAGE_RESOURCE_ENUM.NOTEBOOK;
},
isTerminal() {
return this.active === IMAGE_RESOURCE_ENUM.TERMINAL;
},
disableAdd() {
if (this.isAdmin) return this.isTerminal; // 管理员只有在终端镜像处无法点击上传
if (this.isAdmin) return false; // 管理员可以创建我的镜像和预置镜像
return !this.isCustom; // 其他角色只有在我的镜像处可以点击上传
},
operationProps() {
@@ -456,37 +351,24 @@ export default {
disabled: this.disableAdd,
};
},
imageStatusList() {
const arr = [{ label: '全部', value: null }];
for (const key in this.statusMap) {
arr.push({ label: this.statusMap[key], value: key });
}
return arr;
},
imageTypesList() {
const arr = [{ label: '全部', value: null }];
for (const key in this.imageTypesMap) {
arr.push({ label: this.imageTypesMap[key], value: +key });
for (const key in IMAGE_TYPE_MAP) {
arr.push({ label: IMAGE_TYPE_MAP[key], value: +key });
}
return arr;
},
status() {
return this.progress === 100 ? 'success' : null;
isFormAdd() {
return this.formType === 'add';
},
},
mounted() {
this.crud.refresh();
this.refetch = debounce(3000, this.crud.refresh);
this.updateImagePath();
},
methods: {
hasPermission,
getImageTypes(imageTypes) {
const usageStr = [];
imageTypes.forEach((item) => {
usageStr.push(this.imageTypesMap[item]);
});
return usageStr.join(',');
return imageTypes.map((type) => IMAGE_TYPE_MAP[type]).join(',');
},
getNoteBookTags() {
this.noteBookForm.defaultTag = null;
@@ -497,7 +379,7 @@ export default {
}
return getImageTagList({
imageName: this.noteBookForm.defaultImage,
imageTypes: IMAGE_TYPE.NOTEBOOK,
imageTypes: IMAGE_TYPE_ENUM.NOTEBOOK,
imageResource: Number(IMAGE_RESOURCE_ENUM.PRESET),
}).then((res) => {
this.noteBookTags = res;
@@ -513,46 +395,11 @@ export default {
this.prefabricate = true;
});
},
onFileRemove() {
this.form.imagePath = null;
this.loading = false;
this.$refs.imagePath.validate('manual');
},
uploadStart(files) {
this.updateImagePath();
[this.loading, this.size, this.progress] = [true, files.size, 0];
},
onSetProgress(val) {
this.progress += val;
},
uploadSuccess(res) {
this.progress = 100;
setTimeout(() => {
this.loading = false;
}, 1000);
if (this.loading) {
this.form.imagePath = res[0].data.objectName;
this.$refs.imagePath.validate('manual');
}
},
uploadError() {
this.$message({
message: '上传文件失败',
type: 'error',
});
this.loading = false;
},
// hook
[CRUD.HOOK.afterRefresh]() {
this.checkStatus();
},
[CRUD.HOOK.beforeToAdd]() {
this.isEdit = true;
this.formType = 'add';
if (this.isPreset) {
this.form.imageResource = Number(IMAGE_RESOURCE_ENUM.PRESET);
} else if (this.isNotebook) {
this.form.imageTypes = IMAGE_TYPE.NOTEBOOK;
}
},
[CRUD.HOOK.beforeRefresh]() {
@@ -567,7 +414,7 @@ export default {
}
},
[CRUD.HOOK.beforeToEdit]() {
this.isEdit = false;
this.formType = 'edit';
},
async querySearchAsync(queryString, cb) {
let { harborProjectList } = this;
@@ -597,24 +444,9 @@ export default {
this.form.imageResource = Number(IMAGE_RESOURCE_ENUM.CUSTOM);
this.getImageNameList();
},
onDialogClose() {
if (this.isEdit) {
this.$refs.upload.formRef.reset();
}
this.loading = false;
},
async onDialogOpen() {
this.getImageNameList();
},
checkStatus() {
if (this.crud.data.some((item) => [0].includes(item.imageStatus))) {
this.refetch();
}
},
filterStatus(status) {
this.localQuery.imageStatus = status;
this.crud.toQuery();
},
filterImageTypes(imageTypes) {
this.localQuery.imageTypes = imageTypes;
this.crud.toQuery();
@@ -622,9 +454,6 @@ export default {
resetQuery() {
this.localQuery = { ...defaultQuery };
},
updateImagePath() {
this.uploadParams.objectPath = `upload-temp/${this.user.id}/${getUniqueId()}`;
},
async doEdit(imageObj) {
const dataObj = {
ids: [imageObj.id],
@@ -642,21 +471,6 @@ export default {
this.crud.refresh();
});
},
doPrecast(i) {
setPrecast({ id: i }).then(() => {
this.$message({
message: '设置成功',
type: 'success',
});
this.crud.refresh();
});
},
resourceObj(resource) {
return resource
? { icon: 'el-icon-circle-check', color: '#67C23A', butText: '当前为默认' }
: { icon: 'el-icon-circle-close', color: '#F56C6C', butText: '设为默认' };
},
uploadSizeFomatter,

async doSetDefaultImage() {
// 获取默认镜像
@@ -666,20 +480,20 @@ export default {
this.noteBookForm.id = defaultImage.length ? defaultImage[0].id : '';
// 获取镜像列表
this.noteBookImages = await getImageNameList({
imageTypes: IMAGE_TYPE.NOTEBOOK,
imageTypes: IMAGE_TYPE_ENUM.NOTEBOOK,
imageResource: Number(IMAGE_RESOURCE_ENUM.PRESET),
});
if (this.noteBookForm.defaultImage) {
this.noteBookTags = await getImageTagList({
imageName: this.noteBookForm.defaultImage,
imageTypes: IMAGE_TYPE.NOTEBOOK,
imageTypes: IMAGE_TYPE_ENUM.NOTEBOOK,
imageResource: Number(IMAGE_RESOURCE_ENUM.PRESET),
});
} else {
this.noteBookTags = [];
}

this.formVisible = true;
this.notebookFormVisible = true;
this.$nextTick(() => {
this.clearValidate();
});
@@ -690,17 +504,17 @@ export default {
validateField(field) {
this.$refs.noteBookFormRef.validateField(field);
},
onSubmitForm() {
onSubmitNotebookForm() {
this.$refs.noteBookFormRef.validate((valid) => {
if (valid) {
this.formSubmitting = true;
this.notebookFormSubmitting = true;
setDefaultImage({ id: this.noteBookForm.id })
.then(() => {
this.formVisible = false;
this.notebookFormVisible = false;
this.crud.toQuery();
})
.finally(() => {
this.formSubmitting = false;
this.notebookFormSubmitting = false;
});
}
});


+ 0
- 9
webapp/src/views/trainingJob/utils.js View File

@@ -96,15 +96,6 @@ export const ATLAS_ALGORITHM_TYPE_ENUM = {
DDRL: 4, // ddrl
};

export const IMAGE_TYPE = {
NOTEBOOK: 0, // notebook镜像类型
TRAIN: 1, // 训练镜像和预置镜像类型
SERVING: 2, // serving镜像
TERMINAL: 3, // 终端镜像
POINTCLOUD: 4, // 点云镜像
DATASETMARKED: 5, // 数据标注镜像
};

// 训练可视化列表页查询项
export const trainVisualQueryFormItems = [
{


Loading…
Cancel
Save