| | |
| | | <template> |
| | | <Dialog v-model="dialogVisible" :title="dialogTitle"> |
| | | <el-form |
| | | ref="formRef" |
| | | v-loading="formLoading" |
| | | :model="formData" |
| | | :rules="formRules" |
| | | label-width="80px" |
| | | > |
| | | <el-divider content-position="left">模型信息</el-divider> |
| | | <el-row :gutter="8"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="模型名称" prop="pyName"> |
| | | <el-input disabled v-model="formData.pyName" placeholder=""/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-upload |
| | | ref="uploadRef" |
| | | v-model:file-list="fileList" |
| | | :show-file-list="false" |
| | | :action="importUrl" |
| | | :auto-upload="true" |
| | | :disabled="uploadLoading" |
| | | :before-upload="beforeUpload" |
| | | :headers="uploadHeaders" |
| | | :on-error="submitFormError" |
| | | :on-success="submitFormSuccess" |
| | | accept=".pyd" |
| | | > |
| | | <el-button type="primary"><Icon icon="ep:upload" />模型上传</el-button> |
| | | </el-upload> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="8"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="包名" prop="pkgName"> |
| | | <el-input v-model="formData.pkgName" placeholder=""/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="模型路径" prop="pyModule"> |
| | | <el-input v-model="formData.pyModule" placeholder=""/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注" prop="remark"> |
| | | <el-input v-model="formData.remark" placeholder="" type="textarea"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-divider content-position="left">模型方法</el-divider> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="4"> |
| | | <el-button type="primary" size="small" @click="addRow()">新增</el-button> |
| | | </el-col> |
| | | </el-row> |
| | | <el-table :data="formData.modelMethods" border> |
| | | <el-table-column |
| | | prop="" |
| | | label="方法名" |
| | | align="center" |
| | | width="250"> |
| | | <template #default="scope"> |
| | | <el-select size="small" v-model="scope.row.methodName"> |
| | | <el-option |
| | | v-for="item in getDictOptions(DICT_TYPE.MODEL_METHOD)" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | :disabled="methodSelectDisabled(item.value)" |
| | | /> |
| | | </el-select> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="" |
| | | label="输入个数" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-input-number size="small" v-model="scope.row.dataLength" :min="1" :max="50"/> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="" |
| | | label="是否有model" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-switch size="small" v-model="scope.row.model" :active-value="1" |
| | | :inactive-value="0"/> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" fixed="right" header-align="center" align="center" width="100"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | @click="deleteRow(scope.$index)" |
| | | key="danger" |
| | | type="danger" |
| | | link |
| | | >删除 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" @click="submitForm">确 定</el-button> |
| | | <el-button @click="dialogVisible = false">取 消</el-button> |
| | | </template> |
| | | </Dialog> |
| | | <div class="p-16px" style="background-color: #ffffff"> |
| | | <el-header> |
| | | {{title}} |
| | | </el-header> |
| | | <el-main> |
| | | <el-form |
| | | ref="formRef" |
| | | v-loading="formLoading" |
| | | :model="formData" |
| | | :rules="formRules" |
| | | label-width="120px" |
| | | > |
| | | <el-divider content-position="left">模型信息</el-divider> |
| | | <el-row :gutter="8"> |
| | | <el-col :span="20"> |
| | | <el-form-item label="模型名称" prop="pyName"> |
| | | <el-input disabled v-model="formData.pyName" placeholder=""/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-upload |
| | | ref="uploadRef" |
| | | v-model:file-list="fileList" |
| | | :show-file-list="false" |
| | | :action="importUrl" |
| | | :auto-upload="true" |
| | | :disabled="uploadLoading" |
| | | :before-upload="beforeUpload" |
| | | :headers="uploadHeaders" |
| | | :on-error="submitFormError" |
| | | :on-success="submitFormSuccess" |
| | | accept=".pyd" |
| | | > |
| | | <el-button type="primary"> |
| | | <Icon icon="ep:upload"/> |
| | | 模型上传 |
| | | </el-button> |
| | | </el-upload> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="8"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="模型中文名称" prop="pyChineseName"> |
| | | <el-input v-model="formData.pyChineseName" placeholder=""/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="模型类型" prop="pyType"> |
| | | <el-select |
| | | v-model="formData.pyType" |
| | | placeholder="请选择" |
| | | @change="pyTypeChange" |
| | | > |
| | | <el-option |
| | | v-for="dict in getDictOptions(DICT_TYPE.MODEL_TYPE)" |
| | | :key="dict.value" |
| | | :label="dict.label" |
| | | :value="dict.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="8"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="包名" prop="pkgName"> |
| | | <el-input v-model="formData.pkgName" placeholder=""/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="模型路径" prop="pyModule"> |
| | | <el-input v-model="formData.pyModule" placeholder=""/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="8"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="所属菜单" prop="menuName"> |
| | | <el-input v-model="formData.menuName" placeholder=""/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="所属组" prop="groupName"> |
| | | <el-input v-model="formData.groupName" placeholder=""/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="8"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="icon" prop="icon"> |
| | | <el-input v-model="formData.icon" placeholder=""/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注" prop="remark"> |
| | | <el-input v-model="formData.remark" placeholder="" type="textarea"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-divider content-position="left">模型方法</el-divider> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="4"> |
| | | <el-button type="primary" size="small" @click="addRow()" >新增</el-button> |
| | | </el-col> |
| | | </el-row> |
| | | <el-table :data="formData.modelMethods" border |
| | | @expand-change="methodExpandChange" :expand-row-keys="methodExpandedRowKeys" :row-key="row => row.id"> |
| | | <el-table-column |
| | | prop="" |
| | | label="方法名" |
| | | align="center" |
| | | width="250"> |
| | | <template #default="scope"> |
| | | <el-input size="small" v-model="scope.row.methodName" placeholder=""/> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="" |
| | | label="输入个数" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-input-number size="small" step-strictly v-model="scope.row.dataLength" :min="1" |
| | | :max="50"/> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="" |
| | | label="是否有model" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-switch size="small" v-model="scope.row.model" :active-value="1" |
| | | :inactive-value="0"/> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="" |
| | | label="结果key" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-input size="small" v-model="scope.row.resultKey"/> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="方法参数" type="expand" width="100px"> |
| | | <template #default="props"> |
| | | <div class="m-16px"> |
| | | <el-button type="primary" size="small" @click="addSetting(props.row.methodSettings)">新增参数</el-button> |
| | | <el-table :data="props.row.methodSettings" border size="small"> |
| | | <el-table-column align="center" label="key" prop="settingKey"/> |
| | | <el-table-column align="center" label="参数名称" prop="name"/> |
| | | <el-table-column align="center" label="参数默认值" prop="value"/> |
| | | <el-table-column align="center" label="输入类型" prop="type"> |
| | | <template #default="props"> |
| | | <div class="flex file-row justify-center items-center"> |
| | | {{props.row.type}} |
| | | <div class="ml-8px" v-if="props.row.type === 'select'"> |
| | | <el-popover placement="left" :width="400"> |
| | | <template #reference> |
| | | <el-button size="small" link type="primary"> |
| | | <Icon icon="ep:more" /> |
| | | </el-button> |
| | | </template> |
| | | <el-table width="50%" :data="props.row.settingSelects" border size="small"> |
| | | <el-table-column align="center" label="key" prop="selectKey"/> |
| | | <el-table-column align="center" label="name" prop="name"/> |
| | | </el-table> |
| | | </el-popover> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="参数类型" prop="valueType"/> |
| | | <el-table-column align="center" label="最大值" prop="max"/> |
| | | <el-table-column align="center" label="最小值" prop="min"/> |
| | | <!-- <el-table-column align="center" label="选项" width="50">--> |
| | | <!-- <template #default="props">--> |
| | | <!-- <div v-if="props.row.type === 'select'">--> |
| | | <!-- <el-popover placement="left" :width="400">--> |
| | | <!-- <template #reference>--> |
| | | <!-- <Icon icon="ep:more" />--> |
| | | <!-- </template>--> |
| | | <!-- <el-table width="50%" :data="props.row.settingSelects" border size="small">--> |
| | | <!-- <el-table-column align="center" label="key" prop="selectKey"/>--> |
| | | <!-- <el-table-column align="center" label="name" prop="name"/>--> |
| | | <!-- </el-table>--> |
| | | <!-- </el-popover>--> |
| | | <!-- </div>--> |
| | | <!-- </template>--> |
| | | <!-- </el-table-column>--> |
| | | <el-table-column label="操作" fixed="right" header-align="center" align="center" width="100"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | @click="updateSetting(scope.row)" |
| | | key="danger" |
| | | type="danger" |
| | | link |
| | | >修改 |
| | | </el-button> |
| | | <el-button |
| | | @click="deleteSetting(props.row.methodSettings,scope.$index)" |
| | | key="danger" |
| | | type="danger" |
| | | link |
| | | >删除 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" fixed="right" header-align="center" align="center" width="100"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | @click="deleteRow(scope.$index)" |
| | | key="danger" |
| | | type="danger" |
| | | link |
| | | >删除 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-form> |
| | | </el-main> |
| | | <el-footer> |
| | | <div class="flex flex-row justify-end items-center"> |
| | | <el-button type="primary" @click="submitForm">确 定</el-button> |
| | | </div> |
| | | </el-footer> |
| | | </div> |
| | | <SettingForm ref="settingFormRef"/> |
| | | </template> |
| | | <script lang="ts" setup> |
| | | import {DICT_TYPE, getDictOptions} from '@/utils/dict' |
| | | import * as MpkApi from '@/api/mpk/mpk' |
| | | import {FormRules} from 'element-plus' |
| | | import {getAccessToken, getTenantId} from "@/utils/auth"; |
| | | |
| | | import SettingForm from './SettingForm.vue' |
| | | import {generateUUID} from "@/utils"; |
| | | |
| | | const {t} = useI18n() // 国际化 |
| | | const message = useMessage() // 消息弹窗 |
| | | |
| | | const dialogVisible = ref(false) // 弹窗的是否展示 |
| | | const dialogTitle = ref('') // 弹窗的标题 |
| | | const title = ref('') // 弹窗的标题 |
| | | const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 |
| | | const formType = ref('') // 表单的类型:create - 新增;update - 修改 |
| | | const route = useRoute() // 路由 |
| | | const router = useRouter(); |
| | | |
| | | /** settingForm弹窗 */ |
| | | const settingFormRef = ref() |
| | | // 添加setting |
| | | const addSetting = (methodSettings) => { |
| | | settingFormRef.value.open(undefined,methodSettings) |
| | | } |
| | | |
| | | // 修改setting |
| | | const updateSetting = (info) => { |
| | | settingFormRef.value.open(info) |
| | | } |
| | | |
| | | const methodExpandedRowKeys = ref([]) |
| | | const methodExpandChange = async (row: any, expandedRows: any[]) => { |
| | | methodExpandedRowKeys.value = expandedRows.map(e => e.id) |
| | | } |
| | | |
| | | const formData = ref({ |
| | | id: undefined, |
| | | id: route.params.id, |
| | | pyChineseName: undefined, |
| | | pyName: undefined, |
| | | pkgName: undefined, |
| | | pyType: undefined, |
| | | className: undefined, |
| | | pyModule: undefined, |
| | | icon: undefined, |
| | | menuName: undefined, |
| | | groupName: undefined, |
| | | remark: undefined, |
| | | modelMethods: [], |
| | | filePath: undefined, |
| | |
| | | const formRules = reactive<FormRules>({ |
| | | pyName: [ |
| | | {required: true, message: '模型名称不能为空,请上传模型文件', trigger: 'blur'} |
| | | ], |
| | | pyChineseName: [ |
| | | {required: true, message: '模型中文名称不能为空', trigger: 'blur'} |
| | | ], |
| | | pyType: [ |
| | | {required: true, message: '模型类型不能为空', trigger: 'blur'} |
| | |
| | | pyModule: [ |
| | | {required: true, message: '模型路径不能为空', trigger: 'blur'} |
| | | ], |
| | | menuName: [ |
| | | {required: true, message: '所属目录不能为空', trigger: 'blur'} |
| | | ], |
| | | }) |
| | | |
| | | const formRef = ref() // 表单 Ref |
| | | |
| | | /** 打开弹窗 */ |
| | | const open = async (type: string, id?: number) => { |
| | | dialogVisible.value = true |
| | | dialogTitle.value = t('action.' + type) |
| | | formType.value = type |
| | | resetForm() |
| | | // 修改时,设置数据 |
| | | if (id) { |
| | | formLoading.value = true |
| | | try { |
| | | formData.value = await MpkApi.getMpk(id) |
| | | } finally { |
| | | formLoading.value = false |
| | | } |
| | | } |
| | | } |
| | | defineExpose({open}) // 提供 open 方法,用于打开弹窗 |
| | | |
| | | /** 提交表单 */ |
| | | const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 |
| | | const submitForm = async () => { |
| | | // 校验表单 |
| | | if (!formRef) return |
| | |
| | | return |
| | | } |
| | | // 模型方法名称校验 |
| | | if (formData.value.modelMethods.some(e => e.methodName === undefined || e.methodName === '')) { |
| | | if (formData.value.modelMethods.some(e => e.methodName === undefined || e.methodName === '' || e.dataLength === undefined || e.dataLength === null)) { |
| | | message.error('存在不合法模型方法名') |
| | | return |
| | | } |
| | |
| | | await MpkApi.updateMpk(data) |
| | | message.success(t('common.updateSuccess')) |
| | | } |
| | | dialogVisible.value = false |
| | | // 发送操作成功的事件 |
| | | emit('success') |
| | | } finally { |
| | | formLoading.value = false |
| | | } |
| | | // router.push({path:'/model/mpk'}) |
| | | router.back() |
| | | } |
| | | |
| | | /** 重置表单 */ |
| | |
| | | formData.value = { |
| | | id: undefined, |
| | | pyName: undefined, |
| | | pyChineseName: undefined, |
| | | pkgName: undefined, |
| | | pyType: undefined, |
| | | className: undefined, |
| | | pyModule: undefined, |
| | | icon: undefined, |
| | | menuName: undefined, |
| | | groupName: undefined, |
| | | remark: undefined, |
| | | modelMethods: [], |
| | | filePath: undefined |
| | |
| | | |
| | | const addRow = function () { |
| | | formData.value.modelMethods.push({ |
| | | id: generateUUID(), |
| | | methodName: undefined, |
| | | dataLength: 1, |
| | | model: 0 |
| | | model: 0, |
| | | resultKey: undefined, |
| | | methodSettings: [] |
| | | }) |
| | | } |
| | | const deleteRow = function (index) { |
| | | formData.value.modelMethods.splice(index, 1) |
| | | } |
| | | |
| | | // 模型方法下拉禁用 |
| | | const methodSelectDisabled = (value) => { |
| | | if (formData.value.modelMethods.some(e => e.methodName === value)) { |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | const fileList = ref([]) // 文件列表 |
| | |
| | | } |
| | | const data = response.data; |
| | | formData.value.filePath = data.filePath |
| | | formData.value.pyName = data.fileName.replace('.pyd','') |
| | | formData.value.pyName = data.fileName.replace('.pyd', '') |
| | | message.success('上传成功') |
| | | uploadLoading.value = false |
| | | } |
| | | |
| | | onMounted(async () => { |
| | | const id = formData.value.id; |
| | | const type = id ? 'edit' : 'create' |
| | | title.value = t('action.' + type) |
| | | formType.value = type |
| | | resetForm() |
| | | // 修改时,设置数据 |
| | | if (id) { |
| | | formLoading.value = true |
| | | try { |
| | | formData.value = await MpkApi.getMpk(id) |
| | | } finally { |
| | | formLoading.value = false |
| | | } |
| | | } |
| | | }) |
| | | |
| | | const pyTypeChange = () => { |
| | | if (formData.value.pyType === 'predict') { |
| | | formData.value.modelMethods = [ |
| | | { |
| | | id: generateUUID(), |
| | | methodName: 'train', |
| | | dataLength: 1, |
| | | model: 0, |
| | | resultKey: undefined, |
| | | methodSettings: [] |
| | | }, |
| | | { |
| | | |
| | | id: generateUUID(), |
| | | methodName: 'predict', |
| | | dataLength: 1, |
| | | model: 1, |
| | | resultKey: undefined, |
| | | methodSettings: [] |
| | | } |
| | | ] |
| | | debugger |
| | | }else { |
| | | formData.value.modelMethods = [ |
| | | { |
| | | id: generateUUID(), |
| | | methodName: 'schedul', |
| | | dataLength: 1, |
| | | model: 0, |
| | | resultKey: undefined, |
| | | methodSettings: [] |
| | | } |
| | | ] |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | </style> |