dengzedong
2024-09-20 e650cd4ca7f1d6b69e61cca49699f44edc0bb6be
src/views/mpk/MpkForm.vue
@@ -1,137 +1,282 @@
<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,
@@ -140,6 +285,9 @@
  const formRules = reactive<FormRules>({
    pyName: [
      {required: true, message: '模型名称不能为空,请上传模型文件', trigger: 'blur'}
    ],
    pyChineseName: [
      {required: true, message: '模型中文名称不能为空', trigger: 'blur'}
    ],
    pyType: [
      {required: true, message: '模型类型不能为空', trigger: 'blur'}
@@ -153,30 +301,14 @@
    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
@@ -188,7 +320,7 @@
      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
    }
@@ -203,12 +335,11 @@
        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()
  }
  /** 重置表单 */
@@ -216,10 +347,14 @@
    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
@@ -229,21 +364,16 @@
  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([]) // 文件列表
@@ -271,8 +401,64 @@
    }
    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>