eslint fixed and 页面权限配置 and 默认权限数据加载.

This commit is contained in:
许晓东
2023-05-17 22:29:04 +08:00
parent b08be2aa65
commit 7e98a58f60
37 changed files with 686 additions and 297 deletions

View File

@@ -0,0 +1,84 @@
package com.xuxd.kafka.console.dao.init;
import com.xuxd.kafka.console.config.AuthConfig;
import com.xuxd.kafka.console.dao.SysPermissionMapper;
import com.xuxd.kafka.console.dao.SysRoleMapper;
import com.xuxd.kafka.console.dao.SysUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author: xuxd
* @date: 2023/5/17 13:10
**/
@Slf4j
@Component
public class DataInit implements SmartInitializingSingleton {
private final AuthConfig authConfig;
private final SysUserMapper userMapper;
private final SysRoleMapper roleMapper;
private final SysPermissionMapper permissionMapper;
private final DataSource dataSource;
private final SqlParse sqlParse;
public DataInit(AuthConfig authConfig,
SysUserMapper userMapper,
SysRoleMapper roleMapper,
SysPermissionMapper permissionMapper,
DataSource dataSource) {
this.authConfig = authConfig;
this.userMapper = userMapper;
this.roleMapper = roleMapper;
this.permissionMapper = permissionMapper;
this.dataSource = dataSource;
this.sqlParse = new SqlParse();
}
@Override
public void afterSingletonsInstantiated() {
if (!authConfig.isEnable()) {
log.info("Disable login authentication, no longer try to initialize the data");
return;
}
try {
Connection connection = dataSource.getConnection();
Integer userCount = userMapper.selectCount(null);
if (userCount == null || userCount == 0) {
initData(connection, SqlParse.USER_TABLE);
}
Integer roleCount = roleMapper.selectCount(null);
if (roleCount == null || roleCount == 0) {
initData(connection, SqlParse.ROLE_TABLE);
}
Integer permCount = permissionMapper.selectCount(null);
if (permCount == null || permCount == 0) {
initData(connection, SqlParse.PERM_TABLE);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private void initData(Connection connection, String table) throws SQLException {
log.info("Init default data for {}", table);
String sql = sqlParse.getMergeSql(table);
PreparedStatement statement = connection.prepareStatement(sql);
statement.execute();
}
}

View File

@@ -0,0 +1,85 @@
package com.xuxd.kafka.console.dao.init;
import com.google.common.io.Files;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ResourceUtils;
import scala.collection.mutable.StringBuilder;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author: xuxd
* @date: 2023/5/17 21:22
**/
@Slf4j
public class SqlParse {
private final String FILE = "classpath:db/data-h2.sql";
private final Map<String, List<String>> sqlMap = new HashMap<>();
public static final String ROLE_TABLE = "t_sys_role";
public static final String USER_TABLE = "t_sys_user";
public static final String PERM_TABLE = "t_sys_permission";
public SqlParse() {
sqlMap.put(ROLE_TABLE, new ArrayList<>());
sqlMap.put(USER_TABLE, new ArrayList<>());
sqlMap.put(PERM_TABLE, new ArrayList<>());
String table = null;
try {
File file = ResourceUtils.getFile(FILE);
List<String> lines = Files.readLines(file, Charset.forName("UTF-8"));
for (String str : lines) {
if (StringUtils.isNotEmpty(str)) {
if (str.indexOf("start--") > 0) {
if (str.indexOf(ROLE_TABLE) > 0) {
table = ROLE_TABLE;
}
if (str.indexOf(USER_TABLE) > 0) {
table = USER_TABLE;
}
if (str.indexOf(PERM_TABLE) > 0) {
table = PERM_TABLE;
}
}
if (isSql(str)) {
if (table == null) {
log.error("Table is null, can not load sql: {}", str);
continue;
}
sqlMap.get(table).add(str);
}
}
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public List<String> getSqlList(String table) {
return sqlMap.get(table);
}
public String getMergeSql(String table) {
List<String> list = getSqlList(table);
StringBuilder sb = new StringBuilder();
list.forEach(sql -> sb.append(sql));
return sb.toString();
}
private boolean isSql(String str) {
return StringUtils.isNotEmpty(str) && str.startsWith("insert");
}
}

View File

@@ -81,7 +81,7 @@ public class UserManageServiceImpl implements UserManageService {
}
userDO.setSalt(UUIDStrUtil.random());
userDO.setPassword(UUIDStrUtil.generate(userDTO.getPassword(), userDO.getSalt()));
userMapper.insert(userDTO.toDO());
userMapper.insert(userDO);
} else {
SysUserDO userDO = userMapper.selectById(userDTO.getId());
if (userDO == null) {

View File

@@ -50,6 +50,6 @@ cron:
# 权限认证设置设置为true需要先登录才能访问
auth:
enable: false
enable: true
# 登录用户token的过期时间单位小时
expire-hours: 24

View File

@@ -1,14 +1,11 @@
-- DELETE FROM t_kafka_user;
--
-- INSERT INTO t_kafka_user (id, username, password) VALUES
-- (1, 'Jone', 'p1'),
-- (2, 'Jack', 'p2');
-- 不要随便修改下面的注释,要根据这个注释初始化加载数据
-- t_sys_permission start--
insert into t_sys_permission(id, name,type,parent_id,permission) values(0,'主页',0,null,'home');
insert into t_sys_permission(id, name,type,parent_id,permission) values(11,'集群',0,null,'cluster');
insert into t_sys_permission(id, name,type,parent_id,permission) values(12,'属性配置',1,11,'cluster:property-config');
insert into t_sys_permission(id, name,type,parent_id,permission) values(13,'日志配置',1,11,'cluster:log-config');
insert into t_sys_permission(id, name,type,parent_id,permission) values(14,'编辑配置',1,11,'cluster:edit');
insert into t_sys_permission(id, name,type,parent_id,permission) values(21,'Topic',0,null,'topic');
insert into t_sys_permission(id, name,type,parent_id,permission) values(22,'刷新',1,21,'topic:load');
@@ -49,11 +46,26 @@ insert into t_sys_permission(id, name,type,parent_id,permission) values(83,'客
insert into t_sys_permission(id, name,type,parent_id,permission) values(84,'新增配置',1,83,'quota:client:add');
insert into t_sys_permission(id, name,type,parent_id,permission) values(85,'用户和客户端ID',1,80,'quota:user-client');
insert into t_sys_permission(id, name,type,parent_id,permission) values(86,'新增配置',1,85,'quota:user-client:add');
insert into t_sys_permission(id, name,type,parent_id,permission) values(87,'删除',1,80,'quota:del');
insert into t_sys_permission(id, name,type,parent_id,permission) values(88,'修改',1,80,'quota:edit');
insert into t_sys_permission(id, name,type,parent_id,permission) values(100,'Acl',0,null,'acl');
insert into t_sys_permission(id, name,type,parent_id,permission) values(101,'资源授权',1,100,'acl:authority');
insert into t_sys_permission(id, name,type,parent_id,permission) values(102,'新增权限',1,101,'acl:authority:add');
insert into t_sys_permission(id, name,type,parent_id,permission) values(102,'新增主体权限',1,101,'acl:authority:add-principal');
insert into t_sys_permission(id, name,type,parent_id,permission) values(103,'权限详情',1,101,'acl:authority:detail');
insert into t_sys_permission(id, name,type,parent_id,permission) values(104,'管理生产权限',1,101,'acl:authority:producer');
insert into t_sys_permission(id, name,type,parent_id,permission) values(105,'管理消费权限',1,101,'acl:authority:consumer');
insert into t_sys_permission(id, name,type,parent_id,permission) values(106,'增加权限',1,101,'acl:authority:add');
insert into t_sys_permission(id, name,type,parent_id,permission) values(107,'清除权限',1,101,'acl:authority:clean');
insert into t_sys_permission(id, name,type,parent_id,permission) values(108,'SaslScram用户管理',1,100,'acl:sasl-scram');
insert into t_sys_permission(id, name,type,parent_id,permission) values(109,'新增/更新用户',1,108,'acl:sasl-scram:add-update');
insert into t_sys_permission(id, name,type,parent_id,permission) values(110,'详情',1,108,'acl:sasl-scram:detail');
insert into t_sys_permission(id, name,type,parent_id,permission) values(111,'删除',1,108,'acl:sasl-scram:del');
insert into t_sys_permission(id, name,type,parent_id,permission) values(112,'管理生产权限',1,108,'acl:sasl-scram:producer');
insert into t_sys_permission(id, name,type,parent_id,permission) values(113,'管理消费权限',1,108,'acl:sasl-scram:consumer');
insert into t_sys_permission(id, name,type,parent_id,permission) values(114,'增加权限',1,108,'acl:sasl-scram:add-auth');
insert into t_sys_permission(id, name,type,parent_id,permission) values(115,'彻底删除',1,108,'acl:sasl-scram:pure');
insert into t_sys_permission(id, name,type,parent_id,permission) values(140,'用户',0,null,'user-manage');
insert into t_sys_permission(id, name,type,parent_id,permission) values(141,'用户列表',1,140,'user-manage:user');
@@ -78,8 +90,13 @@ insert into t_sys_permission(id, name,type,parent_id,permission) values(167,'解
insert into t_sys_permission(id, name,type,parent_id,permission) values(168,'首选副本作leader',1,160,'op:replication-preferred');
insert into t_sys_permission(id, name,type,parent_id,permission) values(169,'副本变更详情',1,160,'op:replication-update-detail');
insert into t_sys_permission(id, name,type,parent_id,permission) values(170,'副本重分配',1,160,'op:replication-reassign');
-- t_sys_permission end--
-- t_sys_role start--
insert into t_sys_role(id, role_name, description, permission_ids) VALUES (1,'超级管理员','超级管理员','12,13,14,22,23,24,25,26,27,28,29,30,31,32,33,42,43,44,45,46,47,48,49,50,62,63,64,65,81,82,83,84,85,86,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,141,142,143,144,145,146,147,148,149,150,161,162,163,164,165,166,167,168,169,170');
-- insert into t_sys_role(id, role_name, description, permission_ids) VALUES (2,'访客','访客','12,13,22,26,29,32,44,45,50,62,63,81,83,85,141,146,149,150,161,163');
-- t_sys_role end--
insert into t_sys_role(id, role_name, description, permission_ids) VALUES ( 1,'超级管理员','超级管理员','12,13,22,23,24,25,26,27,28,29,30,31,32,33,42,43,44,45,46,47,48,49,50,62,63,64,65,81,82,83,84,85,86,101,102,141,142,143,144,145,146,147,148,149,150,161,162,163,164,165,166,167,168,169,170' );
insert into t_sys_user(id, username, password, salt, role_ids) VALUES ( 1,'admin','3a3e4d32-5247-321b-9efb-9cbf60b2bf6c','e6973cfc-7583-4baa-8802-65ded1268ab6','1' );
-- t_sys_user start--
insert into t_sys_user(id, username, password, salt, role_ids) VALUES (1,'admin','3a3e4d32-5247-321b-9efb-9cbf60b2bf6c','e6973cfc-7583-4baa-8802-65ded1268ab6','1' );
-- t_sys_user end--

View File

@@ -15,8 +15,10 @@
><router-link to="/client-quota-page" class="pad-l-r">限流</router-link>
<span>|</span
><router-link to="/acl-page" class="pad-l-r">Acl</router-link>
<span>|</span
><router-link to="/user-page" class="pad-l-r">用户</router-link>
<span v-show="showUserMenu">|</span
><router-link to="/user-page" class="pad-l-r" v-show="showUserMenu"
>用户</router-link
>
<span>|</span
><router-link to="/op-page" class="pad-l-r">运维</router-link>
<div class="right">
@@ -44,7 +46,13 @@
import { KafkaClusterApi, AuthApi } from "@/utils/api";
import request from "@/utils/request";
import { mapMutations, mapState } from "vuex";
import {deleteToken, deleteUsername, getClusterInfo, getPermissions, getUsername} from "@/utils/local-cache";
import {
deleteToken,
deleteUsername,
getClusterInfo,
getPermissions,
getUsername,
} from "@/utils/local-cache";
import notification from "ant-design-vue/lib/notification";
import { AUTH, CLUSTER } from "@/store/mutation-types";
@@ -64,6 +72,7 @@ export default {
enableSasl: (state) => state.clusterInfo.enableSasl,
showUsername: (state) => state.auth.enable && state.auth.username,
username: (state) => state.auth.username,
showUserMenu: (state) => state.auth.enable,
}),
},
methods: {

View File

@@ -357,4 +357,4 @@ export const AuthApi = {
url: "/auth/login",
method: "post",
},
};
};

16
ui/src/utils/auth.js Normal file
View File

@@ -0,0 +1,16 @@
import Store from "@/store";
export function isUnauthorized(permission) {
const enableAuth = Store.state.auth.enable;
const permissions = Store.state.auth.permissions;
return enableAuth && (!permissions || permissions.indexOf(permission) < 0);
}
export function isAuthorized(permission) {
const enableAuth = Store.state.auth.enable;
if (!enableAuth) {
return true;
}
const permissions = Store.state.auth.permissions;
return permissions && permissions.indexOf(permission) >= 0;
}

View File

@@ -39,7 +39,7 @@ request.interceptors.request.use((config) => {
config.headers["X-Cluster-Info-Id"] = clusterInfo.id;
// config.headers["X-Cluster-Info-Name"] = encodeURIComponent(clusterInfo.clusterName);
}
const token = localStorage.getItem('access_token')
const token = localStorage.getItem("access_token");
if (token) {
config.headers["X-Auth-Token"] = token;
}

View File

@@ -1,10 +1,14 @@
<template>
<div class="content">
<a-tabs default-active-key="1" size="large" tabPosition="top">
<a-tab-pane key="1" tab="资源授权">
<a-tab-pane key="1" tab="资源授权" v-if="isAuthorized('acl:authority')">
<acl-list></acl-list>
</a-tab-pane>
<a-tab-pane key="2" tab="SaslScram用户管理">
<a-tab-pane
key="2"
tab="SaslScram用户管理"
v-if="isAuthorized('acl:sasl-scram')"
>
<sasl-scram></sasl-scram>
</a-tab-pane>
</a-tabs>
@@ -14,9 +18,11 @@
<script>
import AclList from "@/views/acl/AclList";
import SaslScram from "@/views/acl/SaslScram";
import { isAuthorized } from "@/utils/auth";
export default {
name: "Acl",
methods: { isAuthorized },
components: {
AclList,
SaslScram,

View File

@@ -27,6 +27,7 @@
ok-text="确认"
cancel-text="取消"
@confirm="onDelete(record)"
v-action:acl:authority:clean
>
<a-button>删除</a-button>
</a-popconfirm>

View File

@@ -47,7 +47,10 @@
</a-form>
</div>
<div class="operation-row-button">
<a-button type="primary" @click="onAddPrincipalAuth"
<a-button
type="primary"
@click="onAddPrincipalAuth"
v-action:acl:authority:add-principal
>新增主体权限</a-button
>
<span v-show="hint != ''" class="hint"

View File

@@ -27,7 +27,12 @@
</a-form>
</div>
<div class="operation-row-button">
<a-button type="primary" @click="updateUser">新增/更新用户</a-button>
<a-button
type="primary"
@click="updateUser"
v-action:acl:sasl-scram:add-update
>新增/更新用户</a-button
>
<span class="hint" v-show="!enableSasl"
>未启用SASL SCRAM认证不支持相关操作</span
>
@@ -45,6 +50,7 @@
type="dashed"
style="float: right"
@click="onUserDetail(username)"
v-action:acl:sasl-scram:detail
>详情</a-button
>
</div>
@@ -58,6 +64,7 @@
ok-text="确认"
cancel-text="取消"
@confirm="onDeleteUser(record)"
v-action:acl:sasl-scram:del
>
<a-button size="small" href="javascript:;" class="operation-btn"
>删除</a-button
@@ -68,6 +75,7 @@
href="javascript:;"
class="operation-btn"
@click="onManageProducerAuth(record)"
v-action:acl:sasl-scram:producer
>管理生产权限
</a-button>
@@ -76,6 +84,7 @@
href="javascript:;"
class="operation-btn"
@click="onManageConsumerAuth(record)"
v-action:acl:sasl-scram:consumer
>管理消费权限
</a-button>
<a-button
@@ -83,6 +92,7 @@
href="javascript:;"
class="operation-btn"
@click="onAddAuth(record)"
v-action:acl:sasl-scram:add-auth
>增加权限
</a-button>
<a-popconfirm
@@ -91,7 +101,11 @@
cancel-text="取消"
@confirm="onDeleteUserAndAuth(record)"
>
<a-button size="small" href="javascript:;" class="operation-btn"
<a-button
size="small"
href="javascript:;"
class="operation-btn"
v-action:acl:sasl-scram:pure
>彻底删除</a-button
>
</a-popconfirm>

View File

@@ -35,6 +35,7 @@
class="operation-btn"
v-show="!record.readOnly"
@click="openEditConfigDialog(record)"
v-action:cluster:edit
>编辑
</a-button>
<a-popconfirm
@@ -44,7 +45,11 @@
v-show="isDynamic(record.source)"
@confirm="deleteBrokerConfig(record)"
>
<a-button size="small" href="javascript:;" class="operation-btn"
<a-button
size="small"
href="javascript:;"
class="operation-btn"
v-action:cluster:edit
>删除
</a-button>
</a-popconfirm>

View File

@@ -27,6 +27,7 @@
href="javascript:;"
class="operation-btn"
@click="openBrokerConfigDialog(record, true)"
v-action:cluster:log-config
>日志配置
</a-button>
</div>

View File

@@ -23,7 +23,11 @@
cancel-text="取消"
@confirm="resetTopicOffsetToEndpoint(group, k, 1)"
>
<a-button size="small" type="danger" style="margin-right: 1%"
<a-button
size="small"
type="danger"
style="margin-right: 1%"
v-action:group:consumer-detail:min
>最小位点
</a-button>
</a-popconfirm>
@@ -35,7 +39,11 @@
cancel-text="取消"
@confirm="resetTopicOffsetToEndpoint(group, k, 2)"
>
<a-button size="small" type="danger" style="margin-right: 1%"
<a-button
size="small"
type="danger"
style="margin-right: 1%"
v-action:group:consumer-detail:last
>最新位点
</a-button>
</a-popconfirm>
@@ -45,6 +53,7 @@
type="danger"
style="margin-right: 1%"
@click="openResetOffsetByTimeDialog(k)"
v-action:group:consumer-detail:timestamp
>时间戳
</a-button>
<a-button
@@ -75,6 +84,7 @@
@click="
openResetPartitionOffsetDialog(record.topic, record.partition)
"
v-action:group:consumer-detail:any
>重置位点
</a-button>
</div>

View File

@@ -61,7 +61,10 @@
</a-form>
</div>
<div class="operation-row-button">
<a-button type="primary" @click="openAddSubscriptionDialog"
<a-button
type="primary"
@click="openAddSubscriptionDialog"
v-action:group:add
>新增订阅</a-button
>
</div>
@@ -89,7 +92,11 @@
cancel-text="取消"
@confirm="deleteGroup(record.groupId)"
>
<a-button size="small" href="javascript:;" class="operation-btn"
<a-button
size="small"
href="javascript:;"
class="operation-btn"
v-action:group:del
>删除
</a-button>
</a-popconfirm>
@@ -98,6 +105,7 @@
href="javascript:;"
class="operation-btn"
@click="openConsumerMemberDialog(record.groupId)"
v-action:group:client
>消费端
</a-button>
<a-button
@@ -105,6 +113,7 @@
href="javascript:;"
class="operation-btn"
@click="openConsumerDetailDialog(record.groupId)"
v-action:group:consumer-detail
>消费详情
</a-button>
<a-button
@@ -112,6 +121,7 @@
href="javascript:;"
class="operation-btn"
@click="openOffsetPartitionDialog(record.groupId)"
v-action:group:offset-partition
>位移分区
</a-button>
</div>

View File

@@ -37,10 +37,10 @@
<script>
import request from "@/utils/request";
import {AuthApi} from "@/utils/api";
import { AuthApi } from "@/utils/api";
import notification from "ant-design-vue/lib/notification";
import {mapMutations} from "vuex";
import {AUTH} from "@/store/mutation-types";
import { mapMutations } from "vuex";
import { AUTH } from "@/store/mutation-types";
export default {
name: "Login",

View File

@@ -2,16 +2,25 @@
<div class="content">
<a-spin :spinning="loading">
<a-tabs default-active-key="1" size="large" tabPosition="top">
<a-tab-pane key="1" tab="根据时间查询消息">
<a-tab-pane
v-if="isAuthorized('message:search-time')"
key="1"
tab="根据时间查询消息"
>
<SearchByTime :topic-list="topicList"></SearchByTime>
</a-tab-pane>
<a-tab-pane key="2" tab="根据偏移查询消息">
<a-tab-pane
key="2"
v-if="isAuthorized('message:search-offset')"
tab="根据偏移查询消息"
>
<SearchByOffset :topic-list="topicList"></SearchByOffset>
</a-tab-pane>
<a-tab-pane key="3" tab="在线发送">
<a-tab-pane key="3" tab="在线发送" v-if="isAuthorized('message:send')">
<SendMessage :topic-list="topicList"></SendMessage>
</a-tab-pane>
<a-tab-pane key="4" tab="在线删除">
<a-tab-pane key="4" tab="在线删除" v-if="isAuthorized('message:del')">
<DeleteMessage :topic-list="topicList"></DeleteMessage>
</a-tab-pane>
</a-tabs>
@@ -27,6 +36,7 @@ import { KafkaTopicApi } from "@/utils/api";
import notification from "ant-design-vue/lib/notification";
import SendMessage from "@/views/message/SendMessage";
import DeleteMessage from "./DeleteMessage";
import { isAuthorized, isUnauthorized } from "@/utils/auth";
export default {
name: "Message",
components: { DeleteMessage, SearchByTime, SearchByOffset, SendMessage },
@@ -37,6 +47,8 @@ export default {
};
},
methods: {
isAuthorized,
isUnauthorized,
getTopicNameList() {
request({
url: KafkaTopicApi.getTopicNameList.url,

View File

@@ -17,6 +17,7 @@
href="javascript:;"
class="operation-btn"
@click="openAddClusterInfoDialog"
v-action:op:cluster-switch:add
>新增集群
</a-button>
<br /><br />
@@ -38,6 +39,7 @@
href="javascript:;"
class="operation-btn"
@click="switchCluster(record)"
v-action:op:cluster-switch:switch
>切换
</a-button>
<a-button
@@ -45,6 +47,7 @@
href="javascript:;"
class="operation-btn"
@click="openUpdateClusterInfoDialog(record)"
v-action:op:cluster-switch:edit
>编辑
</a-button>
<a-popconfirm
@@ -58,6 +61,7 @@
href="javascript:;"
class="operation-btn"
type="danger"
v-action:op:cluster-switch:del
>删除
</a-button>
</a-popconfirm>

View File

@@ -2,7 +2,7 @@
<div class="content">
<div class="content-module">
<a-card title="集群管理" style="width: 100%; text-align: left">
<p>
<p v-action:op:cluster-switch>
<a-button type="primary" @click="openClusterInfoDialog">
集群切换
</a-button>
@@ -15,7 +15,7 @@
</div>
<div class="content-module">
<a-card title="Broker管理" style="width: 100%; text-align: left">
<p>
<p v-action:op:config-throttle>
<a-button type="primary" @click="openConfigThrottleDialog">
配置限流
</a-button>
@@ -24,7 +24,7 @@
>设置指定broker上的topic的副本之间数据同步占用的带宽这个设置是broker级别的但是设置后还要去对应的topic上进行限流配置指定对这个topic的相关副本进行限制</span
>
</p>
<p>
<p v-action:op:remove-throttle>
<a-button type="primary" @click="openRemoveThrottleDialog">
解除限流
</a-button>
@@ -35,21 +35,21 @@
</div>
<div class="content-module">
<a-card title="副本管理" style="width: 100%; text-align: left">
<p>
<p v-action:op:replication-preferred>
<a-button type="primary" @click="openElectPreferredLeaderDialog">
首选副本作为leader
</a-button>
<label>说明</label>
<span>将集群中所有分区leader副本设置为首选副本</span>
</p>
<p>
<p v-action:op:replication-update-detail>
<a-button type="primary" @click="openCurrentReassignmentsDialog">
副本变更详情
</a-button>
<label>说明</label>
<span>查看正在进行副本变更/重分配的任务或者将其取消</span>
</p>
<p>
<p v-action:op:replication-reassign>
<a-button type="primary" @click="openReplicaReassignDialog">
副本重分配
</a-button>

View File

@@ -1,56 +1,49 @@
<script src="../../store/index.js"></script>
<template>
<a-modal
title="新增配置"
:visible="show"
:width="800"
:mask="false"
:destroyOnClose="true"
:footer="null"
:maskClosable="false"
@cancel="handleCancel"
title="新增配置"
:visible="show"
:width="800"
:mask="false"
:destroyOnClose="true"
:footer="null"
:maskClosable="false"
@cancel="handleCancel"
>
<div>
<a-spin :spinning="loading">
<a-form
:form="form"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 12 }"
@submit="handleSubmit"
:form="form"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 12 }"
@submit="handleSubmit"
>
<a-form-item label="用户" v-show="showUser">
<a-input
v-decorator="[
'user',
]"
placeholder="输入用户主体标识,比如:用户名,未指定表示用户默认设置"
v-decorator="['user']"
placeholder="输入用户主体标识,比如:用户名,未指定表示用户默认设置"
/>
</a-form-item>
<a-form-item label="客户端ID" v-show="showClientId">
<a-input
v-decorator="[
'client',
]"
placeholder="输入用户客户端ID未指定表示默认客户端设置"
v-decorator="['client']"
placeholder="输入用户客户端ID未指定表示默认客户端设置"
/>
</a-form-item>
<a-form-item label="IP" v-show="showIP">
<a-input
v-decorator="[
'ip',
]"
placeholder="输入客户端IP"
/>
<a-input v-decorator="['ip']" placeholder="输入客户端IP" />
</a-form-item>
<a-form-item label="生产速率">
<a-input-number
:min="1"
:max="102400000"
v-decorator="[
'producerRate',
]"
:min="1"
:max="102400000"
v-decorator="['producerRate']"
/>
<a-select default-value="MB" v-model="producerRateUnit" style="width: 100px">
<a-select
default-value="MB"
v-model="producerRateUnit"
style="width: 100px"
>
<a-select-option value="MB"> MB/s</a-select-option>
<a-select-option value="KB"> KB/s</a-select-option>
<a-select-option value="Byte"> Byte/s</a-select-option>
@@ -58,13 +51,15 @@
</a-form-item>
<a-form-item label="消费速率">
<a-input-number
:min="1"
:max="102400000"
v-decorator="[
'consumerRate',
]"
:min="1"
:max="102400000"
v-decorator="['consumerRate']"
/>
<a-select default-value="MB" v-model="consumerRateUnit" style="width: 100px">
<a-select
default-value="MB"
v-model="consumerRateUnit"
style="width: 100px"
>
<a-select-option value="MB"> MB/s</a-select-option>
<a-select-option value="KB"> KB/s</a-select-option>
<a-select-option value="Byte"> Byte/s</a-select-option>
@@ -72,11 +67,9 @@
</a-form-item>
<a-form-item label="吞吐量">
<a-input-number
:min="1"
:max="102400000"
v-decorator="[
'requestPercentage',
]"
:min="1"
:max="102400000"
v-decorator="['requestPercentage']"
/>
</a-form-item>
<a-form-item :wrapper-col="{ span: 12, offset: 5 }">
@@ -90,7 +83,7 @@
<script>
import request from "@/utils/request";
import {KafkaClientQuotaApi} from "@/utils/api";
import { KafkaClientQuotaApi } from "@/utils/api";
import notification from "ant-design-vue/es/notification";
export default {
@@ -122,7 +115,7 @@ export default {
show: this.visible,
data: [],
loading: false,
form: this.$form.createForm(this, {name: "coordinated"}),
form: this.$form.createForm(this, { name: "coordinated" }),
producerRateUnit: "MB",
consumerRateUnit: "MB",
};
@@ -136,13 +129,15 @@ export default {
handleSubmit() {
this.form.validateFields((err, values) => {
if (!err) {
const params = Object.assign({type: this.type}, values);
const unitMap = {MB: 1024 * 1024, KB: 1024, Byte: 1};
const params = Object.assign({ type: this.type }, values);
const unitMap = { MB: 1024 * 1024, KB: 1024, Byte: 1 };
if (values.consumerRate) {
params.consumerRate = params.consumerRate * unitMap[this.consumerRateUnit];
params.consumerRate =
params.consumerRate * unitMap[this.consumerRateUnit];
}
if (values.producerRate) {
params.producerRate = params.producerRate * unitMap[this.producerRateUnit];
params.producerRate =
params.producerRate * unitMap[this.producerRateUnit];
}
params.types = [];
params.names = [];
@@ -179,7 +174,7 @@ export default {
this.loading = false;
if (res.code == 0) {
this.$message.success(res.msg);
this.$emit("closeAddQuotaDialog", {refresh: true});
this.$emit("closeAddQuotaDialog", { refresh: true });
} else {
notification.error({
message: "error",
@@ -192,7 +187,7 @@ export default {
},
handleCancel() {
this.data = [];
this.$emit("closeAddQuotaDialog", {refresh: false});
this.$emit("closeAddQuotaDialog", { refresh: false });
this.producerRateUnit = "MB";
this.consumerRateUnit = "MB";
},

View File

@@ -3,18 +3,16 @@
<a-spin :spinning="loading">
<div id="search-offset-form-advanced-search">
<a-form
class="ant-advanced-search-form"
:form="form"
@submit="handleSearch"
class="ant-advanced-search-form"
:form="form"
@submit="handleSearch"
>
<a-row :gutter="24">
<a-col :span="16">
<a-form-item label="客户端ID">
<a-input
v-decorator="[
'id',
]"
placeholder="请输入生产者/消费者客户端ID!"
v-decorator="['id']"
placeholder="请输入生产者/消费者客户端ID!"
/>
</a-form-item>
</a-col>
@@ -27,26 +25,39 @@
</a-form>
</div>
<div class="operation-row-button">
<a-button type="primary" @click="openAddQuotaDialog"
>新增配置
<a-button
type="primary"
@click="openAddQuotaDialog"
v-action:quota:client:add
>新增配置
</a-button>
</div>
<QuotaList type="client-id" :columns="columns" :data="data" @refreshQuotaList="refresh"></QuotaList>
<AddQuotaConfig type="client-id" :visible="showAddQuotaDialog" :showClientId="true" @closeAddQuotaDialog="closeAddQuotaDialog"></AddQuotaConfig>
<QuotaList
type="client-id"
:columns="columns"
:data="data"
@refreshQuotaList="refresh"
></QuotaList>
<AddQuotaConfig
type="client-id"
:visible="showAddQuotaDialog"
:showClientId="true"
@closeAddQuotaDialog="closeAddQuotaDialog"
></AddQuotaConfig>
</a-spin>
</div>
</template>
<script>
import request from "@/utils/request";
import {KafkaClientQuotaApi} from "@/utils/api";
import { KafkaClientQuotaApi } from "@/utils/api";
import notification from "ant-design-vue/lib/notification";
import QuotaList from "@/views/quota/QuotaList.vue";
import AddQuotaConfig from "@/views/quota/AddQuotaConfig.vue";
export default {
name: "ClientIDQuota",
components: {QuotaList, AddQuotaConfig},
components: { QuotaList, AddQuotaConfig },
props: {
topicList: {
type: Array,
@@ -55,7 +66,7 @@ export default {
data() {
return {
loading: false,
form: this.$form.createForm(this, {name: "client_id_quota"}),
form: this.$form.createForm(this, { name: "client_id_quota" }),
data: [],
showAlterQuotaDialog: false,
showAddQuotaDialog: false,
@@ -64,8 +75,8 @@ export default {
title: "客户端ID",
dataIndex: "client",
key: "client",
slots: {title: "client"},
scopedSlots: {customRender: "client"},
slots: { title: "client" },
scopedSlots: { customRender: "client" },
},
{
title: "生产速率(带宽/秒)",
@@ -90,7 +101,7 @@ export default {
this.form.validateFields((err, values) => {
if (!err) {
this.loading = true;
const params = {types: ["client-id"]};
const params = { types: ["client-id"] };
if (values.id) {
params.names = [values.id.trim()];
}

View File

@@ -5,13 +5,17 @@
<a-tab-pane key="1" tab="使用说明">
<ClientQuotaIntroduce></ClientQuotaIntroduce>
</a-tab-pane>
<a-tab-pane key="2" tab="用户">
<a-tab-pane v-if="isAuthorized('quota:user')" key="2" tab="用户">
<UserQuota></UserQuota>
</a-tab-pane>
<a-tab-pane key="3" tab="客户端ID">
<a-tab-pane key="3" tab="客户端ID" v-if="isAuthorized('quota:client')">
<ClientIDQuota></ClientIDQuota>
</a-tab-pane>
<a-tab-pane key="4" tab="用户_客户端ID">
<a-tab-pane
key="4"
tab="用户_客户端ID"
v-if="isAuthorized('quota:user-client')"
>
<UserAndClientIDQuota></UserAndClientIDQuota>
</a-tab-pane>
<!-- <a-tab-pane key="5" tab="IP">-->
@@ -27,10 +31,17 @@ import ClientIDQuota from "@/views/quota/ClientIDQuota.vue";
import UserQuota from "@/views/quota/UserQuota.vue";
import UserAndClientIDQuota from "@/views/quota/UserAndClientIDQuota.vue";
import ClientQuotaIntroduce from "@/views/quota/ClientQuotaIntroduce.vue";
import { isAuthorized } from "@/utils/auth";
export default {
name: "ClientQuota",
components: {ClientIDQuota, UserQuota, UserAndClientIDQuota, ClientQuotaIntroduce},
methods: { isAuthorized },
components: {
ClientIDQuota,
UserQuota,
UserAndClientIDQuota,
ClientQuotaIntroduce,
},
data() {
return {
loading: false,

View File

@@ -1,21 +1,24 @@
<template>
<div class="content">
<h1>客户端限流说明资源限额</h1>
<hr/>
<hr/>
<hr />
<hr />
<h2>支持类型</h2>
<ul>
<li>基于用户主体标识的配置</li>
<li>基于客户端ID的配置</li>
<li>基于用户主体+客户端ID的配置</li>
</ul>
<hr/>
<hr />
<h2>默认配置</h2>
<p>比如基于用户的配置在新增配置的时候未设置用户名称则默认应用于所有用户</p>
<p>
比如基于用户的配置在新增配置的时候未设置用户名称则默认应用于所有用户
</p>
<p>基于客户端ID的配置新增的时候未指定客户端ID则应用于所有客户端</p>
<hr/>
<hr />
<h2>配置优先级</h2>
下面的展示中数字越小即越靠上的优先级越高相同的用户名称或者客户端ID优先级最高的配置生效下面的未指定表示 默认配置
下面的展示中数字越小即越靠上的优先级越高相同的用户名称或者客户端ID优先级最高的配置生效下面的未指定表示
默认配置
<ol>
<li>[用户+客户端ID] 指定用户名称并且指定客户端ID优先级最高</li>
<li>[用户+客户端ID] 指定用户名称未指定客户端ID</li>
@@ -26,18 +29,17 @@
<li>[客户端ID] 指定客户端ID</li>
<li>[客户端ID] 未指定客户端ID优先级最低</li>
</ol>
<hr/>
<hr />
<h2>使用注意</h2>
大多数集群一般没有开启认证所以用户配置可能不支持限流可以使用基于客户端ID的配置但是对于优先级最低的"[客户端ID]
未指定客户端ID"默认对于所有客户端生效万一速率配置过小可能无意间影响生产业务所以尽量避免默认配置
<hr/>
<hr />
<h2>查询</h2>
查询默认配置在查询项的输入框内输入一个空格即可
</div>
</template>
<script>
export default {
name: "ClientQuotaIntroduce",
};

View File

@@ -3,19 +3,14 @@
<a-spin :spinning="loading">
<div id="search-offset-form-advanced-search">
<a-form
class="ant-advanced-search-form"
:form="form"
@submit="handleSearch"
class="ant-advanced-search-form"
:form="form"
@submit="handleSearch"
>
<a-row :gutter="24">
<a-col :span="16">
<a-form-item label="IP">
<a-input
v-decorator="[
'ip',
]"
placeholder="请输入ip!"
/>
<a-input v-decorator="['ip']" placeholder="请输入ip!" />
</a-form-item>
</a-col>
<a-col :span="2" :style="{ textAlign: 'right' }">
@@ -28,25 +23,35 @@
</div>
<div class="operation-row-button">
<a-button type="primary" @click="openAddQuotaDialog"
>新增配置
>新增配置
</a-button>
</div>
<QuotaList type="ip" :columns="columns" :data="data" @refreshQuotaList="refresh"></QuotaList>
<AddQuotaConfig type="ip" :visible="showAddQuotaDialog" :showIP="true" @closeAddQuotaDialog="closeAddQuotaDialog"></AddQuotaConfig>
<QuotaList
type="ip"
:columns="columns"
:data="data"
@refreshQuotaList="refresh"
></QuotaList>
<AddQuotaConfig
type="ip"
:visible="showAddQuotaDialog"
:showIP="true"
@closeAddQuotaDialog="closeAddQuotaDialog"
></AddQuotaConfig>
</a-spin>
</div>
</template>
<script>
import request from "@/utils/request";
import {KafkaClientQuotaApi} from "@/utils/api";
import { KafkaClientQuotaApi } from "@/utils/api";
import notification from "ant-design-vue/lib/notification";
import QuotaList from "@/views/quota/QuotaList.vue";
import AddQuotaConfig from "@/views/quota/AddQuotaConfig.vue";
export default {
name: "IpQuota",
components: {QuotaList, AddQuotaConfig},
components: { QuotaList, AddQuotaConfig },
props: {
topicList: {
type: Array,
@@ -55,7 +60,7 @@ export default {
data() {
return {
loading: false,
form: this.$form.createForm(this, {name: "ip_quota"}),
form: this.$form.createForm(this, { name: "ip_quota" }),
data: [],
showAlterQuotaDialog: false,
showAddQuotaDialog: false,
@@ -64,8 +69,8 @@ export default {
title: "IP",
dataIndex: "ip",
key: "ip",
slots: {title: "ip"},
scopedSlots: {customRender: "ip"},
slots: { title: "ip" },
scopedSlots: { customRender: "ip" },
},
{
title: "生产速率(带宽/秒)",
@@ -90,7 +95,7 @@ export default {
this.form.validateFields((err, values) => {
if (!err) {
this.loading = true;
const params = {types: ["ip"]};
const params = { types: ["ip"] };
if (values.ip) {
params.names = [values.ip.trim()];
}

View File

@@ -2,58 +2,69 @@
<div>
<a-spin :spinning="loading">
<a-table
:columns="columns"
:data-source="data"
bordered
:row-key="
(record, index) => {
return index;
}
"
@change="handleChange"
:columns="columns"
:data-source="data"
bordered
:row-key="
(record, index) => {
return index;
}
"
@change="handleChange"
>
<div slot="client" slot-scope="text">
<span v-if="text">{{ text }}</span><span v-else style="color: red">默认配置</span>
<span v-if="text">{{ text }}</span
><span v-else style="color: red">默认配置</span>
</div>
<div slot="user" slot-scope="text">
<span v-if="text">{{ text }}</span><span v-else style="color: red">默认配置</span>
<span v-if="text">{{ text }}</span
><span v-else style="color: red">默认配置</span>
</div>
<div slot="operation" slot-scope="record">
<a-popconfirm
:title="'删除当前配置?'"
ok-text="确认"
cancel-text="取消"
@confirm="deleteConfig(record)"
:title="'删除当前配置?'"
ok-text="确认"
cancel-text="取消"
@confirm="deleteConfig(record)"
>
<a-button size="small" href="javascript:;" class="operation-btn"
>删除
</a-button>
</a-popconfirm>
<a-button
<a-button
size="small"
href="javascript:;"
class="operation-btn"
@click="openUpdateDialog(record)"
>修改
v-action:quota:del
>删除
</a-button>
</a-popconfirm>
<a-button
size="small"
href="javascript:;"
class="operation-btn"
@click="openUpdateDialog(record)"
v-action:quota:edit
>修改
</a-button>
</div>
</a-table>
<UpdateQuotaConfig :type="type" :record="selectRow" :visible="showUpdateDialog"
@closeUpdateQuotaDialog="closeUpdateQuotaDialog"></UpdateQuotaConfig>
<UpdateQuotaConfig
:type="type"
:record="selectRow"
:visible="showUpdateDialog"
@closeUpdateQuotaDialog="closeUpdateQuotaDialog"
></UpdateQuotaConfig>
</a-spin>
</div>
</template>
<script>
import {KafkaClientQuotaApi} from "@/utils/api";
import { KafkaClientQuotaApi } from "@/utils/api";
import request from "@/utils/request";
import notification from "ant-design-vue/lib/notification";
import UpdateQuotaConfig from "@/views/quota/UpdateQuotaConfig.vue";
export default {
name: "QuotaList",
components: {UpdateQuotaConfig},
components: { UpdateQuotaConfig },
props: {
columns: {
type: Array,
@@ -88,7 +99,7 @@ export default {
},
deleteConfig(record) {
this.loading = true;
const params = {type: this.type};
const params = { type: this.type };
params.types = [];
params.names = [];
if (this.type == "user") {
@@ -161,7 +172,7 @@ export default {
this.columns.push({
title: "操作",
key: "operation",
scopedSlots: {customRender: "operation"},
scopedSlots: { customRender: "operation" },
});
},
};

View File

@@ -1,59 +1,58 @@
<script src="../../store/index.js"></script>
<template>
<a-modal
title="修改配置"
:visible="show"
:width="800"
:mask="false"
:destroyOnClose="true"
:footer="null"
:maskClosable="false"
@cancel="handleCancel"
title="修改配置"
:visible="show"
:width="800"
:mask="false"
:destroyOnClose="true"
:footer="null"
:maskClosable="false"
@cancel="handleCancel"
>
<div>
<a-spin :spinning="loading">
<a-form
:form="form"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 12 }"
@submit="handleSubmit"
:form="form"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 12 }"
@submit="handleSubmit"
>
<a-form-item label="用户" v-show="showUser">
<a-input
:disabled="true"
v-decorator="[
'user', { initialValue: record.user }
]"
placeholder="输入用户主体标识,比如:用户名,未指定表示用户默认设置"
:disabled="true"
v-decorator="['user', { initialValue: record.user }]"
placeholder="输入用户主体标识,比如:用户名,未指定表示用户默认设置"
/>
</a-form-item>
<a-form-item label="客户端ID" v-show="showClientId">
<a-input
:disabled="true"
v-decorator="[
'client', { initialValue: record.client }
]"
placeholder="输入用户客户端ID未指定表示默认客户端设置"
:disabled="true"
v-decorator="['client', { initialValue: record.client }]"
placeholder="输入用户客户端ID未指定表示默认客户端设置"
/>
</a-form-item>
<a-form-item label="IP" v-show="showIP">
<a-input
:disabled="true"
v-decorator="[
'ip', { initialValue: record.ip }
]"
placeholder="输入客户端IP"
:disabled="true"
v-decorator="['ip', { initialValue: record.ip }]"
placeholder="输入客户端IP"
/>
</a-form-item>
<a-form-item label="生产速率">
<a-input-number
:min="1"
:max="102400000"
v-decorator="[
'producerRate', { initialValue: record.producerRate }
:min="1"
:max="102400000"
v-decorator="[
'producerRate',
{ initialValue: record.producerRate },
]"
/>
<a-select default-value="MB" v-model="producerRateUnit" style="width: 100px">
<a-select
default-value="MB"
v-model="producerRateUnit"
style="width: 100px"
>
<a-select-option value="MB"> MB/s</a-select-option>
<a-select-option value="KB"> KB/s</a-select-option>
<a-select-option value="Byte"> Byte/s</a-select-option>
@@ -61,13 +60,18 @@
</a-form-item>
<a-form-item label="消费速率">
<a-input-number
:min="1"
:max="102400000"
v-decorator="[
'consumerRate', { initialValue: record.consumerRate }
:min="1"
:max="102400000"
v-decorator="[
'consumerRate',
{ initialValue: record.consumerRate },
]"
/>
<a-select default-value="MB" v-model="consumerRateUnit" style="width: 100px">
<a-select
default-value="MB"
v-model="consumerRateUnit"
style="width: 100px"
>
<a-select-option value="MB"> MB/s</a-select-option>
<a-select-option value="KB"> KB/s</a-select-option>
<a-select-option value="Byte"> Byte/s</a-select-option>
@@ -75,10 +79,11 @@
</a-form-item>
<a-form-item label="吞吐量">
<a-input-number
:min="1"
:max="102400000"
v-decorator="[
'requestPercentage', { initialValue: record.requestPercentage }
:min="1"
:max="102400000"
v-decorator="[
'requestPercentage',
{ initialValue: record.requestPercentage },
]"
/>
</a-form-item>
@@ -93,7 +98,7 @@
<script>
import request from "@/utils/request";
import {KafkaClientQuotaApi} from "@/utils/api";
import { KafkaClientQuotaApi } from "@/utils/api";
import notification from "ant-design-vue/es/notification";
export default {
@@ -110,7 +115,7 @@ export default {
record: {
type: Object,
default: function () {
return {}
return {};
},
},
},
@@ -119,7 +124,7 @@ export default {
show: this.visible,
data: [],
loading: false,
form: this.$form.createForm(this, {name: "coordinated"}),
form: this.$form.createForm(this, { name: "coordinated" }),
producerRateUnit: "MB",
consumerRateUnit: "MB",
showUser: false,
@@ -139,16 +144,24 @@ export default {
handleSubmit() {
this.form.validateFields((err, values) => {
if (!err) {
const params = {type: this.type, deleteConfigs: []};
const unitMap = {MB: 1024 * 1024, KB: 1024, Byte: 1};
const params = { type: this.type, deleteConfigs: [] };
const unitMap = { MB: 1024 * 1024, KB: 1024, Byte: 1 };
if (values.consumerRate) {
const num = typeof (values.consumerRate) == "string" && values.consumerRate.indexOf(" ") > 0 ? values.consumerRate.split(" ")[0] : values.consumerRate;
const num =
typeof values.consumerRate == "string" &&
values.consumerRate.indexOf(" ") > 0
? values.consumerRate.split(" ")[0]
: values.consumerRate;
params.consumerRate = num * unitMap[this.consumerRateUnit];
} else {
params.deleteConfigs.push("consumerRate");
}
if (values.producerRate) {
const num = typeof (values.producerRate) == "string" && values.producerRate.indexOf(" ") > 0 ? values.producerRate.split(" ")[0] : values.producerRate;
const num =
typeof values.producerRate == "string" &&
values.producerRate.indexOf(" ") > 0
? values.producerRate.split(" ")[0]
: values.producerRate;
params.producerRate = num * unitMap[this.producerRateUnit];
} else {
params.deleteConfigs.push("producerRate");
@@ -207,7 +220,7 @@ export default {
this.loading = false;
if (res.code == 0) {
this.$message.success(res.msg);
this.$emit("closeUpdateQuotaDialog", {refresh: true});
this.$emit("closeUpdateQuotaDialog", { refresh: true });
} else {
notification.error({
message: "error",
@@ -220,7 +233,7 @@ export default {
},
handleCancel() {
this.data = [];
this.$emit("closeUpdateQuotaDialog", {refresh: false});
this.$emit("closeUpdateQuotaDialog", { refresh: false });
},
init() {
this.producerRateUnit = "MB";
@@ -243,9 +256,7 @@ export default {
}
},
},
created() {
},
created() {},
};
</script>

View File

@@ -3,28 +3,24 @@
<a-spin :spinning="loading">
<div id="search-offset-form-advanced-search">
<a-form
class="ant-advanced-search-form"
:form="form"
@submit="handleSearch"
class="ant-advanced-search-form"
:form="form"
@submit="handleSearch"
>
<a-row :gutter="24">
<a-col :span="10">
<a-form-item label="用户标识">
<a-input
v-decorator="[
'user',
]"
placeholder="请输入用户标识,如:用户名!"
v-decorator="['user']"
placeholder="请输入用户标识,如:用户名!"
/>
</a-form-item>
</a-col>
<a-col :span="10">
<a-form-item label="客户端ID">
<a-input
v-decorator="[
'client',
]"
placeholder="请输入客户端ID!"
v-decorator="['client']"
placeholder="请输入客户端ID!"
/>
</a-form-item>
</a-col>
@@ -37,27 +33,40 @@
</a-form>
</div>
<div class="operation-row-button">
<a-button type="primary" @click="openAddQuotaDialog"
>新增配置
<a-button
type="primary"
@click="openAddQuotaDialog"
v-action:quota:user-client:add
>新增配置
</a-button>
</div>
<QuotaList type="user&client-id" :columns="columns" :data="data" @refreshQuotaList="refresh"></QuotaList>
<AddQuotaConfig type="user&client-id" :visible="showAddQuotaDialog" :showUser="true" :showClientId="true"
@closeAddQuotaDialog="closeAddQuotaDialog"></AddQuotaConfig>
<QuotaList
type="user&client-id"
:columns="columns"
:data="data"
@refreshQuotaList="refresh"
></QuotaList>
<AddQuotaConfig
type="user&client-id"
:visible="showAddQuotaDialog"
:showUser="true"
:showClientId="true"
@closeAddQuotaDialog="closeAddQuotaDialog"
></AddQuotaConfig>
</a-spin>
</div>
</template>
<script>
import request from "@/utils/request";
import {KafkaClientQuotaApi} from "@/utils/api";
import { KafkaClientQuotaApi } from "@/utils/api";
import notification from "ant-design-vue/lib/notification";
import QuotaList from "@/views/quota/QuotaList.vue";
import AddQuotaConfig from "@/views/quota/AddQuotaConfig.vue";
export default {
name: "UserAndClientIDQuota",
components: {QuotaList, AddQuotaConfig},
components: { QuotaList, AddQuotaConfig },
props: {
topicList: {
type: Array,
@@ -66,7 +75,7 @@ export default {
data() {
return {
loading: false,
form: this.$form.createForm(this, {name: "user_client_id_quota"}),
form: this.$form.createForm(this, { name: "user_client_id_quota" }),
data: [],
showAlterQuotaDialog: false,
showAddQuotaDialog: false,
@@ -75,15 +84,15 @@ export default {
title: "用户标识",
dataIndex: "user",
key: "user",
slots: {title: "user"},
scopedSlots: {customRender: "user"},
slots: { title: "user" },
scopedSlots: { customRender: "user" },
},
{
title: "客户端ID",
dataIndex: "client",
key: "client",
slots: {title: "client"},
scopedSlots: {customRender: "client"},
slots: { title: "client" },
scopedSlots: { customRender: "client" },
},
{
title: "生产速率(带宽/秒)",
@@ -108,7 +117,7 @@ export default {
this.form.validateFields((err, values) => {
if (!err) {
this.loading = true;
const params = {types: ["user", "client-id"], names: []};
const params = { types: ["user", "client-id"], names: [] };
if (values.user) {
params.names.push(values.user.trim());
}

View File

@@ -3,18 +3,16 @@
<a-spin :spinning="loading">
<div id="search-offset-form-advanced-search">
<a-form
class="ant-advanced-search-form"
:form="form"
@submit="handleSearch"
class="ant-advanced-search-form"
:form="form"
@submit="handleSearch"
>
<a-row :gutter="24">
<a-col :span="16">
<a-form-item label="用户标识">
<a-input
v-decorator="[
'user',
]"
placeholder="请输入用户标识,如:用户名!"
v-decorator="['user']"
placeholder="请输入用户标识,如:用户名!"
/>
</a-form-item>
</a-col>
@@ -27,26 +25,39 @@
</a-form>
</div>
<div class="operation-row-button">
<a-button type="primary" @click="openAddQuotaDialog"
>新增配置
<a-button
type="primary"
@click="openAddQuotaDialog"
v-action:quota:user:add
>新增配置
</a-button>
</div>
<QuotaList type="user" :columns="columns" :data="data" @refreshQuotaList="refresh"></QuotaList>
<AddQuotaConfig type="user" :visible="showAddQuotaDialog" :showUser="true" @closeAddQuotaDialog="closeAddQuotaDialog"></AddQuotaConfig>
<QuotaList
type="user"
:columns="columns"
:data="data"
@refreshQuotaList="refresh"
></QuotaList>
<AddQuotaConfig
type="user"
:visible="showAddQuotaDialog"
:showUser="true"
@closeAddQuotaDialog="closeAddQuotaDialog"
></AddQuotaConfig>
</a-spin>
</div>
</template>
<script>
import request from "@/utils/request";
import {KafkaClientQuotaApi} from "@/utils/api";
import { KafkaClientQuotaApi } from "@/utils/api";
import notification from "ant-design-vue/lib/notification";
import QuotaList from "@/views/quota/QuotaList.vue";
import AddQuotaConfig from "@/views/quota/AddQuotaConfig.vue";
export default {
name: "UserQuota",
components: {QuotaList, AddQuotaConfig},
components: { QuotaList, AddQuotaConfig },
props: {
topicList: {
type: Array,
@@ -55,7 +66,7 @@ export default {
data() {
return {
loading: false,
form: this.$form.createForm(this, {name: "user_quota"}),
form: this.$form.createForm(this, { name: "user_quota" }),
data: [],
showAlterQuotaDialog: false,
showAddQuotaDialog: false,
@@ -65,8 +76,8 @@ export default {
dataIndex: "user",
key: "user",
width: 300,
slots: {title: "user"},
scopedSlots: {customRender: "user"},
slots: { title: "user" },
scopedSlots: { customRender: "user" },
},
{
title: "生产速率(带宽/秒)",
@@ -91,7 +102,7 @@ export default {
this.form.validateFields((err, values) => {
if (!err) {
this.loading = true;
const params = {types: ["user"]};
const params = { types: ["user"] };
if (values.user) {
params.names = [values.user.trim()];
}

View File

@@ -44,7 +44,11 @@
cancel-text="取消"
@confirm="electPreferredLeader(record)"
>
<a-button size="small" href="javascript:;" class="operation-btn"
<a-button
size="small"
href="javascript:;"
class="operation-btn"
v-action:topic:partition-detail:preferred
>首选副本作为leader
</a-button>
</a-popconfirm>

View File

@@ -36,7 +36,13 @@
<a-col :span="8" :style="{ textAlign: 'right' }">
<a-form-item>
<a-button type="primary" html-type="submit"> 刷新</a-button>
<a-button
type="primary"
html-type="submit"
v-action:topic:load
>
刷新</a-button
>
<!-- <a-button :style="{ marginLeft: '8px' }" @click="handleReset">-->
<!-- 重置-->
<!-- </a-button>-->
@@ -46,7 +52,10 @@
</a-form>
</div>
<div class="operation-row-button">
<a-button type="primary" @click="openCreateTopicDialog" v-action:action
<a-button
type="primary"
@click="openCreateTopicDialog"
v-action:topic:add
>新增</a-button
>
<a-popconfirm
@@ -60,7 +69,7 @@
class="btn-left"
:disabled="!hasSelected"
:loading="loading"
v-action:action
v-action:topic:batch-del
>
批量删除
</a-button>
@@ -98,7 +107,11 @@
cancel-text="取消"
@confirm="deleteTopic(record.name)"
>
<a-button size="small" href="javascript:;" class="operation-btn"
<a-button
size="small"
href="javascript:;"
class="operation-btn"
v-action:topic:del
>删除
</a-button>
</a-popconfirm>
@@ -107,6 +120,7 @@
href="javascript:;"
class="operation-btn"
@click="openPartitionInfoDialog(record.name)"
v-action:topic:partition-detail
>分区详情
</a-button>
<a-button
@@ -114,6 +128,7 @@
href="javascript:;"
class="operation-btn"
@click="openAddPartitionDialog(record.name)"
v-action:topic:partition-add
>增加分区
</a-button>
<a-button
@@ -121,6 +136,7 @@
href="javascript:;"
class="operation-btn"
@click="openConsumedDetailDialog(record.name)"
v-action:topic:consumer-detail
>消费详情
</a-button>
<a-button
@@ -128,6 +144,7 @@
href="javascript:;"
class="operation-btn"
@click="openTopicConfigDialog(record.name)"
v-action:topic:property-config
>属性配置
</a-button>
<a-button
@@ -135,6 +152,7 @@
href="javascript:;"
class="operation-btn"
@click="openUpdateReplicaDialog(record.name)"
v-action:topic:replication-modify
>变更副本
</a-button>
<a-button
@@ -142,6 +160,7 @@
href="javascript:;"
class="operation-btn"
@click="openMessageStatsDialog(record.name)"
v-action:topic:send-count
>发送统计
</a-button>
<a-button
@@ -149,6 +168,7 @@
href="javascript:;"
class="operation-btn"
@click="openThrottleDialog(record.name)"
v-action:topic:replication-sync-throttle
>限流
</a-button>
</div>

View File

@@ -98,7 +98,10 @@ export default {
this.loading = false;
if (res.code == 0) {
this.$message.success(res.msg);
this.$emit("closeCreateUserDialog", { refresh: true, data: res.data });
this.$emit("closeCreateUserDialog", {
refresh: true,
data: res.data,
});
} else {
notification.error({
message: "error",

View File

@@ -31,24 +31,9 @@ const columns = [
width: "12%",
slots: { title: "type" },
scopedSlots: { customRender: "type" },
}
},
];
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(
`selectedRowKeys: ${selectedRowKeys}`,
"selectedRows: ",
selectedRows
);
},
onSelect: (record, selected, selectedRows) => {
console.log(record, selected, selectedRows);
},
onSelectAll: (selected, selectedRows, changeRows) => {
console.log(selected, selectedRows, changeRows);
},
};
import { UserManageApi } from "@/utils/api";
import notification from "ant-design-vue/lib/notification";
@@ -60,7 +45,6 @@ export default {
loading: false,
data: [],
columns,
rowSelection,
expandedRowKeys: [],
};
},

View File

@@ -26,7 +26,7 @@
cancel-text="取消"
@confirm="deleteRole(item)"
>
<a :style="{ display: 'flex' }">
<a :style="{ display: 'flex' }" v-action:user-manage:role:del>
<a-icon type="delete" />
</a>
</a-popconfirm>
@@ -35,7 +35,9 @@
<span
:style="{ margin: '25px', fontSize: '15px', display: 'block' }"
>
<a @click="addRole()"><a-icon type="plus" /> 新增角色</a>
<a @click="addRole()" v-action:user-manage:role:save
><a-icon type="plus" /> 新增角色</a
>
</span>
</a-col>
<a-col :md="20">
@@ -116,7 +118,11 @@
</div>
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="loading" @click="onSave()"
<a-button
type="primary"
:loading="loading"
@click="onSave()"
v-action:user-manage:role:save
>保存</a-button
>
</a-form-item>
@@ -212,8 +218,8 @@ export default {
const btnArr = [self];
btn.children = btnArr;
const selected = btn.children
.map((bc) => bc.id)
.filter((id) => idSet.has(id));
.map((bc) => bc.id)
.filter((id) => idSet.has(id));
btn.selected = selected || [];
btn.selectAll = btn.selected.length == btn.children.length;
}

View File

@@ -32,7 +32,10 @@
</a-form>
</div>
<div class="operation-row-button">
<a-button type="primary" @click="openCreateUserDialog()"
<a-button
type="primary"
@click="openCreateUserDialog()"
v-action:user-manage:user:add
>新增用户</a-button
>
</div>
@@ -49,7 +52,11 @@
cancel-text="取消"
@confirm="deleteUser(record)"
>
<a-button size="small" href="javascript:;" class="operation-btn"
<a-button
size="small"
href="javascript:;"
class="operation-btn"
v-action:user-manage:user:del
>删除
</a-button>
</a-popconfirm>
@@ -59,7 +66,11 @@
cancel-text="取消"
@confirm="resetPassword(record)"
>
<a-button size="small" href="javascript:;" class="operation-btn"
<a-button
size="small"
href="javascript:;"
class="operation-btn"
v-action:user-manage:user:reset-pass
>重置密码
</a-button>
</a-popconfirm>
@@ -68,6 +79,7 @@
href="javascript:;"
class="operation-btn"
@click="openUpdateUserRoleDialog(record)"
v-action:user-manage:user:change-role
>分配角色
</a-button>
</div>

View File

@@ -2,16 +2,32 @@
<div class="content">
<a-spin :spinning="loading">
<a-tabs default-active-key="1" size="large" tabPosition="top">
<a-tab-pane key="1" tab="用户列表">
<a-tab-pane
key="1"
tab="用户列表"
v-if="isAuthorized('user-manage:user')"
>
<User></User>
</a-tab-pane>
<a-tab-pane key="2" tab="角色列表">
<a-tab-pane
key="2"
tab="角色列表"
v-if="isAuthorized('user-manage:role')"
>
<Role></Role>
</a-tab-pane>
<a-tab-pane key="3" tab="权限列表">
<a-tab-pane
key="3"
tab="权限列表"
v-if="isAuthorized('user-manage:permission')"
>
<Permission></Permission>
</a-tab-pane>
<a-tab-pane key="4" tab="个人设置">
<a-tab-pane
key="4"
tab="个人设置"
v-if="isAuthorized('user-manage:setting')"
>
<UserSetting></UserSetting>
</a-tab-pane>
</a-tabs>
@@ -24,6 +40,7 @@ import Permission from "@/views/user/Permission.vue";
import Role from "@/views/user/Role.vue";
import User from "@/views/user/User.vue";
import UserSetting from "@/views/user/UserSetting.vue";
import { isAuthorized } from "@/utils/auth";
export default {
name: "UserManage",
components: { Permission, Role, User, UserSetting },
@@ -33,7 +50,7 @@ export default {
topicList: [],
};
},
methods: {},
methods: { isAuthorized },
created() {},
};
</script>