dengzedong
2024-09-18 7767e500607c8a47d14728f9fc61952e824c8acc
提交 | 用户 | 时间
9c91ad 1 <template>
7767e5 2   <div class="p-16px" style="background-color: #ffffff">
D 3     <el-header>
4       {{title}}
5     </el-header>
6     <el-main>
7       <el-form
8         ref="formRef"
9         v-loading="formLoading"
10         :model="formData"
11         :rules="formRules"
12         label-width="120px"
13       >
14         <el-divider content-position="left">模型信息</el-divider>
15         <el-row :gutter="8">
16           <el-col :span="20">
17             <el-form-item label="模型名称" prop="pyName">
18               <el-input disabled v-model="formData.pyName" placeholder=""/>
19             </el-form-item>
20           </el-col>
21           <el-col :span="4">
22             <el-upload
23               ref="uploadRef"
24               v-model:file-list="fileList"
25               :show-file-list="false"
26               :action="importUrl"
27               :auto-upload="true"
28               :disabled="uploadLoading"
29               :before-upload="beforeUpload"
30               :headers="uploadHeaders"
31               :on-error="submitFormError"
32               :on-success="submitFormSuccess"
33               accept=".pyd"
34             >
35               <el-button type="primary">
36                 <Icon icon="ep:upload"/>
37                 模型上传
38               </el-button>
39             </el-upload>
40           </el-col>
41         </el-row>
42         <el-row :gutter="8">
43           <el-col :span="12">
44             <el-form-item label="模型中文名称" prop="pyChineseName">
45               <el-input v-model="formData.pyChineseName" placeholder=""/>
46             </el-form-item>
47           </el-col>
48           <el-col :span="12">
49             <el-form-item label="模型类型" prop="pyType">
50               <el-select
51                 v-model="formData.pyType"
52                 placeholder="请选择"
53                 @change="pyTypeChange"
54               >
55                 <el-option
56                   v-for="dict in getDictOptions(DICT_TYPE.MODEL_TYPE)"
57                   :key="dict.value"
58                   :label="dict.label"
59                   :value="dict.value"
60                 />
61               </el-select>
62             </el-form-item>
63           </el-col>
64         </el-row>
65         <el-row :gutter="8">
66           <el-col :span="12">
67             <el-form-item label="包名" prop="pkgName">
68               <el-input v-model="formData.pkgName" placeholder=""/>
69             </el-form-item>
70           </el-col>
71           <el-col :span="12">
72             <el-form-item label="模型路径" prop="pyModule">
73               <el-input v-model="formData.pyModule" placeholder=""/>
74             </el-form-item>
75           </el-col>
76         </el-row>
77         <el-row :gutter="8">
78           <el-col :span="12">
79             <el-form-item label="所属菜单" prop="menuName">
80               <el-input v-model="formData.menuName" placeholder=""/>
81             </el-form-item>
82           </el-col>
83           <el-col :span="12">
84             <el-form-item label="所属组" prop="groupName">
85               <el-input v-model="formData.groupName" placeholder=""/>
86             </el-form-item>
87           </el-col>
88         </el-row>
89         <el-row :gutter="8">
90           <el-col :span="12">
91             <el-form-item label="icon" prop="icon">
92               <el-input v-model="formData.icon" placeholder=""/>
93             </el-form-item>
94           </el-col>
95         </el-row>
96         <el-row :gutter="20">
97           <el-col :span="24">
98             <el-form-item label="备注" prop="remark">
99               <el-input v-model="formData.remark" placeholder="" type="textarea"/>
100             </el-form-item>
101           </el-col>
102         </el-row>
103         <el-divider content-position="left">模型方法</el-divider>
104         <el-row :gutter="20">
105           <el-col :span="4">
106             <el-button type="primary" size="small" @click="addRow()" >新增</el-button>
107           </el-col>
108         </el-row>
109         <el-table :data="formData.modelMethods" border
110                   @expand-change="methodExpandChange" :expand-row-keys="methodExpandedRowKeys" :row-key="row => row.id">
111           <el-table-column
112             prop=""
113             label="方法名"
114             align="center"
115             width="250">
116             <template #default="scope">
117               <el-input size="small" v-model="scope.row.methodName" placeholder=""/>
118             </template>
119           </el-table-column>
120           <el-table-column
121             prop=""
122             label="输入个数"
123             align="center">
124             <template #default="scope">
125               <el-input-number size="small" step-strictly v-model="scope.row.dataLength" :min="1"
126                                :max="50"/>
127             </template>
128           </el-table-column>
129           <el-table-column
130             prop=""
131             label="是否有model"
132             align="center">
133             <template #default="scope">
134               <el-switch size="small" v-model="scope.row.model" :active-value="1"
135                          :inactive-value="0"/>
136             </template>
137           </el-table-column>
138           <el-table-column
139             prop=""
140             label="结果key"
141             align="center">
142             <template #default="scope">
143               <el-input size="small" v-model="scope.row.resultKey"/>
144             </template>
145           </el-table-column>
146           <el-table-column label="方法参数" type="expand" width="100px">
147             <template #default="props">
148               <div class="m-16px">
149                 <el-button type="primary" size="small" @click="addSetting(props.row.methodSettings)">新增参数</el-button>
150                 <el-table :data="props.row.methodSettings" border size="small">
151                   <el-table-column align="center" label="key" prop="settingKey"/>
152                   <el-table-column align="center" label="参数名称" prop="name"/>
153                   <el-table-column align="center" label="参数默认值" prop="value"/>
154                   <el-table-column align="center" label="输入类型" prop="type">
155                     <template #default="props">
156                       <div class="flex file-row justify-center items-center">
157                         {{props.row.type}}
158                         <div class="ml-8px" v-if="props.row.type === 'select'">
159                           <el-popover placement="left" :width="400">
160                             <template #reference>
161                               <el-button size="small" link type="primary">
162                                 <Icon icon="ep:more" />
163                               </el-button>
164                             </template>
165                             <el-table width="50%" :data="props.row.settingSelects" border size="small">
166                               <el-table-column align="center" label="key" prop="selectKey"/>
167                               <el-table-column align="center" label="name" prop="name"/>
168                             </el-table>
169                           </el-popover>
170                         </div>
171                       </div>
172                     </template>
173                   </el-table-column>
174                   <el-table-column align="center" label="参数类型" prop="valueType"/>
175                   <el-table-column align="center" label="最大值" prop="max"/>
176                   <el-table-column align="center" label="最小值" prop="min"/>
177                   <!--                <el-table-column align="center" label="选项" width="50">-->
178                   <!--                  <template #default="props">-->
179                   <!--                    <div v-if="props.row.type === 'select'">-->
180                   <!--                      <el-popover placement="left" :width="400">-->
181                   <!--                        <template #reference>-->
182                   <!--                          <Icon icon="ep:more" />-->
183                   <!--                        </template>-->
184                   <!--                        <el-table width="50%" :data="props.row.settingSelects" border size="small">-->
185                   <!--                          <el-table-column align="center" label="key" prop="selectKey"/>-->
186                   <!--                          <el-table-column align="center" label="name" prop="name"/>-->
187                   <!--                        </el-table>-->
188                   <!--                      </el-popover>-->
189                   <!--                    </div>-->
190                   <!--                  </template>-->
191                   <!--                </el-table-column>-->
192                   <el-table-column label="操作" fixed="right" header-align="center" align="center" width="100">
193                     <template #default="scope">
194                       <el-button
195                         @click="updateSetting(scope.row)"
196                         key="danger"
197                         type="danger"
198                         link
199                       >修改
200                       </el-button>
201                       <el-button
202                         @click="deleteSetting(props.row.methodSettings,scope.$index)"
203                         key="danger"
204                         type="danger"
205                         link
206                       >删除
207                       </el-button>
208                     </template>
209                   </el-table-column>
210                 </el-table>
211               </div>
212             </template>
213           </el-table-column>
214           <el-table-column label="操作" fixed="right" header-align="center" align="center" width="100">
215             <template #default="scope">
216               <el-button
217                 @click="deleteRow(scope.$index)"
218                 key="danger"
219                 type="danger"
220                 link
221               >删除
222               </el-button>
223             </template>
224           </el-table-column>
225         </el-table>
226       </el-form>
227     </el-main>
228     <el-footer>
229       <div class="flex flex-row justify-end items-center">
230         <el-button type="primary" @click="submitForm">确 定</el-button>
231       </div>
232     </el-footer>
233   </div>
234   <SettingForm ref="settingFormRef"/>
9c91ad 235 </template>
D 236 <script lang="ts" setup>
237   import {DICT_TYPE, getDictOptions} from '@/utils/dict'
238   import * as MpkApi from '@/api/mpk/mpk'
239   import {FormRules} from 'element-plus'
240   import {getAccessToken, getTenantId} from "@/utils/auth";
7767e5 241   import SettingForm from './SettingForm.vue'
D 242   import {generateUUID} from "@/utils";
9c91ad 243
D 244   const {t} = useI18n() // 国际化
245   const message = useMessage() // 消息弹窗
7767e5 246   const title = ref('') // 弹窗的标题
9c91ad 247   const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
D 248   const formType = ref('') // 表单的类型:create - 新增;update - 修改
7767e5 249   const route = useRoute() // 路由
D 250   const router = useRouter();
251
252   /** settingForm弹窗 */
253   const settingFormRef = ref()
254   // 添加setting
255   const addSetting = (methodSettings) => {
256     settingFormRef.value.open(undefined,methodSettings)
257   }
258
259   // 修改setting
260   const updateSetting = (info) => {
261     settingFormRef.value.open(info)
262   }
263
264   const methodExpandedRowKeys = ref([])
265   const methodExpandChange = async (row: any, expandedRows: any[]) => {
266     methodExpandedRowKeys.value = expandedRows.map(e => e.id)
267   }
268
9c91ad 269   const formData = ref({
7767e5 270     id: route.params.id,
D 271     pyChineseName: undefined,
9c91ad 272     pyName: undefined,
D 273     pkgName: undefined,
274     pyType: undefined,
275     className: undefined,
276     pyModule: undefined,
7767e5 277     icon: undefined,
D 278     menuName: undefined,
279     groupName: undefined,
9c91ad 280     remark: undefined,
D 281     modelMethods: [],
282     filePath: undefined,
283   })
284
285   const formRules = reactive<FormRules>({
286     pyName: [
287       {required: true, message: '模型名称不能为空,请上传模型文件', trigger: 'blur'}
7767e5 288     ],
D 289     pyChineseName: [
290       {required: true, message: '模型中文名称不能为空', trigger: 'blur'}
9c91ad 291     ],
D 292     pyType: [
293       {required: true, message: '模型类型不能为空', trigger: 'blur'}
294     ],
295     pkgName: [
296       {required: true, message: '包名不能为空', trigger: 'blur'}
297     ],
298     className: [
299       {required: true, message: '类名不能为空', trigger: 'blur'}
300     ],
301     pyModule: [
302       {required: true, message: '模型路径不能为空', trigger: 'blur'}
303     ],
7767e5 304     menuName: [
D 305       {required: true, message: '所属目录不能为空', trigger: 'blur'}
306     ],
9c91ad 307   })
D 308
309   const formRef = ref() // 表单 Ref
310
311   /** 提交表单 */
312   const submitForm = async () => {
313     // 校验表单
314     if (!formRef) return
315     const valid = await formRef.value.validate()
316     if (!valid) return
317     // 模型方法校验
318     if (formData.value.modelMethods?.length <= 0) {
319       message.error('模型方法为空')
320       return
321     }
322     // 模型方法名称校验
7767e5 323     if (formData.value.modelMethods.some(e => e.methodName === undefined || e.methodName === '' || e.dataLength === undefined || e.dataLength === null)) {
9c91ad 324       message.error('存在不合法模型方法名')
D 325       return
326     }
327     // 提交请求
328     formLoading.value = true
329     try {
330       const data = formData.value as unknown as MpkApi.MpkVO
331       if (formType.value === 'create') {
332         await MpkApi.createMpk(data)
333         message.success(t('common.createSuccess'))
334       } else {
335         await MpkApi.updateMpk(data)
336         message.success(t('common.updateSuccess'))
337       }
338     } finally {
339       formLoading.value = false
340     }
7767e5 341     // router.push({path:'/model/mpk'})
D 342     router.back()
9c91ad 343   }
D 344
345   /** 重置表单 */
346   const resetForm = () => {
347     formData.value = {
348       id: undefined,
349       pyName: undefined,
7767e5 350       pyChineseName: undefined,
9c91ad 351       pkgName: undefined,
D 352       pyType: undefined,
353       className: undefined,
354       pyModule: undefined,
7767e5 355       icon: undefined,
D 356       menuName: undefined,
357       groupName: undefined,
9c91ad 358       remark: undefined,
D 359       modelMethods: [],
360       filePath: undefined
361     }
362     formRef.value?.resetFields()
363   }
364
365   const addRow = function () {
366     formData.value.modelMethods.push({
7767e5 367       id: generateUUID(),
9c91ad 368       methodName: undefined,
D 369       dataLength: 1,
7767e5 370       model: 0,
D 371       resultKey: undefined,
372       methodSettings: []
9c91ad 373     })
D 374   }
375   const deleteRow = function (index) {
376     formData.value.modelMethods.splice(index, 1)
377   }
378
379   const fileList = ref([]) // 文件列表
380   const importUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/model/mpk/file/upload'
381   const uploadLoading = ref(false) // 表单的加载中
382   const uploadHeaders = ref() // 上传 Header 头
383   const beforeUpload = function (file) {
384     // 提交请求
385     uploadHeaders.value = {
386       Authorization: 'Bearer ' + getAccessToken(),
387       'tenant-id': getTenantId()
388     }
389     uploadLoading.value = true
390     return true;
391   }
392   const submitFormError = (): void => {
393     message.error('上传失败!')
394     uploadLoading.value = false
395   }
396   const submitFormSuccess = (response: any) => {
397     if (response.code !== 0) {
398       message.error(response.msg)
399       uploadLoading.value = false
400       return
401     }
402     const data = response.data;
403     formData.value.filePath = data.filePath
7767e5 404     formData.value.pyName = data.fileName.replace('.pyd', '')
9c91ad 405     message.success('上传成功')
D 406     uploadLoading.value = false
407   }
7767e5 408
D 409   onMounted(async () => {
410     const id = formData.value.id;
411     const type = id ? 'edit' : 'create'
412     title.value = t('action.' + type)
413     formType.value = type
414     resetForm()
415     // 修改时,设置数据
416     if (id) {
417       formLoading.value = true
418       try {
419         formData.value = await MpkApi.getMpk(id)
420       } finally {
421         formLoading.value = false
422       }
423     }
424   })
425
426   const pyTypeChange = () => {
427     if (formData.value.pyType === 'predict') {
428       formData.value.modelMethods = [
429         {
430           id: generateUUID(),
431           methodName: 'train',
432           dataLength: 1,
433           model: 0,
434           resultKey: undefined,
435           methodSettings: []
436         },
437         {
438
439           id: generateUUID(),
440           methodName: 'predict',
441           dataLength: 1,
442           model: 1,
443           resultKey: undefined,
444           methodSettings: []
445         }
446       ]
447       debugger
448     }else {
449       formData.value.modelMethods = [
450         {
451           id: generateUUID(),
452           methodName: 'schedul',
453           dataLength: 1,
454           model: 0,
455           resultKey: undefined,
456           methodSettings: []
457         }
458       ]
459     }
460   }
9c91ad 461 </script>
7767e5 462
D 463 <style scoped lang="scss">
464 </style>