Compare commits

...

14 Commits

Author SHA1 Message Date
Dreamglade f6c4ee9011 feat: 同步 master 的 Dockerfile 和 pnpm-lock 配置 4 months ago
Dreamglade 93393410dd 小改一下 4 months ago
Dreamglade c73d294c6e 改了一下版本 4 months ago
Dreamglade 6126dc5f60 pnpm 10.14.0 4 months ago
chenjiale 7fe3d8fa38 Merge pull request 'cjl-dev' (#41) from cjl-dev into master 4 months ago
CJL6015 b12fa64f1e Update build scripts to use tsx and upgrade dependencies 4 months ago
CJL6015 961f103ede update pnpm version in lockfile and Dockerfile to 10.14.0 4 months ago
CJL6015 473ab6502d Merge branch 'master' of http://120.26.116.243:3000/root/alert-front into cjl-dev 4 months ago
chenjiale 28769d1eb1 Merge pull request '保持跟服务器pnpm版本一致' (#40) from Zyq into master 4 months ago
chenjiale b970f44375 Merge pull request 'Zyq' (#39) from Zyq into master 4 months ago
Jiale 99e38cf13d feat: 添加底层模型获取功能并更新相关逻辑 5 months ago
CJL6015 ef628de515 feat: add model editing functionality with transfer component and update pagination 5 months ago
Jiale 41c7f01c93 feat: 添加清除训练结果功能并优化模型信息更新逻辑 5 months ago
Jiale f016fd0f21 feat: 添加测试模型API并更新相关功能 5 months ago
  1. 2
      .vscode/settings.json
  2. 7
      package.json
  3. 294
      pnpm-lock.yaml
  4. 2
      scripts/Dockerfile
  5. 6
      src/api/alert/model/models.ts
  6. 124
      src/views/model/list/step/Step3.vue
  7. 555
      src/views/model/train/index.vue

2
.vscode/settings.json

@ -120,7 +120,7 @@
"source.organizeImports": "never",
"source.fixAll.stylelint": "explicit"
},
"editor.defaultFormatter": "stylelint.vscode-stylelint"
"editor.defaultFormatter": "octref.vetur"
},
"i18n-ally.localesPaths": ["src/locales/lang"],
"i18n-ally.keystyle": "nested",

7
package.json

@ -25,9 +25,9 @@
"serve": "pnpm dev",
"dev": "vite",
"front": "vite --mode front",
"build": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 vite build && esno ./build/script/postBuild.ts",
"build:test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode test && esno ./build/script/postBuild.ts",
"build:static": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode static && esno ./build/script/postBuild.ts",
"build": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 vite build && tsx ./build/script/postBuild.ts",
"build:test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode test && tsx ./build/script/postBuild.ts",
"build:static": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode static && tsx ./build/script/postBuild.ts",
"build:no-cache": "pnpm store prune && pnpm build",
"report": "cross-env REPORT=true pnpm build",
"type:check": "vue-tsc --noEmit --skipLibCheck",
@ -128,6 +128,7 @@
"stylelint-order": "^6.0.3",
"stylelint-prettier": "^4.0.2",
"terser": "^5.24.0",
"tsx": "^4.20.3",
"typescript": "^5.2.2",
"unocss": "^0.57.3",
"vite": "^4.5.0",

294
pnpm-lock.yaml

@ -78,7 +78,7 @@ importers:
specifier: ^3.2.0
version: 3.2.0(pinia@2.1.7(typescript@5.2.2)(vue@3.3.8(typescript@5.2.2)))
pnpm:
specifier: ^10.12.1
specifier: ^10.7.0
version: 10.12.1
print-js:
specifier: ^1.6.0
@ -258,6 +258,9 @@ importers:
terser:
specifier: ^5.24.0
version: 5.24.0
tsx:
specifier: ^4.20.3
version: 4.20.3
typescript:
specifier: ^5.2.2
version: 5.2.2
@ -1026,138 +1029,294 @@ packages:
resolution: {integrity: sha512-aKUhyn1QI5Ksbqcr3fFJj16p99QdjUxXAEuFst1Z47DRyoiMwivIH9MV/ARcJOCXVjPfjITciej8ZD2O/6qUmw==}
engines: {node: '>=16'}
'@esbuild/aix-ppc64@0.25.8':
resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.18.20':
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm64@0.25.8':
resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.18.20':
resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
'@esbuild/android-arm@0.25.8':
resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.18.20':
resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
'@esbuild/android-x64@0.25.8':
resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.18.20':
resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-arm64@0.25.8':
resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.18.20':
resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
'@esbuild/darwin-x64@0.25.8':
resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.18.20':
resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-arm64@0.25.8':
resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.18.20':
resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
'@esbuild/freebsd-x64@0.25.8':
resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.18.20':
resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm64@0.25.8':
resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.18.20':
resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
'@esbuild/linux-arm@0.25.8':
resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.18.20':
resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-ia32@0.25.8':
resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.18.20':
resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-loong64@0.25.8':
resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.18.20':
resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-mips64el@0.25.8':
resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.18.20':
resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-ppc64@0.25.8':
resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.18.20':
resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-riscv64@0.25.8':
resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.18.20':
resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-s390x@0.25.8':
resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.18.20':
resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
'@esbuild/linux-x64@0.25.8':
resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.25.8':
resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.18.20':
resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
'@esbuild/netbsd-x64@0.25.8':
resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.25.8':
resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.18.20':
resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
'@esbuild/openbsd-x64@0.25.8':
resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openharmony-arm64@0.25.8':
resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.18.20':
resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
'@esbuild/sunos-x64@0.25.8':
resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.18.20':
resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-arm64@0.25.8':
resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.18.20':
resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-ia32@0.25.8':
resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.18.20':
resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
'@esbuild/win32-x64@0.25.8':
resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@eslint-community/eslint-utils@4.4.0':
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -2716,6 +2875,11 @@ packages:
engines: {node: '>=12'}
hasBin: true
esbuild@0.25.8:
resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==}
engines: {node: '>=18'}
hasBin: true
escalade@3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
engines: {node: '>=6'}
@ -3118,6 +3282,9 @@ packages:
resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
engines: {node: '>= 0.4'}
get-tsconfig@4.10.1:
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
get-tsconfig@4.7.2:
resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
@ -4765,6 +4932,7 @@ packages:
source-map@0.8.0-beta.0:
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
engines: {node: '>= 8'}
deprecated: The work that was done in this beta branch won't be included in future versions
sourcemap-codec@1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
@ -5078,8 +5246,8 @@ packages:
tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
tsx@4.1.1:
resolution: {integrity: sha512-zyPn5BFMB0TB5kMLbYPNx4x/oL/oSlaecdKCv6WeJ0TeSEfx8RTJWjuB5TZ2dSewktgfBsBO/HNA9mrMWqLXMA==}
tsx@4.20.3:
resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==}
engines: {node: '>=18.0.0'}
hasBin: true
@ -6508,72 +6676,150 @@ snapshots:
esquery: 1.5.0
jsdoc-type-pratt-parser: 4.0.0
'@esbuild/aix-ppc64@0.25.8':
optional: true
'@esbuild/android-arm64@0.18.20':
optional: true
'@esbuild/android-arm64@0.25.8':
optional: true
'@esbuild/android-arm@0.18.20':
optional: true
'@esbuild/android-arm@0.25.8':
optional: true
'@esbuild/android-x64@0.18.20':
optional: true
'@esbuild/android-x64@0.25.8':
optional: true
'@esbuild/darwin-arm64@0.18.20':
optional: true
'@esbuild/darwin-arm64@0.25.8':
optional: true
'@esbuild/darwin-x64@0.18.20':
optional: true
'@esbuild/darwin-x64@0.25.8':
optional: true
'@esbuild/freebsd-arm64@0.18.20':
optional: true
'@esbuild/freebsd-arm64@0.25.8':
optional: true
'@esbuild/freebsd-x64@0.18.20':
optional: true
'@esbuild/freebsd-x64@0.25.8':
optional: true
'@esbuild/linux-arm64@0.18.20':
optional: true
'@esbuild/linux-arm64@0.25.8':
optional: true
'@esbuild/linux-arm@0.18.20':
optional: true
'@esbuild/linux-arm@0.25.8':
optional: true
'@esbuild/linux-ia32@0.18.20':
optional: true
'@esbuild/linux-ia32@0.25.8':
optional: true
'@esbuild/linux-loong64@0.18.20':
optional: true
'@esbuild/linux-loong64@0.25.8':
optional: true
'@esbuild/linux-mips64el@0.18.20':
optional: true
'@esbuild/linux-mips64el@0.25.8':
optional: true
'@esbuild/linux-ppc64@0.18.20':
optional: true
'@esbuild/linux-ppc64@0.25.8':
optional: true
'@esbuild/linux-riscv64@0.18.20':
optional: true
'@esbuild/linux-riscv64@0.25.8':
optional: true
'@esbuild/linux-s390x@0.18.20':
optional: true
'@esbuild/linux-s390x@0.25.8':
optional: true
'@esbuild/linux-x64@0.18.20':
optional: true
'@esbuild/linux-x64@0.25.8':
optional: true
'@esbuild/netbsd-arm64@0.25.8':
optional: true
'@esbuild/netbsd-x64@0.18.20':
optional: true
'@esbuild/netbsd-x64@0.25.8':
optional: true
'@esbuild/openbsd-arm64@0.25.8':
optional: true
'@esbuild/openbsd-x64@0.18.20':
optional: true
'@esbuild/openbsd-x64@0.25.8':
optional: true
'@esbuild/openharmony-arm64@0.25.8':
optional: true
'@esbuild/sunos-x64@0.18.20':
optional: true
'@esbuild/sunos-x64@0.25.8':
optional: true
'@esbuild/win32-arm64@0.18.20':
optional: true
'@esbuild/win32-arm64@0.25.8':
optional: true
'@esbuild/win32-ia32@0.18.20':
optional: true
'@esbuild/win32-ia32@0.25.8':
optional: true
'@esbuild/win32-x64@0.18.20':
optional: true
'@esbuild/win32-x64@0.25.8':
optional: true
'@eslint-community/eslint-utils@4.4.0(eslint@8.53.0)':
dependencies:
eslint: 8.53.0
@ -8440,6 +8686,35 @@ snapshots:
'@esbuild/win32-ia32': 0.18.20
'@esbuild/win32-x64': 0.18.20
esbuild@0.25.8:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.8
'@esbuild/android-arm': 0.25.8
'@esbuild/android-arm64': 0.25.8
'@esbuild/android-x64': 0.25.8
'@esbuild/darwin-arm64': 0.25.8
'@esbuild/darwin-x64': 0.25.8
'@esbuild/freebsd-arm64': 0.25.8
'@esbuild/freebsd-x64': 0.25.8
'@esbuild/linux-arm': 0.25.8
'@esbuild/linux-arm64': 0.25.8
'@esbuild/linux-ia32': 0.25.8
'@esbuild/linux-loong64': 0.25.8
'@esbuild/linux-mips64el': 0.25.8
'@esbuild/linux-ppc64': 0.25.8
'@esbuild/linux-riscv64': 0.25.8
'@esbuild/linux-s390x': 0.25.8
'@esbuild/linux-x64': 0.25.8
'@esbuild/netbsd-arm64': 0.25.8
'@esbuild/netbsd-x64': 0.25.8
'@esbuild/openbsd-arm64': 0.25.8
'@esbuild/openbsd-x64': 0.25.8
'@esbuild/openharmony-arm64': 0.25.8
'@esbuild/sunos-x64': 0.25.8
'@esbuild/win32-arm64': 0.25.8
'@esbuild/win32-ia32': 0.25.8
'@esbuild/win32-x64': 0.25.8
escalade@3.1.1: {}
escape-string-regexp@1.0.5: {}
@ -8681,7 +8956,7 @@ snapshots:
esno@4.0.0:
dependencies:
tsx: 4.1.1
tsx: 4.20.3
espree@9.6.1:
dependencies:
@ -8945,6 +9220,10 @@ snapshots:
call-bind: 1.0.5
get-intrinsic: 1.2.2
get-tsconfig@4.10.1:
dependencies:
resolve-pkg-maps: 1.0.0
get-tsconfig@4.7.2:
dependencies:
resolve-pkg-maps: 1.0.0
@ -10958,11 +11237,10 @@ snapshots:
tslib@2.6.2: {}
tsx@4.1.1:
tsx@4.20.3:
dependencies:
esbuild: 0.18.20
get-tsconfig: 4.7.2
source-map-support: 0.5.21
esbuild: 0.25.8
get-tsconfig: 4.10.1
optionalDependencies:
fsevents: 2.3.3

2
scripts/Dockerfile

@ -7,7 +7,7 @@ ENV PATH="$PNPM_HOME:$PATH"
ENV NODE_OPTIONS=--max-old-space-size=8192
ENV TZ=Asia/Shanghai
RUN npm install -g pnpm@10.7.0
RUN npm install -g pnpm@10.14.0
WORKDIR /app
COPY . .

6
src/api/alert/model/models.ts

@ -9,6 +9,8 @@ enum Api {
CALCULATE_BACK = '/alert/model/data/calculate/',
OPTIMISTIC = '/alert/optimistic',
TRAIN_MODEL = '/alert/model/train',
TEST_MODEL = '/alert/model/test',
BOTTOM_MODEL = '/alert/model/bottom/',
}
export function modelCardListApi(params?: ModelQueryParams) {
return defHttp.get<ModelCardItem[]>({ url: Api.MODEL_CARD_LIST, params })
@ -34,3 +36,7 @@ export function calculateBackApi(id: any, params: any) {
export const getOptimisticApi = (params: any) => defHttp.get<any>({ url: Api.OPTIMISTIC, params })
export const trainModelApi = (params: any) => defHttp.post<any>({ url: Api.TRAIN_MODEL, data: params })
export const testModelApi = (params: any) => defHttp.post<any>({ url: Api.TEST_MODEL, data: params })
export const bottomModelApi = (id: any) => defHttp.post<any>({ url: Api.BOTTOM_MODEL + id })

124
src/views/model/list/step/Step3.vue

@ -1,14 +1,14 @@
<script lang="ts">
import type { PropType } from 'vue'
import { computed, defineComponent, ref, toRaw, unref } from 'vue'
import { Alert, Descriptions, Divider } from 'ant-design-vue'
import type { Recordable } from '@vben/types'
import { useDebounceFn } from '@vueuse/core'
import { step3Schemas } from './data'
import { ApiSelect, BasicForm, useForm } from '@/components/Form'
import { Button } from '@/components/Button'
import { pointListApi } from '@/api/alert/model/select'
import { modelSaveApi } from '@/api/alert/model/models'
import type { PropType } from "vue";
import { computed, defineComponent, ref, toRaw, unref } from "vue";
import { Alert, Descriptions, Divider } from "ant-design-vue";
import type { Recordable } from "@vben/types";
import { useDebounceFn } from "@vueuse/core";
import { step3Schemas } from "./data";
import { ApiSelect, BasicForm, useForm } from "@/components/Form";
import { Button } from "@/components/Button";
import { pointListApi } from "@/api/alert/model/select";
import { modelSaveApi } from "@/api/alert/model/models";
export default defineComponent({
components: {
@ -28,114 +28,118 @@ export default defineComponent({
type: Number,
},
},
emits: ['next', 'prev'],
emits: ["next", "prev"],
setup(props, { emit }) {
const [register, { appendSchemaByField, removeSchemaByField, validate, setProps }] = useForm({
const [
register,
{ appendSchemaByField, removeSchemaByField, validate, setProps },
] = useForm({
labelWidth: 100,
schemas: step3Schemas,
actionColOptions: {
span: 14,
},
resetButtonOptions: {
text: '上一步',
text: "上一步",
},
submitButtonOptions: {
text: '创建模型',
text: "创建模型",
},
resetFunc: customResetFunc,
submitFunc: customSubmitFunc,
})
const n = ref(4)
});
const n = ref(4);
function add() {
appendSchemaByField(
[
{
field: `point${n.value}`,
component: 'Input',
component: "Input",
label: `参数${n.value}`,
required: true,
slot: 'remoteSearch',
slot: "remoteSearch",
colProps: {
span: 23,
},
},
{
field: `${n.value}`,
component: 'Input',
label: ' ',
slot: 'add',
component: "Input",
label: " ",
slot: "add",
colProps: {
span: 1,
},
},
],
'',
)
n.value++
""
);
n.value++;
}
function del(field) {
removeSchemaByField([`point${field}`, `${field}`])
n.value--
removeSchemaByField([`point${field}`, `${field}`]);
n.value--;
}
async function customResetFunc() {
emit('prev')
emit("prev");
}
async function customSubmitFunc() {
try {
const values = await validate()
const pointInfo = []
const values = await validate();
const pointInfo = [];
for (const key in values) {
if (key.startsWith('point')) {
const point = values[key].split('|')
if (key.startsWith("point")) {
const point = values[key].split("|");
const p = {
description: point[0],
pointId: point[1],
unit: point[2],
Lower: point[3],
Upper: point[4],
}
pointInfo.push(p)
dead: true,
limit: false,
};
pointInfo.push(p);
}
}
const modelInfo = toRaw(props.beforeData)
modelInfo.pointInfo = pointInfo
const modelId = await modelSaveApi(modelInfo)
const modelInfo = toRaw(props.beforeData);
modelInfo.pointInfo = pointInfo;
const modelId = await modelSaveApi(modelInfo);
setProps({
submitButtonOptions: {
loading: true,
},
})
});
setTimeout(() => {
setProps({
submitButtonOptions: {
loading: false,
},
})
emit('next', modelId)
}, 1500)
}
catch (error) {
console.error(error)
});
emit("next", modelId);
}, 1500);
} catch (error) {
console.error(error);
}
}
const keyword = ref<string>('')
const keyword = ref<string>("");
const searchParams = computed<Recordable>(() => {
return { keyword: unref(keyword) }
})
return { keyword: unref(keyword) };
});
function onSearch(value: string) {
keyword.value = value
keyword.value = value;
}
async function searchPoint(params) {
if (params.keyword) {
const data = await pointListApi(params)
return data
const data = await pointListApi(params);
return data;
}
}
@ -147,11 +151,11 @@ export default defineComponent({
searchParams,
searchPoint,
handleReset: () => {
keyword.value = ''
keyword.value = "";
},
}
};
},
})
});
</script>
<template>
@ -190,20 +194,16 @@ export default defineComponent({
/>
</template>
<template #add="{ field }">
<Button v-if="Number(field) === 0" @click="add">
+
</Button>
<Button v-if="field > 0" @click="del(field)">
-
</Button>
<Button v-if="Number(field) === 0" @click="add"> + </Button>
<Button v-if="field > 0" @click="del(field)"> - </Button>
</template>
</BasicForm>
</div>
</template>
<style lang="less" scoped>
.step3 {
width: 450px;
margin: 0 auto;
}
.step3 {
width: 450px;
margin: 0 auto;
}
</style>

555
src/views/model/train/index.vue

@ -3,12 +3,13 @@ import type { ComponentPublicInstance } from 'vue'
import type { Dayjs } from 'dayjs'
import { debounce } from 'lodash-es'
import dayjs from 'dayjs'
import { computed, defineComponent, onMounted, ref, watch } from 'vue'
import { computed, defineComponent, onMounted, ref, toRaw, watch } from 'vue'
import { useRoute } from 'vue-router'
import {
Button,
Card,
Checkbox,
Col,
Descriptions,
Divider,
Form,
@ -17,17 +18,27 @@ import {
InputNumber,
Modal,
RangePicker,
Row,
Spin,
Steps,
Tabs,
Transfer,
} from 'ant-design-vue'
import VueECharts from 'vue-echarts'
import { func } from 'vue-types'
import { pointTableSchema, sampleInfoTableSchema } from './data'
import { BasicTable, useTable } from '@/components/Table'
import { PageWrapper } from '@/components/Page'
import { modelInfoApi, trainModelApi, updateModelInfo } from '@/api/alert/model/models'
import {
bottomModelApi,
modelInfoApi,
testModelApi,
trainModelApi,
updateModelInfo,
} from '@/api/alert/model/models'
import { getExaHistorys } from '@/api/alert/exa/index'
import { useECharts } from '@/hooks/web/useECharts'
import { useMessage } from '@/hooks/web/useMessage'
import { pointListApi } from '@/api/alert/model/select'
export default defineComponent({
components: {
@ -50,23 +61,30 @@ export default defineComponent({
ACheckbox: Checkbox,
AInputNumber: InputNumber,
AButton: Button,
ASpin: Spin,
ATransfer: Transfer,
ARow: Row,
ACol: Col,
},
setup() {
const { createMessage } = useMessage()
const route = useRoute()
const id = route.params.id
const model = ref(null)
const brushActivated = ref<Set<number>>(new Set())
const spinning = ref(false)
let trainTimeCopy = ''
const fetchModelInfo = async () => {
const modelInfo = await modelInfoApi(id)
model.value = modelInfo
trainTimeCopy = JSON.stringify(model.value.trainTime)
getHistory()
}
const pointData = computed(() => model.value?.pointInfo || [])
const [pointTable] = useTable({
columns: pointTableSchema,
pagination: false,
pagination: true,
dataSource: pointData,
scroll: { y: 300 },
})
@ -74,7 +92,6 @@ export default defineComponent({
const trainTime = computed(() => model.value?.trainTime || [])
const [trainTimeTable] = useTable({
columns: sampleInfoTableSchema,
pagination: false,
dataSource: trainTime,
scroll: { y: 300 },
})
@ -83,7 +100,10 @@ export default defineComponent({
type RangeValue = [Dayjs, Dayjs]
const currentDate: Dayjs = dayjs()
const lastMonthDate: Dayjs = currentDate.subtract(1, 'day')
const rangeValue: RangeValue = [dayjs('2023-10-28 00:00:00'), dayjs('2023-10-28 23:59:59')]
const rangeValue: RangeValue = [
lastMonthDate,
currentDate,
]
const historyTime = ref<RangeValue>(rangeValue)
const historyList = ref<any[]>([])
const echartsRefs = ref<any[]>([])
@ -91,33 +111,103 @@ export default defineComponent({
async function getHistory() {
if (!historyTime.value)
return
spinning.value = true
if (model.value.para) {
getTestData()
}
else {
const params = {
startTime: historyTime.value[0].format('YYYY-MM-DD HH:mm:ss'),
endTime: historyTime.value[1].format('YYYY-MM-DD HH:mm:ss'),
itemName: model.value?.pointInfo
.map(item => item.pointId)
.join(','),
interval: model.value.sampling,
}
const history = await getExaHistorys(params)
historyList.value = history.map((item, index) => {
const point = model.value?.pointInfo[index]
return {
data: [item],
name: `${index + 1}.${point?.description}(${point?.pointId})`,
}
})
echartsRefs.value = Array.from({ length: historyList.value.length })
brushActivated.value = new Set()
}
spinning.value = false
}
async function getTestData() {
const params = {
startTime: historyTime.value[0].format('YYYY-MM-DD HH:mm:ss'),
endTime: historyTime.value[1].format('YYYY-MM-DD HH:mm:ss'),
itemName: model.value?.pointInfo.map(item => item.pointId).join(','),
Model_id: 530,
version: model.value?.Cur_Version ? model.value?.Cur_Version : 'v-test',
Test_Data: {
time: historyTime.value
.map(t => dayjs(t).format('YYYY-MM-DD HH:mm:ss'))
.join(','),
points: model.value.pointInfo.map(t => t.pointId).join(','),
interval: model.value.sampling * 1000,
},
}
console.log(params)
const history = await getExaHistorys(params)
historyList.value = history.map((item, index) => {
const result = await testModelApi(params)
const sampleData = result.sampleData
const reconData = result.reconData
const xData = generateTimeList(
historyTime.value,
model.value.sampling * 1000,
)
historyList.value = sampleData.map((item, index) => {
const point = model.value?.pointInfo[index]
return {
data: item,
name: `${index}.${point?.description}(${point?.pointId})`,
data: [
item.map((t, i) => {
return [xData[i], t]
}),
reconData[index].map((t, i) => {
return [xData[i], t]
}),
],
name: `${index + 1}.${point?.description}(${point?.pointId})`,
}
})
echartsRefs.value = Array.from({ length: historyList.value.length })
console.log('echartsRefs', echartsRefs.value)
brushActivated.value = new Set()
}
function generateTimeList(time: RangeValue, intervalMs: number) {
const [t1, t2] = time
const count = Math.floor(t2.diff(t1, 'millisecond') / intervalMs) + 1
return Array.from({ length: count }, (_, i) => t1.add(i * intervalMs, 'millisecond').valueOf())
}
function getOption(item: any) {
console.log('getOption', item)
return {
const name = ['测量值', '模型值', '']
const color = ['blue', '#ff6f00', 'red']
const yIndex = [0, 0, 1]
const option = {
xAxis: {
type: 'time',
axisLabel: {
formatter(value) {
const date = new Date(value)
return date.toLocaleString()
},
},
},
yAxis: { type: 'value' },
series: [{ data: item.data, type: 'line', smooth: true, symbol: 'none' }],
yAxis: [{ type: 'value' }, { type: 'value', max: 10, show: false }],
series: item.data.map((item, index) => {
return {
data: item,
type: 'line',
smooth: true,
symbol: 'none',
name: name[index],
color: color[index],
yAxisIndex: yIndex[index],
}
}),
legend: {},
dataZoom: [{}],
brush: {
toolbox: ['lineX'],
@ -133,6 +223,7 @@ export default defineComponent({
},
],
}
return option
}
function setChartRef(
@ -161,7 +252,6 @@ export default defineComponent({
const chart = echartsRefs.value[index]
if (!chart)
return
console.log('chart', index, chart)
chart.dispatchAction({
type: 'takeGlobalCursor',
key: 'brush',
@ -173,7 +263,10 @@ export default defineComponent({
const areas = (model.value?.trainTime || []).map(row => ({
brushType: 'lineX',
xAxisIndex: 0,
coordRange: [dayjs(row.st).valueOf(), dayjs(row.et).valueOf()],
coordRange: [
dayjs(row.st).format('YYYY-MM-DD HH:mm:ss'),
dayjs(row.et).format('YYYY-MM-DD HH:mm:ss'),
],
}))
chart.dispatchAction({
type: 'brush',
@ -191,7 +284,6 @@ export default defineComponent({
isInitBrush.value = false
return
}
console.log('brush selected:', params.batch)
const selected = params.batch[0].selected
if (selected.length > 0) {
const areas = mergeAreas(params.batch[0].areas).map(area => ({
@ -200,7 +292,10 @@ export default defineComponent({
coordRange: area.coordRange,
}))
const trainTime = areas.map((area) => {
const [st, et] = area.coordRange
const [stRaw, etRaw] = area.coordRange
const st = typeof stRaw === 'string' ? dayjs(stRaw).valueOf() : stRaw
const et = typeof etRaw === 'string' ? dayjs(etRaw).valueOf() : etRaw
console.log('Selected area:', { st, et }, area)
return {
st: dayjs(st).format('YYYY-MM-DD HH:mm:ss'),
et: dayjs(et).format('YYYY-MM-DD HH:mm:ss'),
@ -210,7 +305,11 @@ export default defineComponent({
mode: '', //
}
})
console.log('Selected train time:', trainTime)
if (trainTimeCopy === JSON.stringify(trainTime))
return
model.value.trainTime = trainTime
trainTimeCopy = JSON.stringify(trainTime)
echartsRefs.value.forEach((chart, index) => {
if (chart) {
chart.dispatchAction({
@ -220,6 +319,7 @@ export default defineComponent({
isInitBrush.value = true
}
})
updateModelInfoDebounced()
}
}, 300)
@ -257,23 +357,12 @@ export default defineComponent({
}
//
const updateModelInfoDebounced = debounce((val) => {
const updateModelInfoDebounced = debounce(() => {
const val = toRaw(model.value)
if (val && val.id)
updateModelInfo(val)
}, 500)
// model
watch(
model,
(newVal, oldVal) => {
console.log('model changed:', newVal, oldVal)
if (oldVal === null)
return
updateModelInfoDebounced(newVal)
},
{ deep: true },
)
function handleDeleteTrainTime(index: number) {
if (!model.value?.trainTime)
return
@ -297,6 +386,12 @@ export default defineComponent({
isInitBrush.value = true
}
})
updateModelInfoDebounced()
}
async function clearModel() {
model.value.para = null
updateModelInfoDebounced()
await getHistory()
}
async function trainModel() {
@ -311,24 +406,46 @@ export default defineComponent({
return
}
const params = {
condition: '1==1',
conditon: modelInfo.alarmmodelset?.alarmcondition || '1==1',
Hyper_para: {
percent: modelInfo.rate,
},
Train_Data: {
points: pointInfo.map(item => item.pointId).join(','),
dead: pointInfo.map(item => item.dead ? '1' : '0').join(','),
limit: pointInfo.map(item => item.limit ? '1' : '0').join(','),
uplow: pointInfo.map(item => `${item.Upper},${item.Lower}`).join(';'),
dead: pointInfo.map(item => (item.dead ? '1' : '0')).join(','),
limit: pointInfo.map(item => (item.limit ? '1' : '0')).join(','),
uplow: pointInfo
.map(
item =>
`${item.Upper ? item.Upper : null},${
item.Lower ? item.Lower : null
}`,
)
.join(';'),
interval: modelInfo.sampling * 1000,
time: modelInfo.trainTime.map(item => `${item.st},${item.et}`).join(';'),
time: modelInfo.trainTime
.map(item => `${item.st},${item.et}`)
.join(';'),
},
type: 'PCA',
smote_config: '[]',
smote_config: [],
smote: true,
}
const response = await trainModelApi(params)
console.log('模型训练结果:', response)
spinning.value = true
try {
const response = await trainModelApi(params)
model.value.para = response
model.value.principal = response.Model_info.K
updateModelInfoDebounced()
getTestData()
createMessage.success('模型训练成功')
}
catch (error) {
console.error('模型训练失败:', error)
createMessage.error('模型训练失败')
}
spinning.value = false
}
const editForm = ref({
index: -1,
@ -336,14 +453,13 @@ export default defineComponent({
Lower: '',
lowerBound: '',
upperBound: '',
dead: false,
limit: true,
dead: true,
limit: false,
})
const openEditPointModal = ref<boolean>(false)
let pointEditRecord = null
function editPoint() {
//
console.log('编辑点')
model.value.pointInfo[editForm.value.index] = {
...model.value.pointInfo[editForm.value.index],
Upper: editForm.value.Upper,
@ -353,6 +469,7 @@ export default defineComponent({
dead: editForm.value.dead,
limit: editForm.value.limit,
}
updateModelInfoDebounced()
pointEditRecord.Upper = editForm.value.Upper
pointEditRecord.Lower = editForm.value.Lower
pointEditRecord.lowerBound = editForm.value.lowerBound
@ -363,21 +480,27 @@ export default defineComponent({
}
function openPointModal(index, record) {
// index
const pageIndex = index
// index
const globalIndex = model.value.pointInfo.findIndex(
item => item.pointId === record.pointId,
)
openEditPointModal.value = true
pointEditRecord = record
editForm.value = {
index,
index: globalIndex,
Upper: record?.Upper ?? '',
Lower: record?.Lower ?? '',
lowerBound: record?.lowerBound ?? '',
upperBound: record?.upperBound ?? '',
dead: record?.dead ?? false,
limit: record?.limit ?? true,
dead: !!record?.dead,
limit: !!record?.limit,
}
}
const mode = ref({
alarmcondition: '1=1',
alarmcondition: '1==1',
alarmname: '全工况运行',
})
const openEditModeModal = ref<boolean>(false)
@ -385,7 +508,7 @@ export default defineComponent({
function openEditMode() {
openEditModeModal.value = true
mode.value = {
alarmcondition: model.value?.alarmmodelset?.alarmcondition || '1=1',
alarmcondition: model.value?.alarmmodelset?.alarmcondition || '1==1',
alarmname: model.value?.alarmmodelset?.alarmname || '全工况运行',
}
}
@ -394,10 +517,107 @@ export default defineComponent({
}
function handleEditMode() {
//
console.log('编辑模式')
model.value.alarmmodelset = mode.value
updateModelInfoDebounced()
closeEditMode()
}
const openEditModelModal = ref(false)
const editModelForm = ref({
sampling: 0,
rate: 0,
selectedKeys: [],
})
// 穿
const transferData = ref([]) // 穿
//
async function fetchAllPoints(keyword = '') {
// keyword
// const res = await fetchPointsApi({ keyword })
//
const data = await pointListApi({ keyword })
console.log('Fetched points:', data)
transferData.value = transferData.value.filter(item => editModelForm.value.selectedKeys.includes(item.key))
.concat(data.map(item => ({
key: item.id,
title: item.name,
})))
console.log('Transfer data:', transferData.value)
}
onMounted(() => {
fetchAllPoints()
})
function handleTransferSearch(dir, value) {
if (dir === 'left') {
//
fetchAllPoints(value)
}
else {
//
transferData.value = (model.value?.pointInfo || [])
.filter(item =>
editModelForm.value.selectedKeys.includes(`${item.description}|${item.pointId}|${item.unit}|${item.Lower}|${item.Upper}`)
&& (`${item.description || ''}(${item.pointId})`).includes(value),
)
.map(item => ({
key: `${item.description}|${item.pointId}|${item.unit}|${item.Lower}|${item.Upper}`,
title: `${item.description || ''} (${item.pointId})`,
}))
}
}
function openEditModel() {
editModelForm.value.sampling = model.value?.sampling || 0
editModelForm.value.rate = model.value?.rate || 0
editModelForm.value.selectedKeys = (model.value?.pointInfo || []).map(item => `${item.description}|${item.pointId}|${item.unit}|${item.Lower}|${item.Upper}`)
transferData.value = (model.value?.pointInfo || []).map(item => ({
key: `${item.description}|${item.pointId}|${item.unit}|${item.Lower}|${item.Upper}`,
title: `${item.description || ''} (${item.pointId})`,
}))
console.log('transferData:', transferData.value)
openEditModelModal.value = true
}
function handleEditModelOk() {
model.value.sampling = editModelForm.value.sampling
model.value.rate = editModelForm.value.rate
model.value.pointInfo = editModelForm.value.selectedKeys.map((key) => {
const [description, pointId, unit, Lower, Upper] = key.split('|')
return {
description,
pointId,
unit,
Lower,
Upper,
dead: true,
limit: false,
}
})
clearModel()
openEditModelModal.value = false
}
function handleEditModelCancel() {
openEditModelModal.value = false
}
async function bottomModel() {
if (!model.value.para) {
createMessage.error('模型未训练,无法下装')
return
}
try {
const response = await bottomModelApi(model.value.id)
model.value = response
createMessage.success('模型下装成功')
}
catch (error) {
console.error('底层模型获取失败:', error)
createMessage.error('底层模型获取失败')
}
}
return {
pointTable,
model,
@ -423,6 +643,16 @@ export default defineComponent({
closeEditMode,
handleEditMode,
mode,
spinning,
clearModel,
openEditModelModal,
editModelForm,
transferData,
handleTransferSearch,
openEditModel,
handleEditModelOk,
handleEditModelCancel,
bottomModel,
}
},
})
@ -432,21 +662,15 @@ export default defineComponent({
<PageWrapper content-background>
<div>
<a-card title="模型信息" :bordered="false">
<template #extra>
<a-button> 下装 </a-button>
<a-button type="primary" style="margin-left: 6px">
修改模型
</a-button>
</template>
<a-descriptions size="small" :column="4" bordered>
<a-descriptions-item label="模型名称">
{{ model?.name }}
</a-descriptions-item>
<a-descriptions-item label="描述">
{{ model?.description || '暂无描述' }}
{{ model?.description || "暂无描述" }}
</a-descriptions-item>
<a-descriptions-item label="版本">
{{ model?.Cur_Version || 'v-test' }}
{{ model?.Cur_Version || "v-test" }}
</a-descriptions-item>
<a-descriptions-item label="评估报告">
暂无
@ -458,22 +682,22 @@ export default defineComponent({
{{ model?.creatTime }}
</a-descriptions-item>
<a-descriptions-item label="最近修改人">
{{ model?.Modifier || '暂无' }}
{{ model?.Modifier || "暂无" }}
</a-descriptions-item>
<a-descriptions-item label="最近修改时间">
{{ model?.modifiedTime || '暂无' }}
{{ model?.modifiedTime || "暂无" }}
</a-descriptions-item>
</a-descriptions>
<a-divider />
<a-descriptions size="small" :column="4" bordered>
<a-descriptions-item label="算法">
{{ model?.algorithm || 'PCA' }}
{{ model?.algorithm || "PCA" }}
</a-descriptions-item>
<a-descriptions-item label="训练采样间隔">
{{ model?.sampling }}
</a-descriptions-item>
<a-descriptions-item label="参数个数">
{{ model?.pointInfo.length || '暂无' }}
{{ model?.pointInfo.length || "暂无" }}
</a-descriptions-item>
<a-descriptions-item label="最小主元贡献率">
{{ model?.rate }}
@ -485,14 +709,25 @@ export default defineComponent({
{{ model?.precision }}
</a-descriptions-item>
<a-descriptions-item label="训练总时长(h)">
{{ (model?.trainTime.reduce((total, item) => total + item.duration, 0) / 3600).toFixed(2) || '暂无' }}
{{
(
model?.trainTime.reduce(
(total, item) => total + item.duration,
0,
) / 3600
).toFixed(2) || "暂无"
}}
</a-descriptions-item>
<a-descriptions-item label="有效样本数">
{{ model?.principal }}
</a-descriptions-item>
</a-descriptions>
</a-card>
<a-card title="模式" :bordered="false" style="margin-top: 16px;margin-bottom: -20px;">
<a-card
title="模式"
:bordered="false"
style="margin-top: 16px; margin-bottom: -20px"
>
<a-button size="large" @click="openEditMode">
{{ model?.alarmmodelset.alarmname }}
</a-button>
@ -503,7 +738,11 @@ export default defineComponent({
<a-tab-pane key="1" tab="训练采样时间">
<BasicTable @register="trainTimeTable">
<template #action="{ record, index }">
<a-button type="link" danger @click="handleDeleteTrainTime(index)">
<a-button
type="link"
danger
@click="handleDeleteTrainTime(index)"
>
删除
</a-button>
</template>
@ -522,46 +761,101 @@ export default defineComponent({
</a-card>
<a-card title="智能训练" :bordered="false">
<div style="display: flex; align-items: center;">
<a-form layout="inline" style="flex: 1;">
<div style="display: flex; align-items: center">
<a-form layout="inline" style="flex: 1">
<a-form-item label="模型预览时间范围">
<a-range-picker v-model:value="historyTime" show-time @change="getHistory" />
<a-range-picker
v-model:value="historyTime"
show-time
@change="getHistory"
/>
</a-form-item>
</a-form>
<a-button type="primary" style="margin-left: auto;" @click="trainModel">
<a-button
type="primary"
style="margin-left: auto"
@click="trainModel"
>
模型训练
</a-button>
<a-button
type="primary"
style="margin-left: 10px"
@click="clearModel"
>
清除训练结果
</a-button>
<a-button type="primary" style="margin-left: 6px" @click="openEditModel">
修改模型
</a-button>
<a-button
danger
style="margin-left: 10px"
@click="bottomModel"
>
下装
</a-button>
</div>
<a-divider />
<div v-for="(item, index) in historyList" :key="index" class="echart-box">
<a-card :bordered="false" style="margin-bottom: 16px;">
<template #title>
<span style="font-size: 20px;">{{ item.name }}</span>
</template>
<VueECharts
:ref="(el) => setEchartsRef(el, index)" :option="getOption(item)" autoresize
style="width: 100%; height: 300px" @finished="() => onChartFinished(index)"
@brush-selected="onBrushSelected"
/>
</a-card>
</div>
<a-spin :spinning="spinning" size="large">
<div
v-for="(item, index) in historyList"
:key="index"
class="echart-box"
style="width: 100%"
>
<a-card :bordered="false" style="margin-bottom: 16px">
<template #title>
<span style="font-size: 20px">{{ item.name }}</span>
</template>
<VueECharts
:ref="(el) => setEchartsRef(el, index)"
:option="getOption(item)"
autoresize
style="width: 100%; height: 200px"
@finished="() => onChartFinished(index)"
@brush-selected="onBrushSelected"
/>
</a-card>
</div>
</a-spin>
</a-card>
</div>
<a-modal v-model:open="openEditPointModal" title="更改上下限" @ok="editPoint">
<a-form :model="editForm" :label-col="{ span: 7 }" :wrapper-col="{ span: 15 }">
<a-modal
v-model:open="openEditPointModal"
title="更改上下限"
@ok="editPoint"
>
<a-form
:model="editForm"
:label-col="{ span: 7 }"
:wrapper-col="{ span: 15 }"
>
<a-form-item label="上限">
<a-input-number v-model:value="editForm.Upper" placeholder="请输入上限" />
<a-input-number
v-model:value="editForm.Upper"
placeholder="请输入上限"
/>
</a-form-item>
<a-form-item label="下限">
<a-input-number v-model:value="editForm.Lower" placeholder="请输入下限" />
<a-input-number
v-model:value="editForm.Lower"
placeholder="请输入下限"
/>
</a-form-item>
<a-form-item label="残差上限">
<a-input-number v-model:value="editForm.upperBound" placeholder="请输入残差上限" />
<a-input-number
v-model:value="editForm.upperBound"
placeholder="请输入残差上限"
/>
</a-form-item>
<a-form-item label="残差下限">
<a-input-number v-model:value="editForm.lowerBound" placeholder="请输入残差下限" />
<a-input-number
v-model:value="editForm.lowerBound"
placeholder="请输入残差下限"
/>
</a-form-item>
<a-form-item label="清洗方式" style="margin-top: 16px;">
<a-form-item label="清洗方式" style="margin-top: 16px">
<a-checkbox v-model:checked="editForm.dead">
死区清洗
</a-checkbox>
@ -571,16 +865,83 @@ export default defineComponent({
</a-form-item>
</a-form>
</a-modal>
<a-modal v-model:open="openEditModeModal" title="编辑模式" @ok="handleEditMode" @cancel="closeEditMode">
<a-form :model="mode" :label-col="{ span: 7 }" :wrapper-col="{ span: 15 }">
<a-modal
v-model:open="openEditModeModal"
title="编辑模式"
@ok="handleEditMode"
@cancel="closeEditMode"
>
<a-form
:model="mode"
:label-col="{ span: 7 }"
:wrapper-col="{ span: 15 }"
>
<a-form-item label="模式名称">
<a-input v-model:value="mode.alarmname" placeholder="请输入模式名称" />
<a-input
v-model:value="mode.alarmname"
placeholder="请输入模式名称"
/>
</a-form-item>
<a-form-item label="报警条件">
<a-input v-model:value="mode.alarmcondition" placeholder="请输入报警条件" />
<a-input
v-model:value="mode.alarmcondition"
placeholder="请输入报警条件"
/>
</a-form-item>
</a-form>
</a-modal>
<a-modal
v-model:open="openEditModelModal"
title="编辑模型"
width="1200px"
@ok="handleEditModelOk"
@cancel="handleEditModelCancel"
>
<a-form
:model="editModelForm"
:label-col="{ span: 7 }"
:wrapper-col="{ span: 15 }"
>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="训练采样间隔" required>
<a-input-number
v-model:value="editModelForm.sampling"
placeholder="请输入训练采样间隔"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="最小主元贡献率" required>
<a-input-number
v-model:value="editModelForm.rate"
placeholder="请输入最小主元贡献率"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<div style="display: flex; justify-content: center; margin-top: 24px;margin-bottom: 10px;">
<a-transfer
v-model:target-keys="editModelForm.selectedKeys"
:data-source="transferData"
:render="item => item.title"
:row-key="(item) => item.key"
:pagination="false"
:show-search="true"
:filter="true"
search-placeholder="搜索测点"
:titles="['可选测点', '已选测点']"
:list-style="{
width: '500px',
height: '450px',
}"
@search="handleTransferSearch"
/>
</div>
</a-form>
</a-modal>
</PageWrapper>
</template>

Loading…
Cancel
Save