get file from ai4m(1)

This commit is contained in:
jhnine 2025-02-20 17:51:11 +08:00
parent 54db9af5f0
commit a16645c060
86 changed files with 8358 additions and 1697 deletions

View File

@ -41,7 +41,8 @@
"passwordMsg": "The password can not be less than 6 digits",
"check": "Check",
"submit": "Submit",
"instance": "Instance"
"instance": "Instance",
"select": "Select"
},
"menu": {
"taskManagement": "Task",
@ -92,6 +93,7 @@
"resourceList": "API Management",
"oplList": "Operation Logs",
"login": "Login",
"register": "Register",
"monitorSelect": "Monitor",
"monitorSelectBk": "Resource Center",
"monitorSelectNew": "PCM Overview",
@ -103,7 +105,14 @@
"appObserve": "App Monitoring",
"logMana": "Log Mgmt",
"monitorSelectPcm": "JCCE",
"fileManagement": "File Management"
"fileManagement": "File Management",
"blockchain": "存证查验",
"blockchainLog": "数据可信核验",
"blockchainBrowser": "区块链浏览器",
"datasetManagement": "数据集管理",
"algorithmManagement": "算法管理",
"imageManagement": "镜像管理",
"modelManagement": "模型管理"
},
"check": {
"input": "Please Input",
@ -551,6 +560,7 @@
"result": "Result",
"pictureUpload": "Picture Upload",
"createDeductiveTask": "Create Deductive Task",
"createTrainingTask": "中国算力网——智算训练任务创建",
"image": "Image",
"initFile": "Init File",
"codeUpload": "Code Upload",
@ -574,6 +584,27 @@
"partition": "Partition",
"cmdScript": "Script",
"fileUpload": "File Upload",
"uploadSuccess": "Upload Success"
"uploadSuccess": "Upload Success",
"cpCluster": "Computing Power Cluster",
"computingResource": "Computing Resource",
"internet": "Internet",
"visualization": "Visualization",
"codeBranch": "Branch",
"runParams": "Params",
"imageName": "Image Name",
"packageName": "Package Name",
"targetCluster": "Target Cluster",
"datasetCategory": "Dataset Category",
"resourceRecom": "Resource Recommendation",
"authSetting": "Authentification Setting",
"applyList": "Apply List",
"applyAccount": "Apply Account",
"applyDataset": "Applied Dataset",
"applyModel": "Applied Model Name",
"category": "Category",
"datasetApplied": "Applied Dataset",
"modelApplied": "Applied Model",
"forApply": "Can Apply List",
"sonAlgorithmName": "Son Algorithm Name"
}
}

View File

@ -41,7 +41,8 @@
"passwordMsg": "密码不能少于6位",
"check": "验证",
"submit": "提交",
"instance": "实例"
"instance": "实例",
"select": "选择"
},
"menu": {
"taskManagement": "任务管理",
@ -92,6 +93,7 @@
"resourceList": "接口管理",
"oplList": "操作日志",
"login": "登录",
"register": "注册",
"monitorSelect": "综合运控",
"monitorSelectBk": "资源中心",
"monitorSelectNew": "PCM总览",
@ -103,7 +105,16 @@
"appObserve": "应用观测",
"logMana": "日志管理",
"monitorSelectPcm": "云际算力网基础平台",
"fileManagement": "文件管理"
"fileManagement": "文件管理",
"blockchain": "存证查验",
"blockchainLog": "数据可信核验",
"blockchainBrowser": "区块链浏览器",
"datasetManagement": "数据集管理",
"algorithmManagement": "算法管理",
"imageManagement": "镜像管理",
"modelManagement": "模型管理",
"personalCenter": "个人中心",
"pointManagement": "积分管理"
},
"check": {
"input": "请输入",
@ -551,6 +562,7 @@
"result": "结果",
"pictureUpload": "图片上传",
"createDeductiveTask": "创建智算推理任务",
"createTrainingTask": "中国算力网——智算训练任务创建",
"image": "镜像",
"initFile": "启动文件",
"codeUpload": "代码上传",
@ -574,6 +586,27 @@
"partition": "分区",
"cmdScript": "脚本",
"fileUpload": "文件上传",
"uploadSuccess": "上传成功"
"uploadSuccess": "上传成功",
"cpCluster": "算力集群",
"computingResource": "计算资源",
"internet": "访问Internet",
"visualization": "可视化",
"codeBranch": "代码分支",
"runParams": "运行参数",
"imageName": "镜像名称",
"packageName": "Package名称",
"targetCluster": "目标集群",
"datasetCategory": "数据集类别",
"resourceRecom": "资源推荐",
"authSetting": "访问权限设置",
"applyList": "申请列表",
"applyAccount": "申请用户",
"applyDataset": "申请使用数据集名称",
"applyModel": "申请使用模型名称",
"category": "类别",
"datasetApplied": "申请使用数据集名称",
"modelApplied": "申请使用模型名称",
"forApply": "可申请数据集",
"sonAlgorithmName": "子算法名称"
}
}

View File

@ -13,7 +13,7 @@ export default {
}
},
mounted() {
localStorage.setItem('jcceTheme', 'jcceDark') // Please delete code if you need other color
// localStorage.setItem('jcceTheme', 'jcceDark') // Please delete code if you need other color
const jcceTheme = this.jcceTheme || 'jcceDark'
document.documentElement.setAttribute('jcceTheme', jcceTheme)
localStorage.setItem('jcceTheme', jcceTheme)

View File

@ -0,0 +1,282 @@
import request from '@/utils/request'
const preUrl = '/jcc-admin'
export function login(username, password) {
return request({
url: preUrl + '/admin/login',
method: 'post',
data: {
username,
password
}
})
}
export function getInfo() {
return request({
url: preUrl + '/admin/info',
method: 'get'
})
}
export function logout() {
return request({
url: preUrl + '/admin/logout',
method: 'post'
})
}
export function getUserList(params) {
return request({
url: preUrl + '/admin/list',
method: 'get',
params: params
})
}
export function createAdmin(data) {
return request({
url: preUrl + '/admin/register',
method: 'post',
data: data
})
}
export function updateAdmin(id, data) {
return request({
url: preUrl + '/admin/update/' + id,
method: 'put',
data: data
})
}
export function updateUserStatus(id, params) {
return request({
url: preUrl + '/admin/updateStatus/' + id,
method: 'put',
params: params
})
}
export function deleteAdmin(id) {
return request({
url: preUrl + '/admin/delete/' + id,
method: 'delete'
})
}
export function getRoleByAdmin(id) {
return request({
url: preUrl + '/admin/role/' + id,
method: 'get'
})
}
export function allocRole(data) {
return request({
url: preUrl + '/admin/role/update',
method: 'put',
data: data
})
}
// 角色列表
export function getRoleList(params) {
return request({
url: preUrl + '/role/list',
method: 'get',
params: params
})
}
export function createRole(data) {
return request({
url: preUrl + '/role/create',
method: 'post',
data: data
})
}
export function updateRole(id, data) {
return request({
url: preUrl + '/role/update/' + id,
method: 'put',
data: data
})
}
export function updateRoleStatus(id, params) {
return request({
url: preUrl + '/role/updateStatus/' + id,
method: 'put',
params: params
})
}
export function deleteRole(params) {
return request({
url: preUrl + '/role/delete',
method: 'delete',
params
})
}
export function fetchAllRoleList() {
return request({
url: preUrl + '/role/listAll',
method: 'get'
})
}
export function listMenuByRole(roleId) {
return request({
url: preUrl + '/role/listMenu/' + roleId,
method: 'get'
})
}
export function listResourceByRole(roleId) {
return request({
url: preUrl + '/role/listResource/' + roleId,
method: 'get'
})
}
export function allocMenu(data) {
return request({
url: preUrl + '/role/allocMenu',
method: 'post',
params: data
})
}
export function allocResource(data) {
return request({
url: preUrl + '/role/allocResource',
method: 'post',
params: data
})
}
// 菜单列表
export function getMenuList(parentId, params) {
return request({
url: preUrl + '/menu/list/' + parentId,
method: 'get',
params: params
})
}
export function deleteMenu(id) {
return request({
url: preUrl + '/menu/delete/' + id,
method: 'delete'
})
}
export function createMenu(data) {
return request({
url: preUrl + '/menu/create',
method: 'post',
data: data
})
}
export function updateMenu(id, data) {
return request({
url: preUrl + '/menu/update/' + id,
method: 'put',
data: data
})
}
export function getMenu(id) {
return request({
url: preUrl + '/menu/' + id,
method: 'get'
})
}
export function updateHidden(id, params) {
return request({
url: preUrl + '/menu/updateHidden/' + id,
method: 'put',
params: params
})
}
export function fetchTreeList() {
return request({
url: preUrl + '/menu/treeList',
method: 'get'
})
}
// 资源列表
export function getResourceList(params) {
return request({
url: preUrl + '/resource/list',
method: 'get',
params: params
})
}
export function createResource(data) {
return request({
url: preUrl + '/resource/create',
method: 'post',
data: data
})
}
export function updateResource(id, data) {
return request({
url: preUrl + '/resource/update/' + id,
method: 'put',
data: data
})
}
export function deleteResource(id) {
return request({
url: preUrl + '/resource/delete/' + id,
method: 'delete'
})
}
export function fetchAllResourceList() {
return request({
url: preUrl + '/resource/listAll',
method: 'get'
})
}
export function listAllCate() {
return request({
url: preUrl + '/resourceCategory/listAll',
method: 'get'
})
}
export function createResourceCategory(data) {
return request({
url: preUrl + '/resourceCategory/create',
method: 'post',
data: data
})
}
export function updateResourceCategory(id, data) {
return request({
url: preUrl + '/resourceCategory/update/' + id,
method: 'put',
data: data
})
}
export function deleteResourceCategory(id) {
return request({
url: preUrl + '/resourceCategory/delete/' + id,
method: 'delete'
})
}

View File

@ -0,0 +1,89 @@
import request from '@/utils/request'
// 区块列表信息-最新区块
export function getBlockList(params) {
return request({
url: '/blockChain/blockChain/block/list',
method: 'get',
params
})
}
// 左上角信息
export function getBlockChain(params) {
return request({
url: '/blockChain/blockChain',
method: 'get',
params
})
}
// 合约调用记录分页查询-最新交易列表
export function getRecordPage(params) {
return request({
url: '/blockChain/invoke/record/page',
method: 'get',
params
})
}
// 查询区块上的交易列表
export function getTransactions(id) {
return request({
url: '/blockChain/blockChain/transactions/' + id,
method: 'get'
})
}
// 合约列表
export function getContractList(params) {
return request({
url: '/blockChain/contract/list',
method: 'get',
params
})
}
// 查询日志列表
export function getBusinessLog(params) {
return request({
url: '/blockChain/businessLog/page',
method: 'get',
params
})
}
// 节点列表
export function getNodeList(params) {
return request({
url: '/blockChain/blockChain/node/list',
method: 'get',
params
})
}
// 合约调用记录详情--交易详情
export function getRecordDetail(params) {
return request({
url: '/blockChain/invoke/record/detail',
method: 'get',
params
})
}
// 查询合约
export function getRecordQuery(params) {
return request({
url: '/blockChain/invoke/record/query',
method: 'get',
params
})
}
// 7日交易量
export function get7dayChart() {
return request({
url: '/blockChain/invoke/record/7dayChart',
method: 'get'
})
}

View File

@ -0,0 +1,35 @@
import request from '@/utils/request'
// 最近7日积分变动情况
export function get7dayChart(params) {
return request({
url: '/blockChain/points/record/7dayChart',
method: 'get',
params
})
}
// 查询余额
export function getBalance(params) {
return request({
url: '/blockChain/points/balance',
method: 'get',
params
})
}
// 积分流水
export function getRecordList(params) {
return request({
url: '/blockChain/points/record/page',
method: 'get',
params
})
}
// 根据txHash查询积分记录
export function getPointByHash(params) {
return request({
url: '/blockChain/points/record/getByTxHash',
method: 'get',
params
})
}

263
src/api/jcs/jcs.js Normal file
View File

@ -0,0 +1,263 @@
import request from '@/utils/jcs-request'
export const taskSubmit = (data) => {
return request({
url: '/jsm/jobSet/submit',
method: 'post',
data,
headers: {
'Content-Type': 'application/json'
}
})
}
export const getFileList = (data) => {
return request({
url: '/jsm/jobSet/queryUploaded',
method: 'post',
data
})
}
export const createPackage = (data) => {
return request({
url: '/jsm/jobSet/createPackage',
method: 'post',
data
})
}
export function registerAdmin(params) {
return request({
url: '/jsm/user/register',
method: 'get',
params
})
}
// export const getUploadData = (data) => {
// return request({
// url: '/jsm/jobSet/notifyUploaded',
// method: 'post',
// data,
// headers: {
// 'Content-Type': 'application/json'
// }
// })
// }
export const getUploadData = (data) => {
return request({
url: '/jsm/jobSet/notifyUploaded',
method: 'post',
data,
headers: {
'Content-Type': 'application/json'
}
})
}
export const uploadData = (data) => {
return request({
url: '/jcs/object/upload',
method: 'post',
data,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
export const downloadFile = (params) => {
return request({
url: '/jcs/object/download',
method: 'get',
params
})
}
export const updatecode = (data) => {
return request({
url: '/jsm/jobSet/updateCode',
method: 'post',
data
})
}
export const uploadSuccessMessage = (data) => {
return request({
url: '/jsm/jobSet/localFileUploaded',
method: 'post',
data,
headers: {
'Content-Type': 'application/json'
}
})
}
export const createFolder = (data) => {
return request({
url: '/jsm/jobSet/createFolder',
method: 'post',
data
})
}
export const deleteFolder = (data) => {
return request({
url: '/jsm/jobSet/deleteFolder',
method: 'post',
data
})
}
export const deleteFile = (data) => {
return request({
url: '/jsm/jobSet/deleteFile',
method: 'post',
data
})
}
export const deletePackage = (data) => {
return request({
url: '/jsm/jobSet/deletePackage',
method: 'post',
data
})
}
export const getBindingList = (data) => {
return request({
url: '/jsm/jobSet/queryBinding',
method: 'post',
data
})
}
export const addBinding = (data) => {
return request({
url: '/jsm/jobSet/binding',
method: 'post',
data
})
}
export const deleteBinding = (data) => {
return request({
url: '/jsm/jobSet/removeBinding',
method: 'post',
data
})
}
export const queryImages = (data) => {
return request({
url: '/jsm/jobSet/queryImages',
method: 'post',
data
})
}
export const getResourceListByfilter = (data) => {
return request({
url: '/jsm/jobSet/queryResource',
method: 'post',
data
})
}
export const getResourceRange = (data) => {
return request({
url: '/jsm/jobSet/resourceRange',
method: 'post',
data
})
}
export const createTrainingTask = (data) => {
return request({
url: '/jsm/jobSet/submit',
method: 'post',
data
})
}
export const getApplyList = (data) => {
return request({
url: '/jsm/access/getAccessRequests',
method: 'post',
data
})
}
export const confirmApply = (data) => {
return request({
url: '/jsm/access/approvalRequestAccess',
method: 'post',
data
})
}
// 访问权限设置
export const accessAuthSetting = (data) => {
return request({
url: '/jsm/access/updateBindingDataAccess',
method: 'post',
data
})
}
// 撤销申请接口 取消访问 status值撤销申请revoked取消访问cancel
export const cancelApply = (data) => {
return request({
url: '/jsm/access/updateAccessRequestStatus',
method: 'post',
data
})
}
// 申请访问接口
export const applyData = (data) => {
return request({
url: '/jsm/access/applyRequestAccess',
method: 'post',
data
})
}
// 获取userID
export const getUserID = (params) => {
return request({
url: '/jsm/user/query',
method: 'get',
params
})
}
// 创建子算法:/jobSet/addChildrenCode
export const addCode = (data) => {
return request({
url: '/jsm/jobSet/addCodeVersion',
method: 'post',
data
})
}
// 查询算法列表
export const queryCodeList = (data) => {
return request({
url: '/jsm/jobSet/queryCodeVersion',
method: 'post',
data
})
}
// 查询算法列表
export const deleteCode = (data) => {
return request({
url: '/jsm/jobSet/removeCodeVersion',
method: 'post',
data
})
}

View File

@ -37,7 +37,9 @@ export const getTrainingTaskStat = () => {
export const getInstanceList = (params) => {
return request({ url: '/pcm/v1/inference/instanceCenter', method: 'get', params })
}
export const getHpcInstanceList = (params) => {
return request({ url: '/pcm/v1/hpc/ListInstanceCenter', method: 'get', params })
}
export const getAppPodsByAppName = (name, params) => {
return request({ url: '/pcm/v1/apps/pods/' + name, method: 'get', params })
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
src/assets/img/file-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -26,6 +26,16 @@
v-model="filterData[key]"
type="date"
/>
<el-date-picker
v-else-if="item.type === 'datetimerange'"
v-model="filterData[key]"
type="datetimerange"
value-format="yyyy-MM-dd HH:mm:ss"
:range-separator="$t('message.to')"
:start-placeholder="$t('message.startDate')"
:end-placeholder="$t('message.endDate')"
align="right"
/>
<template
v-else-if="item.type === 'dateRange'"
>
@ -97,6 +107,7 @@
:key="index"
:prop="item.prop"
:label="item.label"
:min-width="item.minWidth"
:width="item.width"
:formatter="item.formatter"
:sortable="item.sortable"
@ -302,6 +313,12 @@ export default {
...this.query,
...this.filterData
}
//
if (this.filterMap && 'time' in this.filterMap && this.filterMap.time.type === 'datetimerange' && params.time) {
params.startTime = params.time[0]
params.endTime = params.time[1]
params.time = undefined
}
if (this.getListAction) {
const ListData = await this.getListAction(params)
this.total = Number(ListData?.data?.[this.totalKey]) || Number(ListData[this.totalKey]) || 0

File diff suppressed because one or more lines are too long

View File

@ -59,6 +59,9 @@
<el-dropdown-item v-if="menus.includes('fileManagement')" @click.native="toFileManagement">
{{ $t('menu.fileManagement') }}
</el-dropdown-item>
<el-dropdown-item v-if="menus.includes('pointManagement')" @click.native="toPersonalCenter">
积分管理
</el-dropdown-item>
<el-dropdown-item divided @click.native="logout">
<span style="display:block;">{{ $t('message.logout') }}</span>
</el-dropdown-item>
@ -172,6 +175,10 @@ export default {
this.$store.dispatch('user/setRouteType', 'fileManagement')
this.$router.push({ path: '/fileManagement/fileList' })
},
toPersonalCenter() {
this.$store.dispatch('user/setRouteType', 'personalCenter')
this.$router.push({ path: '/personalCenter/pointManagement' })
},
toSetting() {
this.$store.dispatch('user/setRouteType', 'setting')
this.$router.push({ path: '/setting/dictManagement' })
@ -181,7 +188,7 @@ export default {
},
async logout() {
await this.$store.dispatch('user/logout')
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
this.$router.push(`/login`)
}
}
}

17
src/mixin/blockChain.js Normal file
View File

@ -0,0 +1,17 @@
import { getNodeList } from '@/api/blockchain/blockchain'
import { getDictByCode } from '@/api/common/setting'
export default {
data() {
return {
nodeList: [], // 节点列表
typeNameList: [], // 记录类型
pointsList: [] // 积分变动类型
}
},
mounted() {
getNodeList().then(e => { this.nodeList = e.data })
getDictByCode('contract-type-dict').then(e => { this.typeNameList = e.data.list })
getDictByCode('point-type-dict').then(res => { this.pointsList = res.data?.list || [] })
}
}

View File

@ -8,7 +8,7 @@ import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist
const whiteList = ['/login', '/register', '/auth-redirect'] // no redirect whitelist
router.beforeEach(async(to, from, next) => {
// start progress bar
@ -43,16 +43,16 @@ router.beforeEach(async(to, from, next) => {
await store.dispatch('cluster/getClusterList')
}
// get user info
const { roleName } = await store.dispatch('user/getInfo')
const { roles, menuNames, id } = await store.dispatch('user/getInfo')
// get user menus
const menus = await store.dispatch('user/getMenus')
store.dispatch('user/getUserID', id)
// // get user menus
// const menus = await store.dispatch('user/getMenus')
store.dispatch('user/getDict')
// generate accessible routes map based on roles
const accessRoutes = await store.dispatch('permission/generateRoutes', { menus, roleName })
const accessRoutes = await store.dispatch('permission/generateRoutes', { menuNames, roles })
// dynamically add accessible routes
router.addRoutes(accessRoutes)
@ -71,7 +71,6 @@ router.beforeEach(async(to, from, next) => {
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()

View File

@ -7,7 +7,8 @@ import Layout from '@/layout'
// import emptyLayout from '@/layout/emptyLayout'
import podRouter from './modules/pod'
import virtualMachineRouter from './modules/virtualMachine'
import blockChainRouter from './modules/blockChain'
import blockchainManagementRouter from './modules/blockchain'
import personalCenter from './modules/personalCenter'
import functionsRouter from './modules/functions'
import permissionRouter from './modules/permissionManagement'
import monitorRouter from './modules/warnManagement'
@ -68,6 +69,12 @@ export const constantRoutes = [
meta: { title: 'login' },
hidden: true
},
{
path: '/register',
component: () => import('@/views/login/register'),
meta: { title: 'register' },
hidden: true
},
{
path: '/auth-redirect',
component: () => import('@/views/login/auth-redirect'),
@ -155,7 +162,7 @@ export const asyncRoutes = [
},
podRouter,
virtualMachineRouter,
blockChainRouter,
blockchainManagementRouter,
functionsRouter,
permissionRouter,
monitorRouter,
@ -166,7 +173,8 @@ export const asyncRoutes = [
taskManagementRouter,
strategyManagementRouter,
adapterMangementRouter,
fileManagementRouter
fileManagementRouter,
personalCenter
]
const createRouter = () => new Router({

View File

@ -1,40 +1,27 @@
import Layout from '@/layout'
import emptyLayout from '@/layout/emptyLayout'
const blockChainRouter = {
path: '/blockChain',
const blockchainManagementRouter = {
path: '/blockchain',
component: Layout,
name: '云际记账',
name: 'blockchain',
meta: {
title: '云际记账',
icon: 'yunjijizhang'
title: 'blockchain',
icon: 'qukuailian'
},
children: [
{
path: 'blockChainBrowser',
component: () => import('@/views/blockChain/blockChainBrowser'),
name: 'blockChainBrowser',
meta: { title: '块链浏览器', icon: 'gailan', affix: true }
path: 'blockchainBrowser',
component: () => import('@/views/blockChain/blockchainBrowser'),
name: 'blockchainBrowser',
meta: { title: 'blockchainBrowser', icon: 'gailan', affix: true },
hidden: false
},
{
path: 'blockList',
component: emptyLayout,
meta: { activeMenu: '/block/blockChainBrowser' },
hidden: true,
children: [
{
path: '',
component: () => import('@/views/blockChain/blockList'),
name: 'blockList'
}
]
},
{
path: 'blockChainDataCheck',
component: () => import('@/views/blockChain/blockChainDataCheck'),
name: 'blockChainDataCheck',
meta: { title: '数据可信核验', icon: 'cunchu-ziyuanshezhi', affix: true },
path: 'blockchainLog',
component: () => import('@/views/blockChain/blockchainLog'),
name: 'blockchainLog',
meta: { title: 'blockchainLog', icon: 'qukuailianchaxun', affix: true },
hidden: false
}
]
}
export default blockChainRouter
export default blockchainManagementRouter

View File

@ -1,19 +1,57 @@
import Layout from '@/layout'
// import emptyLayout from '@/layout/emptyLayout'
const fileManagementRouter = {
path: '/fileManagement',
component: Layout,
meta: {
title: 'fileManagement',
title: 'storageMana',
icon: 'xunijiguanli-juan'
},
name: 'fileManagement',
children: [
{
path: 'packageList',
component: () => import('@/views/fileManagement/fileList/packageList.vue'),
meta: { title: 'fileManagement', icon: 'wenjiancunchu', affix: true, keepAlive: true },
name: 'packageList',
hidden: false
},
{
path: 'fileList',
component: () => import('@/views/fileManagement/fileList/list'),
name: 'fileManagement',
meta: { title: 'overview', icon: 'gailan', affix: true },
component: () => import('@/views/fileManagement/fileList/fileList.vue'),
meta: { activeMenu: '/fileManagement/packageList' },
hidden: true
},
{
path: 'datasetList',
component: () => import('@/views/fileManagement/datasetManagement/list'),
name: 'datasetManagement',
meta: { title: 'datasetManagement', icon: 'shujuji', affix: true },
hidden: false
},
{
path: 'algorithmList',
component: () => import('@/views/fileManagement/algorithmManagement/list'),
name: 'algorithmManagement',
meta: { title: 'algorithmManagement', icon: 'suanfa', affix: true },
hidden: false
},
{
path: 'imageList',
component: () => import('@/views/fileManagement/imageManagement/list'),
name: 'imageManagement',
meta: { title: 'imageManagement', icon: 'jingxiang', affix: true },
hidden: false
},
{
path: 'modelList',
component: () => import('@/views/fileManagement/modelManagement/list'),
name: 'modelManagement',
meta: { title: 'modelManagement', icon: 'moxing', affix: true },
hidden: false
}
]
}
export default fileManagementRouter

View File

@ -2,6 +2,7 @@ import Layout from '@/layout'
const permissionRouter = {
path: '/permissionManagement',
component: Layout,
name: 'permissionManagement',
meta: {
title: 'authManagement',
icon: 'xiangmuguanli'
@ -35,13 +36,13 @@ const permissionRouter = {
component: () => import('@/views/permissionManagement/resourceList'),
name: 'resourceList',
meta: { title: 'resourceList', icon: 'cunchujuanxinxi', affix: true }
},
{
path: 'oplList',
component: () => import('@/views/permissionManagement/oplList'),
name: 'oplList',
meta: { title: 'oplList', icon: 'gongzuofuzai', affix: true }
}
// {
// path: 'oplList',
// component: () => import('@/views/permissionManagement/resourceCategoryList'),
// name: 'oplList',
// meta: { title: 'oplList', icon: 'gongzuofuzai', affix: true }
// }
]
}
export default permissionRouter

View File

@ -0,0 +1,20 @@
import Layout from '@/layout'
const personalCenterRouter = {
path: '/personalCenter',
component: Layout,
meta: {
title: 'personalCenter',
icon: 'qukuailian'
},
name: 'personalCenter',
children: [
{
path: 'pointManagement',
component: () => import('@/views/blockChain/pointManagement'),
name: 'pointManagement',
meta: { title: 'pointManagement', icon: 'gailan', affix: true },
hidden: false
}
]
}
export default personalCenterRouter

View File

@ -5,7 +5,7 @@ const taskManagementRouter = {
name: 'taskManagement',
meta: {
title: 'taskManagement',
icon: 'rongqizu'
icon: 'renwuguanli1'
},
children: [
{
@ -36,33 +36,47 @@ const taskManagementRouter = {
meta: { activeMenu: '/taskManagement/taskList' },
hidden: true
},
{
path: 'result',
component: () => import('@/views/taskManagement/taskList/result'),
name: 'result',
meta: { activeMenu: '/taskManagement/taskList' },
hidden: true
},
{
path: 'taskList',
component: () => import('@/views/taskManagement/taskList/list'),
name: 'taskList',
meta: { title: 'taskList', icon: 'gailan', affix: true },
meta: { title: 'taskList', icon: 'renwuliebiao1', affix: true },
hidden: false
},
{
path: 'cloudList',
component: () => import('@/views/taskManagement/cloudList/taskList'),
name: 'cloudList',
meta: { title: 'cloudList', icon: 'gailan', affix: true },
meta: { title: 'cloudList', icon: 'shusuanziyuan', affix: true },
hidden: false
},
{
path: 'aiList',
component: () => import('@/views/taskManagement/aiList/taskList'),
name: 'aiList',
meta: { title: 'aiList', icon: 'gailan', affix: true },
meta: { title: 'aiList', icon: 'zhisuanrenwu', affix: true },
hidden: false
},
{
path: 'hpcList',
component: () => import('@/views/taskManagement/hpcList/taskList'),
name: 'hpcList',
meta: { title: 'hpcList', icon: 'gailan', affix: true },
meta: { title: 'hpcList', icon: 'chaosuan', affix: true },
hidden: false
},
{
path: 'appCenter',
component: () => import('@/views/taskManagement/hpcList/appCenter'),
name: 'appCenter',
meta: { activeMenu: '/taskManagement/hpcList' },
hidden: true
}
]
}

View File

@ -5,14 +5,14 @@ import { asyncRoutes, constantRoutes } from '@/router'
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, menus) {
export function filterAsyncRoutes(routes, menuNames) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (menus.includes(tmp.name)) {
if (menuNames.includes(tmp.name)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, menus)
tmp.children = filterAsyncRoutes(tmp.children, menuNames)
}
res.push(tmp)
}
@ -33,13 +33,13 @@ const mutations = {
}
const actions = {
generateRoutes({ commit }, { menus, roleName }) {
generateRoutes({ commit }, { roles, menuNames }) {
return new Promise(resolve => {
let accessedRoutes = []
if (roleName === 'admin') {
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, menus)
accessedRoutes = filterAsyncRoutes(asyncRoutes, menuNames)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)

View File

@ -2,14 +2,18 @@
import {
login,
logout,
getInfo } from '@/api/authority/user'
import { getUserMenus } from '@/api/authority/menu'
import { getToken, setToken, removeToken } from '@/utils/auth'
getInfo
// , getUserMenus
} from '@/api/authority/permissionManagement'
import { getUserID } from '@/api/jcs/jcs'
import { getToken, setToken, removeToken, setBCToken } from '@/utils/auth'
import { resetRouter } from '@/router'
import { getDictByCode } from '@/api/common/setting'
const state = {
token: getToken(),
name: '',
id: '',
userID: Number(localStorage.getItem('userID')) || '',
avatar: '',
introduction: '',
roles: [],
@ -29,6 +33,12 @@ const mutations = {
SET_NAME: (state, name) => {
state.name = name
},
SET_ID: (state, id) => {
state.id = id
},
SET_USERID: (state, userID) => {
state.userID = Number(userID)
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
@ -55,9 +65,10 @@ const actions = {
const { username, password } = userInfo
return login(username, password).then(response => {
const data = response.data
const tokenStr = data.token
const tokenStr = data.tokenHead + data.token
commit('SET_TOKEN', tokenStr)
setToken(tokenStr)
setBCToken(tokenStr)
})
},
@ -75,7 +86,6 @@ const actions = {
res.forEach((e, index) => {
data[dictMap[index]] = e.data.list
})
commit('SET_DICT', data)
resolve(data)
}).catch(error => {
@ -86,15 +96,39 @@ const actions = {
// get user info
getInfo({ commit, state }) {
function collectId(arr, names = []) {
arr.forEach(({ name }) => {
if (name) {
names.push(name)
}
})
return names
}
var names = []
return new Promise((resolve, reject) => {
getInfo().then(response => {
const { data } = response
if (!data) {
reject('Verification failed, please Login again.')
}
const { roleName, username } = data
commit('SET_ROLES', [roleName])
const { roles, username, menus, id } = data
const menuNames = collectId(menus, names)
commit('SET_MENU', menuNames)
commit('SET_ROLES', roles)
commit('SET_NAME', username)
commit('SET_ID', id)
resolve({ menuNames, roles, id })
}).catch(error => {
reject(error)
})
})
},
getUserID({ commit }, id) {
return new Promise((resolve, reject) => {
getUserID({ ssoID: id }).then(response => {
const { data } = response
commit('SET_USERID', data.userID)
localStorage.setItem('userID', data.userID)
resolve(data)
}).catch(error => {
reject(error)
@ -102,35 +136,35 @@ const actions = {
})
},
// get user menus
getMenus({ commit, state }) {
function collectId(arr, names = []) {
arr.forEach(({ name, children }) => {
if (name) {
names.push(name)
}
if (children) {
collectId(children, names)
}
})
return names
}
var names = []
// // get user menus
// getMenus({ commit, state }) {
// function collectId(arr, names = []) {
// arr.forEach(({ name, children }) => {
// if (name) {
// names.push(name)
// }
// if (children) {
// collectId(children, names)
// }
// })
// return names
// }
// var names = []
return new Promise((resolve, reject) => {
getUserMenus().then(response => {
const { data } = response
if (!data) {
reject('Verification failed, please Login again.')
}
const menuNames = collectId(data, names)
commit('SET_MENU', menuNames)
resolve(menuNames)
}).catch(error => {
reject(error)
})
})
},
// return new Promise((resolve, reject) => {
// getUserMenus().then(response => {
// const { data } = response
// if (!data) {
// reject('Verification failed, please Login again.')
// }
// const menuNames = collectId(data, names)
// commit('SET_MENU', menuNames)
// resolve(menuNames)
// }).catch(error => {
// reject(error)
// })
// })
// },
setRouteType({ commit }, type) {
commit('SET_ROUTESTYPE', type)

View File

@ -21,18 +21,18 @@
> .el-tabs__header {
border-bottom: 1px solid var(--tabsHeaderBorderColor);
.el-tabs__item {
border-left: 1px solid var(--tabsHeaderBorderColor);
border-bottom: none;
border-left: none;
background: rgba(255,255,255,0.1);
margin-right: 0.2rem;
&:first-child {
border-left: none;
}
&.is-active {
border-bottom-color: var(--tabsHeaderBorderColor);
border-bottom: 0.15rem solid #3C9CFF;
}
}
.el-tabs__nav {
border: 1px solid var(--tabsHeaderBorderColor);
border-bottom: none;
border: none;
}
}
}
@ -192,3 +192,25 @@
}
}
}
.el-date-editor .el-range-input {
background-color: inherit;
}
.el-date-range-picker .el-picker-panel__body {
background: var(--selectDropdownBgC);
}
.el-picker-panel__footer {
background: var(--selectDropdownBgC);
}
.el-picker-panel, .el-picker-panel__icon-btn, .el-date-table th{
color:white
}
.el-date-table td.in-range div{
background-color: rgba(255,255,255,0.2);
}
.el-date-editor .el-range-separator{
color: white;
}
.el-date-editor .el-range-input{
color: white;
}

View File

@ -477,7 +477,8 @@ span.default {
padding-left: 40px;
}
.el-tabs--card > .el-tabs__header .el-tabs__nav {
border-radius: 0.75rem 2px 0 0;
border-radius: 0;
border: none;
}
.el-tabs__header {
margin: 0 0 1.5rem;
@ -800,16 +801,30 @@ span.default {
.el-radio-button__orig-radio:checked + .el-radio-button__inner {
background-color: rgba(60, 156, 255, 0.5);
border: none;
box-shadow: none;
-webkit-box-shadow: none;
}
.el-radio-button__inner {
.el-checkbox-button.is-checked .el-checkbox-button__inner{
background-color: rgba(60, 156, 255, 0.5);
// border: none;
box-shadow: none;
-webkit-box-shadow: none;
}
.el-radio-button__inner, .el-checkbox-button__inner {
background: rgba(255, 255, 255, 0.4);
border: none;
color: #FFFFFF;
padding: 0.75rem 1.25rem;
}
.el-radio-button:first-child .el-radio-button__inner{
.el-checkbox-group{
text-align: left;
}
.el-radio-button:first-child .el-radio-button__inner, .el-checkbox-button:first-child .el-checkbox-button__inner{
border: none;
}
.el-checkbox-button__inner{
border-left: 1px solid #dddddd;
}
.el-radio-button__inner:hover {
color: #FFFFFF;
}
@ -921,3 +936,75 @@ span.default {
.el-table--medium th, .el-table--medium td {
padding: 0.625rem 0;
}
.el-radio input[aria-hidden="true"] {
display: none !important;
}
.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner {
box-shadow: none !important;
}
.el-table .operateA{
text-decoration: none;
color: #1890ff !important;
}
.el-date-editor .el-range-input{
font-size: 0.9rem;
}
.el-date-editor .el-range__icon{
font-size: 0.9rem;
}
.el-date-editor .el-range-separator{
line-height: 2rem;
padding: 0 0.4rem;
font-size: 0.9rem;
}
.el-date-editor--datetimerange.el-input, .el-date-editor--datetimerange.el-input__inner{
width: 25rem;
}
.el-date-range-picker{
width: 40rem;
}
.el-date-table{
font-size: 0.75rem;
}
.el-date-range-picker .el-picker-panel__body {
min-width: 32rem;
}
.el-picker-panel__icon-btn{
font-size: 0.9rem;
}
.el-date-table td div {
height: 1.9rem;
padding: 0.2rem 0;
}
.el-date-table td span {
width: 1.5rem;
height: 1.5rem;
line-height: 1.5rem;
}
.tableInput{
width: 50%;
border: 1px solid gray;
border-collapse: collapse;
.el-form-item{
margin: 0!important;
}
td, th{
padding: 1em;
border-left: 1px solid gray;
}
td:first-child, th:first-child{
border-left: 0;
}
tr{
border-top: 1px solid gray;
}
tr:first-child {
border-top: 0;
}
}
.width100{
width: 100%!important;
}

64
src/utils/jcs-request.js Normal file
View File

@ -0,0 +1,64 @@
import axios from 'axios'
import {
Message } from 'element-ui'
import { getBCToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
headers: {
'Content-Type': 'application/json'
},
baseURL: process.env.NODE_ENV !== 'production' ? '/' : '/', // url = base url + request url
withCredentials: true, // send cookies when cross-domain requests
timeout: 600000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// do something before request is sent
config.headers['Authorization'] = getBCToken()
return config
},
error => {
// do something with request error
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code && res.code !== 'OK') {
Message({
message: res.message || res.msg || 'Error',
type: 'error',
duration: 5 * 1000
})
return Promise.reject(new Error(res || 'Error'))
} else {
return res
}
},
error => {
Message({
message: error?.response?.data?.message || error?.response?.data?.msg || 'Error',
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service

View File

@ -1,46 +1,18 @@
<template>
<div>
<el-card>
<Exhibition :data="exhibitionArray" />
</el-card>
<div class="blockchainBrowser">
<el-row>
<el-col :span="24">
<el-card class="blockList">
<div slot="header" class="clearfix">
<span>区块</span>
<el-button style="float: right; padding: 0" type="text" @click="toBlockList">更多</el-button>
</div>
<!-- <List
ref="multipleTable"
class="multipleTable"
:columns="columns1"
:table-list-data="blockList"
tooltip-effect="dark"
/>
-->
<div v-for="(item, index) in blockList" :key="index" class="blockListItem">
<div class="item">
<p>区块{{ item.blocknum }}</p> <div class="txcount">{{ item.txcount }}TXNS</div>
<div class="txcount">{{ item.txcount }}TXNS</div>
<span class="tips">区块哈希{{ item.blockhash }}</span>
<div class="time">{{ new Date(item.createdt).toLocaleString() }}</div>
</div>
</div>
</el-card>
<el-col :span="15">
<el-card>
<el-row>
<el-col v-for="(item, index) in numList" :key="item+index" class="num" :span="8">
<img :src="getImg('icon'+(index+1))" alt="">
<div><p> {{ item.title }}</p>{{ item.num }}</div>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-card style="height:350px" class="blockList">
<div class="money">
<div>合同交易总金额</div>
<p>{{ tradeCount }}</p>
</div>
</el-card>
</el-col>
<el-col :span="16">
<el-card style="height:350px">
<el-col :span="9">
<el-card>
<div slot="header" class="clearfix">
<span>最近7日交易量</span>
</div>
@ -49,347 +21,384 @@
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-card class="blockList" style="min-height: 380px">
<el-col :span="12">
<el-card>
<div slot="header" class="clearfix">
<span>交易</span>
<span>最新区块</span>
</div>
<div class="block">
<el-row v-for="(item,index) in blockList" :key="item.hash + index" :class="(currentBlock && item.number === currentBlock.number)? 'active' : ''">
<a @click="changeBlock(item)">
<el-col :span="4">
<img src="../../assets/images/blockchain/block.png" alt="">
</el-col>
<el-col :span="14" class="mid">
<p>区块{{ item.number }}</p><p class="node">区块哈希{{ item.hash }}</p>
</el-col>
<el-col :span="6">
<p>{{ new Date(item.timestamp).toLocaleString() }}</p>
<div class="tag">交易数{{ item.transactionsNum }}</div>
</el-col>
</a>
</el-row>
</div>
<List
ref="multipleTable"
class="multipleTable"
:columns="columns2"
:pagination="true"
tooltip-effect="dark"
:get-list-action="$Api.getTradeList"
list-key="records"
page-key="pageNum"
limit-key="pageSize"
/>
</el-card>
</el-col>
<el-col :span="8">
<el-card class="blockList" style="min-height: 380px">
<el-col :span="12">
<el-card :class="currentBlock === null ? '' : 'arrow-left'">
<div slot="header" class="clearfix">
<span>评论</span>
<span>{{ currentBlock === null ? '最新交易' : '区块'+currentBlock.number+'的交易' }}</span>
<el-button v-if="currentBlock!== null" style="float: right; padding: 0;" type="text" @click="getFiveRecord()">返回</el-button>
</div>
<List
ref="multipleTable"
class="multipleTable"
:columns="columns2"
:pagination="true"
tooltip-effect="dark"
:get-list-action="$Api.getEvaluationList"
list-key="records"
page-key="pageNum"
limit-key="pageSize"
/>
</el-card>
<div class="block">
<el-row v-for="(item,index) in transactionList" :key="item.txHash + index">
<a @click="selectTrans(item.id)">
<el-col :span="4">
<img src="../../assets/images/blockchain/transaction.png" alt="">
</el-col>
<el-col :span="8">
<el-card class="blockList" style="min-height: 380px">
<div slot="header" class="clearfix">
<span>监控</span>
<el-col :span="20">
<p class="text">交易哈希 {{ item.txHash }}</p>
<div>
<span style="font-size: 0.875rem;">{{ new Date(item.createTime).toLocaleString() }}</span>
<span class="tag">调用合约{{ item.contractName }}</span>
</div>
</el-col>
</a>
</el-row>
</div>
<List
ref="multipleTable"
class="multipleTable"
:columns="columns2"
:pagination="true"
tooltip-effect="dark"
:get-list-action="$Api.getMonitorList"
list-key="records"
page-key="pageNum"
limit-key="pageSize"
/>
</el-card>
</el-col>
</el-row>
<el-dialog :close-on-click-modal="false" width="30%" title="交易详情" class="list-detail" :visible.sync="dialogDetailVisible">
<FormData :columns="1" :data="detailData" :data-map="dataMap" />
<el-button style="margin-left: 40%;" type="primary" @click="viewCredential(detailData)">查看证书</el-button>
</el-dialog>
<el-dialog :close-on-click-modal="false" width="40%" title="交易证书" :visible.sync="dialogCertVisible">
<div class="certification">
<p v-if="certType === 'record'">兹证明<br>
申请人 {{ certificationData.buyerName }} {{ new Date(certificationData.transTime).toLocaleString() }} 通过 {{ certificationData.sellerName }} 平台提交了以下电子数据及信息
</p>
<p v-if="certType === 'evaluation'">兹证明<br>
申请人 {{ certificationData.memberName }} 通过 云际计算 平台提交了以下电子数据及信息:
</p>
<p v-if="certType === 'monitor'">兹证明<br>
记录 {{ new Date(certificationData.date).toLocaleString() }} , 生成服务监控运维数据如下:
</p>
<FormData v-if="certType === 'record'" :column="1" :data="certificationData" :data-map="certTransactionMap" />
<FormData v-if="certType === 'evaluation'" :column="1" :data="certificationData" :data-map="certEvaluationMap" />
<FormData v-if="certType === 'monitor'" :column="1" :data="certificationData" :data-map="certMonitorMap" />
<el-dialog width="50%" title="交易信息" :visible.sync="blockchainDetail">
<div>
<FormData
:columns="1"
:data="transactionData"
:data-map="transactionMap"
/>
</div>
<div slot="footer" class="dialog-footer">
<!-- <el-button @click="selectCert()">查看数据</el-button> -->
<el-button type="info" @click="blockchainDetail = false">关闭</el-button>
</div>
</el-dialog>
<!-- <DataList v-model="dialogCertVisible" :transaction-data="transactionData" /> -->
</div>
</template>
<script>
import { FormData } from '@/components/FormData'
import moment from 'moment'
import List from '@/components/list'
import Exhibition from '@/components/Exhibition'
import viewCertification from '@/mixin/viewCertification'
import * as echarts from 'echarts'
import { FormData } from '@/components/FormData'
import blockChain from '@/mixin/blockChain'
// import DataList from './components/dataList'
import { getBlockList, getBlockChain, getRecordPage, getRecordDetail, get7dayChart, getTransactions } from '@/api/blockchain/blockchain'
export default {
components: {
FormData,
Exhibition,
List
},
mixins: [viewCertification],
// DataList,
FormData },
mixins: [blockChain],
data() {
return {
exhibitionArray: [
{ name: '交易数量', src: 'blockChainBrowser-1', value: '0' },
{ name: '参与交易用户数量', src: 'blockChainBrowser-1', value: '0' },
{ name: '当前区块高度', src: 'blockChainBrowser-1', value: '0' },
{ name: '排序服务总数', src: 'blockChainBrowser-1', value: '0' },
{ name: '节点服务总数', src: 'blockChainBrowser-1', value: '0' }
],
columns1: [
{ prop: 'blocknum', label: '区块高度', formatter: (row) =>
<div class='block-name'>
<div>区块{row.blocknum}</div>
<div class='node-hash'>区块哈希: {row.blockhash}</div>
</div>
},
{ prop: 'txcount', width: '70', label: this.$t('page.status'), formatter: (row) => <div class='txcount'>{row.txcount}TXNS</div> },
{ prop: 'createdt', width: '160', label: '创建时间', formatter: (row) => new Date(row.createdt).toLocaleString() }
],
columns2: [
{ prop: 'blocknum', label: '交易ID', formatter: (row) => <div class='hash-name'>
<a onClick={() => this.getTradeDetail(row)}>交易ID</a>
<span class='tips'>交易哈希: {row.txhash}</span></div> },
{ prop: 'createdt', width: '160', label: '创建时间', formatter: (row) => new Date(row.createdt).toLocaleString() }
],
tradeCount: '0',
currentChannel: '',
blockList: [],
tradeList: [],
dialogDetailVisible: false,
detailData: {},
dataMap: {
transId: '交易ID',
ledgerName: '数据标题',
currentBlock: null,
blockchainDetail: false,
dialogCertVisible: false,
transactionData: {},
certificationData: {},
transactionMap: {
id: '交易ID',
typeName: '记录类型',
txHash: '交易哈希',
createDate: '交易时间',
blockHash: '区块哈希',
blockNum: '区块高度'
}
}
createTime: '区块时间',
blockNumber: '所在区块',
blockHash: '所在区块哈希',
from: '交易发送者',
number: '积分变动额',
contractName: '调用合约',
contractAddress: '合约地址'
},
computed: {
tradeListLength() {
return this.tradeList.length >= 5 ? 5 : this.tradeList.length
numList: [
{
title: '参与交易用户数量',
num: 0
},
{
title: '交易总量',
num: 0
},
{
title: '存证查询次数',
num: 0
},
{
title: '当前区块高度',
num: 0
},
{
title: '合约总数',
num: 0
},
{
title: '节点服务总数',
num: 0
}
],
blockList: [],
transactionList: [],
lineChartOption: {
color: ['#33D1C9'],
tooltip: {
trigger: 'axis',
backgroundColor: '#000033',
textStyle: { fontSize: this.fontSize(1.4), color: '#fff' }
},
mounted() {
this.$Api.getCurrentChannel().then(res => {
this.currentChannel = res.currentChannel
localStorage.setItem('currentChannel', this.currentChannel)
this.getBlockList()
this.getStatisticsCount()
this.getUserCount()
this.getTradeCount()
this.getChartData()
})
legend: {
data: ['交易量'],
textStyle: {
fontSize: this.fontSize(1.4),
color: '#ffffff'
},
icon: 'roundRect',
right: '25%',
itemGap: this.fontSize(2),
itemWidth: this.fontSize(2.5)
},
grid: {
left: '4%',
right: '5%',
bottom: '3%',
top: '18%',
containLabel: true
},
methods: {
getChartData() {
this.$Api.getTradeMapByDate({ days: 7 }).then(res => {
const transCount = res.map(i => i.transCount)
const transDate = res.map(i => i.transDate)
const chartOption = {
xAxis: {
type: 'category',
boundaryGap: false,
axisLine: { show: false },
axisTick: { show: false },
data: transDate
splitLine: {
show: false
},
axisLine: {
lineStyle: {
color: 'rgba(22, 208, 255, 0.3)'
}
},
axisTick: {
show: false
},
axisLabel: {
formatter: function(value) {
const arr = value.split('-')
return arr[1] + '/' + arr[2]
},
textStyle: {
color: '#96AAC4',
fontSize: this.fontSize(1.4)
}
}
},
yAxis: {
type: 'value',
axisLine: { lineStyle: { color: '#999999' }}
axisLine: {
show: false
},
series: [{
data: transCount,
symbol: 'none',
axisTick: {
show: false
},
axisLabel: {
textStyle: {
color: '#96AAC4',
fontSize: this.fontSize(1.4)
}
},
splitLine: {
show: false
}
},
series: [
{
name: '交易量',
type: 'line',
itemStyle: {
color: '#3182CE'
stack: 'Total',
smooth: true,
// lineStyle: {
// width: 0
// },
showSymbol: false,
emphasis: {
focus: 'series'
},
markPoint: {
data: [
{ type: 'max', name: 'Max' }
data: []
}
]
},
smooth: true
}]
}
}
},
watch: {
dialogCertVisible(val) {
if (val === false) {
this.getSearch()
}
}
},
mounted() {
this.$nextTick(() => {
this.getSearch()
})
},
methods: {
getSearch() {
getBlockList().then(e => {
this.blockList = e.data || []
})
this.getFiveRecord()
getBlockChain().then(e => {
this.numList[0].num = e.data?.userNum
this.numList[1].num = e.data?.recordNum
this.numList[2].num = e.data?.recordQueryNum
this.numList[3].num = e.data?.blockNum
this.numList[4].num = e.data?.contractNum
this.numList[5].num = e.data?.peerNum
})
this.searchRecord()
},
//
getFiveRecord() {
this.currentBlock = null
getRecordPage({ current: 1, size: 5 }).then(e => {
this.transactionList = e.data.records || []
})
},
//
changeBlock(item) {
this.currentBlock = item
getTransactions(item.number).then(e => {
this.transactionList = e.data.map(n => ({ ...n, txHash: n.hash, createTime: n.importTime })) || []
})
},
//
selectTrans(id) {
getRecordDetail({ id }).then(e => {
this.transactionData = { ...e.data, typeName: this.typeNameList.find(n => n.itemValue === e.data.type).itemText }
this.blockchainDetail = true
})
},
// //
// selectCert() {
// this.blockchainDetail = false
// this.dialogCertVisible = true
// },
getImg(src) {
return require('@/assets/images/blockchain/' + src + '.png')
},
fontSize(rem) {
const scale = window.innerHeight / 100
return parseInt(scale * rem)
},
// 7
searchRecord() {
const lineChart = echarts.init(this.$refs.lineChart)
lineChart.setOption(chartOption)
})
},
getTradeDetail(row) {
this.certType = row.chaincodename
this.$Api.getTradeDetail(row.txhash).then(res => {
this.detailData = res.data
this.dialogDetailVisible = true
})
},
getStatisticsCount() {
this.$Api.getStatisticsCount(this.currentChannel).then(res => {
this.exhibitionArray[0].value = res.txCount
this.exhibitionArray[2].value = res.latestBlock
this.exhibitionArray[3].value = res.chaincodeCount
this.exhibitionArray[4].value = res.peerCount
})
},
getUserCount() {
this.$Api.getUserCount().then(res => {
this.exhibitionArray[1].value = res.data
})
},
getTradeCount() {
this.$Api.getTradeCount().then(res => {
this.tradeCount = res.data
})
},
toBlockList() {
this.$router.push({ path: `/blockChain/blockList` })
},
getBlockList() {
this.$Api.getBlockList(this.currentChannel, { from: moment().subtract(1, 'months').format('YYYY-MM-DD HH:mm:ss.SSS'), to: moment().add(1, 'days').format('YYYY-MM-DD HH:mm:ss.SSS') }).then(res => {
this.blockList = res.rows.splice(0, 4)
get7dayChart().then(e => {
this.lineChartOption.xAxis.data = e.data.dateList
this.lineChartOption.series[0].data = e.data.volumeList
// this.lineChartOption.series[1].data = e.data.volumeList
const that = this
function getSuanli() {
lineChart.clear()
lineChart.setOption(that.lineChartOption)
window.onresize = function() {
lineChart.resize()
}
return getSuanli
}
getSuanli()
})
}
// getTradeList() {
// this.$Api.getTradeList('evaluation', { from: moment().subtract(1, 'months').format('YYYY-MM-DD HH:mm:ss.SSS'), to: moment().add(1, 'days').format('YYYY-MM-DD HH:mm:ss.SSS') }).then(res => {
// console.log(res.rows)
// this.tradeList = res.rows
// })
// }
}
}
</script>
<style lang="scss">
.txcount{
background: #333333;
box-shadow: 0px 3px 7px 0px rgba(51, 51, 51, 0.35);
border-radius: 2px;
font-size: 12px;
color: #FFFFFF;
padding: 0 4px;
<style lang="scss" scoped>
.blockchainBrowser{
.num{
display: flex;
overflow: hidden;
white-space: nowrap;
img{
height: 10vh;
margin-right: 0.625rem;
}
.blockList{
min-height: 200px;
.el-card__body {
padding-top: 0;
}
.money{
width: 290px;
height: 290px;
border: 30px solid var(--blockListMoney);
background: linear-gradient(90deg, #21B8AF 0%, #3182CE 100%);
box-shadow: 0px 10px 26px 5px rgb(52 72 90 / 20%);
border-radius: 50%;
text-align: center;
font-size: 16px;
font-family: PingFang SC;
font-weight: 500;
color: #FFFFFF;
padding-top: 70px;
margin: 0 auto;
margin-top: 33px;
p{
>div{
font-family: Impact;
font-size: 36px;
font-size: 1.625rem;
color: #FFFFFF;
}
}
.block-name{
}
.hash-name{
}
.blockListItem{
width: 25%;
float: left;
// margin: 10px;
padding: 10px;
.item{
border: 1px solid #eeeeee;
padding: 5px 10px;
padding-top: 15px;
position: relative;
}
box-sizing: border-box;
.txcount{
padding: 5px;
margin: 5px;
margin-top: 0;
position: absolute;
right: 8px;
top: 8px;
// float: right;
}
p{ margin-top: 0; margin-bottom: 2rem; font-size: 0.8rem;}
.time{
font-size: 0.8rem;
position: absolute;
right: 10px;
top: 40px;
}
.tips{
margin: 20px 0;
}
}
.node-hash{
color: #999999;
font-size: 12px;
}
cursor: pointer;
}
.certification{
width: 100%;
height: 750px;
background: url('../../assets/cert_bg.png') white center no-repeat;
background-size: auto 100%;
text-align: center;
padding-top: 130px;
p{
font-size: 0.9rem;
color: #9EACBE;
}
}
.block{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-height: 46vh;
.el-row{
transition: all .3s cubic-bezier(.7, .3, .1, 1);
background: rgba(255,255,255,0.1);
border-radius: 2px;
margin-bottom: 1.5vh;
padding: 0 1vh;
img{
height: 2.6vh;
margin: 2.5vh 3vh;
}
.text{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.node{
font-size: 0.875rem;
color: #AAAAAA;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.active{
background-color: #5b7595;
transition: all .3s cubic-bezier(.7, .3, .1, 1);
}
}
.tag{
background: rgba(255, 255, 255, 0.1);
border-radius: 0.75rem;
font-size: 0.8rem;
text-align: left;
width: 410px;
margin: auto;
padding: 10px 15px;
}
table.table.formData {
width: 410px;
margin: 0 auto;
font-size: 0.5rem;
td {
line-height: 0.9rem;
text-align: left;
}
td:nth-child(odd) {
width: 40%;
}
}
margin: 0 0.5rem;
padding: 0.5vh 0.7vh;
float: right;
}
#lineChart{
width: 100%;
height: 30vh;
height: 16vh;
}
}
.arrow-left{
transition: all .3s cubic-bezier(.7, .3, .1, 1);
position: relative;
overflow: visible;
&::before {
content: '';
width: 0;
height: 0;
border-top: 1rem solid transparent;
border-right: 1rem solid rgba(126,165,202,0.1);
border-bottom: 1rem solid transparent;
// border-radius: 50%;
position: absolute;
left: -1rem;
top: 5rem;
}
}
</style>

View File

@ -1,106 +0,0 @@
<template>
<div>
<el-card>
<h4>上链数据真实性与完整性查验</h4>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="证书查验" name="1" />
<el-tab-pane label="身份查验" name="2" />
</el-tabs>
<el-input v-model="content" :placeholder="`${activeName === '1' ? '请输入您想要校验的内容' : '请输入身份指纹'}`">
<el-button slot="append" icon="el-icon-search" type="primary" @click="search">校验查询</el-button>
</el-input>
<List
v-if="listData.length>=1"
ref="multipleTable"
class="multipleTable"
:columns="columns"
:table-list-data="listData"
:pagination="false"
tooltip-effect="dark"
/>
</el-card>
<el-dialog :close-on-click-modal="false" width="40%" title="交易证书" :visible.sync="dialogCertVisible">
<div class="certification">
<p>兹证明<br>
申请人 {{ certificationData.buyerName }} {{ new Date(certificationData.transTime).toLocaleString() }} 通过 {{ certificationData.sellerName }} 平台提交了以下电子数据及信息
</p>
<FormData :column="1" :data="certificationData" :data-map="certDataMap" />
</div>
</el-dialog>
</div>
</template>
<script>
import List from '@/components/list.vue'
import viewCertification from '@/mixin/viewCertification'
import { FormData } from '@/components/FormData'
export default {
components: { List, FormData },
mixins: [viewCertification],
data() {
return {
activeName: '1',
columns: [
{ prop: 'certNum', label: '证书编号' },
{ prop: 'fingerPrint', label: '身份指纹' },
{ prop: 'createDate', label: '创建时间', formatter: (row) => {
return <div>{new Date(row.createDate).toLocaleString() || '-'}</div>
} },
{ prop: 'more', label: '操作', formatter: (row) => {
return <div>
<el-button onClick={() => this.viewCredential(row)} type='text' size='small'>查看</el-button>
</div>
} }
],
listData: [],
content: ''
}
},
created() {
this.currentChannel = localStorage.getItem('currentChannel')
},
methods: {
handleClick() {
this.content = ''
this.listData = []
},
search() {
this.$Api.getCertificationByType(this.activeName === '1' ? { certId: this.content } : { fingerPrint: this.content }, this.activeName === '1' ? 'getCertById' : 'getCertByFP').then(res => {
this.listData = res
})
}
}
}
</script>
<style lang="scss">
.certification{
width: 100%;
height: 750px;
background: url('../../assets/cert_bg.png') white center no-repeat;
background-size: auto 100%;
text-align: center;
padding-top: 120px;
p{
font-size: 0.8rem;
text-align: left;
width: 370px;
margin: auto;
padding: 10px 15px;
}
table.table.formData {
width: 370px;
margin: 0 auto;
font-size: 0.5rem;
td {
line-height: 0.9rem;
text-align: left;
}
td:nth-child(odd) {
width: 40%;
}
}
}
</style>

View File

@ -1,56 +0,0 @@
<template>
<div>
<el-card class="list-detail">
<el-page-header content="区块列表(最近一个月的数据)" @back="goBack" />
<List
v-if="listData.length>=1"
ref="multipleTable"
class="multipleTable"
:columns="columns"
:table-list-data="listData"
:pagination="false"
tooltip-effect="dark"
/>
</el-card>
</div>
</template>
<script>
import List from '@/components/list.vue'
import moment from 'moment'
export default {
components: { List },
data() {
return {
filterMap: {
},
columns: [
{ prop: 'blocknum', label: '区块高度' },
{ prop: 'blockhash', label: '区块哈希' },
{ prop: 'createdt', label: '创建时间', formatter: (row) => new Date(row.createdt).toLocaleString() }
],
listData: []
}
},
created() {
this.currentChannel = localStorage.getItem('currentChannel')
this.getBlockList()
},
methods: {
goBack() {
this.$router.go(-1)
},
getBlockList() {
this.$Api.getBlockList(this.currentChannel, { from: moment().subtract(1, 'months').format('YYYY-MM-DD HH:mm:ss.SSS'), to: moment().add(1, 'days').format('YYYY-MM-DD HH:mm:ss.SSS') }).then(res => {
this.listData = res.rows
})
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,353 @@
<template>
<el-card class="blockChainLog">
<div slot="header" class="clearfix">
<span>上链数据真实性与完整性查验</span>
</div>
<el-tabs v-model="activeName" type="card" @tab-click="switchTab">
<el-tab-pane>
<span slot="label">交易数据查验</span>
<el-form v-if="activeName==='0'" ref="dataForm" :model="dataForm" :rules="dataFormRules" label-width="auto">
<el-row>
<el-col :span="16">
<el-form-item prop="nodesName" label="校验节点选择">
<el-select v-model="dataForm.nodesName" multiple style="width: 90%">
<el-option
v-for="(item, index) in nodeList"
:key="item+ index"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="16">
<el-form-item prop="id" label="交易日志编号">
<el-input v-model="dataForm.id" placeholder="请输入交易日志编号" style="width: 90%" @enter="onSearchClick('searchId')" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item>
<el-button size="medium" type="info" @click="onSearchClick('searchId')">{{ $t('message.search') }}</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<img v-if="activeName ==='0' && certificateList.length === 0" class="emptyImg" src="../../assets/images/blockchain/blockList.png" alt="">
<List
v-if="activeName==='0'"
ref="multipleTable0"
:key="$i18n.locale"
class="multipleTable"
:columns="columns0"
:height="height"
:table-list-data="certificateList"
:pagination="false"
tooltip-effect="dark"
/>
</el-tab-pane>
<el-tab-pane>
<span slot="label">交易身份查验</span>
<el-form v-if="activeName==='1'" ref="identityForm" :model="identityForm" :rules="identityFormRules" label-width="auto">
<el-row>
<el-col :span="16">
<el-form-item prop="nodesName" label="单校验节点选择">
<el-select v-model="identityForm.nodesName" style="width: 90%">
<el-option
v-for="(item, index) in nodeList"
:key="item+ index"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="16">
<el-form-item prop="accountAddress" label="提交人身份地址">
<el-input v-model="identityForm.accountAddress" placeholder="请输入提交人身份地址" style="width: 90%" @enter="onSearchClick('searchId')" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item>
<el-button size="medium" type="info" @click="onSearchClick('accountAddress')">{{ $t('message.search') }}</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<img v-if="activeName==='1' && identityList.length === 0" class="emptyImg" src="../../assets/images/blockchain/blockList.png" alt="">
<List
v-if="activeName==='1'"
ref="multipleTable1"
:key="$i18n.locale"
class="multipleTable"
:columns="columns1"
:height="height"
:table-list-data="identityList"
:pagination="false"
/>
</el-tab-pane>
<el-tab-pane>
<span slot="label">智能合约查验</span>
<el-form v-if="activeName==='2'" ref="contractForm" :model="contractForm" :rules="contractFormRules" label-width="auto">
<el-row>
<el-col :span="16">
<el-form-item prop="nodesName" label="校验节点选择">
<el-select v-model="contractForm.nodesName" multiple style="width: 90%">
<el-option
v-for="(item, index) in nodeList"
:key="item+ index"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="16">
<el-form-item prop="name" label="合约名称">
<el-input v-model="contractForm.name" style="width: 90%" placeholder="请输入合约名称" @enter="onSearchClick('searchId')" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item>
<el-button size="medium" type="info" @click="onSearchClick('contract')">{{ $t('message.search') }}</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<img v-if=" activeName==='2' && contractList.length === 0" class="emptyImg" src="../../assets/images/blockchain/blockList.png" alt="">
<List
v-if="activeName==='2'"
ref="multipleTable2"
:key="$i18n.locale"
class="multipleTable"
:columns="columns2"
:height="height"
:table-list-data="contractList"
:pagination="false"
/>
</el-tab-pane>
</el-tabs>
<DataList v-model="dialogCertVisible" :transaction-data="transactionData" />
</el-card>
</template>
<script>
import List from '@/components/list'
import DataList from './components/dataList'
import blockChain from '@/mixin/blockChain'
import { getRecordPage, getContractList } from '@/api/blockchain/blockchain'
export default {
components: { List, DataList },
mixins: [blockChain],
data() {
return {
activeName: '0',
height: Math.round(document.body.offsetHeight * 0.45).toString(),
certificateList: [],
identityList: [],
contractList: [],
dialogCertVisible: false,
certificationData: {},
transactionData: {},
searchId: '',
accountAddress: '',
contractType: [{ itemText: '存证合约', itemValue: '0' }, { itemValue: '1', itemText: '积分合约' }], //
dataForm: {
nodesName: '',
id: '1872209074928050178'
},
identityForm: {
nodesName: '',
accountAddress: '0x74d6307a016c7ed2ee8733cb07dfc4533c46c5ab'
},
contractForm: {
nodesName: '',
name: 'EvidenceStorage' // LoyaltyPoints
},
dataFormRules: {
nodesName: [{ required: true, message: '请选择节点', trigger: 'select' }],
id: [{ required: true, message: '请输入交易日志编号', trigger: 'blur' }]
},
identityFormRules: {
nodesName: [{ required: true, message: '请选择节点', trigger: 'select' }],
accountAddress: [{ required: true, message: '请输入提交人身份地址', trigger: 'blur' }]
},
contractFormRules: {
nodesName: [{ required: true, message: '请选择节点', trigger: 'select' }],
name: [{ required: true, message: '请输入合约名称', trigger: 'blur' }]
},
columns0: [
{ prop: 'id', label: '交易日志编号' },
{ prop: 'type', label: '记录类型', formatter: (row) => {
return this.typeNameList.find(n => n.itemValue === row.type).itemText
} },
{ prop: 'accountAddress', label: '提交人身份名称' },
{ prop: 'createTime', label: '提交时间', sortable: true, formatter: (row) => {
return <div>{new Date(row.createTime).toLocaleString()}</div>
} },
{ prop: 'consistent', label: '节点对比结果', formatter: (row) => {
if (row.consistent) {
return <div><svg-icon icon-class='yizhixing' style='color: #2897FF;margin: 0.2rem' />一致</div>
} else {
return <div><svg-icon icon-class='mimabuyizhi' style='color: #D66C6C;margin: 0.2rem'/>不一致</div>
}
} },
{ prop: 'more', label: '操作', width: 160, formatter: (row) => {
return <div>
<el-dropdown>
<el-button className='el-dropdown-link' size='mini' icon='el-icon-more' circle>
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => { this.selectCert(row) }}> <el-dropdown-item> {'查看数据'} </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
</div>
} }
],
columns1: [
{ prop: 'id', label: '记录日志编号' },
{ prop: 'type', label: '记录类型', formatter: (row) => {
return this.typeNameList.find(n => n.itemValue === row.type).itemText
} },
{ prop: 'accountAddress', label: '提交人身份名称' },
{ prop: 'createTime', label: '提交时间', sortable: true, formatter: (row) => {
return <div>{new Date(row.createTime).toLocaleString()}</div>
} },
{ prop: 'more', label: '操作', width: 160, formatter: (row) => {
return <div>
<el-dropdown>
<el-button className='el-dropdown-link' size='mini' icon='el-icon-more' circle>
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => { this.selectCert(row) }}> <el-dropdown-item> {'查看数据'} </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
</div>
} }
],
columns2: [
{ prop: 'id', label: '合约ID' },
{ prop: 'name', label: '合约名称' },
{ prop: 'type', label: '合约类型', formatter: (row) => {
return this.contractType.find(n => n.itemValue === row.type).itemText
} },
{ prop: 'address', label: '合约地址' },
{ prop: 'consistent', label: '节点对比结果', formatter: (row) => {
if (row.consistent) {
return <div><svg-icon icon-class='yizhixing' style='color: #2897FF;margin: 0.2rem' />一致</div>
} else {
return <div><svg-icon icon-class='mimabuyizhi' style='color: #D66C6C;margin: 0.2rem'/>不一致</div>
}
} },
{ prop: 'createTime', label: '提交时间', sortable: true, formatter: (row) => {
return <div>{new Date(row.createTime).toLocaleString()}</div>
} }
]
}
},
mounted() {
this.$nextTick(() => {
this.onSearchClick()
})
},
methods: {
resetSearch() {
this.searchForm = {
nodesName: '',
id: ''
}
},
selectCert(row) {
this.transactionData = row
this.dialogCertVisible = true
},
onSearchClick(type) {
if (type === 'searchId') {
this.$refs.dataForm.validate((valid) => {
if (valid) {
console.log(this.dataForm)
getRecordPage({ ...this.dataForm, nodesName: this.dataForm.nodesName.join(', '), current: 1, size: 99999 }).then(e => {
this.certificateList = e.data.records
})
} else {
return false
}
})
} else if (type === 'accountAddress') {
this.$refs.identityForm.validate((valid) => {
if (valid) {
getRecordPage({ ...this.identityForm }).then(e => {
this.identityList = e.data.records
})
} else {
return false
}
})
} else if (type === 'contract') {
this.$refs.contractForm.validate((valid) => {
if (valid) {
getContractList({ ...this.contractForm, nodesName: this.contractForm.nodesName.join(', ') }).then(e => {
this.contractList = e.data
})
} else {
return false
}
})
}
},
switchTab() {
}
}
}
</script>
<style lang="scss" scoped>
.blockChainLog{
.el-tab-pane{
text-align: center;
}
.search{
.el-button{
padding: 1.15rem 2.5rem !important;
margin-left: 2vh;
}
.el-input{
width: 50%;
}
}
.emptyImg{
height: 30vh;
margin: 4vh 0 16vh 0;
}
.certification {
width: 100%;
height: 72vh;
background: url('../../assets/images/blockchain/certificate.png') center no-repeat;
background-size: auto 100%;
text-align: center;
padding-top: 120px;
p {
font-size: 0.8rem;
text-align: left;
width: 370px;
margin: auto;
padding: 10px 15px;
}
table.table.formData {
width: 370px;
margin: 0 auto;
font-size: 0.5rem;
td {
line-height: 0.9rem;
text-align: left;
}
td:nth-child(odd) {
width: 40%;
}
}
}
}
</style>

View File

@ -0,0 +1,97 @@
<template>
<div class="podInfo">
<el-card class="basicInfo">
<h4>数据可信核验</h4>
<!-- <span class="tips title-tips">数据可信核验</span> -->
</el-card>
<el-card>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:filter-map="filterMap"
list-key="records"
:get-list-action="getBusinessLog"
:default-sort="{prop: 'createTime', order: 'descending'}"
:pagination="true"
page-key="current"
limit-key="size"
/>
</el-card>
<DataList v-model="dialogCertVisible" :transaction-data="transactionData" />
</div>
</template>
<script>
import List from '@/components/list'
import { getBusinessLog } from '@/api/blockchain/blockchain'
import blockChain from '@/mixin/blockChain'
import DataList from './components/dataList'
export default {
components: { List, DataList },
mixins: [blockChain],
data() {
return {
getBusinessLog,
dialogCertVisible: false,
transactionData: {}
}
},
computed: {
filterMap() {
return {
type: {
label: '业务日志类型',
type: 'select',
dataSource: this.typeNameList?.filter(n => !['0', '1'].includes(n.itemValue)).map(n => ({
label: n.itemText,
value: n.itemValue
}))
},
time: {
label: '操作时间过滤',
type: 'datetimerange'
},
userIp: {
label: '提交人IP过滤'
}
}
},
columns() {
return this.setColumn()
}
},
methods: {
selectCert(row) {
this.transactionData = { ...row, logType: row.type }
this.dialogCertVisible = true
},
setColumn() {
return [
{ prop: 'username', label: '提交人', minWidth: '8%' },
{ prop: 'type', label: '业务日志类型', minWidth: '10%', formatter: (row) => {
return <div>{this.typeNameList.find(n => n.itemValue === row.type)?.itemText }</div>
} },
{
prop: 'content', label: '日志信息', minWidth: '42%'
},
{ prop: 'createTime', label: '发生时间', minWidth: '20%', sortable: true, formatter: (row) => {
return <div>{row.createTime && new Date(row.createTime).toLocaleString() || '-'}</div>
} },
{ prop: 'userIp', label: '提交人IP', minWidth: '12%' },
{ prop: 'more', label: '操作', minWidth: '8%', formatter: (row) => {
return <div>
<a class='operateA' onClick={() => this.selectCert(row)}> 查询合约 </a>
</div>
} }
]
}
}
}
</script>
<style lang="scss">
</style>

View File

@ -0,0 +1,109 @@
<template>
<el-dialog v-if="dialogCertVisible" :close-on-click-modal="false" width="40%" title="交易证书" :visible.sync="dialogCertVisible">
<div class="certification">
<p>
兹证明<br>
申请人 {{ certificationData.accountAddress }}
{{ new Date(certificationData.createTime).toLocaleString() }} 通过
云际计算 平台提交了以下电子数据及信息
</p>
<FormData
:columns="1"
:data="certificationData.type === '2' ? certificationData.taskDTO : certificationData.fileDTO "
:data-map="certificationData.type === '2' ? taskDataMap : fileDataMap"
/>
<p> 申请人提交的源数据自申请保全之时(授时时间 {{ certificationData.createTime }}已经存在且保全之后一直数据完整未被篡改经中国科学院国家授时中心可信时间服务授时优版权进行电子数据保全信息上链公示至版权联盟链并由马栏山版权服务中心颁发保全证书任何团体及个人均可通过上述机构核验
</p>
<div class="bottom">
<div>核验单位:</div>
<div>中国科学院国家授时中心</div>
<div>版权联盟链</div>
<div>优版权</div>
<div>马栏山版权服务中心</div>
</div>
</div>
</el-dialog>
</template>
<script>
import { FormData } from '@/components/FormData'
export default {
components: { FormData },
props: {
value: {
type: Boolean,
default: false
},
certificationData: {
type: Object,
default: () => ({})
}
},
data() {
return {
taskDataMap: {
id: '任务id',
name: '任务名称',
status: '任务状态',
commitTime: '提交时间'
},
fileDataMap: {
type: '类型',
fileNo: '文件编号',
fileName: '文件名',
fileHash: '文件Hash',
createTime: '提交时间'
}
}
},
computed: {
dialogCertVisible: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
}
}
}
}
</script>
<style lang="scss" scoped>
.certification {
width: 100%;
height: 72vh;
background: url('../../../assets/images/blockchain/certificate.png') center no-repeat;
background-size: auto 100%;
text-align: left;
padding-top: 12vh;
color: black;
::v-deep table td{
color: black;
}
p {
font-size: 1rem;
text-align: left;
width: 40vh;
margin: auto;
padding: 0.9rem 1rem;
}
.bottom{
width: 40vh;
margin: auto;
}
table.table.formData {
width: 40vh;
margin: 0 auto;
font-size: 1rem;
td {
line-height: 0.9rem;
text-align: left;
}
td:nth-child(odd) {
width: 40%;
}
}
}
</style>

View File

@ -0,0 +1,237 @@
<template>
<el-dialog v-if="dialogCertVisible" :close-on-click-modal="false" width="50%" title="交易数据" :visible.sync="dialogCertVisible">
<div>
<el-tabs v-model="eventTab" type="card" @tab-click="getData">
<el-tab-pane v-for="(item,index) in nodeList" :key="item+index" :label="item" :name="item" />
</el-tabs>
<FormData
v-if="JSON.stringify(certificationData) !== '{}'"
:columns="1"
:data="['2','3','4'].includes(transactionData.logType) ? certificationData : (['5','6','7'].includes(transactionData.logType) ? certificationData.fileDTO : certificationData.taskDTO)"
:data-map="dataMap[transactionData.logType]"
/>
<!-- 234积分相关 567文件相关 89任务相关 -->
<div v-else>暂无数据</div>
<h3>积分交易</h3>
<table class="tableInput width100">
<tr>
<th />
<th> 用户 </th>
<th> 积分变动情况 </th>
</tr>
<tr>
<td>积分消费方</td>
<td>{{ jifenData && jifenData.relatedAddress || '-' }}</td>
<td>{{ jifenData && jifenData.consume || '-' }}</td>
</tr>
<tr>
<td>积分获得方</td>
<td>{{ jifenData && jifenData.accountAddress || '-' }}</td>
<td>{{ jifenData && jifenData.obtained || '-' }}</td>
</tr>
<tr>
<td>平台抽成</td>
<td>{{ jifenData && jifenData.platformAddress || '-' }}</td>
<td>{{ jifenData && jifenData.platformCommissionAmount|| '-' }}</td>
</tr>
</table>
</div>
</el-dialog>
</template>
<script>
import { FormData } from '@/components/FormData'
import blockChain from '@/mixin/blockChain'
import { getRecordQuery } from '@/api/blockchain/blockchain'
import { getPointByHash } from '@/api/blockchain/pointManagement'
export default {
components: { FormData },
mixins: [blockChain],
props: {
value: {
type: Boolean,
default: false
},
transactionData: {
type: Object,
default: () => ({})
}
},
data() {
return {
jifenData: {},
eventTab: 'node1',
jifenMap: {
relatedAddress: '积分消费方ID',
consume: '消费积分',
accountAddress: '积分获得方ID',
number: '获得积分',
platformCommissionAmount: '平台抽成'
},
certificationData: {},
dataMap: [
{
id: '合约部署人ID',
type: '部署类型',
hash: '部署哈希',
time: '部署时间'
}, // 0
{
id: '合约查询人ID',
time: '查询时间',
hash: '所查合约哈希'
}, // 1
{
typeName: '积分变动类型',
accountAddress: '注册人地址',
createTime: '注册时间'
}, // 2
{
typeName: '积分变动类型',
id: '初始化账户ID',
time: '初始化时间'
}, // 3
{
typeName: '积分变动类型',
fromAddress: '积分消费方ID',
consume: '消费积分',
accountAddress: '积分获得方ID',
number: '获得积分',
tax: '平台抽成',
createTime: '交易时间'
}, // 4
{
type: '类型:',
userID: '用户id:',
fileNo: '文件编号:',
fileName: '文件名:',
fileHash: '文件哈希:',
fileSize: '文件大小:',
objectID: '对象id',
createTime: '创建时间:'
}, // 5
{
userID: '用户id',
fileHash: '文件哈希:',
objectID: '对象id',
targetCluster: '目标集群',
createTime: '提交时间:',
integral: '积分交易:'
}, // 6
{
id: '访问者ID',
md5: '被访问数据MD5',
userid: '被访问数据ID',
time: '访问时间'
}, // 访7
{
id: '任务id',
name: '任务名称',
description: '任务描述',
status: '状态',
commitTime: '提交时间'
}, // 8
{
id: '任务id',
name: '任务名称',
description: '任务描述',
status: '状态',
commitTime: '提交时间',
startTime: '开始时间',
endTime: '结束时间',
runningTime: '运行时间',
result: '结果'
}// 9
]
}
},
computed: {
dialogCertVisible: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
}
}
},
watch: {
value(val) {
if (val === true) {
this.getData()
}
}
},
methods: {
tabSwitch() {
},
getData() {
if (['2', '3', '4'].includes(this.transactionData.logType)) {
//
getPointByHash({
txHash: this.transactionData.txHash,
nodeName: this.eventTab
}).then(e => {
this.certificationData = e.data || {}
this.jifenData = this.transactionData.logType === '2' ? this.certificationData : this.certificationData.pointsRecordDTO
if (e.data) {
this.certificationData.typeName = this.pointsList.find(item => item.itemValue === e.data.type).itemText
} else {
this.certificationData = {}
}
})
} else {
getRecordQuery({
txHash: this.transactionData.txHash,
nodeName: this.eventTab
}).then(e => {
this.certificationData = e.data || {}
this.jifenData = this.certificationData.pointsRecordDTO
if (e.data) {
this.certificationData.logType = this.transactionData.logType
}
})
}
}
}
}
</script>
<style lang="scss" scoped >
.certification {
width: 100%;
height: 72vh;
background: url('../../../assets/images/blockchain/certificate.png') center no-repeat;
background-size: auto 100%;
text-align: left;
padding-top: 12vh;
color: black;
::v-deep table td{
color: black;
}
p {
font-size: 1rem;
text-align: left;
width: 40vh;
margin: auto;
padding: 0.9rem 1rem;
}
.bottom{
width: 40vh;
margin: auto;
}
table.table.formData {
width: 40vh;
margin: 0 auto;
font-size: 1rem;
td {
line-height: 0.9rem;
text-align: left;
}
td:nth-child(odd) {
width: 40%;
}
}
}
</style>

View File

@ -0,0 +1,288 @@
<template>
<div class="blockchainBrowser">
<el-row>
<el-col :span="12">
<el-card style="position: relative;">
<div slot="header" class="clearfix">
<span>积分管理</span>
</div>
<el-dropdown class="node-select" @command="handleCommand">
<el-button type="primary">
节点选择<i class="el-icon-arrow-down el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="item in nodeList" :key="item" :command="item">{{ item }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-row style="margin: 3vh 0 3vh 0;">
<el-col class="num" :span="12">
<img src="@/assets/images/blockchain/icon1.png" alt="">
<div><p> 当前用户ID</p><div class="impact">{{ id }}</div></div>
</el-col>
<el-col class="num" :span="12">
<img src="@/assets/images/blockchain/icon2.png" alt="">
<div><p> 当前用户可用积分</p><div class="impact">{{ jifen }}</div> <div class="node"><span />{{ currentBlock }}</div></div>
</el-col>
</el-row>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<div slot="header" class="clearfix">
<span>最近7日积分变动情况</span>
</div>
<div id="lineChart" ref="lineChart" />
</el-card>
</el-col>
</el-row>
<el-row>
<el-card>
<el-row>
<List
ref="multipleTable2"
:key="$i18n.locale"
class="multipleTable"
:columns="columns2"
:get-list-action="getRecordList"
page-key="current"
limit-key="size"
list-key="records"
:pagination="true"
/>
</el-row>
</el-card>
</el-row>
<DataList v-model="dialogCertVisible" :transaction-data="transactionData" />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import * as echarts from 'echarts'
import List from '@/components/list'
import blockChain from '@/mixin/blockChain'
import DataList from './components/dataList'
import { get7dayChart, getBalance, getRecordList } from '@/api/blockchain/pointManagement'
export default {
components: { DataList, List },
mixins: [blockChain],
data() {
return {
currentBlock: null,
blockchainDetail: false,
dialogCertVisible: false,
transactionData: {},
jifen: 0,
getRecordList,
columns2: [
// { prop: 'id', label: '' },
{ prop: 'type', label: '积分变动类型', formatter: (row) => {
return this.pointsList.find(item => item.itemValue === row.type)?.itemText
} },
{ prop: 'oldData', label: '原积分' },
{ prop: 'variationValue', label: '变化数值' },
{ prop: 'currentData', label: '最终积分' },
{ prop: 'more', label: '操作', width: 160, formatter: (row) => {
return <div>
<el-button type='text' onClick={() => this.selectCert(row)}> 查询合约 </el-button>
</div>
} }
],
lineChartOption: {
color: ['#33D1C9'],
tooltip: {
trigger: 'axis',
backgroundColor: '#000033',
textStyle: { fontSize: this.fontSize(1.4), color: '#fff' }
},
legend: {
data: ['积分变动量'],
textStyle: {
fontSize: this.fontSize(1.4),
color: '#ffffff'
},
icon: 'roundRect',
right: '25%',
itemGap: this.fontSize(2),
itemWidth: this.fontSize(2.5)
},
grid: {
left: '4%',
right: '5%',
bottom: '3%',
top: '18%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
splitLine: {
show: false
},
axisLine: {
lineStyle: {
color: 'rgba(22, 208, 255, 0.3)'
}
},
axisTick: {
show: false
},
axisLabel: {
formatter: function(value) {
const arr = value.split('-')
return arr[1] + '/' + arr[2]
},
textStyle: {
color: '#96AAC4',
fontSize: this.fontSize(1.4)
}
}
},
yAxis: {
type: 'value',
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
textStyle: {
color: '#96AAC4',
fontSize: this.fontSize(1.4)
}
},
splitLine: {
show: false
}
},
series: [
{
name: '积分变动量',
type: 'line',
stack: 'Total',
smooth: true,
// lineStyle: {
// width: 0
// },
showSymbol: false,
emphasis: {
focus: 'series'
},
data: []
}
]
}
}
},
computed: {
...mapGetters([
'id'
])
},
watch: {
dialogCertVisible(val) {
if (val === false) {
this.getSearch()
}
},
currentBlock(val) {
if (val) {
getBalance({ nodeName: val }).then(res => {
this.jifen = res.data || 0
})
}
},
nodeList(val) {
if (val.length > 0) {
this.currentBlock = val[0]
}
}
},
mounted() {
this.$nextTick(() => {
this.getSearch()
})
},
methods: {
handleCommand(command) {
this.currentBlock = command
},
getSearch() {
this.searchRecord()
},
//
selectCert(row) {
this.transactionData = { ...row, logType: row.type === '1' ? '2' : ['2', '4'].includes(row.type) ? '7' : '8', txHash: row.businessTxHash }
this.blockchainDetail = false
this.dialogCertVisible = true
},
fontSize(rem) {
const scale = window.innerHeight / 100
return parseInt(scale * rem)
},
// 7
searchRecord() {
const lineChart = echarts.init(this.$refs.lineChart)
get7dayChart().then(e => {
this.lineChartOption.xAxis.data = e.data.dateList
this.lineChartOption.series[0].data = e.data.volumeList
// this.lineChartOption.series[1].data = e.data.volumeList
const that = this
function getSuanli() {
lineChart.clear()
lineChart.setOption(that.lineChartOption)
window.onresize = function() {
lineChart.resize()
}
return getSuanli
}
getSuanli()
})
}
}
}
</script>
<style lang="scss" scoped>
.blockchainBrowser{
.node-select{
position: absolute;
right: 0;
top: 4vh;
}
.num{
display: flex;
img{
height: 10vh;
margin-right: 0.625rem;
}
.impact{
font-family: Impact;
font-size: 1.625rem;
color: #FFFFFF;
}
p{
font-size: 0.9rem;
color: #9EACBE;
}
.node{
font-size: 1rem;
span{
display: inline-block;
height: 1vh;
width: 1vh;
border-radius: 10vh;
background-color: rgba(22, 208, 255, 1);
margin: 1vh 1vh 0 0;
}
}
}
#lineChart{
width: 100%;
height: 16vh;
}
}
</style>

View File

@ -0,0 +1,162 @@
<template>
<div class="podInfo">
<el-card class="basicInfo">
<Exhibition :data="exhibitionArray" :span="8" />
</el-card>
<el-card>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:table-list-data="dataList"
tooltip-effect="dark"
>
<template v-slot:filterBtns>
<el-button size="medium" type="primary" @click="handelAdd">{{ $t('message.create') }}</el-button>
</template>
</List>
</el-card>
<bind-dataset v-model="addFormVisible" data-type="code" :is-edit="isEdit" :data="updateData" @get-list="getList" />
<son-dataset v-if="addSonVisible" v-model="addSonVisible" :data="sonData" @getList="getList" />
<el-dialog :close-on-click-modal="false" title="查看子算法" :visible.sync="dialogSonListVisible" width="70%">
<p>父算法{{ fatherName }}</p>
<List
ref="sonList"
:key="$i18n.locale"
class="multipleTable"
:columns="sonColumns"
:table-list-data="sonDataList"
tooltip-effect="dark"
/>
<span v-if="!sonDataList.length">没有子算法</span>
</el-dialog>
<!-- <CreateAdapter v-model="createAdapterVisible" :is-edit="isEdit" :data="updateData" @getList="() => this.$refs.multipleTable0.getList()" />
<CreateCluster v-model="createClusterVisible" :is-edit="isEdit" :data="updateData" @getList="() => this.$refs.multipleTable1.getList()" /> -->
</div>
</template>
<script>
import List from '@/components/list'
import Exhibition from '@/components/Exhibition'
import { deleteCode, queryCodeList } from '@/api/jcs/jcs'
import bindDataset from '../datasetManagement/addForm'
import { getClusterList } from '@/api/container/cluster'
import { mapGetters } from 'vuex'
import sonDataset from './sonDataset'
export default {
components: { Exhibition, List, bindDataset, sonDataset },
data() {
return {
exhibitionArray: [
{
name: '算法个数',
src: 'algo-1',
value: '0'
}
],
isEdit: false,
// num: 0,
activeName: '0',
updateData: {},
dataList: [],
addFormVisible: false,
clusterList: [],
addSonVisible: false,
sonData: {},
dialogSonListVisible: false,
sonDataList: [],
fatherName: ''
}
},
computed: {
...mapGetters([
'userID'
]),
columns() {
return [
{ prop: 'name', label: this.$t('page.algorithmName'), formatter: (row) => row.name },
{ prop: 'description', label: this.$t('page.description'), formatter: (row) => row.description },
{ prop: 'createTime', label: this.$t('page.creationTime'), sortable: true, formatter: (row) => {
return <div>{new Date(row.createTime).toLocaleString()}</div>
} },
{ prop: 'clusters', label: this.$t('page.clusterIn'), formatter: (row) => this.getClusterData(row.clusterID) },
{ prop: 'more', label: this.$t('page.more'), width: 160, formatter: (row) => {
return <el-dropdown>
<el-button className='el-dropdown-link' size='mini' icon='el-icon-more' circle>
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => { this.getSonList(row.parentPackageID, row.name) }}> <el-dropdown-item> 查看子算法</el-dropdown-item> </span>
<span onClick={() => { this.createSon(row) }}> <el-dropdown-item>创建子算法</el-dropdown-item> </span>
<span onClick={() => { this.deleteItem(row) }}> <el-dropdown-item> {this.$t('page.delete')} </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
} }
]
},
sonColumns() {
return [
{ prop: 'name', label: this.$t('page.algorithmName'), formatter: (row) => row.name },
{ prop: 'description', label: this.$t('page.description'), formatter: (row) => row.description },
{ prop: 'createTime', label: this.$t('page.creationTime'), sortable: true, formatter: (row) => {
return <div>{new Date(row.createTime).toLocaleString()}</div>
} },
{ prop: 'clusters', label: this.$t('page.clusterIn'), formatter: (row) => this.getClusterData(row.clusterID) }
]
}
},
mounted() {
this.getList()
getClusterList({ 'storageSchedule': 1, pageSize: 1000, pageNum: 1 }).then(e => {
this.clusterList = e?.data?.list || []
})
},
methods: {
getSonList(val, name) {
this.fatherName = name
queryCodeList({ dataType: 'code', userID: this.userID, parentPackageID: val }).then(e => {
// const arr = e?.data?.datas?.[0]?.packages?.[0]?.versions || []
this.sonDataList = e?.data?.packages || []
this.dialogSonListVisible = true
})
},
getClusterData(cluster) {
return this.clusterList.find(s => s.id === cluster)?.name || '-'
},
getList() {
queryCodeList({ dataType: 'code', userID: this.userID, parentPackageID: -1 }).then(e => {
this.dataList = e?.data?.packages || []
this.exhibitionArray[0].value = e?.data?.packages?.length || 0
})
},
handelAdd() {
this.addFormVisible = true
},
createSon(row) {
this.sonData = row
this.addSonVisible = true
},
deleteItem(row) {
this.$confirm('确认删除么?', '提示', {
confirmButtonText: this.$t('message.confirm'),
cancelButtonText: this.$t('message.cancel'),
type: 'warning'
}).then(() => {
// packageIDs
deleteCode({ 'userID': this.userID,
'type': 'parent', // parent
'packageIDs': [row.parentPackageID] }).then((e) => {
if (e.code === 'OK') {
this.$message.success('删除成功')
this.getList()
}
})
}).catch(() => {
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@ -0,0 +1,412 @@
<template>
<div id="createContainer">
<el-dialog
v-if="createFormVisible"
width="70%"
:close-on-click-modal="false"
:title="'创建'+ editInfoForm.packageName +'子算法'"
:visible.sync="createFormVisible"
>
<el-form
ref="editInfoForm"
:rules="rules"
:model="editInfoForm"
label-width="auto"
label-position="left"
>
<el-form-item
prop="name"
label="子算法名称"
>
<el-input v-model="editInfoForm.name" :maxlength="63" :disabled="isEdit" />
</el-form-item>
<el-form-item
prop="description"
label="描述"
>
<el-input v-model="editInfoForm.description" :disabled="isEdit" type="textarea" :rows="5" :maxlength="200" />
</el-form-item>
<el-form-item
prop="clusterID"
:label="'选择子算法创建平台'"
>
<!-- <el-checkbox-group v-model="editInfoForm.clusterIDs" size="mini" :disabled="isEdit">
<el-col v-for="item in clusterList" :key="item.id" :span="8">
<el-checkbox :label="item.id" border>{{ item.name }}</el-checkbox>
</el-col>
</el-checkbox-group> -->
<el-radio v-for="item in clusterList" :key="item.id" v-model="editInfoForm.clusterID" :disabled="isEdit" :label="item.id">{{ item.name }}</el-radio>
</el-form-item>
<el-card>
<div class="folder-list">
<div class="fold-item">
<el-link type="info" :underline="false" @click="intoFold('/', true)">全部文件</el-link>
</div>
<div v-for="(item, key ) in path.split('/')" :key="key" class="fold-item">
<span v-if="item" class="arrow">></span>
<el-link v-if="key !== path.split('/').length-1" type="primary" @click="intoFold(item, true)">{{ item }}</el-link>
<el-link v-else type="info" :underline="false">{{ item }}</el-link>
</div>
</div>
<List
ref="multipleTable"
:key="$i18n.locale || editInfoForm.filePath"
class="multipleTable"
:columns="fileColumns"
:pagination="false"
:table-list-data="allFileList"
/>
<br>
<div class="add-pagination">
<el-pagination
background
hide-on-single-page
:current-page="page"
layout="total, prev, pager, next, jumper"
:total="total"
:page-size="pageSize"
:pager-count="5"
@current-change="pageChange"
/>
</div>
</el-card>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="createFormVisible = false">{{ $t("message.cancel") }}</el-button>
<el-button type="primary" @click="submitInfoEdit">{{ $t("message.confirm") }}</el-button>
</div>
</el-dialog>
<el-dialog v-if="codeVisible" :close-on-click-modal="false" title="自定义参数设置" :visible.sync="codeVisible">
<codemirror v-model="code" class="code-mirror" :options="cmOption" />
<div slot="footer" class="dialog-footer">
<el-button @click="closeCodeVisible">{{ $t("message.cancel") }}</el-button>
<el-button type="primary" @click="submitCode">{{ $t("message.submit") }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
// import { getDictByCode } from '@/api/common/setting'
import { getFileList, addBinding, downloadFile, addCode, uploadData } from '@/api/jcs/jcs'
import List from '@/components/list.vue'
import { getClusterList } from '@/api/container/cluster'
// import editBaseInfo from '@/mixin/editBaseInfo'
// import { getModelType } from '@/api/task/task'
import { mapGetters } from 'vuex'
export default {
components: { List },
props: {
value: {
type: Boolean,
default: false
},
data: {
type: Object,
default: () => {
return {}
}
},
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
codeVisible: false,
editInfoForm: {
clusterID: '',
name: '',
description: '',
packageID: '',
filePath: '',
packageName: ''
},
cmOption: {
theme: 'darcula',
autoCloseBrackets: true,
tabSize: 4,
styleActiveLine: true,
lineNumbers: true,
line: true,
mode: 'text/x-yaml'
},
itemList: [],
dialogSelectVisible: false,
currentList: [],
allPackageList: [],
allFileList: [],
clusterList: [],
categoryList: [],
modelTypeList: [],
chipList: [],
imageList: [],
inputVisible: false,
inputValue: '',
packageID: '',
page: 1,
total: 0,
pageSize: 10,
path: '/',
foldList: []
}
},
computed: {
...mapGetters([
'userID'
]),
createFormVisible: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
}
},
columns() {
return [
{ type: 'selection' },
{
prop: 'packageName',
label: this.$t('page.packageName')
},
{
prop: 'clusters',
label: this.$t('page.targetCluster')
},
{
prop: 'createTime',
label: this.$t('page.createTime')
}
]
},
fileColumns() {
return [
{ prop: 'name', label: '文件名', formatter: (row) => {
const img1 = require('@/assets/img/file-1.png')
return <div>
{((this.path === '/' ? '' : this.path) + row.path) === this.editInfoForm.filePath ? <i class='el-icon-s-flag' /> : ''}
{row.objectID === -1 ? <a onclick={() => this.intoFold(row.path)} ><img style='height: 1.2rem;margin-right: 0.5rem;vertical-align: text-bottom;' src={img1} /><span>{row.path.split('/')[row.path.split('/').length - 1]}</span></a> : <span>{row.path.indexOf('/') === 0 ? row.path.substring(row.path.indexOf('/') + 1) : row.path}</span> }
</div>
}
},
{
prop: 'size',
label: this.$t('page.size')
},
{
prop: 'createTime', label: this.$t('page.creationTime'), sortable: true, formatter: (row) => {
return <div>{new Date(row.createTime).toLocaleString()}</div>
} },
{ prop: 'more', label: this.$t('message.operation'), formatter: (row) => {
return row.objectID !== -1 ? <div>
<el-button disabled={((this.path === '/' ? '' : this.path) + row.path) === this.editInfoForm.filePath} onClick={() => this.selectCodeFile(row)}> {((this.path === '/' ? '' : this.path) + row.path) === this.editInfoForm.filePath ? '已选择' : (this.$t('message.select') + '入口文件')} </el-button>
<el-button onClick={() => this.downloadCode(row)}> 编辑 </el-button>
</div> : ''
} }
]
},
rules() {
return {
name: [
{ required: true, message: this.$t('message.pleaseInput') }
],
category: [
{ required: true, message: this.$t('message.pleaseChoose') }
],
clusterID: [
{ required: true, message: this.$t('message.pleaseChoose') }
],
packageIDs: [
{ required: true, message: this.$t('message.pleaseChoose') }
]
}
}
},
watch: {
'data'() {
this.editInfoForm.packageID = this.data.parentPackageID
this.editInfoForm.packageName = this.data.name
this.getCodeFileList()
}
},
mounted() {
getClusterList({ 'storageSchedule': 1, pageSize: 1000, pageNum: 1 }).then(e => {
this.clusterList = e?.data?.list || []
})
this.editInfoForm.packageID = this.data.parentPackageID
this.editInfoForm.packageName = this.data.name
this.getCodeFileList()
},
methods: {
downloadCode(row) {
this.currentFile = row
downloadFile({
userID: this.userID,
objectID: row.objectID
}).then(n => {
this.codeVisible = true
this.code = n + ''
})
},
closeCodeVisible() {
if (this.getNewVersion) {
this.selectCode(this.form.code)
}
this.codeVisible = false
this.getNewVersion = false
},
submitCode() {
const params = {
userID: this.userID,
type: 'children',
param: {
...this.editInfoForm,
parentImageID: this.data.parentImageID,
packageName: this.data.packageName,
bucketID: this.data.bucketID,
imageID: this.data.imageID
}
}
addCode(params).then(n => {
const form = new FormData()
form.append('info', JSON.stringify({
'userID': this.userID,
'packageID': n.data.newPackage?.packageID,
'loadTo': this.storageIDs,
'loadToPath': ['/']
}))
const blob = new Blob([this.code], { type: 'text/plain' })
const file = new File([blob], encodeURIComponent(this.currentFile.path), { type: 'text/plain' })
form.append(`files`, file)
uploadData(form).then(e => {
if (e.code === 'OK') {
this.$message.success('修改成功')
this.codeVisible = false
this.getNewVersion = true
}
})
})
},
handleClose(tag) {
this.editInfoForm.tags.splice(this.editInfoForm.tags.indexOf(tag), 1)
},
showInput() {
this.inputVisible = true
this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus()
})
},
handleInputConfirm() {
const inputValue = this.inputValue
if (inputValue) {
this.editInfoForm.tags.push(inputValue)
}
this.inputVisible = false
this.inputValue = ''
},
pageChange(page) {
this.page = page
this.getCodeFileList()
},
intoFold(path, flag) {
if (flag) {
this.path = this.path.substring(0, this.path.indexOf(path) + path.length)
} else {
this.path = (this.path === '/' ? '' : this.path) + path
}
this.getCodeFileList()
// this.$router.push({ path: this.$route.path, query })
},
getCodeFileList() {
const params = {
'queryParams': {
'dataType': 'code',
'userID': this.userID,
'packageID': Number(this.editInfoForm.packageID),
'path': this.path === '/' ? '' : this.path,
'CurrentPage': this.page,
'pageSize': this.pageSize,
'orderBy': 'name'
}
}
getFileList(params).then(e => {
this.total = e.data?.totalCount
const arr = e.data?.uploadedDatas[0].objects || []
arr.forEach(item => {
item.disabled = item.objectID === -1
})
this.allFileList = arr
})
},
getFile() {
const params = {
'queryParams': {
'dataType': this.dataType,
'userID': this.userID,
// "path": "/path",
'packageID': -1,
'CurrentPage': this.page,
'pageSize': this.pageSize,
'orderBy': 'name'
}
}
getFileList(params).then(e => {
this.total = e.data?.totalCount
this.allPackageList = e.data?.uploadedDatas || []
})
},
checkBeforeSubmit() {
},
addItem() {
this.currentList = []
this.dialogSelectVisible = true
},
currentChange(val) {
this.currentList = val
},
selectCodeFile(val) {
this.dialogSelectVisible = false
this.itemList = this.itemList.concat(val)
this.editInfoForm.filePath = (this.path === '/' ? '' : this.path) + val.path
},
deleteItem(item) {
this.itemList = this.itemList.filter(it => it !== item)
},
ok() {
this.dialogSelectVisible = false
this.itemList = this.itemList.concat(this.currentList)
},
submitInfoEdit() {
this.$refs.editInfoForm.validate((valid) => {
if (valid) {
const params = {
userID: this.userID,
info: {
...this.editInfoForm
}
}
if (this.dataType === 'code') {
params.info.packageID = params.info.packageIDs[0]
delete params.info.packageIDs
params.info.objectID = this.itemList[0].objectID
}
addBinding(params).then(res => {
if (res.code === 'OK') {
this.$message.success(this.$t('message.success'))
this.createFormVisible = false
this.$emit('get-list')
}
})
}
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@ -0,0 +1,556 @@
<template>
<div id="createContainer">
<el-dialog
v-if="createFormVisible"
width="70%"
:close-on-click-modal="false"
:title="(isEdit? '查看' : '绑定')+tabName[dataType]"
:visible.sync="createFormVisible"
>
<el-form
ref="editInfoForm"
:rules="rules"
:model="editInfoForm"
label-width="auto"
label-position="left"
>
<el-form-item
v-if="dataType !== 'code'"
prop="clusterIDs"
:label="'选择'+tabName[dataType]+'创建平台'"
>
<el-checkbox-group v-model="editInfoForm.clusterIDs" size="mini" :disabled="isEdit">
<el-col v-for="item in clusterList" :key="item.id" :span="8">
<el-checkbox :label="item.id" border>{{ item.name }}</el-checkbox>
</el-col>
</el-checkbox-group>
</el-form-item>
<el-form-item
prop="name"
:label="tabName[dataType]+'名称'"
>
<el-input v-model="editInfoForm.name" :maxlength="63" :disabled="isEdit" />
</el-form-item>
<el-form-item
prop="description"
:label="tabName[dataType]+'描述'"
>
<el-input v-model="editInfoForm.description" :disabled="isEdit" type="textarea" :rows="5" :maxlength="200" />
</el-form-item>
<el-form-item
v-if="dataType === 'dataset'"
prop="category"
label="数据集类别"
>
<el-radio v-for="item in categoryList" :key="item.itemValue" v-model="editInfoForm.category" :label="item.itemValue">{{ item.itemText }}</el-radio>
</el-form-item>
<el-form-item v-if="dataType === 'model'" :label="$t('page.modelType')" prop="modelType">
<el-select v-model="editInfoForm.modelType" :disabled="isEdit" style="width: 280px;margin-right: 10px;">
<el-option v-for="item in modelTypeList" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item v-if="dataType === 'model'" label="模型环境" prop="env">
<el-select v-model="editInfoForm.env" style="width: 280px;margin-right: 10px;" :disabled="isEdit">
<el-option label="Pytorch" value="pytorch" />
<el-option label="Tensorflow" value="tensorflow" />
</el-select>
</el-form-item>
<el-form-item v-if="dataType === 'model'" label="模型版本" prop="version">
<el-input v-model="editInfoForm.version" :maxlength="63" :disabled="isEdit" />
</el-form-item>
<el-form-item v-if="dataType === 'image'" label="架构选择" prop="architecture">
<el-select v-model="editInfoForm.architecture" style="width: 280px;margin-right: 10px;" :disabled="isEdit">
<el-option label="Pytorch" value="pytorch" />
<el-option label="Tensorflow" value="tensorflow" />
</el-select>
</el-form-item>
<el-form-item v-if="dataType === 'image'" label="计算类型选择" prop="resourceType ">
<el-radio v-for="item in chipList" :key="item.itemValue" v-model="editInfoForm.resourceType" :label="item.itemValue" :disabled="isEdit">{{ item.itemText }}</el-radio>
</el-form-item>
<el-form-item v-if="dataType === 'image'" label="标签" prop="tags">
<el-tag
v-for="tag in editInfoForm.tags"
:key="tag"
closable
:disabled="isEdit"
:disable-transitions="false"
@close="handleClose(tag)"
>
{{ tag }}
</el-tag>
<el-input
v-if="inputVisible&&!isEdit"
ref="saveTagInput"
v-model="inputValue"
class="input-new-tag"
size="small"
@keyup.enter.native="handleInputConfirm"
@blur="handleInputConfirm"
/>
<el-button v-else class="button-new-tag" size="small" :disabled="isEdit" @click="showInput">+</el-button>
</el-form-item>
<el-form-item v-if="dataType === 'code'" label="选择镜像" prop="imageID">
<el-select v-model="editInfoForm.imageID" style="width: 280px;margin-right: 10px;" :disabled="isEdit">
<el-option v-for="item in imageList" :key="item.info.id" :label="item.info.name" :value="item.info.id" />
</el-select>
</el-form-item>
<el-form-item
v-if="dataType !== 'code'"
prop="packageIDs"
label="绑定文件"
>
<el-card>
<table class="tableInput width100">
<tr>
<th>{{ $t('page.packageName') }}</th>
<th>{{ $t('page.targetCluster') }}</th>
<th>{{ $t('page.createTime') }}</th>
<th>{{ $t('page.more') }}</th>
</tr>
<tr v-for="(it, index) in itemList" :key="index">
<td>
<span v-if="!isEdit">{{ it.packageName }}</span>
<a v-else style="text-decoration: underline;" @click="toFileManage(it)">{{ it.packageName }}</a>
</td>
<td>
{{ it.clusters }}
</td>
<td>
{{ it.createTime }}
</td>
<td>
<el-button v-if="!isEdit" @click="deleteItem(it)">删除</el-button>
</td>
</tr>
</table>
<el-button v-if="!isEdit" @click="addItem">添加</el-button>
</el-card>
</el-form-item>
<el-form-item v-if="dataType === 'code'" label="选择package" prop="packageID">
<el-select v-model="editInfoForm.packageID" style="width: 280px;margin-right: 10px;">
<el-option v-for="item in allPackageList" :key="item.packageID" :label="item.packageName" :value="item.packageID" />
</el-select>
</el-form-item>
<el-form-item v-if="dataType === 'code'" label="选择入口文件" prop="filePath">
<el-row :gutter="20">
<el-col :span="10">
<el-input v-model="editInfoForm.filePath" placeholder="请选择文件" :readonly="true" />
</el-col>
<el-col :span="10">
<el-button :disabled="!editInfoForm.packageID" @click="addItem">选择文件</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
<div v-if="!isEdit" slot="footer" class="dialog-footer">
<el-button @click="createFormVisible = false">{{ $t("message.cancel") }}</el-button>
<el-button type="primary" @click="submitInfoEdit">{{ $t("message.confirm") }}</el-button>
</div>
</el-dialog>
<el-dialog v-if="dialogSelectVisible" title="选择文件" :visible.sync="dialogSelectVisible">
<List
v-if="dataType !== 'code'"
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:pagination="false"
:table-list-data="allPackageList"
/>
<p />
<!-- <br> -->
<div v-if="dataType === 'code'">
<div class="folder-list">
<div class="fold-item">
<el-link type="info" :underline="false" @click="intoFold('/', true)">全部文件</el-link>
</div>
<div v-for="(item, key ) in path.split('/')" :key="key" class="fold-item">
<span v-if="item" class="arrow">></span>
<el-link v-if="key !== path.split('/').length-1" type="primary" @click="intoFold(item, true)">{{ item }}</el-link>
<el-link v-else type="info" :underline="false">{{ item }}</el-link>
</div>
</div>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="fileColumns"
:pagination="false"
:table-list-data="allFileList"
/>
<br>
<div class="add-pagination">
<el-pagination
background
hide-on-single-page
:current-page="page"
layout="total, prev, pager, next, jumper"
:total="total"
:page-size="pageSize"
:pager-count="5"
@current-change="pageChange"
/>
</div>
</div>
<!-- <div slot="footer" class="dialog-footer">
<el-button icon="el-icon-close" circle @click="dialogSelectVisible = false" />
<el-button icon="el-icon-check" circle @click="ok" />
</div> -->
</el-dialog>
</div>
</template>
<script>
import { getDictByCode } from '@/api/common/setting'
import { getFileList, addBinding, getBindingList, addCode } from '@/api/jcs/jcs'
import List from '@/components/list.vue'
import { getClusterList } from '@/api/container/cluster'
// import editBaseInfo from '@/mixin/editBaseInfo'
import { getModelType } from '@/api/task/task'
import { mapGetters } from 'vuex'
export default {
components: { List },
props: {
value: {
type: Boolean,
default: false
},
dataType: {
type: String,
default: 'dataset'
},
isEdit: {
type: Boolean,
default: false
},
data: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {
tabName: {
'dataset': '数据集',
'image': '镜像',
'code': '算法',
'model': '模型'
},
editInfoForm: {
clusterIDs: [],
name: '',
category: '',
description: '',
packageIDs: [],
type: this.dataType,
resourceType: '',
architecture: '',
tags: [],
imageID: '',
filePath: '',
packageID: '',
bootstrapObjectID: ''
},
itemList: [],
dialogSelectVisible: false,
currentList: [],
allPackageList: [],
allFileList: [],
clusterList: [],
categoryList: [],
modelTypeList: [],
chipList: [],
imageList: [],
inputVisible: false,
inputValue: '',
packageID: '',
page: 1,
total: 0,
pageSize: 10,
path: '/',
foldList: []
}
},
computed: {
...mapGetters([
'userID'
]),
createFormVisible: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
}
},
columns() {
return [
{
prop: 'packageName',
label: this.$t('page.packageName')
},
{
prop: 'clusters',
label: this.$t('page.targetCluster')
},
{
prop: 'createTime',
label: this.$t('page.createTime'), formatter: (row) => {
return <div>{new Date(row.createTime).toLocaleString()}</div>
} },
{
prop: 'operation',
label: this.$t('message.operation'), formatter: (row) => {
return <div>
<el-button onClick={() => this.ok(row)} type='text' size='small'>选择</el-button>
</div>
} }
]
},
fileColumns() {
return [
{ prop: 'name', label: '文件名', formatter: (row) => {
const img1 = require('@/assets/img/file-1.png')
return <div>
{row.objectID === -1 ? <a onclick={() => this.intoFold(row.path)} ><img style='height: 1.2rem;margin-right: 0.5rem;vertical-align: text-bottom;' src={img1} /><span>{row.path.split('/')[row.path.split('/').length - 1]}</span></a> : <span>{row.path.indexOf('/') === 0 ? row.path.substring(row.path.indexOf('/') + 1) : row.path}</span> }
</div>
}
},
{
prop: 'size',
label: this.$t('page.size')
},
{
prop: 'createTime',
label: this.$t('page.createTime')
},
{ prop: 'more', label: this.$t('message.operation'), formatter: (row) => {
return row.objectID !== -1 ? <el-button onClick={() => this.selectCodeFile(row)}> {this.$t('message.select')} </el-button> : ''
} }
]
},
rules() {
return {
name: [
{ required: true, message: this.$t('message.pleaseInput') }
],
category: [
{ required: true, message: this.$t('message.pleaseChoose') }
],
clusterIDs: [
{ required: true, message: this.$t('message.pleaseChoose') }
],
packageIDs: [
{ required: true, message: this.$t('message.pleaseChoose') }
],
packageID: [
{ required: true, message: this.$t('message.pleaseChoose') }
],
imageID: [
{ required: true, message: this.$t('message.pleaseChoose') }
],
filePath: [
{ required: true, message: this.$t('message.pleaseChoose') }
]
}
}
},
watch: {
itemList: {
handler(val) {
this.editInfoForm.packageIDs = val.map(item => item.packageID)
},
deep: true
},
'editInfoForm.packageID'(val) {
if (!this.isEdit) {
this.page = 1
this.getCodeFileList()
}
},
'data'(val) {
this.editInfoForm = val.info
}
},
mounted() {
if (this.isEdit) {
console.log(this.data)
this.itemList = this.data.packages
}
getClusterList({ 'storageSchedule': 1, pageSize: 1000, pageNum: 1 }).then(e => {
this.clusterList = e?.data?.list || []
})
if (this.dataType === 'dataset') {
getDictByCode('dataset-category-dict').then(res => {
this.categoryList = res.data?.list || []
})
}
if (this.dataType === 'model') {
getModelType().then(e => {
this.modelTypeList = e.data.types
})
}
if (this.dataType === 'image') {
getDictByCode('chip-type-dict').then(res => {
this.chipList = res.data?.list || []
})
}
if (this.dataType === 'code') {
getBindingList({ dataType: 'image', param: { userID: this.userID, bindingID: -1, type: 'private' }}).then(e => {
this.imageList = e?.data?.datas || []
})
}
this.getFile() // code
},
methods: {
toFileManage(it) {
window.open(window.location.origin + '/fileManagement/fileList?packageID=' + it.packageID + '&packageName= ' + it.packageName + '&path=%2F&type=' + this.dataType, '_blank')
},
handleClose(tag) {
this.editInfoForm.tags.splice(this.editInfoForm.tags.indexOf(tag), 1)
},
showInput() {
this.inputVisible = true
this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus()
})
},
handleInputConfirm() {
const inputValue = this.inputValue
if (inputValue) {
this.editInfoForm.tags.push(inputValue)
}
this.inputVisible = false
this.inputValue = ''
},
pageChange(page) {
this.page = page
this.getCodeFileList()
},
intoFold(path, flag) {
if (flag) {
this.path = this.path.substring(0, this.path.indexOf(path) + path.length)
} else {
this.path = (this.path === '/' ? '' : this.path) + path
}
this.getCodeFileList()
// this.$router.push({ path: this.$route.path, query })
},
getCodeFileList() {
const params = {
'queryParams': {
'dataType': 'code',
'userID': this.userID,
'packageID': Number(this.editInfoForm.packageID),
'path': this.path === '/' ? '' : this.path,
'CurrentPage': this.page,
'pageSize': this.pageSize,
'orderBy': 'name'
}
}
getFileList(params).then(e => {
this.total = e.data?.totalCount
const arr = e.data?.uploadedDatas[0].objects || []
arr.forEach(item => {
item.disabled = item.objectID === -1
})
this.allFileList = arr
})
},
getFile() {
const params = {
'queryParams': {
'dataType': this.dataType,
'userID': this.userID,
// "path": "/path",
'packageID': -1,
'CurrentPage': this.page,
'pageSize': this.pageSize,
'orderBy': 'name'
}
}
getFileList(params).then(e => {
this.total = e.data?.totalCount
this.allPackageList = e.data?.uploadedDatas || []
})
},
checkBeforeSubmit() {
},
addItem() {
this.currentList = []
this.dialogSelectVisible = true
},
// currentChange(val) {
// this.currentList = val
// },
selectCodeFile(val) {
this.dialogSelectVisible = false
this.editInfoForm.bootstrapObjectID = val.objectID
this.editInfoForm.filePath = (this.path === '/' ? '' : this.path) + val.path
},
deleteItem(item) {
this.itemList = this.itemList.filter(it => it !== item)
},
ok(val) {
this.dialogSelectVisible = false
this.itemList = [val]
},
submitInfoEdit() {
this.$refs.editInfoForm.validate((valid) => {
if (valid) {
if (this.dataType === 'code') {
const { description, name, packageID, imageID } = this.editInfoForm
const image = this.imageList.find(e => e.info.id === imageID).info.imageID
const item = this.allPackageList.find(e => e.packageID === packageID)
const params = {
userID: this.userID,
type: 'parent',
param: {
description,
name,
packageID,
parentImageID: imageID,
packageName: item.packageName,
bucketID: item.bucketID,
imageID: image
}
}
addCode(params).then(res => {
if (res.code === 'OK') {
this.$message.success(this.$t('message.success'))
this.createFormVisible = false
this.$emit('get-list')
}
})
} else {
const params = {
userID: this.userID,
info: {
...this.editInfoForm
}
}
params.info.packageID = params.info.packageIDs[0]
addBinding(params).then(res => {
if (res.code === 'OK') {
this.$message.success(this.$t('message.success'))
this.createFormVisible = false
this.$emit('get-list')
}
})
}
}
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@ -0,0 +1,116 @@
<template>
<div>
<el-dialog
v-if="createFormVisible"
width="70%"
:close-on-click-modal="false"
:title="this.$t('page.applyList')"
:visible.sync="createFormVisible"
>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:table-list-data="dataList"
list-key="data"
tooltip-effect="dark"
/>
<div slot="footer" class="dialog-footer">
<el-button @click="createFormVisible = false">{{ $t("message.close") }}</el-button>
</div>
<div v-if="!dataList.length">
<p>无申请列表</p>
</div>
</el-dialog>
</div>
</template>
<script>
import List from '@/components/list.vue'
import { getApplyList, confirmApply } from '@/api/jcs/jcs'
import { mapGetters } from 'vuex'
export default {
components: { List },
props: {
value: {
type: Boolean,
default: false
},
dataType: {
type: String,
default: 'dataset'
}
},
data() {
return {
dataList: [],
statusMap: {
'pending': '申请中',
'approved': '申请通过',
'rejected': '拒绝',
'revoked': '撤回申请',
'expired': '申请过期(失效)',
'cancel': '取消申请'
}
}
},
computed: {
...mapGetters([
'userID'
]),
createFormVisible: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
}
},
columns() {
return [{ prop: 'name', label: this.$t('page.applyAccount'), formatter: (row) => row.userName || '-' },
// { prop: 'createTime', label: this.$t('page.creationTime'), sortable: true, formatter: (row) => {
// return <div>{new Date(row.info.createTime).toLocaleString()}</div>
// } },
{ prop: 'clusters', label: this.dataType === 'dataset' ? this.$t('page.applyDataset') : this.$t('page.applyModel'), formatter: (row) => row.Name },
{ prop: 'description', label: this.$t('page.description'), formatter: (row) => { return row.Content ? JSON.parse(decodeURIComponent(row.Content))?.description || '-' : '-' } },
{ prop: 'status', label: this.$t('page.status'), formatter: (row) => row?.status ? this.statusMap[row.status] : '-' },
{ prop: 'more', label: this.$t('page.more'), width: 160, formatter: (row) => {
return <div>
{row.status !== 'approved' && (
<el-button onClick={() => this.editItem(row, 'approved')} type='success' icon='el-icon-s-setting' size='small'>同意</el-button>
)}
{(row.status === 'pending' || row.status === 'approved') && (
<el-button onClick={() => this.editItem(row, 'rejected')} type='danger' icon='el-icon-s-setting' size='small'>拒绝</el-button>
)}
</div>
} }]
}
},
mounted() {
this.getList()
},
methods: {
getList() {
getApplyList({ userID: this.userID, dataType: this.dataType }).then(res => {
this.dataList = res?.data?.datas || []
})
},
editItem(row, status) {
// if (status===)
confirmApply({ approval: { ID: row.ID, status: status, reason: '' }}).then(res => {
if (res.code === 'OK') {
this.$message.success('操作成功')
this.getList()
}
})
}
}
}
</script>
<style scoped>
/* Add your styles here */
</style>

View File

@ -0,0 +1,179 @@
<template>
<div>
<el-dialog
v-if="createFormVisible"
width="70%"
:close-on-click-modal="false"
:title="this.$t('page.forApply')"
:visible.sync="createFormVisible"
>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:table-list-data="dataList"
list-key="data"
tooltip-effect="dark"
/>
<div slot="footer" class="dialog-footer">
<el-button @click="createFormVisible = false">{{ $t("message.close") }}</el-button>
</div>
<div v-if="!dataList.length">
<p>无申请列表</p>
</div>
</el-dialog>
</div>
</template>
<script>
import List from '@/components/list.vue'
import { getBindingList, applyData, cancelApply } from '@/api/jcs/jcs'
import { mapGetters } from 'vuex'
import { getDictByCode } from '@/api/common/setting'
export default {
components: { List },
props: {
value: {
type: Boolean,
default: false
},
dataType: {
type: String,
default: 'dataset'
}
},
data() {
return {
dataList: [],
statusMap: {
'pending': '申请中',
'approved': '申请通过',
'rejected': '拒绝',
'revoked': '撤回申请',
'expired': '申请过期(失效)',
'cancel': '取消申请'
},
renderMoreButtons(row) {
if (row.status) {
switch (row.status) {
case 'pending':
return (
<el-button onClick={() => this.editItem(row, 'revoked')} type='success' size='small'>
撤销访问
</el-button>
)
case 'approved':
return (
<div>
<el-button onClick={() => this.editItem(row, 'cancel')} type='danger' size='small'>
取消访问权限
</el-button>
</div>
)
case 'cancel':
return (
<div>
<el-button onClick={() => this.editItem(row, 'applyData')} type='primary' size='small'>
再次申请访问
</el-button>
</div>
)
default:
return ''
}
} else {
return (
<el-button onClick={() => this.editItem(row, 'applyData')} type='primary' size='small'>
申请访问
</el-button>
)
}
}
}
},
computed: {
...mapGetters([
'userID'
]),
createFormVisible: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
}
},
columns() {
return [{ prop: 'Name', minWidth: '15%', label: this.$t('page.' + this.dataType + 'Applied'), formatter: (row) => row.Name },
{ prop: 'Name', minWidth: '15%', label: this.$t('page.owner'), formatter: (row) => row.userName },
{ prop: 'description', minWidth: '15%', label: this.$t('page.description'), formatter: (row) => row?.info?.description || '-' },
{ prop: 'type', minWidth: '15%', label: this.$t('page.category'), formatter: (row) => this.getResourceType(row?.info?.category) || '' },
{ prop: 'status', minWidth: '15%', label: this.$t('page.status'), formatter: (row) => row.status ? this.statusMap[row.status] : '未申请使用' },
{ prop: 'more', minWidth: '25%', label: this.$t('page.more'), formatter: (row) => this.renderMoreButtons(row) }]
}
},
mounted() {
if (this.dataType === 'dataset') {
getDictByCode('dataset-category-dict').then(res => {
this.categoryList = res.data?.list || []
})
}
this.getList()
},
methods: {
getResourceType(category) {
return this.categoryList.find(item => item.itemValue === category)?.itemText || '-'
},
getList() {
const filterData = { dataType: this.dataType, param: { userID: this.userID, bindingID: -1, type: 'apply' }}
// if (param && (param.status || param.name)) {
// filterData.param.status = param?.status || ''
// filterData.param.name = param?.name || ''
// }
if (this.activeName === 'apply') {
filterData.param.status = 'appoved'
}
getBindingList(filterData).then(res => {
this.dataList = res?.data?.datas || []
if (this.dataList.length === 0) {
this.$message.info('暂无数据')
}
})
},
editItem(row, status) {
if (status === 'applyData') {
const data = {
'apply': {
'bindingID': row.ID,
'applicantID': this.userID,
'ownerID': row.ownerID,
'reason': '申请使用',
'expirationDate': '2028-02-15T14:30:00Z'
}
}
applyData(data).then(res => {
if (res.code === 'OK') {
this.$message.success('操作成功')
this.getList()
}
})
} else {
cancelApply({ userID: this.userID, bindingID: row.ID, status: status }).then(res => {
if (res.code === 'OK') {
this.$message.success('操作成功')
this.getList()
}
})
}
}
}
}
</script>
<style scoped>
/* Add your styles here */
</style>

View File

@ -0,0 +1,183 @@
<template>
<div class="podInfo">
<el-card class="basicInfo">
<Exhibition :data="exhibitionArray" :span="8" />
</el-card>
<el-card>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:table-list-data="datasetList"
list-key="data"
tooltip-effect="dark"
>
<template v-slot:filterBtns>
<el-button size="medium" type="primary" @click="forApplyVisible = true">{{ $t('page.forApply') }}</el-button>
<el-button size="medium" type="primary" @click="applyListVisible = true">{{ $t('page.applyList') }}</el-button>
<el-button size="medium" type="primary" @click="handelAdd">{{ $t('message.create') }}</el-button>
</template>
</List>
</el-card>
<bind-dataset v-model="addFormVisible" data-type="dataset" :is-edit="isEdit" :data="updateData" @get-list="getList" />
<apply-list v-if="applyListVisible" v-model="applyListVisible" />
<for-apply v-if="forApplyVisible" v-model="forApplyVisible" />
<el-dialog :close-on-click-modal="false" width="50%" title="访问权限设置" :visible.sync="dialogSettingVisible">
<el-radio-group v-model="radioValue">
<el-radio label="private">仅本用户可访问</el-radio>
<el-radio label="apply">申请后可访问</el-radio>
<el-radio label="public">公开所有可访问</el-radio>
</el-radio-group>
<el-button type="primary" @click="authSetting">确定</el-button>
</el-dialog>
</div>
</template>
<script>
import List from '@/components/list'
import Exhibition from '@/components/Exhibition'
import bindDataset from './addForm'
import { getDictByCode } from '@/api/common/setting'
import { getBindingList, deleteBinding, accessAuthSetting } from '@/api/jcs/jcs'
import applyList from './applyList'
import forApply from './canApplyList'
import { getClusterList } from '@/api/container/cluster'
import { mapGetters } from 'vuex'
export default {
components: { Exhibition, List, bindDataset, applyList, forApply },
data() {
return {
dialogSettingVisible: false,
datasetList: [],
currentID: '',
exhibitionArray: [
{
name: '数据集个数',
src: 'dataset-1',
value: '0'
},
{
name: '图片类数据集',
src: 'dataset-2',
value: '0'
},
{
name: '文本类数据集',
src: 'dataset-3',
value: '0'
}
],
isEdit: false,
// num: 0,
typeList: [this.$t('page.cloud'), this.$t('page.ai'), this.$t('page.hpc'), this.$t('page.cloud')],
activeName: '0',
updateData: {},
addFormVisible: false,
categoryList: [],
radioValue: 'private',
applyListVisible: false,
forApplyVisible: false,
clusterList: []
}
},
computed: {
...mapGetters([
'userID'
]),
columns() {
return [
{ prop: 'name', label: this.$t('page.datasetName'), formatter: (row) => { return <a onclick={() => this.handleDetail(row.ID)} >{row.info.name}</a> } },
// { prop: 'createTime', label: this.$t('page.creationTime'), sortable: true, formatter: (row) => {
// return <div>{new Date(row.info.createTime).toLocaleString()}</div>
// } },
{ prop: 'clusters', label: this.$t('page.clusterIn'), formatter: (row) => this.getClusterData(row.info.clusterIDs) },
{ prop: 'status', label: this.$t('page.status'), formatter: (row) => row.accessLevel === 'public' ? '公开' : row.accessLevel === 'apply' ? '可申请' : '私有' },
{ prop: 'type', label: this.$t('page.datasetCategory'), formatter: (row) => this.getResourceType(row.info.category) || '' },
{ prop: 'more', label: this.$t('page.more'), width: 160, formatter: (row) => {
return <div>
<el-dropdown>
<el-button className='el-dropdown-link' size='mini' icon='el-icon-more' circle>
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => { this.radioValue = row.accessLevel; this.dialogSettingVisible = true; this.currentID = row.ID }}> <el-dropdown-item> {this.$t('page.authSetting')}</el-dropdown-item> </span>
<span onClick={() => { this.deleteItem(row) }}> <el-dropdown-item> {this.$t('page.delete')} </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
</div>
} }
]
}
},
mounted() {
this.getList()
getDictByCode('dataset-category-dict').then(res => {
this.categoryList = res.data?.list || []
})
getClusterList({ 'storageSchedule': 1, pageSize: 1000, pageNum: 1 }).then(e => {
this.clusterList = e?.data?.list || []
})
},
methods: {
getClusterData(cluster) {
if (this.clusterList.length) {
return cluster?.map(e => {
return this.clusterList.find(s => s.id === e)?.name || ''
}).filter(Boolean).join(',') || '-'
} else {
return '-'
}
},
getList() {
getBindingList({ dataType: 'dataset', param: { userID: this.userID, bindingID: -1, type: 'private' }}).then(e => {
this.datasetList = e?.data?.datas || []
this.exhibitionArray[0].value = e?.data?.datas?.length || 0
this.exhibitionArray[1].value = e?.data?.datas?.filter(item => item.info.category === 'image').length || 0
this.exhibitionArray[2].value = e?.data?.datas?.filter(item => item.info.category === 'text').length || 0
})
},
getResourceType(category) {
return this.categoryList.find(item => item.itemValue === category)?.itemText || '-'
},
authSetting() {
accessAuthSetting({ ID: this.currentID, level: this.radioValue }).then(e => {
if (e.code === 'OK') {
this.$message.success('设置成功')
this.dialogSettingVisible = false
this.getList()
}
})
},
handelAdd() {
this.isEdit = false
this.addFormVisible = true
},
handleDetail(bindingID) {
this.isEdit = true
getBindingList({ dataType: 'dataset', param: { userID: this.userID, bindingID: bindingID, type: 'private' }}).then(e => {
this.updateData = e.data.datas[0]
this.addFormVisible = true
})
},
deleteItem(row) {
this.$confirm('确认删除么?', '提示', {
confirmButtonText: this.$t('message.confirm'),
cancelButtonText: this.$t('message.cancel'),
type: 'warning'
}).then(() => {
deleteBinding({ packageIDs: [row.info.packageID], bindingIDs: [row.ID] }).then((e) => {
if (e.code === 'OK') {
this.$message.success('删除成功')
this.getList()
}
})
}).catch(() => {
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@ -0,0 +1,232 @@
<template>
<div>
<el-dialog
v-if="createFormVisible"
width="70%"
:close-on-click-modal="false"
:title="this.$t('message.select') + tabName[dataType]"
:visible.sync="createFormVisible"
>
<el-tabs v-model="activeName" type="card" @tab-click="handleClick">
<el-tab-pane :label="'我的'+tabName[dataType]" name="private" />
<el-tab-pane :label="'已申请'+tabName[dataType]" name="apply" />
<el-tab-pane :label="'公开'+tabName[dataType]" name="public" />
</el-tabs>
<List
ref="multipleTable"
:key="activeName"
class="multipleTable"
:columns="columns"
:table-list-data="dataList"
:filter-map="filterMap"
list-key="data"
tooltip-effect="dark"
@get-list="getList"
/>
<!-- <div slot="footer" class="dialog-footer">
<el-button @click="createFormVisible = false">{{ $t("message.close") }}</el-button>
</div> -->
</el-dialog>
</div>
</template>
<script>
import List from '@/components/list.vue'
import { getBindingList, cancelApply, applyData } from '@/api/jcs/jcs'
import { getDictByCode } from '@/api/common/setting'
import { mapGetters } from 'vuex'
export default {
components: { List },
props: {
value: {
type: Boolean,
default: false
},
dataType: {
type: String,
default: 'dataset'
},
selectedItem: {
type: Object,
default: () => {}
}
},
data() {
return {
tabName: {
'dataset': '数据集',
'image': '镜像',
'code': '算法',
'model': '模型'
},
dataList: [],
activeName: 'private',
categoryList: [],
statusMap: {
'pending': '申请中',
'approved': '申请通过',
'rejected': '拒绝',
'revoked': '撤回申请',
'expired': '申请过期(失效)',
'cancel': '取消申请'
},
renderMoreButtons(row) {
if (row.status) {
switch (row.status) {
case 'pending':
return (
<el-button onClick={() => this.editItem(row, 'revoked')} type='success' size='small'>
撤销访问
</el-button>
)
case 'approved':
return (
<div>
{/* <el-button onClick={() => this.editItem(row, 'cancel')} type='danger' size='small'>
取消访问权限
</el-button> */}
<el-button onClick={() => this.selectItem(row)} type='success' size='small'>
选择
</el-button>
</div>
)
case 'cancel':
return (
<div>
<el-button onClick={() => this.editItem(row, 'applyData')} type='primary' size='small'>
再次申请访问
</el-button>
</div>
)
default:
return ''
}
} else {
return (
<el-button onClick={() => this.editItem(row, 'applyData')} type='primary' size='small'>
申请访问
</el-button>
)
}
}
}
},
computed: {
...mapGetters([
'userID'
]),
createFormVisible: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
}
},
filterMap() {
return this.activeName !== 'apply' ? {
name: {
label: this.$t('page.name')
}
} : {
name: {
label: this.$t('page.name')
}
// status: {
// label: '',
// type: 'select',
// dataSource: Object.keys(this.statusMap).map(key => {
// return {
// label: this.statusMap[key],
// value: key
// }
// })
// }
}
},
columns() {
return this.activeName !== 'apply' ? [{ prop: 'name', label: this.$t('page.' + this.dataType + 'Name'), formatter: (row) => row.info?.name },
{ prop: 'description', label: this.$t('page.description'), formatter: (row) => row?.info?.description || '-' },
{ prop: 'type', label: this.$t('page.category'), formatter: (row) => this.getResourceType(row?.info?.category) || '' },
{ prop: 'more', label: this.$t('page.more'), width: 160, formatter: (row) => {
return <div>
<el-button onClick={() => this.selectItem(row)} type='success' size='small'>选择</el-button>
</div>
} }] : [{ prop: 'Name', minWidth: '15%', label: this.$t('page.' + this.dataType + 'Applied'), formatter: (row) => row.Name },
{ prop: 'Name', minWidth: '15%', label: this.$t('page.owner'), formatter: (row) => row.userName },
{ prop: 'description', minWidth: '15%', label: this.$t('page.description'), formatter: (row) => row?.info?.description || '-' },
{ prop: 'type', minWidth: '15%', label: this.$t('page.category'), formatter: (row) => this.getResourceType(row?.info?.category) || '' },
{ prop: 'status', minWidth: '15%', label: this.$t('page.status'), formatter: (row) => row.status ? this.statusMap[row.status] : '未申请使用' },
{ prop: 'more', minWidth: '25%', label: this.$t('page.more'), formatter: (row) => this.renderMoreButtons(row) }]
}
},
mounted() {
getDictByCode('dataset-category-dict').then(res => {
this.categoryList = res.data?.list || []
})
this.getList()
},
methods: {
handleClick() {
// reset
this.getList()
},
getResourceType(category) {
return this.categoryList.find(item => item.itemValue === category)?.itemText || '-'
},
getList(param) {
const filterData = { dataType: this.dataType, param: { userID: this.userID, bindingID: -1, type: this.activeName }}
if (param && (param.status || param.name)) {
filterData.param.status = param?.status || ''
filterData.param.name = param?.name || ''
}
if (this.activeName === 'apply') {
filterData.param.status = 'appoved'
}
getBindingList(filterData).then(res => {
this.dataList = res?.data?.datas || []
if (this.dataList.length === 0) {
this.$message.info('暂无数据')
}
})
},
selectItem(row) {
console.log('select')
this.$emit('select-item', row)
},
editItem(row, status) {
if (status === 'applyData') {
const data = {
'apply': {
'bindingID': row.ID,
'applicantID': 1,
'ownerID': row.ownerID,
'reason': '申请使用',
'expirationDate': '2025-02-15T14:30:00Z'
}
}
applyData(data).then(res => {
if (res.code === 'OK') {
this.$message.success('操作成功')
this.getList()
}
})
} else {
cancelApply({ userID: this.userID, bindingID: row.ID, status: status }).then(res => {
if (res.code === 'OK') {
this.$message.success('操作成功')
this.getList()
}
})
}
}
}
}
</script>
<style scoped>
/* Add your styles here */
</style>

View File

@ -0,0 +1,535 @@
<template>
<div class="UsabilityMonitor">
<el-card class="list-detail">
<el-page-header content="文件列表" @back="$router.go(-1)" />
<br>
<el-row>
<div class="select-option">
<el-button v-if="$route.query.type === 'code'" class="opr-btn" size="middle" type="primary" @click="uploadType = 'online';dialogCreateVisible=true">在线上传文件</el-button>
<el-button v-if="storageIDs.length" class="opr-btn" size="middle" type="primary" @click="uploadType = 'local';dialogCreateVisible=true">本地上传文件</el-button>
<el-button class="opr-btn" size="middle" @click="createFold">创建目录</el-button>
</div>
<br>
<div class="folder-list">
<div class="fold-item">
<el-link type="info" :underline="false" @click="intoFold('/', true)">全部文件</el-link>
</div>
<div v-for="(item, key ) in foldList" :key="key" class="fold-item">
<span v-if="item" class="arrow">></span>
<el-link v-if="key !== foldList.length-1" type="primary" @click="intoFold(item, true)">{{ item }}</el-link>
<el-link v-else type="info" :underline="false">{{ item }}</el-link>
</div>
</div>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:table-list-data="allFileList"
:pagination="false"
tooltip-effect="dark"
/>
<br>
<div class="add-pagination">
<el-pagination
background
hide-on-single-page
:current-page="page"
layout="total, prev, pager, next, jumper"
:total="total"
:page-size="pageSize"
:pager-count="5"
@current-change="currentChange"
/>
</div>
<el-dialog v-if="dialogCreateVisible" :visible.sync="dialogCreateVisible" :title="(uploadType === 'online' ? '在线' : '本地') +'上传'" width="70%" @close="resetData">
<div class="upload-area">
<el-form>
<el-card v-if="uploadType !== 'online'" border>
<el-upload
ref="upload"
class="upload-demo"
:file-list="fileList"
:auto-upload="false"
multiple
action="#"
>
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
</el-upload>
</el-card>
<el-form-item v-if="uploadType === 'online'" label="在线文件地址填写" prop="url">
<el-input
v-model="formData.url"
:max-length="200"
/>
</el-form-item>
<div v-if="loading">
<img style="height: 25vh;" src="../../../assets/images/loading.gif">
<div style="margin-top: -7vh;">上传中</div>
</div>
</el-form>
<!-- <el-divider /> -->
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="resetData">取消</el-button>
<el-button type="primary" :disabled="submitLoading" @click="submitUpload">确认</el-button>
</div>
</el-dialog>
</el-row>
</el-card>
</div>
</template>
<script>
import List from '@/components/list'
import clip from '@/utils/clipboard'
import { numberToStr } from '@/utils/data-process'
// import deletePng from '@/assets/images/exhibition/statusMonitor-2.png'
import { getToken } from '@/utils/auth'
// import { getAvailableClusterList } from '@/api/fileManagement/index'
// import { getDictItems, getDictByCode } from '@/api/common/setting'
import { getUploadData, uploadData, getFileList, createFolder, deleteFolder, deleteFile } from '@/api/jcs/jcs'
import { mapGetters } from 'vuex'
export default {
components: {
List
},
data() {
return {
foldList: [],
formData: {
url: '',
bias: [],
region: [],
chip: [],
selectedCluster: []
},
page: 1,
total: 0,
pageSize: 10,
uploadType: '',
regionList: [],
chipList: [],
biasList: [],
isScheduled: 'yes',
canOnline: false,
dialogCreateVisible: false,
dialogDetailVisible: false,
submitLoading: false,
token: getToken(),
// clusterPath: {},
exhibitionArray: [
{
name: '文件个数(个)',
src: 'usabilityMonitor-1',
value: '0'
},
{
name: '文件总大小GB)',
src: 'usabilityMonitor-2',
value: '0'
}
],
columns: [
// { type: 'selection' },
{ prop: 'name', label: '文件名', formatter: (row) => {
const img1 = require('@/assets/img/file-1.png')
return <div>
{row.objectID === -1 ? <a onclick={() => this.intoFold(row.path)} ><img style='height: 1.2rem;margin-right: 0.5rem;vertical-align: text-bottom;' src={img1} /><span>{row.path.split('/')[row.path.split('/').length - 1]}</span></a> : <span>{row.path.indexOf('/') === 0 ? row.path.substring(row.path.indexOf('/') + 1) : row.path}</span> }
</div>
}
},
{ prop: 'createTime', label: '创建时间', formatter: (row) => { return <span>{row.objectID === -1 ? '-' : row.createTime}</span> } },
{ prop: 'size', label: '大小', formatter: (row) => { return <span>{row.objectID !== -1 ? numberToStr(row.size - 0, 'B') : '-'}</span> } },
{ prop: '', label: '操作', formatter: (row) => {
// const options = { width: 120 }
return <div class='opr-group'>
<svg-icon icon-class='shanchu' onClick={() => this.deleteItem(row)} />
</div>
} }
],
tapTableData0: [],
tapTableData1: [],
diskfileList: [],
fileList: [],
clusterList: [],
loading: false,
allFileList: [],
storageIDs: [],
clusterIDs: []
}
},
computed: {
...mapGetters([
'userID'
])
},
watch: {
'$route.query'(newValue, oldValue) {
this.foldList = this.$route.query.path.substring(1).split('/') || []
this.getList()
}
},
mounted() {
if (!this.$route.query.packageID) {
this.foldList = this.$route.query.path.substring(1).split('/') || []
this.$router.push({ path: '/fileManagement/packageList' })
}
this.getList()
},
methods: {
getList() {
const params = {
'queryParams': {
'dataType': this.$route.query.type,
'userID': this.userID,
// 'clustersIDs': [],
'packageID': Number(this.$route.query.packageID),
'path': this.$route.query.path === '/' ? '' : this.$route.query.path,
'CurrentPage': this.page,
'pageSize': this.pageSize,
'orderBy': 'name'
}
}
getFileList(params).then(e => {
this.total = e.data?.totalCount
this.allFileList = e.data?.uploadedDatas[0].objects || []
this.storageIDs = e.data?.uploadedDatas[0].uploadedCluster.map(e => e.storageID).filter(Boolean)
this.clusterIDs = e.data?.uploadedDatas[0].uploadedCluster.map(e => e.clusterID).filter(Boolean)
this.canOnline = this.storageIDs.length === 0 && this.clusterIDs.length !== 0
})
},
currentChange(page) {
this.page = page
this.getList()
},
intoFold(path, flag) {
const query = JSON.parse(JSON.stringify(this.$route.query))
if (flag) {
query.path = this.$route.query.path.substring(0, this.$route.query.path.indexOf(path) + path.length)
} else {
query.path = (query.path === '/' ? '' : query.path) + path
}
this.$router.push({ path: this.$route.path, query })
},
createFold() {
this.$prompt('请输入文件夹名称', '新建文件夹', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(({ value }) => {
const pr = {
userID: this.userID,
packageID: Number(this.$route.query.packageID),
path: this.$route.query.path + (this.$route.query.path !== '/' ? '/' : '') + value
}
createFolder(pr).then(e => {
if (e.code === 'OK') {
this.$message.success('创建成功')
this.getList()
}
})
})
},
handleUploadSuccess() {
this.$message.success('文件上传成功')
this.dialogCreateVisible = false
this.fileList = []
this.getList()
},
handleUploadError() {
this.$message.warning('上传失败')
},
handleCopy(text, event) {
clip(text, event)
},
resetData() {
this.formData = {
url: '',
bias: [],
region: [],
chip: [],
selectedCluster: []
}
this.dialogCreateVisible = false
this.fileList = []
this.getList()
},
deleteItem(item) {
this.$confirm('<p>文件/文件夹删除后将不可恢复,是否确认删除?</p>', '温馨提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确认',
center: true,
cancelButtonText: '取消'
}).then(() => {
item.objectID === -1 ? deleteFolder({ packageID: Number(this.$route.query.packageID), userID: this.userID, path: (this.$route.query.path !== '/' ? this.$route.query.path : '') + item.path }).then(e => {
if (e.code === 'OK') {
this.$message.success('删除成功')
this.getList()
}
}) : deleteFile({ objectIDs: [item.objectID], userID: this.userID }).then(e => {
if (e.code === 'OK') {
this.$message.success('删除成功')
this.getList()
}
})
})
},
submitUpload() {
this.submitLoading = true
if (this.uploadType === 'local') {
const form = new FormData()
form.append('info', JSON.stringify({
'userID': this.userID,
'packageID': Number(this.$route.query.packageID),
'loadTo': this.storageIDs,
'loadToPath': Array(this.storageIDs.length).fill('/' + this.$route.query.type + '/' + this.$route.query.packageName + this.$route.query.path + (this.$route.query.path !== '/' ? '/' : ''))
}))
this.$refs.upload.uploadFiles.forEach((file, index) => {
const newFile = new File([file.raw], encodeURIComponent(this.$route.query.path + (this.$route.query.path !== '/' ? '/' : '') + file.raw.name))
form.append(`files`, newFile)
})
this.loading = true
uploadData(form).then(e => {
if (e.code === 'OK') {
this.submitNotify(e.data.uploadeds.map(e => e.objectID) || [])
} else {
this.submitLoading = false
this.loading = false
}
}).catch(() => {
this.submitLoading = false
this.loading = false
})
} else {
this.submitNotify()
}
},
submitNotify(objectID) {
const FormDatas = {
'userID': this.userID,
'packageID': Number(this.$route.query.packageID),
'uploadParams': {
'dataType': this.$route.query.type
}
}
if (this.uploadType !== 'local') {
FormDatas.uploadParams.uploadInfo = {
'type': 'url',
'url': this.formData.url,
'clusterID': this.clusterIDs,
'packageID': Number(this.$route.query.packageID)
}
const arr = this.formData.url.split('/')
FormDatas.uploadParams.uploadInfo.dataName = arr[arr.length - 1].split('.')[0]
} else {
FormDatas.uploadParams.uploadInfo = {
'type': 'local',
'localPath': this.$route.query.path + (this.$route.query.path !== '/' ? '/' : '') + this.$refs.upload.uploadFiles[0].name, // localPath arr?
'objectIDs': objectID
}
}
getUploadData(FormDatas).then(e => {
if (e.code === 'OK') {
this.$message.success('上传成功!')
this.submitLoading = false
this.loading = false
this.resetData()
} else {
this.submitLoading = false
this.loading = false
}
}).catch(() => {
this.submitLoading = false
this.loading = false
})
},
getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
let fileResult = ''
reader.readAsDataURL(file)
reader.onload = () => {
fileResult = reader.result
}
reader.onerror = (error) => {
reject(error)
}
reader.onloadend = () => {
resolve(fileResult)
}
})
},
getCount() {
// getFileCount().then(e => {
// this.exhibitionArray[0].value = e.data?.totalFiles
// this.exhibitionArray[1].value = numberToStr(e.data?.totalSize - 0, 'B')
// })
},
// getList() {
// getFileList({ }).then(e => {
// this.diskfileList = e.lists
// this.total = e.total
// })
// },
deleteFunc(id) {
},
handleClick() {
if (this.activeName === '1') {
this.form = { env: 'python' }
}
},
transBase64(code) {
return Buffer.from(code).toString('base64')
},
getResult() {
// this.dynamicData();
}
}
}
</script>
<style lang="scss">
.folder-list{
.fold-item{
float: left;
.arrow{
margin: 0 0.5rem;
}
}
}
.progress{
position: relative;
width: 100%;
height: 10px;
margin: 25px 0;
border-radius: 10px;
overflow: hidden;
background-color: #ccdcff;
}
.progress::before{
position: absolute;
content: '';
width: 0%;
height: 100%;
left: 0;
background: #3182CE;
}
.progress::before{
animation: progress 10s infinite;
}
@keyframes progress {
to {
width: 100%
}
}
.select-option{
padding: 20px;
color: var(--buttonDivC);
background: var(--listBtnColor);
}
.opr-group{
color:#999999;
svg{
margin-right: 37px;
cursor: pointer
}
.svg-icon{width:20px; height: 20px;}
}
.file-breadcrumb{
padding: 17px 32px;
.el-breadcrumb__item:last-child .el-breadcrumb__inner, .el-breadcrumb__item:last-child .el-breadcrumb__inner:hover, .el-breadcrumb__item:last-child .el-breadcrumb__inner a, .el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover{
font-size: 16px!important;
color: var(--buttonDivC);
font-weight: 500!important;
}
.floatRight{
text-align: right;
float: right;
font-size: 16px!important;
color: var(--buttonDivC);
font-weight: 500!important;
}
}
.el-popper .qrcode-popover{
padding: 12px;
text-align: center;
p{
margin-top: 0;
text-align: left;
}
.qrcode{
margin: 24px auto;
margin-top: 40px;
width: 140px;
padding: 10px;
border: 1px solid #eeeeee;
border-radius: 10px;
}
>span{
display: block;
text-align: center;
font-size: 12px;
margin-bottom: 24px;
color: #999999;
}
.el-button{
margin-bottom: 16px;
}
}
.el-divider{
background-color: #EEEEEE;
}
.upload-area{
text-align: center;
p{
text-align: left;
color: var(--buttonDivC);
font-size: 12px;
margin-bottom: 8px;
}
.el-row--flex.is-justify-space-between{
margin-top: 20px;
}
.upload-file{
text-align: right;
}
.el-upload__tip{
float: left;
color: #999999;
font-size: 12px;
margin-top: 12px;
}
.el-upload{
.el-button{
float: right;
}
}
.el-upload-list__item-name{
text-align: left;
color: #ffffff;
}
.el-radio{
width: 100%;
}
}
.red{
color: #D9001B;
}
.blue {
color: #3182CE;
}
.tap-name{
height: 48px;
line-height: 48px;
background: #3182CE;
border-radius: 2px;
margin-bottom: 24px;
padding: 0 27px;
color: #ffffff;
}
.add-pagination{
margin-bottom: 40px;
}
</style>

View File

@ -1,423 +0,0 @@
<template>
<div class="UsabilityMonitor">
<el-card>
<Exhibition :data="exhibitionArray" :span="8" />
</el-card>
<el-row>
<el-card>
<el-row>
<el-tabs v-model="activeName" type="card" @tab-click="handleClick">
<el-tab-pane label="数据集" name="0" />
<el-tab-pane label="镜像" name="1" />
<el-tab-pane label="算法" name="2" />
<el-tab-pane label="模型" name="3" />
</el-tabs>
<div class="select-option">
<el-button class="opr-btn" size="middle" type="primary" @click="form={runEnv: 'python3'};dialogCreateVisible=true">上传文件</el-button>
<!-- <el-button class="opr-btn" size="middle" @click="downloadList">下载任务列表</el-button> -->
</div>
<br>
<List
ref="multipleTable"
:key="$i18n.locale && activeName"
class="multipleTable"
:columns="columns"
:get-list-action="getFileList"
:default-filter-data="{'label': activeName}"
:pagination="true"
page-key="pageNum"
limit-key="pageSize"
tooltip-effect="dark"
/>
<!-- <div class="add-pagination">
<el-pagination
background
:hide-on-single-page="false"
:current-page="page"
layout="total, prev, pager, next, jumper"
:total="total"
@current-change="currentChange"
/>
</div> -->
<el-dialog :visible.sync="dialogCreateVisible" title="上传文件选择" width="50%" @close="cancelUpload">
<div class="upload-area">
<p>选择集群</p>
<el-row :gutter="20">
<el-checkbox-group v-model="selectedCluster" size="mini">
<el-col v-for="item in clusterList" :key="item.id" :span="8">
<el-checkbox :label="item.id" border>{{ item.name }}</el-checkbox>
<p />
<el-input v-model="clusterPath[item.id]" placeholder="存储路径" />
</el-col>
</el-checkbox-group>
</el-row>
<p style="margin-top: 30px">本地上传</p>
<el-upload
ref="upload"
class="upload-demo"
:file-list="fileList"
:auto-upload="false"
action="#"
:multiple="true"
>
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
<div slot="tip" class="el-upload__tip">单个文件支持最大上传2G</div>
</el-upload>
<!-- <el-divider /> -->
<el-button type="info" :disabled="submitLoading" @click="submitUpload">确认</el-button>
<el-button @click="cancelUpload">取消</el-button>
</div>
</el-dialog>
<el-dialog :visible.sync="dialogDetailVisible" title="存储文件详情" width="80%">
<div>
<!-- <div class="select-option" /> -->
<div class="tap-name">
存储文件名称
{{ tapTableData0.length > 0 ? tapTableData0[0].name : '' }}
<!-- 条带名/t1efne231_0 -->
</div>
<List
ref="tapTable0"
class="tapTable"
:columns="tapTableColumns"
:table-list-data="tapTableData0"
:pagination="false"
/>
<!-- <div class="add-pagination">
<el-pagination
background
:hide-on-single-page="false"
:current-page="1"
layout="total, prev, pager, next, jumper"
:total="10"
/>
</div> -->
</div>
</el-dialog>
</el-row>
</el-card>
</el-row>
</div>
</template>
<script>
import List from '@/components/list'
import Exhibition from '@/components/Exhibition'
import clip from '@/utils/clipboard'
import { numberToStr } from '@/utils/data-process'
// import deletePng from '@/assets/images/exhibition/statusMonitor-2.png'
import { getToken } from '@/utils/auth'
import { getFileList, getFileCount, getAvailableClusterList, uploadFile, deleteFile } from '@/api/fileManagement/index'
export default {
components: {
List,
Exhibition
},
data() {
return {
getFileList,
location: window.location.origin,
activeName: '0',
dialogCreateVisible: false,
dialogDetailVisible: false,
submitLoading: false,
radio1: '1',
token: getToken(),
selectedCluster: [],
clusterPath: {},
exhibitionArray: [
{
name: '文件个数',
src: 'usabilityMonitor-1',
value: '0'
},
{
name: '文件总大小',
src: 'usabilityMonitor-2',
value: '0'
}
],
columns: [
{ type: 'selection' },
{ prop: 'name', label: '文件名', formatter: (row) => { return <span>{row.name}</span> } },
{ prop: 'createdAt', label: '上传时间' },
{ prop: 'size', label: '大小', formatter: (row) => { return <span>{numberToStr(row.size - 0, 'B')}</span> } },
{ prop: 'cluster', label: '所在集群', formatter: (row) => {
const dat = row.clusters.map(e => e.name)
return <span>{
dat.join(',')
}</span>
} },
{ prop: '', label: '操作', formatter: (row) => {
// const options = { width: 120 }
return <div class='opr-group'>
<svg-icon icon-class='shanchu' onClick={() => this.deleteFile(row.id)} />
</div>
} }
],
tapTableColumns: [
{ type: 'index', label: '序号' },
{ prop: 'clusterName', label: '集群/虚拟机' },
{ prop: 'size', label: '占用大小' },
{ prop: 'ip', label: 'IP' },
{ prop: 'address', label: '地点' },
// { prop: 'status', label: '', formatter: (row) => { return <span class={row.status === '' ? 'red' : 'nlue'}>{row.status}</span> } }
{ prop: 'status', label: '状态', formatter: (row) => <span class='nlue'>正常</span> } // wangqi
],
tapTableData0: [],
tapTableData1: [],
diskfileList: [],
fileList: [],
clusterList: []
}
},
mounted() {
// this.getList()
this.getCount()
getAvailableClusterList().then(e => {
this.clusterList = e.data
})
},
methods: {
currentChange(page) {
this.page = page
this.getList()
},
handleUploadSuccess() {
this.$message.success('文件上传成功')
this.dialogCreateVisible = false
this.fileList = []
this.getList()
},
handleUploadError() {
this.$message.warning('上传失败')
},
handleCopy(text, event) {
clip(text, event)
},
cancelUpload() {
this.dialogCreateVisible = false
this.selectedCluster = []
this.clusterPath = {}
},
deleteFile(id) {
this.$confirm('<img src=""><p>文件删除后将不可恢复,是否确认删除?</p>', '温馨提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确认',
center: true,
cancelButtonText: '取消'
}).then(() => {
deleteFile(id).then(e => {
this.$message.success('删除成功')
this.$refs.multipleTable.getList()
})
})
},
downloadFile(row) {
const a = document.createElement('a')
a.href = row.url
a.download = row.name //
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
},
addFolder() {},
downloadList() {},
viewDetail(id) {
this.$Api.getFileInfoById(id).then(res => {
this.tapTableData0 = [
{
name: res.data.name,
clusterName: res.data.clusterName,
size: res.data.size,
ip: res.data.ip,
address: res.data.city,
status: res.data.status === 1 ? '正常' : '异常'
}
]
this.dialogDetailVisible = true
})
},
submitUpload() {
const form = new FormData()
this.$refs.upload.uploadFiles.forEach((file, index) => {
form.append(`file`, file.raw)
})
const obj = {}
this.selectedCluster.forEach(e => {
obj[e] = this.clusterPath[e] || ''
})
form.set('clusters', JSON.stringify(obj))
form.set('label', this.activeName)
this.submitLoading = true
uploadFile(form).then(e => {
if (e.code === 200) {
this.$message.success('文件上传成功')
this.$refs.multipleTable.getList()
this.dialogCreateVisible = false
this.submitLoading = false
} else {
this.submitLoading = false
}
}).catch(() => {
this.submitLoading = false
})
},
getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
let fileResult = ''
reader.readAsDataURL(file)
reader.onload = () => {
fileResult = reader.result
}
reader.onerror = (error) => {
reject(error)
}
reader.onloadend = () => {
resolve(fileResult)
}
})
},
getCount() {
getFileCount().then(e => {
this.exhibitionArray[0].value = e.data?.totalFiles
this.exhibitionArray[1].value = numberToStr(e.data?.totalSize - 0, 'B')
})
},
// getList() {
// getFileList({ }).then(e => {
// this.diskfileList = e.lists
// this.total = e.total
// })
// },
deleteFunc(id) {
},
handleClick() {
if (this.activeName === '1') {
this.form = { env: 'python' }
}
},
transBase64(code) {
return Buffer.from(code).toString('base64')
},
getResult() {
// this.dynamicData();
}
}
}
</script>
<style lang="scss">
.select-option{
padding: 20px;
color: var(--buttonDivC);
background: var(--listBtnColor);
}
.opr-group{
color:#999999;
svg{
margin-right: 37px;
cursor: pointer
}
.svg-icon{width:20px; height: 20px;}
}
.file-breadcrumb{
padding: 17px 32px;
.el-breadcrumb__item:last-child .el-breadcrumb__inner, .el-breadcrumb__item:last-child .el-breadcrumb__inner:hover, .el-breadcrumb__item:last-child .el-breadcrumb__inner a, .el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover{
font-size: 16px!important;
color: var(--buttonDivC);
font-weight: 500!important;
}
.floatRight{
text-align: right;
float: right;
font-size: 16px!important;
color: var(--buttonDivC);
font-weight: 500!important;
}
}
.el-popper .qrcode-popover{
padding: 12px;
text-align: center;
p{
margin-top: 0;
text-align: left;
}
.qrcode{
margin: 24px auto;
margin-top: 40px;
width: 140px;
padding: 10px;
border: 1px solid #eeeeee;
border-radius: 10px;
}
>span{
display: block;
text-align: center;
font-size: 12px;
margin-bottom: 24px;
color: #999999;
}
.el-button{
margin-bottom: 16px;
}
}
.el-divider{
background-color: #EEEEEE;
}
.upload-area{
text-align: center;
p{
text-align: left;
color: var(--buttonDivC);
font-size: 12px;
margin-bottom: 8px;
}
.el-row--flex.is-justify-space-between{
margin-top: 20px;
}
.upload-file{
text-align: right;
}
.el-upload__tip{
float: left;
color: #999999;
font-size: 12px;
margin-top: 12px;
}
.el-upload{
.el-button{
float: right;
}
}
.el-upload-list__item-name{
text-align: left;
}
.el-radio{
width: 100%;
}
>.el-button{
margin: 100px 18px 10px 18px;
}
}
.red{
color: #D9001B;
}
.blue {
color: #3182CE;
}
.tap-name{
height: 48px;
line-height: 48px;
background: #3182CE;
border-radius: 2px;
margin-bottom: 24px;
padding: 0 27px;
color: #ffffff;
}
.add-pagination{
margin-bottom: 40px;
}
</style>

View File

@ -0,0 +1,676 @@
<template>
<div class="UsabilityMonitor">
<el-card>
<Exhibition :data="exhibitionArray" :span="8" />
</el-card>
<el-row>
<el-card>
<el-row>
<el-tabs v-model="activeName" type="card" @tab-click="handleClick">
<el-tab-pane label="数据集" name="dataset" />
<el-tab-pane label="镜像" name="image" />
<el-tab-pane label="算法" name="code" />
<el-tab-pane label="模型" name="model" />
</el-tabs>
<div class="select-option">
<el-button class="opr-btn" size="middle" @click="createPackage">创建Package</el-button>
</div>
<!-- <List
ref="multipleTable"
:key="$i18n.locale && activeName"
class="multipleTable"
:columns="columns"
:table-list-data="allFileList"
:pagination="false"
tooltip-effect="dark"
/> -->
<el-row :gutter="20">
<el-col v-for="(item,index) in allFileList" :key="index" :span="6">
<el-card class="functionCard">
<p class="title">{{ item.packageName }}</p>
<p>{{ item.uploadPriority.type === 'specify' ? '指定集群模式' : '智能调度模式' }}</p>
<p v-if="item.uploadPriority.type === 'specify'">亲和集群{{ getClusterData(item.uploadedCluster) }}</p>
<p v-else>优先选择
<span v-for="it in item.uploadPriority.priorities" :key="it.type">
<el-tag
v-for="tag in it.options"
:key="tag"
type="info"
>{{ tag }}</el-tag>
</span>
</p>
<div class="btn-group">
<el-popconfirm
confirm-button-text="好的"
cancel-button-text="不用了"
icon="el-icon-info"
icon-color="red"
title="确认删除此Package么"
@onConfirm="deleteItem(item.packageID)"
>
<el-button slot="reference" style="margin-right:10px">删除</el-button>
</el-popconfirm>
<el-button
type="primary"
@click="toFileList(item)"
>打开</el-button>
</div>
</el-card>
</el-col>
</el-row>
<br>
<div class="add-pagination">
<el-pagination
background
hide-on-single-page
:current-page="page"
layout="total, prev, pager, next, jumper"
:total="total"
:page-size="pageSize"
:pager-count="5"
@current-change="currentChange"
/>
</div>
<el-dialog :visible.sync="dialogCreateVisible" :title="'创建' + tabName[activeName] + 'Package'" width="70%" @close="resetData">
<div class="upload-area">
<el-form ref="formData" :model="formData" :rules="rules">
<el-form-item label="Packge名称" prop="name">
<el-input
v-model="formData.name"
:max-length="200"
/>
</el-form-item>
<el-tabs v-model="isScheduled" @tab-click="handleClick">
<el-tab-pane label="智能调度模式" name="yes" />
<el-tab-pane label="集群指定模式" name="no" />
</el-tabs>
<div v-if="isScheduled === 'yes'">
<p>根据您所选择的标签偏好系统将为您把文件调度至合适的存储服务</p>
</div>
<el-card v-if="isScheduled === 'yes'">
<el-form-item label="优先选择" prop="bias">
<el-checkbox-group v-model="formData.bias">
<el-checkbox-button v-for="item in biasList" :key="item.itemValue" :label="item.itemText">{{ item.itemText }}</el-checkbox-button>
</el-checkbox-group>
</el-form-item>
<el-form-item label="地域选择" prop="region">
<el-checkbox-group v-model="formData.region">
<el-checkbox-button v-for="item in regionList" :key="item.itemValue" :label="item.itemText">{{ item.itemText }}</el-checkbox-button>
</el-checkbox-group>
</el-form-item>
<el-form-item label="计算类型选择" prop="chip">
<el-checkbox-group v-model="formData.chip">
<el-checkbox-button v-for="item in chipList" :key="item.itemValue" :label="item.itemText">{{ item.itemText }}</el-checkbox-button>
</el-checkbox-group>
</el-form-item>
</el-card>
<el-card v-if="isScheduled === 'no'">
<el-row :gutter="20">
<el-form-item label="集群选择" prop="selectedCluster">
<el-card>
<el-checkbox-group v-model="formData.selectedCluster" size="mini">
<el-col v-for="item in clusterList" :key="item.id" :span="8">
<el-checkbox :label="item.id" border>{{ item.name }}</el-checkbox>
<!-- <el-input v-if="uploadType === 'online'" v-model="clusterPath[item.id]" placeholder="在线url填写" /> -->
</el-col>
</el-checkbox-group>
</el-card>
</el-form-item>
</el-row>
</el-card>
</el-form>
<!-- <el-divider /> -->
<div v-if="loading" class="progress" />
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="resetData">取消</el-button>
<el-button type="primary" :disabled="submitLoading" @click="submitUpload">确认</el-button>
</div>
</el-dialog>
<el-dialog :visible.sync="dialogDetailVisible" title="存储文件详情" width="80%">
<div>
<!-- <div class="select-option" /> -->
<div class="tap-name">
存储文件名称
{{ tapTableData0.length > 0 ? tapTableData0[0].name : '' }}
<!-- 条带名/t1efne231_0 -->
</div>
<List
ref="tapTable0"
class="tapTable"
:columns="tapTableColumns"
:table-list-data="tapTableData0"
:pagination="false"
/>
<!-- <div class="add-pagination">
<el-pagination
background
:hide-on-single-page="false"
:current-page="1"
layout="total, prev, pager, next, jumper"
:total="10"
/>
</div> -->
</div>
</el-dialog>
</el-row>
</el-card>
</el-row>
</div>
</template>
<script>
import List from '@/components/list'
import Exhibition from '@/components/Exhibition'
import clip from '@/utils/clipboard'
// import { numberToStr } from '@/utils/data-process'
// import deletePng from '@/assets/images/exhibition/statusMonitor-2.png'
import { getToken } from '@/utils/auth'
// import { getAvailableClusterList } from '@/api/fileManagement/index'
import { getClusterList } from '@/api/container/cluster'
import { mapGetters } from 'vuex'
import { getDictItems, getDictByCode } from '@/api/common/setting'
import { createPackage, getFileList, deletePackage } from '@/api/jcs/jcs'
export default {
components: {
List,
Exhibition
},
data() {
return {
location: window.location.origin,
activeName: 'dataset',
tabName: {
'dataset': '数据集',
'image': '镜像',
'code': '算法',
'model': '模型'
},
currentFold: -1,
foldList: [{
id: -1,
name: '全部文件'
}],
formData: {
name: '',
bias: [],
region: [],
chip: [],
selectedCluster: []
},
page: 1,
total: 0,
pageSize: 10,
uploadType: '',
regionList: [],
chipList: [],
biasList: [],
isScheduled: 'yes',
dialogCreateVisible: false,
dialogDetailVisible: false,
submitLoading: false,
token: getToken(),
// clusterPath: {},
exhibitionArray: [
{
name: '文件个数(个)',
src: 'usabilityMonitor-1',
value: '0'
},
{
name: '文件总大小GB)',
src: 'usabilityMonitor-2',
value: '0'
}
],
tapTableColumns: [
{ type: 'index', label: '序号' },
{ prop: 'clusterName', label: '集群/虚拟机' },
{ prop: 'size', label: '占用大小' },
{ prop: 'ip', label: 'IP' },
{ prop: 'address', label: '地点' },
// { prop: 'status', label: '', formatter: (row) => { return <span class={row.status === '' ? 'red' : 'nlue'}>{row.status}</span> } }
{ prop: 'status', label: '状态', formatter: (row) => <span class='nlue'>正常</span> } // wangqi
],
tapTableData0: [],
tapTableData1: [],
diskfileList: [],
fileList: [],
clusterList: [],
loading: false,
allFileList: []
}
},
computed: {
...mapGetters([
'userID'
]),
rules() {
return {
name: [
{ required: true, message: this.$t('message.pleaseInput') }
]
}
}
},
watch: {
activeName(newValue, oldValue) {
this.getList()
}
},
mounted() {
// this.getList()
this.getCount()
getClusterList({ 'storageSchedule': 1, pageSize: 1000, pageNum: 1 }).then(e => {
this.clusterList = e?.data?.list || []
})
getDictByCode('preference-type-dict').then(res => {
this.biasList = res.data?.list || []
})
getDictItems({ dictId: 1, pageNum: 1, pageSize: 999 }).then(res => {
this.regionList = res.data?.list || []
})
getDictByCode('chip-type-dict').then(res => {
this.chipList = res.data?.list || []
})
this.getList()
},
methods: {
toFileList(item) {
this.$router.push({ path: '/fileManagement/fileList', query: { packageID: item.packageID, packageName: item.packageName, path: '/', type: this.activeName }})
},
getList() {
const params = {
'queryParams': {
'dataType': this.activeName,
'userID': this.userID,
// "path": "/path",
'packageID': -1,
'CurrentPage': this.page,
'pageSize': this.pageSize,
'orderBy': 'name'
}
}
getFileList(params).then(e => {
this.total = e.data?.totalCount
this.allFileList = e.data?.uploadedDatas || []
})
},
getClusterData(cluster) {
if (this.clusterList.length) {
return cluster.map(e => {
return this.clusterList.find(s => s.id === e.clusterID)?.name || ''
}).filter(Boolean).join(',') || '-'
} else {
return '-'
}
},
currentChange(page) {
this.page = page
this.getList()
},
intoFold(foldId, foldName, flag) {
this.currentFold = foldId
if (flag) {
this.foldList = this.foldList.slice(0, this.foldList.findIndex(ar => ar.id === foldId))
}
this.foldList.push({ id: foldId, name: foldName })
this.getList()
},
createPackage() {
this.dialogCreateVisible = true
},
handleUploadSuccess() {
this.$message.success('文件上传成功')
this.dialogCreateVisible = false
this.fileList = []
this.getList()
},
handleUploadError() {
this.$message.warning('上传失败')
},
handleCopy(text, event) {
clip(text, event)
},
resetData() {
this.formData = {
name: '',
bias: [],
region: [],
chip: [],
selectedCluster: []
}
this.dialogCreateVisible = false
this.submitLoading = false
this.getList()
},
deleteItem(id) {
this.$confirm('<img src=""><p>Package删除后将不可恢复是否确认删除</p>', '温馨提示', {
dangerouslyUseHTMLString: true,
confirmButtonText: '确认',
center: true,
cancelButtonText: '取消'
}).then(() => {
deletePackage({ userID: this.userID, packageID: id }).then(e => {
if (e.code === 'OK') {
this.$message.success('删除成功')
this.getList()
}
})
})
},
downloadFile(row) {
const a = document.createElement('a')
a.href = row.url
a.download = row.name //
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
},
downloadList() {},
viewDetail(id) {
this.$Api.getFileInfoById(id).then(res => {
this.tapTableData0 = [
{
name: res.data.name,
clusterName: res.data.clusterName,
size: res.data.size,
ip: res.data.ip,
address: res.data.city,
status: res.data.status === 1 ? '正常' : '异常'
}
]
this.dialogDetailVisible = true
})
},
submitUpload() {
this.$refs.formData.validate((valid) => {
if (valid) {
this.submitLoading = true
const FormDatas = {
'userID': this.userID,
'name': this.formData.name,
'dataType': this.activeName
}
if (this.isScheduled === 'yes') {
FormDatas.uploadPriority = {
'type': 'preference',
'priorities': [
{
'type': 'region',
'options': this.formData.region
},
{
'type': 'chip',
'options': this.formData.chip
},
{
'type': 'bias',
'options': this.formData.bias
}
]
}
} else {
FormDatas.uploadPriority = {
'type': 'specify',
'clusters': this.formData.selectedCluster
}
}
createPackage(FormDatas).then(e => {
if (e.code === 'OK') {
this.getList()
this.resetData()
this.dialogCreateVisible = false
} else {
this.submitLoading = false
}
}).catch(() => {
this.submitLoading = false
})
}
})
},
getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
let fileResult = ''
reader.readAsDataURL(file)
reader.onload = () => {
fileResult = reader.result
}
reader.onerror = (error) => {
reject(error)
}
reader.onloadend = () => {
resolve(fileResult)
}
})
},
getCount() {
// getFileCount().then(e => {
// this.exhibitionArray[0].value = e.data?.totalFiles
// this.exhibitionArray[1].value = numberToStr(e.data?.totalSize - 0, 'B')
// })
},
// getList() {
// getFileList({ }).then(e => {
// this.diskfileList = e.lists
// this.total = e.total
// })
// },
deleteFunc(id) {
},
handleClick() {
if (this.activeName === '1') {
this.form = { env: 'python' }
}
},
transBase64(code) {
return Buffer.from(code).toString('base64')
},
getResult() {
// this.dynamicData();
}
}
}
</script>
<style lang="scss">
.folder-list{
.fold-item{
float: left;
.arrow{
margin: 0 0.5rem;
}
}
}
.progress{
position: relative;
width: 100%;
height: 10px;
margin: 25px 0;
border-radius: 10px;
overflow: hidden;
background-color: #ccdcff;
}
.progress::before{
position: absolute;
content: '';
width: 0%;
height: 100%;
left: 0;
background: #3182CE;
}
.progress::before{
animation: progress 10s infinite;
}
@keyframes progress {
to {
width: 100%
}
}
.select-option{
padding: 20px;
color: var(--buttonDivC);
background: var(--listBtnColor);
}
.opr-group{
color:#999999;
svg{
margin-right: 37px;
cursor: pointer
}
.svg-icon{width:20px; height: 20px;}
}
.file-breadcrumb{
padding: 17px 32px;
.el-breadcrumb__item:last-child .el-breadcrumb__inner, .el-breadcrumb__item:last-child .el-breadcrumb__inner:hover, .el-breadcrumb__item:last-child .el-breadcrumb__inner a, .el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover{
font-size: 16px!important;
color: var(--buttonDivC);
font-weight: 500!important;
}
.floatRight{
text-align: right;
float: right;
font-size: 16px!important;
color: var(--buttonDivC);
font-weight: 500!important;
}
}
.el-popper .qrcode-popover{
padding: 12px;
text-align: center;
p{
margin-top: 0;
text-align: left;
}
.qrcode{
margin: 24px auto;
margin-top: 40px;
width: 140px;
padding: 10px;
border: 1px solid #eeeeee;
border-radius: 10px;
}
>span{
display: block;
text-align: center;
font-size: 12px;
margin-bottom: 24px;
color: #999999;
}
.el-button{
margin-bottom: 16px;
}
}
.el-divider{
background-color: #EEEEEE;
}
.upload-area{
text-align: center;
p{
text-align: left;
color: var(--buttonDivC);
font-size: 12px;
margin-bottom: 8px;
}
.el-row--flex.is-justify-space-between{
margin-top: 20px;
}
.upload-file{
text-align: right;
}
.el-upload__tip{
float: left;
color: #999999;
font-size: 12px;
margin-top: 12px;
}
.el-upload{
.el-button{
float: right;
}
}
.el-upload-list__item-name{
text-align: left;
color: #ffffff;
}
.el-radio{
width: 100%;
}
}
.red{
color: #D9001B;
}
.blue {
color: #3182CE;
}
.tap-name{
height: 48px;
line-height: 48px;
background: #3182CE;
border-radius: 2px;
margin-bottom: 24px;
padding: 0 27px;
color: #ffffff;
}
.add-pagination{
margin-bottom: 40px;
}
.functionCard{
margin: 0px auto;
margin-top: 20px;
.el-card__body{
padding:0;
padding-bottom: 20px;
text-align: center;
}
p{
margin: 10px 30px;
font-size: 14px;
text-align: left;
position: relative;
text-indent: 1em;
}
p:before{
content: '';
position: absolute;
width: 8px;
height: 8px;
background: #2FB4AA;
border-radius: 50%;
top: 5px;
left: 0em;
}
.title{
margin:20px 27px;
// margin-bottom: 10px;
font-size: 18px;
text-indent: 0;
font-weight: bold;
}
.title:before{
width: 0;
height: 0;
}
.el-button--text{
line-height: 1.1rem;
}
.el-button{
margin:10px;
border:1px solid #468EFC ;
border-radius: 0;
}
.el-button--default{
color: #468EFC;
}
.insideFloatRight {
margin: 0;
padding:0;
float: right;
}
}
</style>

View File

@ -0,0 +1,144 @@
<template>
<div class="podInfo">
<el-card class="basicInfo">
<Exhibition :data="exhibitionArray" :span="8" />
</el-card>
<el-card>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:table-list-data="dataList"
list-key="data"
tooltip-effect="dark"
>
<template v-slot:filterBtns>
<!-- <el-button size="medium" type="primary" @click="handelAdd">{{ $t('message.create') }}</el-button> -->
</template>
</List>
</el-card>
<bind-dataset v-model="addFormVisible" data-type="image" :is-edit="isEdit" :data="updateData" @get-list="getList" />
<el-dialog v-if="dialogCertVisible" :close-on-click-modal="false" width="50%" title="镜像信息" :visible.sync="dialogCertVisible">
<FormData
v-if="JSON.stringify(imageData) !== '{}'"
:columns="1"
:data="imageData"
:data-map="imageMap"
/>
</el-dialog>
</div>
</template>
<script>
import { FormData } from '@/components/FormData'
import List from '@/components/list'
import Exhibition from '@/components/Exhibition'
import { getBindingList, deleteBinding, queryImages } from '@/api/jcs/jcs'
import bindDataset from '../datasetManagement/addForm'
import { getClusterList } from '@/api/container/cluster'
import { mapGetters } from 'vuex'
export default {
components: { Exhibition, List, bindDataset, FormData },
data() {
return {
imageData: {},
imageMap: {
id: 'id',
name: '名称',
clusterID: '集群ID',
clusterImageID: '镜像ID',
cardType: '卡类型'
},
dialogCertVisible: false,
exhibitionArray: [
{
name: '镜像个数',
src: 'image-1',
value: '0'
}
],
isEdit: false,
dataList: [],
updateData: {},
addFormVisible: false,
clusterList: []
}
},
computed: {
...mapGetters([
'userID'
]),
columns() {
return [
{ prop: 'name', label: this.$t('page.imageName'), formatter: (row) => row.info.name },
{ prop: 'createTime', label: this.$t('page.creationTime'), sortable: true, formatter: (row) => {
return <div>{new Date(row.createTime).toLocaleString()}</div>
} },
{ prop: 'clusters', label: this.$t('page.clusterIn'), formatter: (row) => this.getClusterData(row.info.clusterIDs) },
{ prop: 'more', label: this.$t('page.more'), width: 160, formatter: (row) => {
return <el-dropdown>
<el-button className='el-dropdown-link' size='mini' icon='el-icon-more' circle>
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => { this.getDetail(row.ID) }}> <el-dropdown-item> {this.$t('message.detail')}</el-dropdown-item> </span>
<span onClick={() => { this.deleteItem(row) }}> <el-dropdown-item> {this.$t('page.delete')} </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
} }
]
}
},
mounted() {
this.getList()
getClusterList({ 'storageSchedule': 1, pageSize: 1000, pageNum: 1 }).then(e => {
this.clusterList = e?.data?.list || []
})
},
methods: {
getDetail(id) {
queryImages({ ids: [id] }).then(e => {
this.dialogCertVisible = true
this.imageData = e.data?.images?.[0] || {}
})
},
getClusterData(cluster) {
if (this.clusterList.length) {
return cluster?.map(e => {
return this.clusterList.find(s => s.id === e)?.name || ''
}).filter(Boolean).join(',') || '-'
} else {
return '-'
}
},
getList() {
getBindingList({ dataType: 'image', param: { userID: this.userID, bindingID: -1, type: 'private' }}).then(e => {
this.dataList = e?.data?.datas || []
this.exhibitionArray[0].value = e?.data?.datas?.length || 0
})
},
handelAdd() {
this.addFormVisible = true
},
deleteItem(row) {
this.$confirm('确认删除么?', '提示', {
confirmButtonText: this.$t('message.confirm'),
cancelButtonText: this.$t('message.cancel'),
type: 'warning'
}).then(() => {
deleteBinding({ packageIDs: [row.info.packageID], bindingIDs: [row.ID] }).then((e) => {
if (e.code === 'OK') {
this.$message.success('删除成功')
this.getList()
}
})
}).catch(() => {
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@ -0,0 +1,146 @@
<template>
<div class="podInfo">
<el-card class="basicInfo">
<Exhibition :data="exhibitionArray" :span="8" />
</el-card>
<el-card>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:table-list-data="dataList"
list-key="data"
tooltip-effect="dark"
>
<template v-slot:filterBtns>
<el-button size="medium" type="primary" @click="applyListVisible = true">{{ $t('page.applyList') }}</el-button>
<el-button size="medium" type="primary" @click="handelAdd">{{ $t('message.create') }}</el-button>
</template>
</List>
</el-card>
<bind-dataset v-model="addFormVisible" data-type="model" :is-edit="isEdit" :data="updateData" @get-list="getList" />
<apply-list v-model="applyListVisible" data-type="model" />
<el-dialog :close-on-click-modal="false" width="50%" title="访问权限设置" :visible.sync="dialogSettingVisible">
<el-radio-group v-model="radioValue">
<el-radio label="private">仅本用户可访问</el-radio>
<el-radio label="apply">申请后可访问</el-radio>
<el-radio label="public">公开所有可访问</el-radio>
</el-radio-group>
<el-button type="primary" @click="authSetting">确定</el-button>
</el-dialog>
</div>
</template>
<script>
import List from '@/components/list'
import Exhibition from '@/components/Exhibition'
import { getBindingList, deleteBinding, accessAuthSetting } from '@/api/jcs/jcs'
import bindDataset from '../datasetManagement/addForm'
import { getClusterList } from '@/api/container/cluster'
import applyList from '../datasetManagement/applyList'
import { mapGetters } from 'vuex'
export default {
components: { Exhibition, List, bindDataset, applyList },
data() {
return {
exhibitionArray: [
{
name: '模型个数',
src: 'model-1',
value: '0'
}
],
dialogSettingVisible: false,
currentID: '',
isEdit: false,
dataList: [],
updateData: {},
addFormVisible: false,
clusterList: [],
radioValue: 'private',
applyListVisible: false
}
},
computed: {
...mapGetters([
'userID'
]),
columns() {
return [
{ prop: 'name', label: this.$t('page.modelName'), formatter: (row) => row.info.name },
{ prop: 'createTime', label: this.$t('page.creationTime'), sortable: true, formatter: (row) => {
return <div>{new Date(row.createTime).toLocaleString()}</div>
} },
{ prop: 'clusters', label: this.$t('page.clusterIn'), formatter: (row) => this.getClusterData(row.info.clusterIDs) },
{ prop: 'modelType', label: '模型类型', formatter: (row) => row.info.modelType },
{ prop: 'status', label: this.$t('page.status'), formatter: (row) => row.accessLevel === 'public' ? '公开' : row.accessLevel === 'apply' ? '可申请' : '私有' },
{ prop: 'more', label: this.$t('page.more'), width: 160, formatter: (row) => {
return <el-dropdown>
<el-button className='el-dropdown-link' size='mini' icon='el-icon-more' circle>
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => { this.radioValue = row.accessLevel; this.dialogSettingVisible = true; this.currentID = row.ID }}> <el-dropdown-item> {this.$t('page.authSetting')}</el-dropdown-item> </span>
<span onClick={() => { this.deleteItem(row) }}> <el-dropdown-item> {this.$t('page.delete')} </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
} }
]
}
},
mounted() {
this.getList()
getClusterList({ 'storageSchedule': 1, pageSize: 1000, pageNum: 1 }).then(e => {
this.clusterList = e?.data?.list || []
})
},
methods: {
authSetting() {
accessAuthSetting({ ID: this.currentID, level: this.radioValue }).then(e => {
if (e.code === 'OK') {
this.$message.success('设置成功')
this.dialogSettingVisible = false
this.getList()
}
})
},
getClusterData(cluster) {
if (this.clusterList.length) {
return cluster?.map(e => {
return this.clusterList.find(s => s.id === e)?.name || ''
}).filter(Boolean).join(',') || '-'
} else {
return '-'
}
},
getList() {
getBindingList({ dataType: 'model', param: { userID: this.userID, bindingID: -1, type: 'private' }}).then(e => {
this.dataList = e?.data?.datas || []
this.exhibitionArray[0].value = e?.data?.datas?.length || 0
})
},
handelAdd() {
this.addFormVisible = true
},
deleteItem(row) {
this.$confirm('确认删除么?', '提示', {
confirmButtonText: this.$t('message.confirm'),
cancelButtonText: this.$t('message.cancel'),
type: 'warning'
}).then(() => {
deleteBinding({ packageIDs: [row.info.packageID], bindingIDs: [row.ID] }).then((e) => {
if (e.code === 'OK') {
this.$message.success('删除成功')
this.getList()
}
})
}).catch(() => {
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@ -0,0 +1,335 @@
<template>
<div class="login-container">
<div class="header">
<img src="@/assets/AI4M-logo.png">{{ $t("page.title") }} | 注册
</div>
<div class="manufacturer">
<a class="login-btn" href="/login">登录</a>
<el-form
ref="registerForm"
:model="loginForm"
:rules="loginRules"
class="register-form"
autocomplete="on"
label-position="left"
label-width="auto"
>
<div class="title-container"> 注册</div>
<el-form-item prop="username" label="名称">
<el-input
ref="username"
v-model="loginForm.username"
placeholder="请输入账号"
name="username"
type="text"
autocomplete="new-username"
/>
</el-form-item>
<el-form-item prop="nickName" label="昵称">
<el-input
v-model="loginForm.nickName"
placeholder="请输入昵称"
name="nickName"
type="text"
/>
</el-form-item>
<el-form-item prop="password" label="密码">
<el-input
:key="passwordType"
ref="password"
v-model="loginForm.password"
:type="passwordType"
placeholder="请输入密码"
name="password"
autocomplete="new-password"
@keyup.native="checkCapslock"
@blur="capsTooltip = false"
@keyup.enter.native="handleRegister"
/>
</el-form-item>
<el-form-item prop="passwordR" label="密码">
<el-input
:key="passwordType"
ref="passwordR"
v-model="loginForm.passwordR"
:type="passwordType"
placeholder="请再次输入密码"
name="passwordR"
autocomplete="new-password"
@keyup.native="checkCapslock"
@blur="capsTooltip = false"
@keyup.enter.native="handleRegister"
/>
</el-form-item>
<el-form-item prop="bcosPublicKey" label="公钥">
<el-input
v-model="loginForm.bcosPublicKey"
name="bcosPublicKey"
type="text"
placeholder="请输入公钥"
/>
</el-form-item>
<el-form-item prop="bcosAccountAddress" label="地址">
<el-input
v-model="loginForm.bcosAccountAddress"
name="bcosAccountAddress"
placeholder="请输入链上账户地址"
type="text"
/>
</el-form-item>
<el-button
:loading="loading"
style="width:100%;margin-top: 2vh;margin-bottom:30px;"
@click.native.prevent="handleRegister"
>注册</el-button>
</el-form>
</div>
<el-dialog
:visible.sync="successVisible"
width="40%"
class="successModel"
center
>
<img style="height: 28vh;" src="../../assets/images/register_success.png">
<h2>注册成功</h2>
<div v-if="JSON.stringify(info) !=='{}'">注意:请用户在当前页面下载并妥善保存公钥私钥账户地址平台不会保存用户相关信息关闭当前后用户将无法再次获取</div>
<span slot="footer" class="dialog-footer">
<el-button v-if="JSON.stringify(info) !=='{}'" @click="downloadCodeAsFile">下载文件</el-button>
<el-button type="primary" @click="$router.push({ path: '/login' })"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { createAdmin } from '@/api/authority/permissionManagement'
import { registerAdmin } from '@/api/jcs/jcs'
export default {
name: 'Register',
data() {
return {
successVisible: false,
loginForm: {
id: null,
username: null,
password: null,
nickName: null,
email: null,
note: null,
status: 1
},
loginRules: {
username: [
{ required: true, trigger: 'blur', message: '请输入用户名' }
],
nickName: [{ required: true, trigger: 'blur', message: '请输入昵称' }],
password: [{ required: true, trigger: 'blur', message: '请输入密码' }],
passwordR: [
{ required: true, trigger: 'blur', message: '请再次输入密码' }
],
email: [
{
validator: this.checkEmail,
trigger: 'blur'
}
]
},
rememberMe: false,
passwordType: 'password',
capsTooltip: false,
loading: false,
showDialog: false,
redirect: undefined,
otherQuery: {},
info: {}
}
},
watch: {
$route: {
handler(route) {
const { query } = route
if (query) {
this.redirect = query.redirect
// this.otherQuery = this.getOtherQuery(query);
}
},
immediate: true
}
},
mounted() {
this.$refs.username.focus()
},
destroyed() {
// window.removeEventListener('storage', this.afterQRScan)
},
methods: {
downloadCodeAsFile() {
const blob = new Blob([JSON.stringify(this.info)], { type: 'text/plain' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = '用户信息.txt'
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
},
checkEmail(rule, value, callback) {
if (
value &&
!value.match(
/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/
)
) {
callback(new Error('请输入正确的邮箱'))
}
callback()
},
checkCapslock(e) {
const { key } = e
this.capsTooltip = key && key.length === 1 && key >= 'A' && key <= 'Z'
},
handleRegister() {
this.$refs.registerForm.validate(valid => {
if (valid) {
const { password, passwordR } = this.loginForm
if (password === passwordR) {
createAdmin(this.loginForm).then(res => {
if (res) {
registerAdmin({ ssoID: res.data.id, userName: res.data.username }).then(res => {})
if (res.data?.bcosAccountAddress) {
this.info = {
bcosAccountAddress: res.data.bcosAccountAddress,
bcosPrivateKey: res.data.bcosPrivateKey,
bcosPublicKey: res.data.bcosPublicKey
}
}
this.successVisible = true
}
})
} else {
this.$message({
message: '两次输入密码不一致!',
type: 'warning'
})
}
}
})
}
}
}
</script>
<style scoped lang="scss">
.login-container {
position: relative;
height: 100%;
overflow: hidden;
background-color: var(--loginBgC);
background: url('../../assets/images/login_bg.png') white center no-repeat;
background-size: cover;
}
::v-deep {
.el-form-item__label{
line-height: 3.2rem ;
padding: 0 2rem;
background-color: rgba(87, 149, 157, 0.5);
position: relative;
&::before{
position: absolute;
left: 4.5rem;
top: 0.2rem;
}
}
}
.header{
display: flex;
margin: 3rem;
font-size: 1.88rem;
line-height: 3rem;
img{
width:auto;
height: 3.2rem;
margin-right: 2vh;
}
}
.ai4mDark .el-button {
padding: 1.2rem 1.25rem;
border-radius: 0;
}
.manufacturer{
width: 66vh;
height: 82vh;
box-sizing: border-box;
padding: 7rem 5rem;
text-align: left;
position: absolute;
background: url('../../assets/images/register_input.png') center no-repeat;
background-size: contain;
right: calc(50vw - 33vh);
top: 10vh;
.login-btn{
position: absolute;
font-size: 1.2rem;
top: 9.5vh;
right: 4vh;
z-index: 3;
}
&::after{
content: '';
width: 11vh;
height: 11vh;
display: block;
position: absolute;
background: url(../../assets/images/login-btn.png) no-repeat;
background-size: auto 100%;
background-position: center;
top: 7vh;
right: 3vh;
z-index: 2;
}
::v-deep{
.el-input__inner {
line-height: 3.2rem !important;
height: 3.2rem !important;
border-radius: 0;
padding: 0 0.9375rem;
background: #A0BDEF;
font-size: 1.1rem !important;
}
.el-button {
font-size: 1.5rem;
}
.el-input__inner {
background: rgba(87, 149, 157, 0.5);
color: white;
}
}
.title-container{
font-size: 1.5rem;
text-align: center;
margin: 0;
margin-bottom: 4.2vh;
}
::v-deep{
.el-button--default {
background: rgb(0, 167, 186);
border-color: #00A6BAFF;
}
.el-button:hover, .el-button:focus {
color: white;
border-color: rgb(0, 186, 186);
background-color: rgb(0, 186, 186);
}
}
}
.successModel{
::v-deep{
.el-dialog--center .el-dialog__body{
text-align: center;
font-size: 1.2rem;
}
}
}
</style>

View File

@ -3,23 +3,23 @@
<el-card>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:table-list-data="listData"
:pagination="true"
:pagination-auto="true"
row-key="id"
:default-sort="{prop: 'sort', order: 'ascending'}"
tooltip-effect="dark"
>
<template v-slot:filterBtns>
<el-button size="medium" type="primary" @click="handleAdd"> {{ $t('message.create') }} </el-button>
<el-button size="medium" type="primary" @click="handleAdd">创建</el-button>
<el-button v-if="showBtn" size="medium" type="info" @click="goBack">返回上级菜单</el-button>
</template>
</List>
</el-card>
<el-dialog
:close-on-click-modal="false"
:title="isEdit? $t('permission.editMenu'): $t('permission.addMenu')"
:title="isEdit?'编辑菜单':'添加菜单'"
:visible.sync="dialogVisible"
width="50%"
>
@ -27,81 +27,40 @@
ref="menuFrom"
:model="menu"
:rules="rules"
label-width="auto"
label-width="150px"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item prop="parentId" :label="$t('permission.fatherNode')">
<el-cascader
ref="cascader"
<el-form-item label="菜单名称:" prop="title">
<el-input v-model="menu.title" />
</el-form-item>
<el-form-item label="上级菜单:">
<el-select
v-model="menu.parentId"
style="width: 100%;"
class="cascader-box"
:options="selectMenuList"
:props="{ value: 'id', label: 'title', checkStrictly: true, expandTrigger: 'hover' }"
:show-all-levels="false"
clearable
@change="handleCascader"
placeholder="请选择菜单"
>
<el-option
v-for="item in selectMenuList"
:key="item.id"
:label="item.title"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('permission.menuName')" prop="title"> <el-input v-model="menu.title" /> </el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="$t('permission.routerName')" prop="name"> <el-input v-model="menu.name" /> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('permission.router')" prop="path"> <el-input v-model="menu.path" /> </el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="$t('permission.redirect')" prop="redirect"> <el-input v-model="menu.redirect" /> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('permission.componentPath')" prop="component"> <el-input v-model="menu.component" /> </el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item prop="sort" :label="$t('permission.sort')">
<el-input-number v-model="menu.sort" :min="1" />
<el-form-item label="前端名称:" prop="name">
<el-input v-model="menu.name" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="status" :label="$t('permission.isShow')">
<el-radio-group v-model="menu.status">
<el-radio :label="true"> {{ $t('permission.yes') }} </el-radio>
<el-radio :label="false"> {{ $t('permission.no') }} </el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<!-- <el-row :gutter="20">
<el-col :span="12">
<el-form-item prop="icon" label="图标:">
<el-radio-group v-model="menu.icon">
<el-radio :label="0"></el-radio>
<el-radio :label="1"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="固定:">
<el-form-item label="是否显示:">
<el-radio-group v-model="menu.hidden">
<el-radio :label="0"></el-radio>
<el-radio :label="1"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row> -->
<el-form-item label="排序:">
<el-input v-model="menu.sort" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="dialogVisible = false">{{ $t("message.cancel") }}</el-button>
<el-button type="primary" size="small" @click="handleDialogConfirm()">{{ $t("message.confirm") }}</el-button>
<el-button size="small" @click="dialogVisible = false"> </el-button>
<el-button type="primary" size="small" @click="handleDialogConfirm()"> </el-button>
</span>
</el-dialog>
</div>
@ -109,19 +68,17 @@
<script>
import List from '@/components/list'
import { deleteMenu, getUserMenus, createMenu, updateMenu } from '@/api/authority/menu'
import { deleteMenu, getMenuList, createMenu, updateMenu, getMenu, updateHidden } from '@/api/authority/permissionManagement'
// import { newEval } from '@/utils'
import moment from 'moment'
const defaultMenu = {
'parentId': [0],
'name': '',
'path': '',
'redirect': '',
'component': '',
'sort': null,
'title': '',
'icon': '',
'status': true,
'defaultMenu': false
title: '',
parentId: 0,
name: '',
icon: '',
hidden: 0,
sort: 0
}
export default {
components: { List },
@ -131,64 +88,111 @@ export default {
dialogVisible: false,
menu: Object.assign({}, defaultMenu),
isEdit: false,
selectMenuList: []
selectMenuList: [],
listQuery: {
pageNum: 1,
pageSize: 999
},
parentId: 0,
columns: [
{ prop: 'id', label: '编号', sortable: true },
{ prop: 'title', label: '菜单名称', sortable: true },
{ prop: 'level', label: '菜单级数', sortable: true, formatter: (row, index) => {
if (row.level === 0) {
return '一级'
} else if (row.level === 1) {
return '二级'
}
} },
{ prop: 'name', label: '前端名称', sortable: true },
{ prop: 'hidden', label: '是否显示', formatter: (row, index) => {
return <el-switch
onChange={() => this.handleHiddenChange(index, row)}
activeValue={0}
inactiveValue={1}
value={row.hidden}>
</el-switch>
} },
{ prop: 'sort', label: '排序', sortable: true },
{ prop: 'level', label: '设置', formatter: (row, index) => {
return <el-button
size='mini'
type='text'
disabled={row.level !== 0}
onClick={() => this.handleShowNextLevel(index, row)}>查看下级
</el-button>
} },
{
prop: 'more', label: '更多操作', formatter: (row, index) => {
return <div>
<el-button type='text' onClick={() => this.handleUpdate(index, row)}> 编辑 </el-button>
<el-button type='text' onClick={() => this.handleDelete(index, row)}> 删除 </el-button>
</div>
}
}
],
rules: {
title: [
{ required: true, message: '请输入菜单名称', trigger: 'blur' },
{ min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur' }
],
name: [
{ required: true, message: '请输入前端名称', trigger: 'blur' },
{ min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur' }
]
}
}
},
computed: {
columns() {
return [
{ prop: 'id', label: 'ID' },
{ prop: 'parentId', label: this.$t('permission.fatherNode') },
{ prop: 'title', label: this.$t('permission.menuName') },
{ prop: 'path', label: this.$t('permission.routerName') },
// { prop: 'redirect', label: ' ' },
{ prop: 'status', label: this.$t('permission.isShow'), formatter: (row, index) => {
return row.status === true ? <el-tag type='success'>{this.$t('permission.show')}</el-tag> : <el-tag type='warning'>{this.$t('permission.hide')}</el-tag>
} },
{ prop: 'sort', label: this.$t('permission.sort'), sortable: true },
{ prop: 'name', label: this.$t('permission.routerName') },
{
prop: 'more', label: this.$t('page.more'), formatter: (row, index) => {
return <el-dropdown>
<el-button size='mini' className='el-dropdown-link' icon='el-icon-more' circle>
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => this.handleUpdate(index, row)}> <el-dropdown-item><i class='el-icon-edit'></i> {this.$t('page.edit')} </el-dropdown-item> </span>
<span onClick={() => this.handleDelete(index, row)}> <el-dropdown-item divided><i class='el-icon-delete'></i> { this.$t('message.delete') } </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
}
}
]
},
rules() {
return {
parentId: [{ required: true, message: this.$t('message.pleaseChoose'), trigger: 'change' }],
path: [{ required: true, message: this.$t('message.pleaseInput'), trigger: 'blur' }],
name: [{ required: true, message: this.$t('message.pleaseInput'), trigger: 'blur' }],
title: [{ required: true, message: this.$t('message.pleaseInput'), trigger: 'blur' }]
}
showBtn() {
return this.$route.query.parentId
}
},
watch: {
$route(route) {
this.resetParentId()
this.getList()
}
},
mounted() {
this.resetParentId()
this.getList()
},
methods: {
goBack() {
this.$router.go(-1)
},
getList() {
getUserMenus().then(e => {
this.listData = e.data
this.selectMenuList = [].concat(e.data)
this.selectMenuList.unshift({ id: '0', title: this.$t('permission.root') })
getMenuList(this.parentId, { pageNum: this.listQuery.pageNum, pageSize: this.listQuery.pageSize }).then(e => {
this.listData = e.data.list
this.selectMenuList = [].concat(e.data.list)
this.selectMenuList.unshift({ id: 0, title: '无上级菜单' })
}).catch(e => {
})
},
handleCascader(val) {
this.menu.parentId = val
resetParentId() {
this.listQuery.pageNum = 1
if (this.$route.query.parentId != null) {
this.parentId = this.$route.query.parentId
} else {
this.parentId = 0
}
},
formatDate(t) {
return moment(t).local().format('YYYY-MM-DD hh:mm:ss')
},
handleShowNextLevel(index, row) {
this.$router.push({ path: '/permissionManagement/menuList', query: { parentId: row.id }})
},
handleHiddenChange(index, row) {
updateHidden(row.id, { hidden: !row.hidden ? 0 : 1 }).then(response => {
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
})
this.getList()
})
},
handleAdd() {
this.dialogVisible = true
@ -196,44 +200,66 @@ export default {
this.menu = Object.assign({}, defaultMenu)
},
handleDelete(index, row) {
this.$confirm(this.$t('permission.wantDelete'), this.$t('page.info'), {
confirmButtonText: this.$t('message.confirm'),
cancelButtonText: this.$t('message.cancel'),
this.$confirm('是否要删除该菜单', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteMenu({ id: row.id }).then(response => {
this.$message({ message: this.$t('page.deleteSuccess'), type: 'success', duration: 1000 })
deleteMenu(row.id).then(response => {
this.$message({
message: '删除成功',
type: 'success',
duration: 1000
})
this.getList()
})
})
},
handleUpdate(index, row) {
this.dialogVisible = true
this.menuId = row.id
this.menu = Object.assign({}, row)
this.menu.parentId = [row.parentId]
getMenu(this.menuId).then(response => {
this.menu = response.data
this.isEdit = true
this.dialogVisible = true
})
},
handleDialogConfirm() {
this.$refs.menuFrom.validate((valid) => {
if (valid) {
this.menu.parentId = this.menu.parentId[this.menu.parentId.length - 1]
this.$confirm('是否提交数据', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateMenu(this.menu).then(response => {
this.$message({ message: this.$t('message.updateSuccess'), type: 'success', duration: 1000 })
updateMenu(this.menuId, this.menu).then(response => {
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
})
this.dialogVisible = false
this.getList()
})
} else {
createMenu(this.menu).then(response => {
this.$refs.menuFrom.resetFields()
this.$message({ message: this.$t('message.submitSuccess'), type: 'success', duration: 1000 })
this.$message({
message: '提交成功',
type: 'success',
duration: 1000
})
this.dialogVisible = false
this.getList()
})
}
})
} else {
this.$message({ message: this.$t('message.verificationFailed'), type: 'success', duration: 1000 })
this.$message({
message: '验证失败',
type: 'error',
duration: 1000
})
return false
}
})
@ -241,10 +267,10 @@ export default {
}
}
</script>
<style scoped lang="scss">
::v-deep{
.el-table__expand-icon {
color: #FFFFFF;
}
<style scoped>
.btnBox {
display: flex;
align-items: center;
justify-content: center;
}
</style>

View File

@ -1,99 +0,0 @@
<template>
<div class="">
<el-card>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:filter-map="filterMap"
:get-list-action="getOplList"
:pagination="true"
page-key="pageNum"
limit-key="pageSize"
tooltip-effect="dark"
>
<!-- <template v-slot:filterBtns>
<el-button size="medium" type="primary" @click="handleDelete">删除</el-button>
</template> -->
</List>
</el-card>
</div>
</template>
<script>
import List from '@/components/list'
import { getOplList, deleteOpl
// deleteOplByIds
} from '@/api/authority/opl'
export default {
components: { List },
data() {
return {
getOplList,
isEdit: false
}
},
computed: {
filterMap() {
return {
path: {
label: this.$t('page.path')
},
method: {
label: this.$t('page.method'),
type: 'select',
dataSource: [
{ label: 'GET', value: 'GET' },
{ label: 'POST', value: 'POST' },
{ label: 'PUT', value: 'PUT' },
{ label: 'DELETE', value: 'DELETE' }
]
}
}
},
columns() {
return [
// { type: 'selection' },
{ prop: 'id', label: 'ID' },
{ prop: 'userName', label: this.$t('page.user') },
{ prop: 'ip', label: 'IP' },
{ prop: 'path', label: this.$t('page.path') },
{ prop: 'status', label: this.$t('page.code') },
{ prop: 'method', label: this.$t('page.request') },
{ prop: 'latency', label: this.$t('page.responseTime'), formatter: res => res.latency + 'ms' },
{ prop: 'createTime', label: this.$t('page.creationTime'), formatter: row => {
return <div>{new Date(row.createTime).toLocaleString()}</div>
} },
{
prop: 'more', label: this.$t('page.more'), formatter: (row, index) => {
return <div>
<el-button type='text' onClick={() => this.handleDelete(row)}> {this.$t('page.delete')} </el-button>
</div>
}
}
]
}
},
watch: {
},
mounted() {
},
methods: {
handleDelete(row) {
this.$confirm(this.$t('page.isDeleteLog'), this.$t('page.info'), {
confirmButtonText: this.$t('message.confirm'),
cancelButtonText: this.$t('message.cancel'),
type: 'warning'
}).then(() => {
deleteOpl({ id: row.id }).then(response => {
this.$message({ message: this.$t('page.deleteSuccess'), type: 'success', duration: 1000 })
this.$refs.multipleTable.getList()
})
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,151 @@
<template>
<div class="">
<List
ref="multipleTable"
class="multipleTable"
:columns="columns"
:table-list-data="listData"
tooltip-effect="dark"
>
<template v-slot:filterBtns>
<el-button size="medium" type="primary" @click="handleAdd">添加</el-button>
</template>
</List>
<el-dialog
:close-on-click-modal="false"
title="添加分类"
:visible.sync="dialogVisible"
append-to-body
width="40%"
>
<el-form
ref="resourceCategoryForm"
:model="resourceCategory"
label-width="150px"
size="small"
>
<el-form-item label="名称:">
<el-input v-model="resourceCategory.name" style="width: 250px" />
</el-form-item>
<el-form-item label="排序:">
<el-input v-model="resourceCategory.sort" style="width: 250px" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="dialogVisible = false"> </el-button>
<el-button type="primary" size="small" @click="handleDialogConfirm()"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import List from '@/components/list'
import { listAllCate, createResourceCategory, updateResourceCategory, deleteResourceCategory } from '@/api/authority/permissionManagement'
// import { newEval } from '@/utils'
import moment from 'moment'
const defaultResourceCategory = {
name: null,
sort: 0
}
export default {
components: { List },
data() {
return {
listData: [],
dialogVisible: false,
resourceCategory: Object.assign({}, defaultResourceCategory),
isEdit: false,
categoryOptions: [],
defaultCategoryId: null,
columns: [
{ prop: 'id', label: '编号', sortable: true },
{ prop: 'name', label: '名称', sortable: true },
{ prop: 'createTime', label: '添加时间', sortable: true, formatter: (row) => { return <span>{this.formatDate(row.createTime)}</span> } },
{ prop: 'sort', label: '排序', sortable: true },
{
prop: 'more', label: '更多操作', formatter: (row, index) => {
return <div>
<el-button type='text' onClick={() => this.handleUpdate(index, row)}> 编辑 </el-button>
<el-button type='text' onClick={() => this.handleDelete(index, row)}> 删除 </el-button>
</div>
}
}
]
}
},
mounted() {
this.getList()
},
methods: {
getList() {
listAllCate().then(e => {
this.listData = e.data
})
},
formatDate(t) {
return moment(t).local().format('YYYY-MM-DD hh:mm:ss')
},
handleAdd() {
this.dialogVisible = true
this.isEdit = false
this.resourceCategory = Object.assign({}, defaultResourceCategory)
},
handleUpdate(index, row) {
this.dialogVisible = true
this.isEdit = true
this.resourceCategory = Object.assign({}, row)
},
handleDelete(index, row) {
this.$confirm('是否要删除该分类?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteResourceCategory(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
})
this.getList()
})
})
},
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateResourceCategory(this.resourceCategory.id, this.resourceCategory).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
})
this.dialogVisible = false
this.getList()
})
} else {
createResourceCategory(this.resourceCategory).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
})
this.dialogVisible = false
this.getList()
})
}
})
}
}
}
</script>
<style scoped>
.btnBox {
display: flex;
align-items: center;
justify-content: center;
}
</style>

View File

@ -3,150 +3,119 @@
<el-card>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:get-list-action="getResourceList"
:filter-map="filterMap"
:pagination="true"
page-key="pageNum"
limit-key="pageSize"
tooltip-effect="dark"
>
<template v-slot:filterBtns>
<el-button size="medium" type="primary" @click="handleAdd"> {{ $t('page.add') }} </el-button>
<el-button size="medium" type="primary" @click="resourceCategoryVisible=true">资源分类</el-button>
<el-button size="medium" type="primary" @click="handleAdd">添加</el-button>
</template>
</List>
</el-card>
<el-dialog
:close-on-click-modal="false"
:title="isEdit? $t('page.editResource') : $t('page.addResource')"
:title="isEdit?'编辑资源':'添加资源'"
:visible.sync="dialogVisible"
width="40%"
>
<p>
<el-alert
:title="$t('page.apiInfo')"
type="warning"
:closable="false"
/>
</p>
<el-form
ref="resourceForm"
:model="resource"
:rules="rules"
label-width="auto"
label-width="150px"
size="small"
>
<el-form-item :label="$t('page.apiPath')" prop="path">
<el-input v-model="resource.path" />
<el-form-item label="资源名称:">
<el-input v-model="resource.name" style="width: 250px" />
</el-form-item>
<el-form-item :label="$t('page.request')" prop="method">
<el-select v-model="resource.method" style="width: 100%;">
<el-form-item label="资源路径:">
<el-input v-model="resource.url" style="width: 250px" />
</el-form-item>
<el-form-item label="资源分类:">
<el-select v-model="resource.categoryId" placeholder="全部" clearable style="width: 250px">
<el-option
v-for="it in filterMap.method.dataSource"
:key="it.label"
:label="it.label"
:value="it.label"
v-for="item in categoryOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('page.apiGroup')" prop="apiGroup">
<el-input v-model="resource.apiGroup" />
</el-form-item>
<el-form-item :label="$t('page.apiDesc')" prop="description">
<el-input v-model="resource.description" type="textarea" :rows="5" />
<el-form-item label="描述:">
<el-input
v-model="resource.description"
type="textarea"
:rows="5"
style="width: 250px"
/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="dialogVisible = false">{{ $t("message.cancel") }}</el-button>
<el-button type="primary" size="small" @click="handleDialogConfirm()">{{ $t("message.confirm") }}</el-button>
<el-button size="small" @click="dialogVisible = false"> </el-button>
<el-button type="primary" size="small" @click="handleDialogConfirm()"> </el-button>
</span>
</el-dialog>
<el-dialog
:close-on-click-modal="false"
title="资源列表"
:visible.sync="resourceCategoryVisible"
width="80%"
>
<resource-category-list />
</el-dialog>
</div>
</template>
<script>
import List from '@/components/list'
import { getResourceList, addApi, editApi, deleteApi
// deleteApisByIds
} from '@/api/authority/api'
import { getResourceList, createResource, updateResource, deleteResource, listAllCate } from '@/api/authority/permissionManagement'
// import { newEval } from '@/utils'
import moment from 'moment'
import ResourceCategoryList from './resourceCategoryList.vue'
const defaultResource = {
'name': null,
'path': null,
'description': '',
'apiGroup': null,
'method': null
id: null,
name: null,
url: null,
categoryId: null,
description: ''
}
export default {
components: { List },
components: { List, ResourceCategoryList },
data() {
return {
getResourceList,
listData: [],
dialogVisible: false,
resourceCategoryVisible: false,
resource: Object.assign({}, defaultResource),
isEdit: false,
categoryOptions: [],
defaultCategoryId: null
}
},
computed: {
filterMap() {
return {
path: {
label: this.$t('page.path')
},
apiGroup: {
label: this.$t('page.apiGroup')
},
method: {
label: this.$t('page.method'),
type: 'select',
dataSource: [
{ label: 'GET', value: 'GET' },
{ label: 'POST', value: 'POST' },
{ label: 'PUT', value: 'PUT' },
{ label: 'DELETE', value: 'DELETE' }
]
}
}
},
columns() {
return [
{ prop: 'id', label: 'ID', width: 100 },
{ prop: 'path', label: this.$t('page.path') },
{ prop: 'description', label: this.$t('page.description') },
{ prop: 'apiGroup', label: this.$t('page.apiGroup') },
{ prop: 'method', label: this.$t('page.request') },
defaultCategoryId: null,
columns: [
{ prop: 'id', label: '编号', sortable: true },
{ prop: 'name', label: '资源名称', sortable: true },
{ prop: 'url', label: '资源路径', sortable: true },
{ prop: 'description', label: '描述', sortable: true },
{ prop: 'createTime', label: '添加时间', sortable: true, formatter: (row) => { return <span>{this.formatDate(row.createTime)}</span> } },
{
prop: 'more', label: this.$t('page.more'), width: 150, formatter: (row, index) => {
return <el-dropdown>
<el-button size='mini' className='el-dropdown-link' icon='el-icon-more' circle>
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => this.handleUpdate(index, row)}> <el-dropdown-item><i class='el-icon-edit'></i> {this.$t('page.edit')} </el-dropdown-item> </span>
<span onClick={() => this.handleDelete(index, row)}> <el-dropdown-item divided><i class='el-icon-delete'></i> { this.$t('message.delete') } </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
prop: 'more', label: '更多操作', formatter: (row, index) => {
return <div>
<el-button type='text' onClick={() => this.handleUpdate(index, row)}> 编辑 </el-button>
<el-button type='text' onClick={() => this.handleDelete(index, row)}> 删除 </el-button>
</div>
}
}
]
},
rules() {
return {
name: [{ required: true, message: this.$t('message.pleaseInput') }],
description: [{ required: true, message: this.$t('message.pleaseInput') }],
apiGroup: [{ required: true, message: this.$t('message.pleaseInput') }],
path: [{ required: true, message: this.$t('message.pleaseInput') }],
method: [{ required: true, message: this.$t('message.pleaseChoose') }]
}
}
},
mounted() {
// this.getList()
this.getCateList()
},
methods: {
getList() {
@ -163,15 +132,19 @@ export default {
this.dialogVisible = true
this.isEdit = false
this.resource = Object.assign({}, defaultResource)
this.resource.categoryId = this.defaultCategoryId
},
handleDelete(index, row) {
this.$confirm(this.$t('message.isDeleteResource'), this.$t('page.info'), {
confirmButtonText: this.$t('message.confirm'),
cancelButtonText: this.$t('message.cancel'),
this.$confirm('是否要删除该资源?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteApi({ id: row.id + '' }).then(response => {
this.$message({ message: this.$t('page.deleteSuccess'), type: 'success', duration: 1000 })
deleteResource(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
})
this.$refs.multipleTable.getList()
})
})
@ -182,24 +155,40 @@ export default {
this.resource = Object.assign({}, row)
},
handleDialogConfirm() {
this.$refs.resourceForm.validate((valid) => {
if (valid) {
this.resource.name = this.resource.description
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
this.resource.ID = this.resource.id
editApi(this.resource).then(response => {
this.$message({ message: this.$t('message.updateSuccess'), type: 'success', duration: 1000 })
updateResource(this.resource.id, this.resource).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
})
this.dialogVisible = false
this.$refs.multipleTable.getList()
})
} else {
addApi(this.resource).then(response => {
this.$message({ message: this.$t('message.addSuccess'), type: 'success', duration: 1000 })
createResource(this.resource).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
})
this.dialogVisible = false
this.$refs.multipleTable.getList()
})
}
})
},
getCateList() {
listAllCate().then(response => {
const cateList = response.data
for (let i = 0; i < cateList.length; i++) {
const cate = cateList[i]
this.categoryOptions.push({ label: cate.name, value: cate.id })
}
this.defaultCategoryId = cateList[0].id
})
}
}

View File

@ -3,35 +3,35 @@
<el-card>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:get-list-action="getRoleList"
list-key="data"
:pagination="true"
page-key="pageNum"
limit-key="pageSize"
tooltip-effect="dark"
>
<template v-slot:filterBtns>
<el-button size="medium" type="primary" @click="handleRoleAdd">{{ $t('message.create') }}</el-button>
<el-button size="medium" type="primary" @click="handleAdd">创建</el-button>
</template>
</List>
</el-card>
<el-dialog
:close-on-click-modal="false"
:title="isEdit?$t('permission.editRole'):$t('permission.addRole')"
:title="isEdit?'编辑角色':'添加角色'"
:visible.sync="dialogVisible"
width="40%"
>
<el-form
ref="roleForm"
:model="role"
:rules="rules"
label-width="auto"
label-width="150px"
size="small"
>
<el-form-item prop="name" :label="$t('permission.roleName')">
<el-form-item label="角色名称:">
<el-input v-model="role.name" style="width: 250px" />
</el-form-item>
<el-form-item prop="description" :label="$t('page.description')">
<el-form-item label="描述:">
<el-input
v-model="role.description"
type="textarea"
@ -39,28 +39,27 @@
style="width: 250px"
/>
</el-form-item>
<el-form-item prop="status" :label="$t('permission.isUse')">
<el-form-item label="是否启用:">
<el-radio-group v-model="role.status">
<el-radio :label="true"> {{ $t('permission.yes') }} </el-radio>
<el-radio :label="false"> {{ $t('permission.no') }} </el-radio>
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="dialogVisible = false">{{ $t("message.cancel") }}</el-button>
<el-button type="primary" size="small" @click="handleDialogConfirm()">{{ $t("message.confirm") }}</el-button>
<el-button size="small" @click="dialogVisible = false"> </el-button>
<el-button type="primary" size="small" @click="handleDialogConfirm()"> </el-button>
</span>
</el-dialog>
<el-dialog
:close-on-click-modal="false"
:title="$t('permission.assignMenu')"
title="分配菜单"
:visible.sync="menuSelectDialog"
width="50%"
>
<el-tree
ref="menuTree"
ref="tree"
:data="menuTreeList"
:default-checked-keys="menuIds"
show-checkbox
default-expand-all
node-key="id"
@ -68,27 +67,37 @@
:props="defaultProps"
/>
<div style="margin-top: 20px" align="center">
<el-button type="primary" @click="handleMenuSave()"> {{ $t('message.save') }} </el-button>
<el-button type="primary" @click="handleSave()">保存</el-button>
<el-button @click="handleClear()">清空</el-button>
</div>
</el-dialog>
<el-dialog
:close-on-click-modal="false"
:title="$t('permission.assignResource')"
title="分配资源"
:visible.sync="resourceSelectDialog"
width="50%"
>
<el-tree
ref="apiTree"
:data="apiTreeList"
:default-checked-keys="apiIds"
show-checkbox
default-expand-all
node-key="key"
highlight-current
:props="apiDefaultProps"
/>
<div v-for="(cate,index) in allResourceCate" :key="'cate'+cate.id" :class="index===0?'top-line':null">
<el-row class="table-layout choose-line">
<el-checkbox
v-model="cate.checked"
:indeterminate="isIndeterminate(cate.id)"
@change="handleCheckAllChange(cate)"
>
{{ cate.name }}
</el-checkbox>
</el-row>
<el-row class="table-layout">
<el-col v-for="resource in getResourceByCate(cate.id)" :key="resource.id" :span="8" style="padding: 4px 0">
<el-checkbox v-model="resource.checked" @change="handleCheckChange(resource)">
{{ resource.name }}
</el-checkbox>
</el-col>
</el-row>
</div>
<div style="margin-top: 20px" align="center">
<el-button type="primary" @click="handleResourceSave()"> {{ $t('message.save') }} </el-button>
<el-button type="primary" @click="handleResourceSave()">保存</el-button>
<el-button @click="handleResourceClear()">清空</el-button>
</div>
</el-dialog>
</div>
@ -96,16 +105,16 @@
<script>
import List from '@/components/list'
import { createRole, updateRole, deleteRole, allocMenu, getRoleList, allocApis } from '@/api/authority/role'
import { getMenuTree } from '@/api/authority/menu'
import { getElTreeApis } from '@/api/authority/api'
import { getRoleList, createRole, updateRole, updateRoleStatus, deleteRole, fetchTreeList, listMenuByRole, allocMenu, fetchAllResourceList, listAllCate, allocResource, listResourceByRole } from '@/api/authority/permissionManagement'
// import { newEval } from '@/utils'
import moment from 'moment'
const defaultRole = {
'id': undefined,
'name': undefined,
'description': undefined,
'status': true
id: null,
name: null,
description: null,
adminCount: 0,
status: 1
}
export default {
components: { List },
@ -118,149 +127,316 @@ export default {
resourceSelectDialog: false,
role: Object.assign({}, defaultRole),
isEdit: false,
labelWidth: Math.round(document.body.offsetHeight / 1048 * 100) + 'px',
apiTreeList: [],
apiIds: [],
columns: [
{ prop: 'id', label: '编号', sortable: true },
{ prop: 'name', label: '角色名称', sortable: true },
{ prop: 'description', label: '描述', sortable: true },
{ prop: 'adminCount', label: '用户数', sortable: true },
{ prop: 'createTime', label: '添加时间', sortable: true, formatter: (row) => { return <span>{this.formatDate(row.createTime)}</span> } },
{ prop: 'status', label: '是否启用', formatter: (row) => {
return <el-switch
onChange={() => this.handleStatusChange(row)}
activeValue={1}
inactiveValue={0}
value={row.status}>
</el-switch>
} },
{
prop: 'more', label: '更多操作', formatter: (row, index) => {
return <div>
<el-button type='text' onClick={() => this.handleSelectMenu(index, row)}> 分配菜单 </el-button>
<el-button type='text' onClick={() => this.handleSelectResource(index, row)}> 分配资源 </el-button>
<el-button type='text' onClick={() => this.handleUpdate(index, row)}> 编辑 </el-button>
<el-button type='text' onClick={() => this.handleDelete(index, row)}> 删除 </el-button>
</div>
}
}
],
menuTreeList: [],
menuIds: [],
defaultProps: {
children: 'children',
label: 'title'
},
apiDefaultProps: {
children: 'children',
label: (data) => {
return data.apiGroup
}
},
roleId: null,
allResource: null,
allResourceCate: null
}
},
computed: {
columns() {
return [
{ prop: 'id', label: 'ID', sortable: true },
{ prop: 'name', label: this.$t('permission.roleName'), sortable: true },
{ prop: 'description', label: this.$t('page.description'), sortable: true },
{ prop: 'status', label: this.$t('permission.isUse'), formatter: (row, index) => {
return row.status === true ? <el-tag type='success'>{this.$t('permission.enable')}</el-tag> : <el-tag type='warning'>{this.$t('permission.deactivate')}</el-tag>
} },
{
prop: 'more', label: this.$t('page.more'), formatter: (row, index) => {
return <el-dropdown>
<el-button size='mini' className='el-dropdown-link' icon='el-icon-more' circle>
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => this.handleSelectMenu(index, row)}> <el-dropdown-item><i class='el-icon-edit'></i> {this.$t('permission.assignMenu')} </el-dropdown-item> </span>
<span onClick={() => this.handleSelectResource(index, row)}> <el-dropdown-item><i class='el-icon-edit'></i> {this.$t('permission.assignResource')} </el-dropdown-item> </span>
<span onClick={() => this.handleRoleUpdate(index, row)}> <el-dropdown-item><i class='el-icon-edit'></i> {this.$t('page.edit')} </el-dropdown-item> </span>
<span onClick={() => this.handleRoleDelete(index, row)}> <el-dropdown-item divided><i class='el-icon-delete'></i> { this.$t('message.delete') } </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
}
}
]
},
rules() {
return {
name: [{ required: true, message: this.$t('check.input') }],
description: [{ required: true, message: this.$t('check.input') }],
status: [{ required: true, message: this.$t('check.select') }]
}
}
mounted() {
},
methods: {
handleRoleAdd() {
formatDate(t) {
return moment(t).local().format('YYYY-MM-DD hh:mm:ss')
},
handleAdd() {
this.dialogVisible = true
this.isEdit = false
this.role = Object.assign({}, defaultRole)
},
handleRoleDelete(index, row) {
this.$confirm(this.$t('permission.wantDeleteRole'), this.$t('page.info'), {
confirmButtonText: this.$t('message.confirm'),
cancelButtonText: this.$t('message.cancel'),
handleStatusChange(row) {
console.log(row.status)
this.$confirm('是否要修改该状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteRole({ id: row.id + '' }).then(response => {
this.$message({ type: 'success', message: this.$t('page.deleteSuccess') })
updateRoleStatus(row.id, { status: !row.status ? 1 : 0 }).then(response => {
this.$refs.multipleTable.getList()
this.$message({
type: 'success',
message: '修改成功!'
})
})
}).catch(() => {
this.$message({
type: 'info',
message: '取消修改'
})
this.$refs.multipleTable.getList()
})
},
handleDelete(index, row) {
this.$confirm('是否要删除该角色?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const ids = []
ids.push(row.id)
const params = new URLSearchParams()
params.append('ids', ids)
deleteRole({ ids: row.id }).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
})
this.$refs.multipleTable.getList()
})
})
},
handleRoleUpdate(row) {
handleUpdate(index, row) {
this.dialogVisible = true
this.isEdit = true
this.role = Object.assign({}, row)
},
handleDialogConfirm() {
this.$refs.roleForm.validate((valid) => {
if (valid) {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateRole(this.role).then(response => {
this.$message({ type: 'success', message: this.$t('message.updateSuccess') })
updateRole(this.role.id, this.role).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
})
this.dialogVisible = false
this.$refs.multipleTable.getList()
})
} else {
createRole(this.role).then(response => {
this.$message({ type: 'success', message: this.$t('message.addSuccess') })
this.$message({
message: '添加成功!',
type: 'success'
})
this.dialogVisible = false
this.$refs.multipleTable.getList()
})
}
}
})
},
handleSelectMenu(index, row) {
this.roleId = row.id
getMenuTree({ id: row.id }).then(response => {
this.menuTreeList = response.data.list
this.menuIds = response.data.menuIds
this.treeList()
this.getRoleMenu(row.id)
},
treeList() {
fetchTreeList().then(response => {
this.menuTreeList = response.data
this.menuSelectDialog = true
})
},
handleMenuSave() {
const checkedNodes = this.$refs.menuTree.getCheckedKeys()
const halfCheckedNodes = this.$refs.menuTree.getHalfCheckedKeys()
let menuIds = [...checkedNodes, ...halfCheckedNodes]
menuIds = menuIds.map(item => item + '')
allocMenu({ roleId: this.roleId + '', menuIds })
.then((res) => {
this.$message({ message: this.$t('message.allocationSucce'), type: 'success', duration: 1000 })
getRoleMenu(roleId) {
listMenuByRole(roleId).then(response => {
const menuList = response.data
const checkedMenuIds = []
if (menuList != null && menuList.length > 0) {
for (let i = 0; i < menuList.length; i++) {
const menu = menuList[i]
if (menu.parentId !== 0) {
checkedMenuIds.push(menu.id)
}
}
}
this.$refs.tree.setCheckedKeys(checkedMenuIds)
})
},
handleSave() {
const checkedNodes = this.$refs.tree.getCheckedNodes()
const checkedMenuIds = new Set()
if (checkedNodes != null && checkedNodes.length > 0) {
for (let i = 0; i < checkedNodes.length; i++) {
const checkedNode = checkedNodes[i]
checkedMenuIds.add(checkedNode.id)
if (checkedNode.parentId !== 0) {
checkedMenuIds.add(checkedNode.parentId)
}
}
}
this.$confirm('是否分配菜单?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const params = {
roleId: this.roleId,
menuIds: Array.from(checkedMenuIds).join(',')
}
allocMenu(params).then(response => {
this.menuSelectDialog = false
this.$message({
message: '分配成功',
type: 'success',
duration: 1000
})
this.$refs.multipleTable.getList()
})
.catch(() => {})
})
},
handleClear() {
this.$refs.tree.setCheckedKeys([])
},
handleSelectResource(index, row) {
this.roleId = row.id
getElTreeApis({ id: row.id }).then(response => {
this.apiTreeList = response.data.list
this.apiIds = response.data.checkedKey
this.resourceSelectDialog = true
this.getAllResourceCateList()
},
getAllResourceList() {
fetchAllResourceList().then(response => {
this.allResource = response.data
for (let i = 0; i < this.allResource.length; i++) {
this.allResource[i].checked = false
}
this.getResourceByRole(this.roleId)
})
},
handleResourceSave() {
const casbinInfos = []
const checkedNodes = this.$refs.apiTree.getCheckedNodes()
for (const item of checkedNodes) {
if (item.path && item.method) {
const casbinInfo = {
path: item.path,
method: item.method
getAllResourceCateList() {
listAllCate().then(response => {
this.resourceSelectDialog = true
this.allResourceCate = response.data
for (let i = 0; i < this.allResourceCate.length; i++) {
this.allResourceCate[i].checked = false
}
casbinInfos.push(casbinInfo)
}
}
allocApis({ roleId: this.roleId, casbinInfos: casbinInfos })
.then((res) => {
this.$message({ message: this.$t('message.allocationSucce'), type: 'success', duration: 1000 })
this.resourceSelectDialog = false
this.$refs.multipleTable.getList()
this.getAllResourceList()
})
.catch(() => {})
},
getResourceByCate(categoryId) {
const cateResource = []
if (this.allResource == null) return null
for (let i = 0; i < this.allResource.length; i++) {
const resource = this.allResource[i]
if (resource.categoryId === categoryId) {
cateResource.push(resource)
}
}
return cateResource
},
getResourceByRole(roleId) {
listResourceByRole(roleId).then(response => {
const allocResource = response.data
this.allResource.forEach(item => {
item.checked = this.getResourceChecked(item.id, allocResource)
})
this.allResourceCate.forEach(item => {
item.checked = this.isAllChecked(item.id)
})
this.$forceUpdate()
})
},
getResourceChecked(resourceId, allocResource) {
if (allocResource == null || allocResource.length === 0) return false
for (let i = 0; i < allocResource.length; i++) {
if (allocResource[i].id === resourceId) {
return true
}
}
return false
},
isIndeterminate(categoryId) {
const cateResources = this.getResourceByCate(categoryId)
if (cateResources == null) return false
let checkedCount = 0
for (let i = 0; i < cateResources.length; i++) {
if (cateResources[i].checked === true) {
checkedCount++
}
}
return !(checkedCount === 0 || checkedCount === cateResources.length)
},
isAllChecked(categoryId) {
const cateResources = this.getResourceByCate(categoryId)
if (cateResources == null) return false
let checkedCount = 0
for (let i = 0; i < cateResources.length; i++) {
if (cateResources[i].checked === true) {
checkedCount++
}
}
if (checkedCount === 0) {
return false
}
return checkedCount === cateResources.length
},
handleResourceSave() {
this.$confirm('是否分配资源?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const checkedResourceIds = new Set()
if (this.allResource != null && this.allResource.length > 0) {
this.allResource.forEach(item => {
if (item.checked) {
checkedResourceIds.add(item.id)
}
})
}
const params = new URLSearchParams()
params.append('roleId', this.roleId)
params.append('resourceIds', Array.from(checkedResourceIds))
allocResource(params).then(response => {
this.$message({
message: '分配成功',
type: 'success',
duration: 1000
})
this.$router.back()
})
})
},
handleResourceClear() {
this.allResourceCate.forEach(item => {
item.checked = false
})
this.allResource.forEach(item => {
item.checked = false
})
this.$forceUpdate()
},
handleCheckAllChange(cate) {
const cateResources = this.getResourceByCate(cate.id)
for (let i = 0; i < cateResources.length; i++) {
cateResources[i].checked = cate.checked
}
this.$forceUpdate()
},
handleCheckChange(resource) {
this.allResourceCate.forEach(item => {
if (item.id === resource.categoryId) {
item.checked = this.isAllChecked(resource.categoryId)
}
})
this.$forceUpdate()
}
}
}

View File

@ -3,7 +3,6 @@
<el-card>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:get-list-action="getUserList"
@ -13,83 +12,71 @@
tooltip-effect="dark"
>
<template v-slot:filterBtns>
<el-button size="medium" type="primary" @click="handleAdd">{{ $t('message.create') }}</el-button>
<el-button size="medium" type="primary" @click="handleAdd">创建</el-button>
</template>
</List>
</el-card>
<el-dialog
:close-on-click-modal="false"
:title="isEdit ? $t('permission.editUser') : $t('permission.addUser')"
:title="isEdit?'编辑用户':'添加用户'"
:visible.sync="dialogVisible"
width="40%"
>
<el-form
ref="adminForm"
:model="admin"
:rules="rules"
label-width="auto"
label-width="150px"
size="small"
>
<el-form-item prop="username" :label="$t('page.userName')">
<el-input v-model="admin.username" />
<el-form-item label="帐号:">
<el-input v-model="admin.username" style="width: 250px" />
</el-form-item>
<el-form-item prop="nickname" :label="$t('permission.nickName')">
<el-input v-model="admin.nickname" />
<el-form-item label="姓名:">
<el-input v-model="admin.nickName" style="width: 250px" />
</el-form-item>
<el-form-item v-if="!isEdit" prop="password" :label="$t('page.password')">
<el-input v-model="admin.password" type="password" show-password />
<el-form-item label="邮箱:">
<el-input v-model="admin.email" style="width: 250px" />
</el-form-item>
<el-form-item prop="phone" :label="$t('permission.phone')">
<el-input v-model="admin.phone" type="tel" />
<el-form-item label="密码:">
<el-input v-model="admin.password" type="password" style="width: 250px" />
</el-form-item>
<el-form-item prop="email" :label="$t('permission.email')">
<el-input v-model="admin.email" type="email" />
</el-form-item>
<el-form-item prop="roleId" :label="$t('permission.role')">
<el-select v-model="admin.roleId" :placeholder="$t('message.pleaseChoose')" size="small">
<el-option
v-for="item in allRoleList"
:key="item.id"
::label="item.name"
:value="item.id"
<el-form-item label="备注:">
<el-input
v-model="admin.note"
type="textarea"
:rows="5"
style="width: 250px"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('permission.isUse')">
<el-form-item label="是否启用:">
<el-radio-group v-model="admin.status">
<el-radio :label="true">{{ $t('permission.enable') }}</el-radio>
<el-radio :label="false">{{ $t('permission.deactivate') }}</el-radio>
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="dialogVisible = false">{{ $t("message.cancel") }}</el-button>
<el-button type="primary" size="small" @click="handleDialogConfirm()">{{ $t("message.confirm") }}</el-button>
<el-button size="small" @click="dialogVisible = false"> </el-button>
<el-button type="primary" size="small" @click="handleDialogConfirm()"> </el-button>
</span>
</el-dialog>
<el-dialog
:close-on-click-modal="false"
:title="$t('permission.editPass')"
:visible.sync="passDialogVisible"
width="40%"
title="分配角色"
:visible.sync="allocDialogVisible"
width="30%"
>
<el-form
ref="passForm"
:model="changePass"
:rules="passRules"
label-width="auto"
size="small"
>
<el-form-item prop="newPassword" :label="$t('permission.newPass')">
<el-input v-model="changePass.newPassword" type="password" show-password />
</el-form-item>
<el-form-item prop="rePassword" :label="$t('permission.confirmPass')">
<el-input v-model="changePass.rePassword" type="password" show-password />
</el-form-item>
</el-form>
<el-select v-model="allocRoleIds" multiple placeholder="请选择" size="small" style="width: 80%">
<el-option
v-for="item in allRoleList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="passDialogVisible = false">{{ $t("message.cancel") }}</el-button>
<el-button type="primary" size="small" @click="handlePassConfirm()">{{ $t("message.confirm") }}</el-button>
<el-button size="small" @click="allocDialogVisible = false"> </el-button>
<el-button type="primary" size="small" @click="handleAllocDialogConfirm()"> </el-button>
</span>
</el-dialog>
</div>
@ -97,135 +84,65 @@
<script>
import List from '@/components/list'
import { editUser, addUser, getUserList, deleteUser, switchStatus, modifyPass } from '@/api/authority/user'
import { getRoleList } from '@/api/authority/role'
import { getUserList, createAdmin, updateAdmin, updateUserStatus, deleteAdmin, getRoleByAdmin, allocRole, fetchAllRoleList } from '@/api/authority/permissionManagement'
// import { newEval } from '@/utils'
import moment from 'moment'
const defaultAdmin = {
id: null,
username: null,
password: null,
nickName: null,
email: null,
status: true
note: null,
status: 1
}
export default {
components: { List },
data() {
return {
getUserList,
// userList: [],
admin: Object.assign({}, defaultAdmin),
changePass: { newPassword: undefined },
dialogVisible: false,
passDialogVisible: false,
isEdit: false,
allocDialogVisible: false,
allocRoleIds: [],
allRoleList: [],
allocAdminId: null
}
},
computed: {
columns() {
return [
{ prop: 'id', label: 'ID', sortable: true, width: 100 },
{ prop: 'username', label: this.$t('page.userName') },
{ prop: 'nickname', label: this.$t('permission.nickName') },
{ prop: 'phone', label: this.$t('permission.phone') },
{ prop: 'email', label: this.$t('permission.email') },
{ prop: 'roleName', label: this.$t('permission.role') },
{ prop: 'status', label: this.$t('permission.isUse'), formatter: (row) => {
allocAdminId: null,
columns: [
// { prop: 'id', label: '', sortable: true },
{ prop: 'username', label: '帐号', sortable: true },
{ prop: 'nickName', label: '姓名', sortable: true },
{ prop: 'email', label: '邮箱', sortable: true },
{ prop: 'createTime', label: '添加时间', sortable: true, formatter: (row) => { return <span>{this.formatDate(row.createTime)}</span> } },
{ prop: 'loginTime', label: '最后登录', sortable: true, formatter: (row) => { return <span>{this.formatDate(row.loginTime)}</span> } },
{ prop: 'status', label: '是否启用', formatter: (row) => {
return <el-switch
onChange={() => this.handleStatusChange(row)}
activeValue={true}
inactiveValue={false}
activeValue={1}
inactiveValue={0}
value={row.status}>
</el-switch>
} },
{
prop: 'more', label: this.$t('page.more'), formatter: (row, index) => {
return <el-dropdown>
<el-button size='mini' className='el-dropdown-link' icon='el-icon-more' circle>
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => this.handleUpdate(index, row)}> <el-dropdown-item><i class='el-icon-edit'></i> {this.$t('page.edit')} </el-dropdown-item> </span>
<span onClick={() => this.handleSelectRole(index, row)}> <el-dropdown-item><i class='el-icon-edit'></i> {this.$t('permission.editPass')} </el-dropdown-item> </span>
<span onClick={() => this.handleDelete(index, row)}> <el-dropdown-item divided><i class='el-icon-delete'></i> { this.$t('message.delete') } </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
prop: 'more', label: '更多操作', formatter: (row, index) => {
return <div>
<el-button type='text' onClick={() => this.handleSelectRole(index, row)}> 分配角色 </el-button>
<el-button type='text' onClick={() => this.handleUpdate(index, row)}> 编辑 </el-button>
<el-button type='text' onClick={() => this.handleDelete(index, row)}> 删除 </el-button>
</div>
}
}
]
},
rules() {
return {
username: [{ required: true, message: this.$t('message.pleaseInput'), trigger: 'blur' }],
nickname: [{ required: true, message: this.$t('message.pleaseInput'), trigger: 'blur' }],
phone: [{ required: true, message: this.$t('message.pleaseInput'), trigger: 'blur' }, { pattern: /^1[3456789]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }],
email: [{ required: true, message: this.$t('message.pleaseInput'), trigger: 'blur' }, { pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/, message: '邮箱格式不正确', trigger: 'blur' }],
password: [{ required: true, message: this.$t('message.pleaseInput'), trigger: 'blur' }],
roleId: [{ required: true, message: this.$t('message.pleaseChoose') }]
}
},
passRules() {
return {
newPassword: [
{ required: true, trigger: 'blur', message: this.$t('permission.create') },
{ min: 6, max: 20, message: this.$t('permission.lengthRequire'), trigger: 'blur' }
],
rePassword: [
{ required: true, message: this.$t('permission.notEmputy') },
{ required: true, validator: this.equalToPassword, trigger: 'blur' }
]
}
}
},
mounted() {
this.getAllRoleList()
},
methods: {
equalToPassword(rule, value, callback) {
if (this.changePass.newPassword !== value) {
callback(new Error(this.$t('permission.differentPass')))
} else {
callback()
}
},
handleDialogConfirm() {
this.$refs.adminForm.validate((valid) => {
if (valid) {
if (this.isEdit) {
editUser({
'id': this.admin.id,
'username': this.admin.username,
'password': this.admin.password,
'nickname': this.admin.nickname,
'phone': this.admin.phone,
'email': this.admin.email,
'status': this.admin.status,
'roleId': this.admin.roleId
}).then(response => {
this.$message({ message: this.$t('message.updateSuccess'), type: 'success' })
this.dialogVisible = false
this.$refs.multipleTable.getList()
})
} else {
addUser(this.admin).then(response => {
this.$message({ message: this.$t('message.addSuccess'), type: 'success' })
this.dialogVisible = false
this.$refs.multipleTable.getList()
})
}
}
})
},
handlePassConfirm() {
this.$refs.passForm.validate((valid) => {
if (valid) {
modifyPass(this.changePass).then(response => {
this.$message({ message: this.$t('message.updateSuccess'), type: 'success' })
this.passDialogVisible = false
this.$refs.multipleTable.getList()
})
}
})
formatDate(t) {
return moment(t).local().format('YYYY-MM-DD hh:mm:ss')
},
handleAdd() {
this.dialogVisible = true
@ -233,46 +150,119 @@ export default {
this.admin = Object.assign({}, defaultAdmin)
},
handleStatusChange(row) {
this.$confirm(this.$t('permission.modifyStatus'), this.$t('page.info'), {
confirmButtonText: this.$t('message.confirm'),
cancelButtonText: this.$t('message.cancel'),
this.$confirm('是否要修改该状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
switchStatus({ id: row.id, status: !row.status }).then(response => {
updateUserStatus(row.id, { status: !row.status ? 1 : 0 }).then(response => {
this.$refs.multipleTable.getList()
this.$message({ message: this.$t('message.updateSuccess'), type: 'success' })
this.$message({
type: 'success',
message: '修改成功!'
})
})
}).catch(() => {
this.$message({ type: 'info', message: this.$t('permission.cancelModify') })
this.$message({
type: 'info',
message: '取消修改'
})
this.$refs.multipleTable.getList()
})
},
handleDelete(id) {
this.$confirm(this.$t('permission.isDelUser'), this.$t('page.info'), {
confirmButtonText: this.$t('message.confirm'),
cancelButtonText: this.$t('message.cancel'),
handleDelete(index, row) {
this.$confirm('是否要删除该用户?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteUser({ id }).then(response => {
this.$message({ type: 'success', message: this.$t('page.deleteSuccess') })
deleteAdmin(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
})
this.$refs.multipleTable.getList()
})
})
},
handleUpdate(row) {
handleUpdate(index, row) {
this.dialogVisible = true
this.isEdit = true
this.admin = Object.assign({}, row)
},
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateAdmin(this.admin.id, this.admin).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
})
this.dialogVisible = false
this.$refs.multipleTable.getList()
})
} else {
createAdmin(this.admin).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
})
this.dialogVisible = false
this.$refs.multipleTable.getList()
})
}
})
},
handleAllocDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const params = new FormData()
params.append('adminId', this.allocAdminId)
params.append('roleIds', this.allocRoleIds)
allocRole(params).then(response => {
this.$message({
message: '分配成功!',
type: 'success'
})
this.allocDialogVisible = false
})
})
},
handleSelectRole(index, row) {
this.changePass.id = row.id
this.passDialogVisible = true
this.allocAdminId = row.id
this.allocDialogVisible = true
this.getRoleListByAdmin(row.id)
},
getAllRoleList() {
getRoleList().then(response => {
fetchAllRoleList().then(response => {
this.allRoleList = response.data
})
},
getRoleListByAdmin(adminId) {
getRoleByAdmin(adminId).then(response => {
const allocRoleList = response.data
this.allocRoleIds = []
if (allocRoleList != null && allocRoleList.length > 0) {
for (let i = 0; i < allocRoleList.length; i++) {
this.allocRoleIds.push(allocRoleList[i].id)
}
}
})
}
}
}
</script>
<style scoped>
.btnBox {
display: flex;
align-items: center;
justify-content: center;
}
</style>

View File

@ -163,6 +163,7 @@ export default {
this.renderer = null
this.camera = null
this.aniId = null
this.raycaster = null
}
},
methods: {
@ -942,7 +943,7 @@ export default {
if (this.camera && this.scene && this.scene.getObjectByName('cs')) {
this.raycaster.setFromCamera(this.mouse, this.camera)
const arr = this.scene.getObjectByName('cs').children.concat(this.scene.getObjectByName('dsxs').children)
var intersects = this.raycaster.intersectObjects(arr, true)
var intersects = (this.raycaster && arr) ? this.raycaster.intersectObjects(arr, true) : []
if (this.activeTab !== 'dsxs') {
if (intersects.length) {
this.scene.getObjectByName('csText').visible = false
@ -978,7 +979,7 @@ export default {
if (this.camera && this.scene && this.raycaster) {
this.raycaster.setFromCamera(this.mouse, this.camera)
const arr = this.scene.getObjectByName('cs')?.children
var intersects = this.raycaster.intersectObjects(arr, true)
var intersects = this.raycaster ? this.raycaster.intersectObjects(arr, true) : []
if (intersects && intersects.length) {
const hubCode = intersects[0].object.userData.hubCode
if (hubCode === 9 || hubCode === 5) {

View File

@ -116,7 +116,7 @@ export default {
this.orbitControls.update()
this.zoomAnimate()
this.originPosition = this.camera.position
console.log(this.originPosition)
// console.log(this.originPosition)
const tweenL1 = new TWEEN.Tween(
this.camera.position).to({ x: -400, y: 900, z: 1700 }, 1800)
.easing(TWEEN.Easing.Sinusoidal.InOut)
@ -332,7 +332,7 @@ export default {
return material
},
flyAnimation(delta = 0.015) {
console.log(this.flyArr)
// console.log(this.flyArr)
TWEEN.update()
if (delta > 0.2) return
this.flyArr.forEach(elem => {

View File

@ -0,0 +1,234 @@
<template>
<div class="appCenter">
<el-card>
<h2>应用中心</h2>
<div class="list-btns">
<el-form :inline="true" class="demo-form-inline">
<el-form-item label="应用类型">
<el-select
v-model="filterData.instance_type"
size="medium"
>
<el-option label="全部" :value="0" />
<el-option label="应用实例" :value="1" />
<el-option label="模型实例" :value="2" />
</el-select>
</el-form-item>
<el-form-item
label="应用名称"
>
<el-input
v-model="filterData.instance_name"
size="medium"
@enter="onSearchClick"
/>
</el-form-item>
<el-form-item>
<el-button
size="medium"
type="info"
@click="onSearchClick"
>
{{ $t('message.search') }}
</el-button>
<el-button
size="medium"
@click="resetSearch"
>
{{ $t('message.reset') }}
</el-button>
</el-form-item>
</el-form>
</div>
<el-row :gutter="20">
<el-col v-for="(item,index) in appCenter" :key="index" :span="6">
<el-card class="instanceCard">
<img :src="item.logoPath" alt="">
<p class="title">{{ item.instanceName }}</p>
<el-tag>{{ item.instanceType === 1 ? '应用实例' : '模型实例' }}</el-tag>
<el-tag>{{ item.instanceClassChinese }}</el-tag>
<br>
<p>描述 </p>
<div class="desc">{{ item.description }}</div>
<p class="version">版本 {{ item.version }}</p>
<el-button
type="primary"
@click="goDetail(item)"
>立即启用</el-button>
</el-card>
</el-col>
</el-row>
<el-pagination
background
:hide-on-single-page="true"
:current-page="page"
:page-size="10"
layout="prev, pager, next"
:total="total"
@current-change="currentChange"
/>
</el-card>
</div>
</template>
<script>
import { getHpcInstanceList } from '@/api/task/task'
export default {
data() {
return {
filterData: {
instance_type: 0,
instance_name: ''
},
appCenter: [
],
fileList: [],
page: 1,
size: 12,
total: 0
}
},
mounted() {
this.getList()
},
methods: {
currentChange(e) {
this.page = e
this.getList()
},
getList() {
getHpcInstanceList({ ...this.filterData, pageSize: this.size, pageNum: this.page }).then(e => {
this.appCenter = e.data.list
this.total = e.total
})
},
onSearchClick() {
this.getList()
},
resetSearch() {
this.filterData = {
instance_type: 0,
instance_name: ''
}
this.getList()
},
handleClick() {
if (this.activeName === '1') {
this.form = { env: 'python' }
}
},
goDetail(val) {
// this.form = val;
// this.activeName = '2';
// this.$router.push({ path: '/taskManagement/createInstance' })
}
}
}
</script>
<style lang="scss">
.appCenter{
// margin: 30px 20px;
// .opr-btn{
// font-size: 16px;
// padding: 10px 36px;
// margin-bottom: 30px;
// }
.el-button--primary{
background-color: #468EFC;
}
}
.instanceCard{
img{
width: 5rem;
height: 5rem;
display: block;
float: left;
background: #ddd;
margin: 0 2rem;
margin-right: 0rem;
}
margin: 30px auto;
margin-top: 0;
.el-tag{
margin-left:1rem;
float: left;
}
.desc{
margin: 0.05rem 2rem;
padding: 0.5rem;
border: 1px solid #b1b0b0;
text-indent: 1rem;
text-align: left;
height: 12vh;
/* white-space: nowrap; */
text-overflow: ellipsis;
overflow: hidden;
}
.el-card__body{
padding:0;
padding-bottom: 20px;
text-align: center;
}
p{
margin: 10px 30px;
clear: both;
// margin-top: 1rem;
font-size: 0.875rem;
text-align: left;
position: relative;
text-indent: 1em;
padding-top: 1rem;
}
p:before{
content: '';
position: absolute;
width: 0.5rem;
height: 0.5rem;
background: #2FB4AA;
border-radius: 50%;
top: 1.25rem;
left: 0em;
}
.title{
margin:20px 0;
// margin-bottom: 10px;
font-size: 18px;
text-indent: 1rem;
font-weight: bold;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
clear: none;
padding-top: 0;
}
.title:before{
width: 0;
height: 0;
}
.version{
height: 3vh;
padding-top: 0rem
}
.version:before{
top: 0.25rem;
}
.el-button--text{
line-height: 1.1rem;
}
.el-button{
margin:10px;
border:1px solid #468EFC ;
border-radius: 0;
}
.el-button--default{
color: #468EFC;
}
.insideFloatRight {
margin: 0;
padding:0;
float: right;
}
}
</style>

View File

@ -20,6 +20,7 @@
tooltip-effect="dark"
>
<template v-slot:filterBtns>
<el-button size="medium" type="primary" @click="openAppCenter">超算应用中心</el-button>
<el-button size="medium" type="primary" @click="createItem">{{ $t('message.create') }}</el-button>
</template>
</List>
@ -88,6 +89,9 @@ export default {
// }
},
methods: {
openAppCenter() {
this.$router.push({ path: `appCenter` })
},
setColumn() {
return [
{ prop: 'name', label: this.$t('page.taskName'), sortable: true, formatter: (row) => { return <a onClick={() => this.viewDetail(row)}>{row.name}</a> } },

View File

@ -0,0 +1,682 @@
<template>
<div>
<h3> {{ $t('page.createTrainingTask') }} </h3>
<!-- <el-alert
v-if="!adapterId"
:title="$t('page.selectDiver')"
type="warning"
show-icon
/> -->
<br>
<el-form
ref="formData"
class="form-wrap"
label-position="left"
:model="formData"
:rules="formDataRules"
>
<el-form-item :label="$t('page.taskName')" prop="name">
<el-input
v-model="formData.name"
:placeholder="$t('page.inputWarn')"
:max-length="30"
/>
</el-form-item>
<el-form-item :label="$t('page.taskDes')" prop="description">
<el-input
v-model="formData.description"
:max-length="100"
/>
</el-form-item>
<el-form-item :label="$t('page.resourceType')" prop="resource">
<el-radio-group v-model="formData.resource" @input="chooseType">
<el-radio-button v-for="(item,index) in resourceRanges" :key="item.type+index" :label="item.type" />
</el-radio-group>
</el-form-item>
<el-form-item label="资源范围选择">
<el-card v-show="formData.resource">
<table class="rangeSelectArea">
<tr>
<td>CPU使用范围</td>
<td><el-slider
v-model="formData.cpu"
:min="0"
:max="singeRange.cpu.max"
range
/></td>
</tr>
<tr>
<td>内存使用范围</td>
<td><el-slider
v-model="formData.memory"
:min="0"
:max="singeRange.memory.max"
range
show-stops
/></td>
</tr>
<tr>
<td>算力卡张数范围</td>
<td>
<el-input-number v-model="formData.gpu.min" :min="0" :max="singeRange.gpuNumber" />
-
<el-input-number v-model="formData.gpu.max" :min="formData.cardStart" :max="singeRange.gpuNumber" />
</td>
</tr>
<tr>
<td>存储使用范围</td>
<td>
<el-input-number v-model="formData.storage.min" :min="0" :max="singeRange.storage.max" />G
-
<el-input-number v-model="formData.storage.max" :min="formData.storage.min" :max="singeRange.storage.max" />G
</td>
</tr>
</table>
</el-card>
</el-form-item>
<el-form-item :label="$t('page.imageName')" prop="image">
<el-select v-model="formData.image">
<el-option v-for="item in imageList" :key="item.ID" :label="item.info.name" :value="item.ID" />
</el-select>
</el-form-item>
<el-form-item :label="$t('page.modelName')" prop="model">
<el-row :gutter="20">
<el-col :span="10">
<el-input v-model="formData.modelName" placeholder="请选择模型" :readonly="true" />
</el-col>
<el-col :span="10">
<el-button @click="() => { dataType = 'model'; clickSelect()}">选择模型</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item :label="$t('page.algorithmName')" prop="code">
<el-row :gutter="20">
<el-col :span="10">
<el-select v-model="formData.code" placeholder="请选择名称" @change="selectCode">
<el-option v-for="item in codeList" :key="item.ID" :label="item.info.name" :value="item.ID" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item :label="$t('page.datasetName')" prop="dataset">
<el-row :gutter="20">
<el-col :span="10">
<el-input v-model="formData.datasetName" placeholder="请选择数据集" :readonly="true" />
</el-col>
<el-col :span="10">
<el-button @click="dataType = 'dataset'; clickSelect()">选择数据集</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item :label="$t('page.resourceRecom')">
<el-button type="primary" style="margin-bottom: 1vh;" @click="getResource">刷新</el-button>
<table class="tableInput width100">
<tr>
<th>{{ $t('page.centerName') }}</th>
<!-- <th>ID</th> -->
<th>选择</th>
<th>CPU核数</th>
<th>GPU张数</th>
<th>卡类型</th>
<th>内存1</th>
<th>内存2</th>
<th>存储空间</th>
<th>积分</th>
<th>{{ $t('page.more') }}</th>
</tr>
<tr v-for="(it, index) in algorithmList" :key="index">
<td>
{{ clusterList.find(s => s.id === it.clusterID).name }}
</td>
<td>
<el-checkbox v-model="it.checked" />
</td>
<td>
{{ it.cpu }}
</td>
<td>
{{ it.gpu }}
</td>
<td>
{{ it.type }}
</td>
<td>
{{ it.ram }}
</td>
<td>
{{ it.vram }}
</td>
<td>
{{ it.storage }}
</td>
<td>
{{ it.jifen }}
</td>
<td>
<el-button @click="viewAlgo(index)">自定义参数编辑</el-button>
</td>
</tr>
</table>
</el-form-item>
<!-- <el-form-item label="计算结果选择" prop="reasonTarget">
<el-checkbox-group v-model="formData.reasonTarget">
<el-checkbox label="1">回源</el-checkbox>
<el-checkbox label="2">直接创建为模型</el-checkbox>
</el-checkbox-group>
</el-form-item> -->
<el-form-item label="调度策略" prop="scheduleStrategy">
<el-radio v-model="formData.scheduleStrategy" label="1">动态集群权重填写</el-radio>
<el-radio v-model="formData.scheduleStrategy" label="2" disabled>静态集群权重填写</el-radio>
<el-card v-if="formData.scheduleStrategy === '1'">
<el-radio v-model="formData.strategy" label="dataLocality">数据优先</el-radio>
<el-radio v-model="formData.strategy" label="leastLoadFirst">最小负载</el-radio>
</el-card>
<el-card v-if="formData.scheduleStrategy === '2'">
<el-form-item :label="$t('page.copiesNum')" prop="replicas">
<el-input-number v-model="formData.replicas" />
</el-form-item>
<el-form-item :label="$t('page.staticWeight')" prop="strategy">
<table class="tableInput">
<tr v-for="env in formData.staticWeightMap" :key="env.key">
<td>{{ env.key }}</td>
<td>
<el-input-number
v-model="env.value"
size="small"
/>
</td>
</tr>
</table>
</el-form-item>
</el-card>
</el-form-item>
</el-form>
<el-dialog v-if="dialogFormVisible" :close-on-click-modal="false" title="自定义参数设置" :visible.sync="dialogFormVisible">
<el-form ref="editInfoForm" :model="editInfoForm">
<el-form-item label="子算法编辑">
<el-radio-group v-model="editInfoForm.codeType">
<el-radio-button label="1">直接沿用算法</el-radio-button>
<el-radio-button label="2">选择子算法</el-radio-button>
<el-radio-button label="3">新建子算法</el-radio-button>
</el-radio-group>
<List
v-if="editInfoForm.codeType === '3'"
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:table-list-data="allFileList"
:pagination="false"
tooltip-effect="dark"
/>
</el-form-item>
<el-form-item :label="$t('page.programVariables')">
<el-card>
<el-button
v-if="editInfoForm.params.length === 0"
type="primary"
icon="el-icon-circle-plus"
@click="appendParams"
/>
<template v-else>
<div v-for="(env, index) in editInfoForm.params" :key="env.id" class="network-wrap">
<div class="list-item">
<el-input v-model="env.key" placeholder="key" />
</div>
<div class="list-item">
<span>=</span>
</div>
<div class="list-item">
<el-input v-model="env.value" placeholder="value" />
</div>
<div class="list-item">
<el-button
v-if="index === 0"
icon="el-icon-circle-plus"
style="margin-right:8px"
@click="appendParams"
/>
<el-button v-else icon="el-icon-delete-solid" @click="removeParams(index)" />
</div>
</div>
</template>
</el-card>
</el-form-item>
<el-form-item :label="$t('page.commandLine')" prop="command">
<el-input
v-model="editInfoForm.command"
type="textarea"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">{{ $t("message.cancel") }}</el-button>
<el-button type="primary" @click="submitInfoEdit">{{ $t("message.submit") }}</el-button>
</div>
</el-dialog>
<el-dialog v-if="codeVisible" :close-on-click-modal="false" title="自定义参数设置" :visible.sync="codeVisible">
<codemirror v-model="code" class="code-mirror" :options="cmOption" />
<div slot="footer" class="dialog-footer">
<el-button @click="closeCodeVisible">{{ $t("message.cancel") }}</el-button>
<el-button type="primary" @click="submitCode">{{ $t("message.submit") }}</el-button>
</div>
</el-dialog>
<select-item v-if="dialogSelectDatasetVisible" v-model="dialogSelectDatasetVisible" :data-type="dataType" @select-item="selectItem" />
</div>
</template>
<script>
// import { getInstanceListByModelType, getSubInstanceList } from '@/api/task/task'
import generate from 'nanoid/generate'
import List from '@/components/list'
import { numberToStr } from '@/utils/data-process'
import { getResourceRange, getResourceListByfilter, getFileList, getBindingList, downloadFile, updatecode, uploadData } from '@/api/jcs/jcs'
import selectItem from '@/views/fileManagement/datasetManagement/selectItem.vue'
import { mapGetters } from 'vuex'
import { getClusterList } from '@/api/container/cluster'
export default {
components: { selectItem, List },
props: {
adapterId: {
type: String,
default: ''
}
},
data() {
return {
codeVisible: false,
code: ``,
cmOption: {
theme: 'darcula',
autoCloseBrackets: true,
tabSize: 4,
styleActiveLine: true,
lineNumbers: true,
line: true,
mode: 'text/x-yaml'
},
currentFile: {},
allFileList: [],
columns: [
{ prop: 'name', label: '文件名', minWidth: '30%', formatter: (row) => {
const img1 = require('@/assets/img/file-1.png')
return <div>
{((this.path === '/' ? '' : this.path) + row.path) === this.editInfoForm.filePath ? <i class='el-icon-s-flag' /> : ''}
{row.objectID === -1
? <span>
<img style='height: 1.2rem;margin-right: 0.5rem;vertical-align: text-bottom;' src={img1} />
<span>{row.path.split('/')[row.path.split('/').length - 1]}</span>
</span>
: <span>{row.path.indexOf('/') === 0 ? row.path.substring(row.path.indexOf('/') + 1) : row.path}</span>
}
</div>
}
},
{ prop: 'createTime', label: '创建时间', minWidth: '30%', formatter: (row) => { return <span>{row.objectID === -1 ? '-' : row.createTime}</span> } },
{ prop: 'size', label: '大小', minWidth: '20%', formatter: (row) => { return <span>{row.objectID !== -1 ? numberToStr(row.size - 0, 'B') : '-'}</span> } },
{ prop: '', label: '操作', minWidth: '20%', formatter: (row) => {
return row.objectID !== -1 && <div>
<el-button type='text' onClick={() => this.downloadCode(row)}> 编辑 </el-button>
</div>
} }
],
formData: {
'name': 'trainingtask-' + generate('abcdefghijklmnopqrstuvwxyz', 12),
'description': '',
'resource': '',
'strategy': '',
'datasetName': '',
'scheduleStrategy': '',
'envs': [],
'gpu': {
'min': 0,
'max': 0
},
'storage': {
'min': 0,
'max': 0
},
'reasonTarget': [],
'packageID': ''
},
editInfoForm: {
params: []
},
fileList: [],
instanceList: [],
resourceRanges: [],
singeRange: {
'gpuNumber': 0,
'cpu': {
'max': 0
},
'memory': {
'max': 0
},
'storage': {
'max': 0
}
},
dialogFormVisible: false,
algorithmList: [],
storageIDs: [],
codeList: [],
codeVersionList: [],
algorithmIndex: null,
imageList: [],
modelList: [],
getNewVersion: false,
dialogSelectDatasetVisible: false,
clusterList: []
}
},
computed: {
...mapGetters([
'userID'
]),
formDataRules() {
return {
name: [
{ required: true, message: this.$t('check.input') + this.$t('message.name') }
// {
// pattern: /^[a-z]([-a-z0-9]*[a-z0-9])?$/,
// message: this.$t('check.inputInvalid')
// }
// { validator: this.nameValidator }
],
description: [
{ required: true, message: this.$t('check.input') + this.$t('page.taskDes') }
],
image: [{ required: true, message: this.$t('check.requireSelect'), trigger: 'change' }],
model: [{ required: true, message: this.$t('check.requireSelect'), trigger: 'change' }],
code: [{ required: true, message: this.$t('check.requireSelect'), trigger: 'change' }],
dataset: [{ required: true, message: this.$t('check.requireSelect'), trigger: 'change' }],
resource: [{ required: true, message: this.$t('check.requireSelect'), trigger: 'change' }]
}
}
},
watch: {
},
mounted() {
getResourceRange({ userID: this.userID }).then(res => {
const arr = res.data.resourceRanges || []
if (arr.length !== 0) {
this.resourceRanges = arr.filter(e => {
return !(e.gpuNumber === 0 || e.cpu.max === 0 || e.memory.max === 0 || e.storage.max === 0)
})
}
})
getClusterList({ 'storageSchedule': 1, pageSize: 1000, pageNum: 1 }).then(e => {
this.clusterList = e?.data?.list || []
})
this.getAllList()
},
methods: {
downloadCode(row) {
this.currentFile = row
downloadFile({
userID: this.userID,
objectID: row.objectID
}).then(n => {
this.codeVisible = true
this.code = n + ''
})
},
closeCodeVisible() {
if (this.getNewVersion) {
this.selectCode(this.form.code)
}
this.codeVisible = false
this.getNewVersion = false
},
submitCode() {
updatecode({
'userID': this.userID,
'bucketID': this.currentFile.bucketID,
'packageID': this.currentFile.packageID,
'packageName': this.currentFile.packageName
}).then(n => {
const form = new FormData()
form.append('info', JSON.stringify({
'userID': this.userID,
'packageID': n.data.newPackage?.packageID,
'loadTo': this.storageIDs,
'loadToPath': ['/']
}))
const blob = new Blob([this.code], { type: 'text/plain' })
const file = new File([blob], encodeURIComponent(this.currentFile.path), { type: 'text/plain' })
form.append(`files`, file)
uploadData(form).then(e => {
if (e.code === 'OK') {
this.$message.success('修改成功')
this.codeVisible = false
this.getNewVersion = true
}
})
})
},
selectCode(val) {
getBindingList({ dataType: 'code', param: { userID: this.userID, bindingID: val, type: 'private' }}).then(res => {
const arr = res?.data?.datas?.[0]?.packages?.[0]?.versions || []
//
this.formData.packageID = arr[0]?.packageID
this.codeVersionList = arr
})
},
selectItem(data) {
if (this.dataType === 'dataset') {
this.formData.dataset = data.ID
this.formData.datasetName = data.info?.name
} else if (this.dataType === 'model') {
this.formData.model = data.ID
this.formData.modelName = data.info?.name
}
this.dialogSelectDatasetVisible = false
},
clickSelect() {
this.dialogSelectDatasetVisible = true
},
getAllList() {
getBindingList({ dataType: 'code', param: { userID: this.userID, bindingID: -1, type: 'private' }}).then(res => {
this.codeList = res?.data?.datas || []
})
getBindingList({ dataType: 'image', param: { userID: this.userID, bindingID: -1, type: 'private' }}).then(res => {
this.imageList = res?.data?.datas || []
})
},
getResource() {
this.$refs.formData.validate((valid) => {
if (valid) {
getResourceListByfilter({
'queryResource': {
cpu: { min: this.formData.cpu[0], max: this.formData.cpu[1] },
memory: { min: this.formData.memory[0], max: this.formData.memory[1] },
gpu: this.formData.gpu,
storage: this.formData.storage,
type: this.formData.resource
}
}).then(res => {
const algorithmList = []
res.data?.resource?.forEach(n => {
const arr = n.resources.filter(r => r.resource.type === this.formData.resource)
arr?.forEach(i => {
algorithmList.push({
clusterID: n.clusterID,
type: i.resource.type,
cpu: i.baseResources?.find(r => r.type === 'CPU')?.available.value,
ram: i.baseResources?.find(r => r.name === 'RAM')?.available.value,
vram: i.baseResources?.find(r => r.name === 'VRAM')?.available.value,
storage: i.baseResources?.find(r => r.type === 'STORAGE')?.available.value,
runtime: { command: '', envs: {}, params: {}}
})
})
})
this.algorithmList = algorithmList
})
} else {
this.$message({
message: '请先输入上述内容',
type: 'warning'
})
}
})
},
chooseType(val) {
const range = this.resourceRanges.find(e => e.type === val)
this.singeRange = range
},
submitUpload() {
//
return false
},
submitInfoEdit() {
// if ( === '') {
// this.$message.error('')
// return
// }
const obj = {}
this.editInfoForm.params.forEach((item) => {
obj[item.key] = item.value
})
this.dialogFormVisible = false
this.algorithmList[this.algorithmIndex].runtime = {
command: this.editInfoForm.command,
envs: obj,
params: obj
}
},
appendParams() {
this.editInfoForm.params.push(
{
key: '',
value: ''
}
)
},
removeParams(i) {
this.editInfoForm.params.splice(i, 1)
},
viewAlgo(index) {
this.algorithmIndex = index
if (this.formData?.code) {
const params = {
'queryParams': {
'dataType': 'code',
'userID': this.userID,
'packageID': Number(this.formData.packageID),
'path': '',
'CurrentPage': 1,
'pageSize': 9999,
'orderBy': 'name'
}
}
getFileList(params).then(e => {
if (e.data?.uploadedDatas?.[0].objects) {
this.allFileList = e.data?.uploadedDatas?.[0].objects.map(n => ({
...n,
bucketID: e.data?.uploadedDatas?.[0].bucketID,
packageName: e.data?.uploadedDatas?.[0].packageName
}))
this.storageIDs = e.data?.uploadedDatas[0].uploadedCluster.map(e => e.storageID).filter(Boolean)
}
})
this.getNewVersion = false
this.dialogFormVisible = true
} else {
this.$message({ message: '请选择算法', type: 'warning' })
}
},
checkForm() {
let returnVal
this.$refs.formData.validate((valid) => {
if (valid) {
const clusters = []
this.algorithmList.forEach(n => {
if (n.checked) {
clusters.push({
clusterID: n.clusterID,
runtime: { command: n.runtime.command, envs: n.runtime.envs, params: n.runtime.params },
resources: [
{ type: 'CPU', number: n.cpu },
{ type: 'GPU', number: n.gpu },
{ type: 'RAM', number: n.ram },
{ type: 'VRAM', number: n.vram }
]
})
}
})
const form = {
'userID': this.userID,
'jobSetInfo': {
'jobs': [
{
'localJobID': '1',
'name': this.formData.name,
'description': this.formData.description,
'type': 'PCM',
'files': {
'dataset': {
'type': 'Binding',
'bindingID': this.formData.dataset
},
'model': {
'type': 'Binding',
'bindingID': this.formData.model
},
'code': {
'type': 'Binding',
'bindingID': this.formData.code
},
'image': {
'type': 'Binding',
'bindingID': this.formData.image
}
},
'jobResources': {
'scheduleStrategy': this.formData.strategy,
// dataLocality, leastLoadFirst
'clusters': clusters
}
}
]
}
}
returnVal = form
} else {
returnVal = false
}
})
return returnVal
}
}
}
</script>
<style lang="scss">
.form-wrap .el-form-item__label-wrap .el-form-item__label{
text-indent: -1rem!important;
}
.rangeSelectArea{
padding-right: 1rem;
width: 100%;
td:nth-child(odd) {
width: 8rem;
}
tr{
height: 4rem;
}
// .el-slider{
// // width: 50%;
// }
}
.network-wrap {
display: flex;
align-items: center;
margin-bottom: 0.625rem;
// margin: 0 0.625rem;
.list-item+.list-item {
margin-left: 0.5rem;
}
}
</style>

View File

@ -27,7 +27,7 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="$t('page.dispatchAdapter')" prop="adapterId">
<el-form-item v-if="taskType!=='trainingTask'" :label="$t('page.dispatchAdapter')" prop="adapterId">
<el-select v-model="formData.adapterId" :disabled="isEdit" class="selectPro" :placeholder="$t('message.pleaseChoose')">
<el-option
v-for="item in adapterList[taskType]"
@ -45,9 +45,10 @@
<application-form v-show="cpType === 'cloud'&&taskType === 'application'" ref="application" />
<hpc-create v-if="cpType === 'hpc'&&taskType === 'hpcBase'" ref="hpcBase" />
<vasp-create v-if="cpType === 'hpc'&&taskType === 'hpcVasp'" ref="hpcVasp" />
<ai-create v-if="cpType === 'ai'&&taskType!=='deductive'" ref="aiBase" :type="taskType" :adapter-id="formData.adapterId" />
<ai-create v-if="cpType === 'ai'&&taskType!=='deductive'&&taskType!=='trainingTask'" ref="aiBase" :type="taskType" :adapter-id="formData.adapterId" />
<vm-form v-if="taskType === 'virtualmachine'" ref="virtualmachine" />
<deductive-form v-if="taskType === 'deductive'" ref="deductive" :adapter-id="formData.adapterId" />
<training-task v-if="taskType === 'trainingTask'" ref="trainingTask" :adapter-id="formData.adapterId" />
</div>
<!-- <div v-if="taskType === 'virtualmachine' && selectCluster">
</div> -->
@ -204,11 +205,11 @@
<el-row type="flex" justify="end">
<el-col :span="2.5">
<el-button size="medium" @click="goBack">{{ $t("message.cancel") }}</el-button>
<el-button v-if="!selectCluster&&!strategySetting&&taskType!=='aiCard'&&taskType!=='deductive'&&taskType!=='hpcVasp'" size="medium" type="primary" @click="next">{{ $t('message.next') }}</el-button>
<el-button v-if="!selectCluster&&!strategySetting&&taskType!=='aiCard'&&taskType!=='deductive'&&taskType!=='hpcVasp'&&taskType !== 'trainingTask'" size="medium" type="primary" @click="next">{{ $t('message.next') }}</el-button>
<el-button v-if="selectCluster" size="medium" type="primary" @click="selectCluster=false">{{ $t('message.before') }}</el-button>
<el-button v-if="selectCluster&&!strategySetting&&taskType!=='aiCard'&&taskType!=='deductive'" size="medium" type="primary" @click="setStrategy">{{ $t('message.next') }}</el-button>
<el-button v-if="selectCluster&&!strategySetting&&taskType!=='aiCard'&&taskType!=='deductive'&&taskType !== 'trainingTask'" size="medium" type="primary" @click="setStrategy">{{ $t('message.next') }}</el-button>
<el-button v-if="strategySetting" size="medium" type="primary" @click="strategySetting=false;selectCluster=true">{{ $t('message.before') }}</el-button>
<el-button v-if="strategySetting || taskType==='aiCard' || taskType==='deductive' || taskType==='hpcVasp'" v-loading="submitLoading" size="medium" type="primary" @click="saveForm">{{ $t('message.create') }}</el-button>
<el-button v-if="strategySetting || taskType==='aiCard' || taskType==='deductive' || taskType==='hpcVasp' || taskType === 'trainingTask'" v-loading="submitLoading" size="medium" type="primary" @click="saveForm">{{ $t('message.create') }}</el-button>
<!-- <el-button size="medium" type="primary" @click="saveForm">{{ $t("message.easyCreate") }}</el-button> -->
</el-col>
</el-row>
@ -223,14 +224,15 @@ import hpcCreate from './components/hpcCreate.vue'
import vaspCreate from './components/hpcVasp.vue'
import { getClusterList } from '@/api/container/cluster'
import aiCreate from './components/aiCreate.vue'
import trainingTask from './components/trainingTask.vue'
import { getAdapterList } from '@/api/pcm/adapter'
import vmForm from './components/virtualmachineForm.vue'
import deductiveForm from './components/deductiveForm.vue'
import { mapGetters } from 'vuex'
import { createTrainingTask } from '@/api/jcs/jcs'
// import jobForm from './components/jobForm.vue'
export default {
components: { applicationForm, List, hpcCreate, aiCreate, vmForm, deductiveForm, vaspCreate },
components: { applicationForm, List, hpcCreate, aiCreate, vmForm, deductiveForm, vaspCreate, trainingTask },
data() {
return {
getClusterList,
@ -263,7 +265,8 @@ export default {
'ai': {
'aiBase': 'createAibase',
'aiCard': 'createAiCard',
'deductive': 'createDeductiveTask'
'deductive': 'createDeductiveTask',
'trainingTask': 'createTrainingTask'
}
},
adapterList: {
@ -392,6 +395,10 @@ export default {
list[i] = e.data.list.filter(r => r.type === '1')
break
}
case 'trainingTask': {
list[i] = e.data.list.filter(r => r.type === '1')
break
}
default: {
list[i] = e.data.list.filter(r => r.resourceType === '01')
break
@ -457,14 +464,14 @@ export default {
this.strategySetting = true
},
saveForm() {
if (!this.formData.adapterId) {
if (!this.formData.adapterId && this.taskType !== 'trainingTask') {
this.$message.warning(this.$t('page.selectDiver'))
return false
}
const type = this.taskType === 'aiCard' ? 'aiBase' : this.taskType
let formHook = this.$refs[type].checkForm()
if (!formHook) return false
if (this.taskType !== 'deductive') {
if (this.taskType !== 'deductive' && this.taskType !== 'trainingTask') {
if (this.taskType !== 'aiCard') {
formHook = { ...formHook, ...this.formData }
}
@ -482,7 +489,7 @@ export default {
formHook.adapterIds = [formHook.adapterId]
delete formHook.adapterId
}
console.log(formHook)
this.submitLoading = true
switch (type) {
case 'application': {
@ -573,6 +580,18 @@ export default {
break
}
case 'trainingTask': {
// formHook.clusterIds = formHook.aiClusterIds
delete formHook.aiClusterIds
console.log(formHook)
createTrainingTask(formHook).then(() => {
this.$message.success(this.$t('page.createdSuccess'))
this.$router.push({ path: '/taskManagement/taskList' })
}).catch(e => {
this.submitLoading = false
})
break
}
case 'virtualmachine': {
formHook.clusterIds = formHook.aiClusterIds
delete formHook.aiClusterIds

View File

@ -121,6 +121,8 @@ export default {
</el-button>
<el-dropdown-menu slot='dropdown'>
<span onClick={() => this.viewDetail(row)}> <el-dropdown-item> {txt} </el-dropdown-item> </span>
<span onClick={() => this.viewResult(row)}> <el-dropdown-item> 计算结果 </el-dropdown-item> </span>
</el-dropdown-menu>
</el-dropdown>
</div>
@ -130,6 +132,9 @@ export default {
viewDetail(row) {
this.$router.push({ path: `detail`, query: { id: row.id }})
},
viewResult(row) {
this.$router.push({ path: `result`, query: { id: row.id }})
},
createItem() {
this.$router.push({ path: `create` })
}

View File

@ -0,0 +1,224 @@
<template>
<div class="list-detail">
<el-card>
<el-page-header :content="$t('page.result')" @back="goBack" />
<p />
任务结果查看
<el-tabs v-model="activeName" type="card">
<el-tab-pane :label="$t('page.taskProperties')">
<FormData :data="formData" :columns="1" :data-map="dataMap" />
<p> {{ $t('page.inCluster') }} {{ formData.clusterInfos ? formData.clusterInfos.map(e=> e.name).join(',') : '-' }}</p>
<p>子任务列表:</p>
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="columns"
:table-list-data="formData.subTaskInfos"
tooltip-effect="dark"
/>
<!-- <p>资源详情:</p>
<div class="resourceList">
<div class="cpu"><p>CPU</p><p>100%</p></div>
<div class="mem"><p>MEM</p><p>100%</p></div>
<div class="disk"><p>DISK</p><p>100%</p></div>
</div> -->
</el-tab-pane>
<!-- <el-tab-pane label="任务拓扑">
<FormData :data="formData" :data-map="dataMap" />
</el-tab-pane> -->
<el-tab-pane v-if="formData.clusterInfos && formData.taskTypeDict !== '11' && formData.taskTypeDict !== '12'" :label="$t('page.taskLog')">
<el-select v-if="!formData.subTaskInfos[0].workDir" v-model="cluster" style="width:70%" @change="selectCluster(cluster)">
<el-option
v-for="item in formData.clusterInfos"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<p />
<el-input v-model="log" type="textarea" rows="20" />
</el-tab-pane>
<el-tab-pane v-if="formData.taskTypeDict === '11' || formData.taskTypeDict === '12'" :label="$t('page.taskResult')">
<el-form>
<!-- <el-form-item :label="$t('page.taskCmdInput')">
<el-input v-model="taskInput" type="textarea" rows="10" />
</el-form-item>
<el-form-item :label="$t('page.taskSimulateNum')">
<el-input v-model="taskSimulateNum" style="width: 20%;" />
</el-form-item>
<el-button type="primary">{{ $t('message.submit') }}</el-button>
<el-divider />
<el-form-item :label="$t('page.taskResult')">
<el-input v-model="taskOutput" type="textarea" rows="10" />
</el-form-item> -->
<!-- <el-button type="primary" @click="submitUpload">{{ $t('message.submit') }}</el-button> -->
<!-- <el-divider /> -->
<el-form-item v-if="formData.taskTypeDict === '11'" :label="$t('page.taskResult')">
<List
ref="multipleTable"
:key="$i18n.locale"
class="multipleTable"
:columns="taskColumns"
:table-list-data="taskResult"
tooltip-effect="dark"
/>
</el-form-item>
<el-form-item v-if="formData.taskTypeDict === '12'" :label="$t('page.selectCluster')">
<el-select v-if="formData.subTaskInfos" v-model="inferId" style="width:30%">
<el-option
v-for="item in formData.subTaskInfos"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<br>
<bootChart v-if="formData.taskTypeDict === '12'" :id="inferId" />
</el-form>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<script>
import List from '@/components/list'
import { FormData } from '@/components/FormData'
import { getTaskDetail, getTaskLog, getDeductiveDetail, downloadVaspContent } from '@/api/task/task'
import { mapGetters } from 'vuex'
import bootChart from './components/boot'
export default {
components: { FormData, List, bootChart },
data() {
return {
dataMap: {
name: this.$t('page.taskName'),
description: this.$t('page.taskDes'),
startTime: this.$t('page.startTime2'),
endTime: this.$t('page.endTime'),
strategy: this.$t('page.policy'),
synergyStatus: this.$t('page.synergyStatus')
},
taskInput: '',
taskSimulateNum: '',
taskOutput: '',
cluster: '',
formData: {
'clusterInfos': [],
'name': '',
'description': '',
'startTime': '',
'endTime': '',
'strategy': '0',
'synergyStatus': '0'
},
activeName: '',
log: '',
taskResult: [],
inferId: ''
}
},
computed: {
...mapGetters([
'dict'
]),
columns() {
return [
{ prop: 'name', label: this.$t('page.subtaskName') },
{ prop: 'clusterName', label: this.$t('page.clusterIn') },
{ prop: 'status', label: this.$t('page.taskStatus') },
{ prop: 'remark', label: this.$t('page.description') }
]
},
taskColumns() {
return [
{ prop: 'imageName', label: this.$t('page.picName') },
{ prop: 'clusterName', label: this.$t('page.clusterIn') },
{ prop: 'card', label: this.$t('page.card') },
{ prop: 'result', label: this.$t('page.result') }
]
}
},
mounted() {
this.getFormData()
},
methods: {
goBack() {
this.$router.push('taskList')
},
getFormData() {
getTaskDetail({ id: this.$route.query.id }).then(res => {
if (res.data) {
this.formData = res.data
this.formData.strategy = this.dict.schedule_Strategy.filter(e => { return res.data.strategy.toString() === e.itemValue })[0][this.$i18n.locale === 'en' ? 'itemText' : 'description']
this.formData.synergyStatus = !res.data.synergyStatus ? this.$t('page.notSynergistic') : this.$t('page.synergistic')
// this.formData.clusterInfos ? this.selectCluster(this.formData.clusterInfos[0]) : ''
// this.cluster = this.formData.clusterInfos ? this.formData.clusterInfos[0] : []
if (this.formData.taskTypeDict === '11') {
getDeductiveDetail(this.$route.query.id).then(e => {
this.taskResult = e.data
})
}
if (this.formData.taskTypeDict === '12') {
this.inferId = this.formData.subTaskInfos[0].id
}
if (res.data.subTaskInfos[0].workDir) {
downloadVaspContent({ workDir: res.data.subTaskInfos[0].workDir, fileName: 'demo.out', clusterId: this.formData.clusterInfos[0].id }).then(e => {
this.log = e
})
}
}
})
},
selectCluster(id) {
this.getLog(this.formData.clusterInfos[0].adapterId, id)
},
getLog(adapterId, clusterId) {
getTaskLog(adapterId, clusterId, this.$route.query.id, 0).then(e => {
this.log = e.log
})
}
}
}
</script>
<style lang="scss" scoped>
.resourceList{
width: 99%;
margin: 20px auto;
.cpu, .disk, .mem{
width: 33%;
height: 15vh;
float: left;
margin: 20px 0;
background: url(../../../assets/images/CPU.png) no-repeat center;
background-size: auto 100%;
p{
text-align: center;
font-weight: bold;
text-shadow: 0px 5px 10px rgba(108,190,254,0.25);
background: linear-gradient(0deg, #A3D2FF 0%, #00C0FA 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-top: 2.5vh;
margin-bottom: 5vh;
}
}
.disk{
background: url(../../../assets/images/DISK.png) no-repeat center;
background-size: auto 100%;
}
.mem{
background: url(../../../assets/images/MEM.png) no-repeat center;
background-size: auto 100%;
}
}
</style>

View File

@ -54,6 +54,18 @@ module.exports = {
// changeOrigin: true,
// secure: false
// },
'/jcs': {
ws: false,
target: 'https://ai4m.jointcloud.net/',
changeOrigin: true,
secure: false
},
'/jsm': {
ws: false,
target: 'https://ai4m.jointcloud.net/',
changeOrigin: true,
secure: false
},
'^/pcm': {
ws: false,
// target: 'https://jcos.jointcloud.net:443/',
@ -63,12 +75,25 @@ module.exports = {
changeOrigin: true,
secure: false
},
'^/jcc-': {
ws: false,
// target: 'https://10.101.15.6/apis', /* 测试环境 */
target: 'https://dev.jointcloud.net/apis',
changeOrigin: true,
secure: false
},
'^/ai4m': {
ws: false,
target: 'https://ai4m.jointcloud.net:443/',
changeOrigin: true,
secure: false
},
'^/blockChain': {
ws: false,
target: 'https://ai4m.jointcloud.net:443/',
changeOrigin: true,
secure: false
},
'^/auth': {
ws: false,
target: 'https://comnet.jointcloud.net/',