This is an automated email from the ASF dual-hosted git repository. dengliming pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/shenyu-dashboard.git
The following commit(s) were added to refs/heads/master by this push: new c3946cf2 [type:feat]Add namespace management interface and plugin management interface adaptation and transformation (#459) c3946cf2 is described below commit c3946cf20d0f0dbb77493b342de2a4ed02fefd0b Author: xcsnx <1192709...@qq.com> AuthorDate: Thu Jul 18 12:50:53 2024 +0800 [type:feat]Add namespace management interface and plugin management interface adaptation and transformation (#459) * namespace module * fix * fix code checkstyle * move namespace position and fix bug * fix cr * rename pluginNamespace to namespacePlugin * fix * fix cr * update * fix * fix --------- Co-authored-by: ‘xcsnx’ <‘1192709...@qq.com’> --- src/common/menu.js | 10 + src/common/router.js | 23 +- src/locales/en-US.json | 14 +- src/locales/zh-CN.json | 14 +- src/models/namespace.js | 133 ++++++++ src/models/namespacePlugin.js | 182 +++++++++++ src/routes/System/Namespace/AddModal.js | 132 ++++++++ src/routes/System/{Plugin => Namespace}/index.js | 361 ++++++--------------- src/routes/System/NamespacePlugin/AddModal.js | 242 ++++++++++++++ .../System/{Plugin => NamespacePlugin}/index.js | 121 +++---- src/routes/System/Plugin/index.js | 18 - src/services/api.js | 98 ++++++ src/utils/namespacePlugin.js | 103 ++++++ 13 files changed, 1090 insertions(+), 361 deletions(-) diff --git a/src/common/menu.js b/src/common/menu.js index fbb29507..1f092a69 100644 --- a/src/common/menu.js +++ b/src/common/menu.js @@ -65,6 +65,16 @@ export const menuData = [ path: "plugin", locale: "SHENYU.MENU.SYSTEM.MANAGMENT.PLUGIN", }, + { + name: getIntlContent("SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACEPLUGIN"), + path: "namespacePlugin", + locale: "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACEPLUGIN", + }, + { + name: getIntlContent("SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACE"), + path: "namespace", + locale: "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACE", + }, { name: getIntlContent("SHENYU.PLUGIN.PLUGINHANDLE"), path: "pluginhandle", diff --git a/src/common/router.js b/src/common/router.js index 328f54dc..53b78a53 100644 --- a/src/common/router.js +++ b/src/common/router.js @@ -113,7 +113,6 @@ export const getRouterData = (app) => { ), }, "/system/role": { - // 角色管理 component: dynamicWrapper( app, ["role"], @@ -121,7 +120,6 @@ export const getRouterData = (app) => { ), }, "/system/manage": { - // 用户管理 component: dynamicWrapper( app, ["manage", "dataPermission"], @@ -129,7 +127,6 @@ export const getRouterData = (app) => { ), }, "/system/resource": { - // 资源管理 component: dynamicWrapper( app, ["resource"], @@ -137,15 +134,20 @@ export const getRouterData = (app) => { ), }, "/system/alert": { - // 告警管理 component: dynamicWrapper( app, ["alert"], () => import("../routes/System/Alert"), ), }, + "/config/namespace": { + component: dynamicWrapper( + app, + ["namespace"], + () => import("../routes/System/Namespace"), + ), + }, "/config/metadata": { - // 元数据管理 component: dynamicWrapper( app, ["metadata"], @@ -153,15 +155,20 @@ export const getRouterData = (app) => { ), }, "/config/plugin": { - // 插件管理 component: dynamicWrapper( app, ["plugin"], () => import("../routes/System/Plugin"), ), }, + "/config/namespacePlugin": { + component: dynamicWrapper( + app, + ["namespacePlugin"], + () => import("../routes/System/NamespacePlugin"), + ), + }, "/config/pluginhandle": { - // 插件处理管理 component: dynamicWrapper( app, ["pluginHandle"], @@ -169,7 +176,6 @@ export const getRouterData = (app) => { ), }, "/config/auth": { - // 认证管理 component: dynamicWrapper( app, ["auth"], @@ -177,7 +183,6 @@ export const getRouterData = (app) => { ), }, "/config/dict": { - // 字典管理 component: dynamicWrapper( app, ["shenyuDict"], diff --git a/src/locales/en-US.json b/src/locales/en-US.json index e99a8125..7e2a7779 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -69,11 +69,12 @@ "SHENYU.MENU.SYSTEM.MANAGMENT.USER": "User", "SHENYU.MENU.SYSTEM.MANAGMENT.RESOURCE": "Resource", "SHENYU.MENU.SYSTEM.MANAGMENT.ALERT": "Alert", - "SHENYU.MENU.SYSTEM.MANAGMENT.PLUGIN": "Plugin", + "SHENYU.MENU.SYSTEM.MANAGMENT.PLUGIN": "PluginTemplate", "SHENYU.MENU.SYSTEM.MANAGMENT.PLUGINHANDLE": "PluginHandle", "SHENYU.MENU.SYSTEM.MANAGMENT.AUTHEN": "Authentication", "SHENYU.MENU.SYSTEM.MANAGMENT.METADATA": "Metadata", "SHENYU.MENU.SYSTEM.MANAGMENT.DICTIONARY": "Dictionary", + "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACE": "Namespace", "SHENYU.MENU.CONFIG.MANAGMENT": "BasicConfig", "SHENYU.PLUGIN.SELECTOR.LIST.TITLE": "SelectorList", "SHENYU.PLUGIN.SELECTOR.LIST.ADD": "Add", @@ -430,5 +431,14 @@ "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.CONFIRM": "Configuration Import Confirmation", "SHENYU.DISCOVERY.SELECTOR.DELETE.CONFIRM": "Are you sure to delete this selector?", "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.SURE": "Import", - "SHENYU.COMMON.REFRESH": "Refresh" + "SHENYU.COMMON.REFRESH": "Refresh", + "SHENYU.SYSTEM.NAMESPACE": "Namespace", + "SHENYU.SYSTEM.NAMESPACE.NAMESPACEID": "namespaceId", + "SHENYU.SYSTEM.NAMESPACE.NAME": "name", + "SHENYU.SYSTEM.NAMESPACE.DESC": "description", + "SHENYU.NAMESPACE.INPUTNAME":"namespaceName", + "SHENYU.NAMESPACE.INPUTDESC":"description", + "SHENYU.NAMESPACE.INPUTNAMESPACEID":"namespaceId", + "SHENYU.NAMESPACE.ALERTNAMESPACEID":"Automatically generated namespaceId", + "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACEPLUGIN":"Plugin" } diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index ee6e6500..d71cf9c3 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -70,12 +70,13 @@ "SHENYU.MENU.SYSTEM.MANAGMENT.USER": "用户管理", "SHENYU.MENU.SYSTEM.MANAGMENT.RESOURCE": "资源管理", "SHENYU.MENU.SYSTEM.MANAGMENT.ALERT": "告警管理", - "SHENYU.MENU.SYSTEM.MANAGMENT.PLUGIN": "插件管理", + "SHENYU.MENU.SYSTEM.MANAGMENT.PLUGIN": "插件模板管理", "SHENYU.MENU.SYSTEM.MANAGMENT.PLUGINHANDLE": "插件处理管理", "SHENYU.MENU.SYSTEM.MANAGMENT.AUTHEN": "认证管理", "SHENYU.MENU.SYSTEM.MANAGMENT.METADATA": "元数据管理", "SHENYU.MENU.SYSTEM.MANAGMENT.DICTIONARY": "字典管理", "SHENYU.MENU.CONFIG.MANAGMENT": "基础配置", + "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACE": "命名空间管理", "SHENYU.PLUGIN.SELECTOR.LIST.TITLE": "选择器列表", "SHENYU.PLUGIN.SELECTOR.LIST.ADD": "添加选择器", "SHENYU.PLUGIN.SELECTOR.BATCH.OPENED": "批量开启", @@ -436,5 +437,14 @@ "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.CONFIRM": "后台配置导入确认", "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.SURE": "导入", "SHENYU.DISCOVERY.SELECTOR.DELETE.CONFIRM": "确定删除该选择器吗?", - "SHENYU.COMMON.REFRESH": "刷新" + "SHENYU.COMMON.REFRESH": "刷新", + "SHENYU.SYSTEM.NAMESPACE": "命名空间", + "SHENYU.SYSTEM.NAMESPACE.NAMESPACEID": "namespaceId", + "SHENYU.SYSTEM.NAMESPACE.NAME": "名称", + "SHENYU.SYSTEM.NAMESPACE.DESC": "描述", + "SHENYU.NAMESPACE.INPUTNAME":"请输入命名空间名称", + "SHENYU.NAMESPACE.INPUTDESC":"请输入命名空间描述", + "SHENYU.NAMESPACE.INPUTNAMESPACEID":"请输入namespaceId", + "SHENYU.NAMESPACE.ALERTNAMESPACEID":"系统自动生成namespaceId", + "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACEPLUGIN":"插件管理" } diff --git a/src/models/namespace.js b/src/models/namespace.js new file mode 100644 index 00000000..13864c6d --- /dev/null +++ b/src/models/namespace.js @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { message } from "antd"; +import { + getAllNamespaces, + findNamespace, + insertOrUpdateNamespace, + deleteNamespace, +} from "../services/api"; +import { getIntlContent } from "../utils/IntlUtils"; + +export default { + namespace: "namespace", + + state: { + namespaceList: [], + total: 0, + }, + + effects: { + *fetch(params, { call, put }) { + const { payload } = params; + const json = yield call(getAllNamespaces, payload); + if (json.code === 200) { + let { page, dataList } = json.data; + dataList = dataList.map((item) => { + item.key = item.id; + return item; + }); + yield put({ + type: "saveNamespaces", + payload: { + total: page.totalCount, + dataList, + }, + }); + } + }, + *fetchItem(params, { call }) { + const { payload, callback } = params; + const json = yield call(findNamespace, payload); + if (json.code === 200) { + const plugin = json.data; + callback(plugin); + } + }, + *add(params, { call, put }) { + const { payload, callback, fetchValue } = params; + const json = yield call(insertOrUpdateNamespace, payload); + if (json.code === 200) { + message.success(getIntlContent("SHENYU.COMMON.RESPONSE.ADD.SUCCESS")); + callback(); + yield put({ type: "reload", fetchValue }); + } else { + message.warn(json.message); + } + }, + *delete(params, { call, put }) { + const { payload, fetchValue, callback } = params; + const { list } = payload; + const json = yield call(deleteNamespace, { list }); + if (json.code === 200) { + message.success( + getIntlContent("SHENYU.COMMON.RESPONSE.DELETE.SUCCESS"), + ); + callback(); + yield put({ type: "reload", fetchValue }); + } else { + message.warn(json.message); + } + }, + *update(params, { call, put }) { + const { payload, callback, fetchValue } = params; + const json = yield call(insertOrUpdateNamespace, payload); + if (json.code === 200) { + message.success( + getIntlContent("SHENYU.COMMON.RESPONSE.UPDATE.SUCCESS"), + ); + callback(); + if (fetchValue) { + yield put({ type: "reload", fetchValue }); + } + } else { + message.warn(json.message); + } + }, + + *reload(params, { put }) { + const { fetchValue } = params; + const { name, currentPage, enabled, pageSize } = fetchValue; + const payload = { name, enabled, currentPage, pageSize }; + yield put({ type: "fetch", payload }); + }, + }, + + reducers: { + saveNamespaces(state, { payload }) { + return { + ...state, + namespaceList: payload.dataList, + total: payload.total, + }; + }, + updatePlugins(state, { payload }) { + let pluginList = state.pluginList; + pluginList = pluginList.map((item) => { + if (item.id === payload.id) { + item.enabled = payload.enabled; + } + return item; + }); + return { + ...state, + pluginList, + }; + }, + }, +}; diff --git a/src/models/namespacePlugin.js b/src/models/namespacePlugin.js new file mode 100644 index 00000000..ad8fc328 --- /dev/null +++ b/src/models/namespacePlugin.js @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { message } from "antd"; +import { + addPlugin, + fetchPluginHandleByPluginId, + addPluginResource, + findNamespacePlugin, + getAllNamespacePlugins, + updateNamespacePluginEnabled, + updateNamespacePlugin, + deleteNamespacePlugin, + asyncNamespacePlugin, +} from "../services/api"; +import { getIntlContent } from "../utils/IntlUtils"; + +export default { + namespace: "namespacePlugin", + + state: { + namespacePluginList: [], + total: 0, + }, + + effects: { + *fetch(params, { call, put }) { + const { payload } = params; + const json = yield call(getAllNamespacePlugins, payload); + if (json.code === 200) { + let { page, dataList } = json.data; + dataList = dataList.map((item) => { + item.key = item.id; + return item; + }); + yield put({ + type: "savePlugins", + payload: { + total: page.totalCount, + dataList, + }, + }); + } + }, + *fetchItem(params, { call }) { + const { payload, callback } = params; + const json = yield call(findNamespacePlugin, payload); + if (json.code === 200) { + const plugin = json.data; + callback(plugin); + } + }, + *add(params, { call, put }) { + const { payload, callback, fetchValue } = params; + const json = yield call(addPlugin, payload); + if (json.code === 200) { + message.success(getIntlContent("SHENYU.COMMON.RESPONSE.ADD.SUCCESS")); + callback(); + yield put({ type: "reload", fetchValue }); + } else { + message.warn(json.message); + } + }, + *delete(params, { call, put }) { + const { payload, fetchValue, callback } = params; + // const { list } = payload; + const json = yield call(deleteNamespacePlugin, payload); + if (json.code === 200) { + message.success( + getIntlContent("SHENYU.COMMON.RESPONSE.DELETE.SUCCESS"), + ); + callback(); + yield put({ type: "reload", fetchValue }); + } else { + message.warn(json.message); + } + }, + *update(params, { call, put }) { + const { payload, callback, fetchValue } = params; + const json = yield call(updateNamespacePlugin, payload); + if (json.code === 200) { + message.success( + getIntlContent("SHENYU.COMMON.RESPONSE.UPDATE.SUCCESS"), + ); + callback(); + if (fetchValue) { + yield put({ type: "reload", fetchValue }); + } + } else { + message.warn(json.message); + } + }, + *updateEn(params, { call, put }) { + const { payload, fetchValue, callback } = params; + const json = yield call(updateNamespacePluginEnabled, payload); + + if (json.code === 200) { + message.success( + getIntlContent("SHENYU.COMMON.RESPONSE.UPDATE.SUCCESS"), + ); + callback(); + if (fetchValue) { + yield put({ type: "reload", fetchValue }); + } + } else { + message.warn(json.message); + } + }, + + *reload(params, { put }) { + const { fetchValue } = params; + const { name, currentPage, enabled, pageSize } = fetchValue; + const payload = { name, enabled, currentPage, pageSize }; + yield put({ type: "fetch", payload }); + }, + + *asyncAll(_, { call }) { + const json = yield call(asyncNamespacePlugin); + if (json.code === 200) { + message.success(getIntlContent("SHENYU.COMMON.RESPONSE.SYNC.SUCCESS")); + } else { + message.warn(json.message); + } + }, + + *fetchByPluginId(params, { call }) { + const { payload, callback } = params; + const json = yield call(fetchPluginHandleByPluginId, payload); + if (json.code === 200) { + callback(json); + } + }, + + *createPluginResource(params, { call }) { + const { payload, callback } = params; + const json = yield call(addPluginResource, payload); + if (json.code === 200) { + message.success(getIntlContent("SHENYU.COMMON.RESPONSE.ADD.SUCCESS")); + callback(); + } else { + message.warn(json.message); + } + }, + }, + + reducers: { + savePlugins(state, { payload }) { + return { + ...state, + namespacePluginList: payload.dataList, + total: payload.total, + }; + }, + updatePlugins(state, { payload }) { + let pluginList = state.pluginList; + pluginList = pluginList.map((item) => { + if (item.id === payload.id) { + item.enabled = payload.enabled; + } + return item; + }); + return { + ...state, + pluginList, + }; + }, + }, +}; diff --git a/src/routes/System/Namespace/AddModal.js b/src/routes/System/Namespace/AddModal.js new file mode 100644 index 00000000..852adfc9 --- /dev/null +++ b/src/routes/System/Namespace/AddModal.js @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { Component } from "react"; +import { Modal, Form, Input } from "antd"; +import { getIntlContent } from "../../../utils/IntlUtils"; + +const FormItem = Form.Item; + +class AddModal extends Component { + handleSubmit = (e) => { + const { form, handleOk, id = "" } = this.props; + e.preventDefault(); + form.validateFieldsAndScroll((err, values) => { + if (!err) { + handleOk({ ...values, id }); + } + }); + }; + + render() { + let { + handleCancel, + form, + name = "", + description = "", + namespaceId = "", + } = this.props; + + const { getFieldDecorator } = form; + const formItemLayout = { + labelCol: { + sm: { span: 6 }, + }, + wrapperCol: { + sm: { span: 18 }, + }, + }; + return ( + <Modal + width={450} + centered + title={getIntlContent("SHENYU.SYSTEM.NAMESPACE")} + visible + okText={getIntlContent("SHENYU.COMMON.SURE")} + cancelText={getIntlContent("SHENYU.COMMON.CALCEL")} + onOk={this.handleSubmit} + onCancel={handleCancel} + > + <Form onSubmit={this.handleSubmit} className="login-form"> + <FormItem + label={getIntlContent("SHENYU.SYSTEM.NAMESPACE.NAME")} + {...formItemLayout} + > + {getFieldDecorator("name", { + rules: [ + { + required: true, + message: getIntlContent("SHENYU.NAMESPACE.INPUTNAME"), + }, + ], + initialValue: name, + })( + <Input + allowClear + placeholder={getIntlContent("SHENYU.NAMESPACE.INPUTNAME")} + />, + )} + </FormItem> + <FormItem + label={getIntlContent("SHENYU.SYSTEM.NAMESPACE.DESC")} + {...formItemLayout} + > + {getFieldDecorator("description", { + rules: [ + { + required: false, + message: getIntlContent("SHENYU.NAMESPACE.INPUTDESC"), + }, + ], + initialValue: description, + })( + <Input.TextArea + autoSize + allowClear + placeholder={getIntlContent("SHENYU.NAMESPACE.INPUTDESC")} + />, + )} + </FormItem> + <FormItem + label={getIntlContent("SHENYU.SYSTEM.NAMESPACE.NAMESPACEID")} + {...formItemLayout} + > + {getFieldDecorator("namespaceId", { + rules: [ + { + required: false, + message: getIntlContent("SHENYU.NAMESPACE.ALERTNAMESPACEID"), + }, + ], + initialValue: namespaceId, + })( + <Input + allowClear + disabled={true} + placeholder={getIntlContent( + "SHENYU.NAMESPACE.ALERTNAMESPACEID", + )} + />, + )} + </FormItem> + </Form> + </Modal> + ); + } +} + +export default Form.create()(AddModal); diff --git a/src/routes/System/Plugin/index.js b/src/routes/System/Namespace/index.js similarity index 51% copy from src/routes/System/Plugin/index.js copy to src/routes/System/Namespace/index.js index ea442965..3cdda422 100644 --- a/src/routes/System/Plugin/index.js +++ b/src/routes/System/Namespace/index.js @@ -16,38 +16,21 @@ */ import React, { Component } from "react"; -import { - Table, - Input, - Button, - message, - Popconfirm, - Select, - Popover, - Tag, - Typography, - Switch, -} from "antd"; +import { Table, Input, Button, message, Popconfirm } from "antd"; import { connect } from "dva"; -import { Link } from "dva/router"; import { resizableComponents } from "../../../utils/resizable"; import AddModal from "./AddModal"; import { getCurrentLocale, getIntlContent } from "../../../utils/IntlUtils"; import AuthButton from "../../../utils/AuthButton"; import { refreshAuthMenus } from "../../../utils/AuthRoute"; -import { getUpdateModal, updatePluginsEnabled } from "../../../utils/plugin"; -const { Text } = Typography; - -const { Option } = Select; - -@connect(({ plugin, resource, loading, global }) => ({ - plugin, +@connect(({ namespace, resource, loading, global }) => ({ + namespace, authMenu: resource.authMenu, language: global.language, - loading: loading.effects["plugin/fetch"], + loading: loading.effects["namespace/fetch"], })) -export default class Plugin extends Component { +export default class Namespace extends Component { components = resizableComponents; constructor(props) { @@ -57,6 +40,8 @@ export default class Plugin extends Component { pageSize: 12, selectedRowKeys: [], name: "", + namespaceId: "", + // eslint-disable-next-line react/no-unused-state enabled: null, popup: "", localeName: window.sessionStorage.getItem("locale") @@ -68,14 +53,14 @@ export default class Plugin extends Component { componentDidMount() { this.query(); - this.initPluginColumns(); + this.initNamespaceColumns(); } componentDidUpdate() { const { language } = this.props; const { localeName } = this.state; if (language !== localeName) { - this.initPluginColumns(); + this.initNamespaceColumns(); this.changeLocale(language); } } @@ -98,10 +83,10 @@ export default class Plugin extends Component { }; currentQueryPayload = (override) => { - const { name, enabled, currentPage, pageSize } = this.state; + const { name, namespaceId, currentPage, pageSize } = this.state; return { name, - enabled, + namespaceId, currentPage, pageSize, ...override, @@ -111,7 +96,7 @@ export default class Plugin extends Component { query = () => { const { dispatch } = this.props; dispatch({ - type: "plugin/fetch", + type: "namespace/fetch", payload: this.currentQueryPayload(), }); }; @@ -134,39 +119,39 @@ export default class Plugin extends Component { editClick = (record) => { const { dispatch } = this.props; - getUpdateModal({ - pluginId: record.id, - dispatch, - fetchValue: this.currentQueryPayload(), - callback: (popup) => { - this.setState({ popup }); - }, - updatedCallback: () => { - this.setState({ selectedRowKeys: [] }); - this.closeModal(true); - }, - canceledCallback: () => { - this.closeModal(); - }, - }); - }; - resourceClick = (record) => { - // code here... - const { dispatch } = this.props; - const { name, role, sort, config, id, enabled } = record; dispatch({ - type: "plugin/createPluginResource", + type: "namespace/fetchItem", payload: { - name, - role, - sort, - config, - id, - enabled, + id: record.namespaceId, }, - callback: () => { - refreshAuthMenus({ dispatch }); + callback: (namespace) => { + this.setState({ + popup: ( + <AddModal + {...namespace} + handleOk={(values) => { + const { name, description } = values; + dispatch({ + type: "namespace/update", + payload: { + id: record.id, + name, + description, + namespaceId: record.namespaceId, + }, + fetchValue: this.currentQueryPayload(), + callback: () => { + this.closeModal(); + }, + }); + }} + handleCancel={() => { + this.closeModal(); + }} + /> + ), + }); }, }); }; @@ -175,8 +160,8 @@ export default class Plugin extends Component { this.setState({ name: e.target.value }, this.query); }; - enabledOnchange = (e) => { - this.setState({ enabled: e }, this.query); + searchNamespaceIdOnchange = (e) => { + this.setState({ namespaceId: e.target.value }, this.query); }; searchClick = () => { @@ -188,7 +173,7 @@ export default class Plugin extends Component { const { selectedRowKeys } = this.state; if (selectedRowKeys && selectedRowKeys.length > 0) { dispatch({ - type: "plugin/delete", + type: "namespace/delete", payload: { list: selectedRowKeys, }, @@ -213,16 +198,12 @@ export default class Plugin extends Component { disabled={false} handleOk={(values) => { const { dispatch } = this.props; - const { name, enabled, role, config, sort, file } = values; + const { name, description } = values; dispatch({ - type: "plugin/add", + type: "namespace/add", payload: { name, - config, - role, - enabled, - sort, - file, + description, }, fetchValue: this.currentQueryPayload(), callback: () => { @@ -239,52 +220,6 @@ export default class Plugin extends Component { }); }; - // 数据状态切换 - statusSwitch = ({ list, enabled, callback }) => { - const { dispatch } = this.props; - updatePluginsEnabled({ - list, - dispatch, - callback, - enabled, - fetchValue: this.currentQueryPayload(), - }); - }; - - // 批量启用或禁用 - enableClick = () => { - const { dispatch } = this.props; - const { selectedRowKeys } = this.state; - if (selectedRowKeys && selectedRowKeys.length > 0) { - dispatch({ - type: "plugin/fetchItem", - payload: { - id: selectedRowKeys[0], - }, - callback: (user) => { - this.statusSwitch({ - list: selectedRowKeys, - enabled: !user.enabled, - callback: () => { - this.setState({ selectedRowKeys: [] }); - }, - }); - }, - }); - } else { - message.destroy(); - message.warn("Please select data"); - } - }; - - // 同步插件数据 - syncAllClick = () => { - const { dispatch } = this.props; - dispatch({ - type: "plugin/asyncAll", - }); - }; - changeLocale(locale) { this.setState({ localeName: locale, @@ -292,134 +227,41 @@ export default class Plugin extends Component { getCurrentLocale(this.state.localeName); } - initPluginColumns() { + initNamespaceColumns() { this.setState({ columns: [ { align: "center", - title: getIntlContent("SHENYU.PLUGIN.PLUGIN.NAME"), - dataIndex: "name", - key: "name", + title: getIntlContent("SHENYU.SYSTEM.NAMESPACE.NAMESPACEID"), + dataIndex: "namespaceId", + key: "namespaceId", ellipsis: true, - width: 120, - render: (text, record) => { - return record.url ? ( - <Link to={record.url}> - <div - style={{ - color: "#1890ff", - fontWeight: "bold", - textDecorationLine: "underline", - }} - > - {text || "----"} - </div> - </Link> - ) : ( - <div style={{ color: "#260033", fontWeight: "bold" }}> - {text || "----"} - </div> - ); + width: 300, + render: (text) => { + return <div style={{ color: "#1f640a" }}>{text || "----"}</div>; }, }, { align: "center", - title: getIntlContent("SHENYU.SYSTEM.ROLE"), - dataIndex: "role", + title: getIntlContent("SHENYU.SYSTEM.NAMESPACE.NAME"), + dataIndex: "name", ellipsis: true, - key: "role", - width: 120, - sorter: (a, b) => (a.role > b.role ? 1 : -1), + key: "name", + width: 240, render: (text) => { return <div style={{ color: "#1f640a" }}>{text || "----"}</div>; }, }, { align: "center", - title: getIntlContent("SHENYU.PLUGIN.SORT"), - dataIndex: "sort", + title: getIntlContent("SHENYU.SYSTEM.NAMESPACE.DESC"), + dataIndex: "description", ellipsis: true, - key: "sort", - width: 120, - sorter: (a, b) => (a.role > b.role ? 1 : -1), + key: "desc", render: (text) => { return <div style={{ color: "#014955" }}>{text}</div>; }, }, - { - align: "center", - title: getIntlContent("SHENYU.COMMON.SETTING"), - dataIndex: "config", - key: "config", - ellipsis: true, - render: (text, record) => { - const tag = ( - <div> - <Tag color="#9dd3a8">{record.name}</Tag> - <Tag color="#CCCC99">{record.role}</Tag> - <Tag color="#3b9a9c">{record.sort}</Tag> - </div> - ); - const t = JSON.stringify( - JSON.parse(text !== null && text.length > 0 ? text : "{}"), - null, - 4, - ); - const content = ( - <div> - <Text type="secondary">{`${getIntlContent("SHENYU.SYSTEM.CREATETIME")}: ${record.dateCreated}`}</Text> - <br /> - <Text type="secondary">{`${getIntlContent("SHENYU.SYSTEM.UPDATETIME")}: ${record.dateUpdated}`}</Text> - <hr /> - <div style={{ fontWeight: "bold" }}> - <pre> - <code>{t}</code> - </pre> - </div> - </div> - ); - return ( - <Popover content={content} title={tag}> - <div>{text || "----"}</div> - </Popover> - ); - }, - }, - { - align: "center", - title: getIntlContent("SHENYU.SYSTEM.STATUS"), - dataIndex: "enabled", - key: "enabled", - ellipsis: true, - width: 80, - sorter: (a, b) => - (a.enabled || "-1") > (b.enabled || "-1") ? 1 : -1, - render: (text, row) => ( - <AuthButton - perms="system:plugin:disable" - noAuth={ - text ? ( - <div className="open"> - {getIntlContent("SHENYU.COMMON.OPEN")} - </div> - ) : ( - <div className="close"> - {getIntlContent("SHENYU.COMMON.CLOSE")} - </div> - ) - } - > - <Switch - checkedChildren={getIntlContent("SHENYU.COMMON.OPEN")} - unCheckedChildren={getIntlContent("SHENYU.COMMON.CLOSE")} - checked={text} - onChange={(checked) => { - this.statusSwitch({ list: [row.id], enabled: checked }); - }} - /> - </AuthButton> - ), - }, { align: "center", title: getIntlContent("SHENYU.COMMON.OPERAT"), @@ -429,9 +271,12 @@ export default class Plugin extends Component { width: 160, fixed: "right", render: (text, record) => { - return ( + return record.namespaceId === + "649330b6-c2d7-4edc-be8e-8a54df9eb385" ? ( + "" + ) : ( <div className="optionParts"> - <AuthButton perms="system:plugin:edit"> + <AuthButton perms="system:namespace:edit"> <div className="edit" onClick={() => { @@ -441,15 +286,29 @@ export default class Plugin extends Component { {getIntlContent("SHENYU.SYSTEM.EDITOR")} </div> </AuthButton> - <AuthButton perms="system:plugin:resource"> - <div - className="edit" - onClick={() => { - this.resourceClick(record); + <AuthButton perms="system:namespace:delete"> + <Popconfirm + title={getIntlContent("SHENYU.COMMON.DELETE")} + placement="bottom" + onCancel={(e) => { + e.stopPropagation(); + }} + onConfirm={(e) => { + e.stopPropagation(); + this.deleteClick(record); }} + okText={getIntlContent("SHENYU.COMMON.SURE")} + cancelText={getIntlContent("SHENYU.COMMON.CALCEL")} > - {getIntlContent("SHENYU.BUTTON.SYSTEM.RESOURCE")} - </div> + <span + className="edit" + onClick={(e) => { + e.stopPropagation(); + }} + > + {getIntlContent("SHENYU.COMMON.DELETE.NAME")} + </span> + </Popconfirm> </AuthButton> </div> ); @@ -460,9 +319,9 @@ export default class Plugin extends Component { } render() { - const { plugin, loading, authMenu } = this.props; - const { pluginList, total } = plugin; - const { currentPage, pageSize, selectedRowKeys, name, enabled, popup } = + const { namespace, loading, authMenu } = this.props; + const { namespaceList, total } = namespace; + const { currentPage, pageSize, selectedRowKeys, name, namespaceId, popup } = this.state; const columns = this.state.columns.map((col, index) => ({ ...col, @@ -487,7 +346,7 @@ export default class Plugin extends Component { }; const flatAuthMenu = flatList({}, authMenu); - pluginList.forEach((p) => { + namespaceList.forEach((p) => { p.url = (flatAuthMenu[p.id] ?? {}).path; }); @@ -498,19 +357,16 @@ export default class Plugin extends Component { allowClear value={name} onChange={this.searchOnchange} - placeholder={getIntlContent("SHENYU.PLUGIN.INPUTNAME")} + placeholder={getIntlContent("SHENYU.NAMESPACE.INPUTNAME")} style={{ width: 240 }} /> - <Select - value={enabled != null ? enabled : undefined} - onChange={this.enabledOnchange} - placeholder={getIntlContent("SHENYU.PLUGIN.SELECT.STATUS")} - style={{ width: 150, marginLeft: 20 }} + <Input allowClear - > - <Option value="0">{getIntlContent("SHENYU.COMMON.CLOSE")}</Option> - <Option value="1">{getIntlContent("SHENYU.COMMON.OPEN")}</Option> - </Select> + value={namespaceId} + onChange={this.searchNamespaceIdOnchange} + placeholder={getIntlContent("SHENYU.NAMESPACE.INPUTNAMESPACEID")} + style={{ width: 300, marginLeft: 20 }} + /> <AuthButton perms="system:plugin:list"> <Button type="primary" @@ -544,25 +400,6 @@ export default class Plugin extends Component { {getIntlContent("SHENYU.SYSTEM.ADDDATA")} </Button> </AuthButton> - <AuthButton perms="system:plugin:modify"> - <Button - style={{ marginLeft: 20 }} - icon="reload" - type="primary" - onClick={this.syncAllClick} - > - {getIntlContent("SHENYU.PLUGIN.SYNCALLDATA")} - </Button> - </AuthButton> - <AuthButton perms="system:plugin:disable"> - <Button - style={{ marginLeft: 20 }} - type="primary" - onClick={this.enableClick} - > - {getIntlContent("SHENYU.PLUGIN.BATCH")} - </Button> - </AuthButton> </div> <Table @@ -572,7 +409,7 @@ export default class Plugin extends Component { bordered loading={loading} columns={columns} - dataSource={pluginList} + dataSource={namespaceList} rowSelection={rowSelection} pagination={{ total, diff --git a/src/routes/System/NamespacePlugin/AddModal.js b/src/routes/System/NamespacePlugin/AddModal.js new file mode 100644 index 00000000..b6e83867 --- /dev/null +++ b/src/routes/System/NamespacePlugin/AddModal.js @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { Component, Fragment } from "react"; +import { Modal, Form, Switch, Input, Select, Divider, InputNumber } from "antd"; +import { connect } from "dva"; +import { getIntlContent } from "../../../utils/IntlUtils"; + +const { Option } = Select; +const FormItem = Form.Item; + +@connect(({ global }) => ({ + platform: global.platform, +})) +class AddModal extends Component { + handleSubmit = (e) => { + const { form, handleOk, id = "", data } = this.props; + e.preventDefault(); + form.validateFieldsAndScroll((err, values) => { + if (!err) { + let { name, enabled, config, sort } = values; + if (data && data.length > 0) { + config = {}; + data.forEach((item) => { + let fieldName = `__${item.field}__`; + if (values[fieldName]) { + config[item.field] = values[fieldName]; + } + }); + config = JSON.stringify(config); + if (config === "{}") { + config = ""; + } + } + handleOk({ name, enabled, config, id, sort }); + } + }); + }; + + render() { + let { + handleCancel, + form, + config, + name, + enabled = true, + id, + data, + sort, + } = this.props; + let disable = id !== undefined; + const { getFieldDecorator } = form; + const formItemLayout = { + labelCol: { + sm: { span: 7 }, + }, + wrapperCol: { + sm: { span: 17 }, + }, + }; + if (config) { + config = JSON.parse(config); + } + return ( + <Modal + width={520} + centered + title={getIntlContent("SHENYU.PLUGIN")} + visible + okText={getIntlContent("SHENYU.COMMON.SURE")} + cancelText={getIntlContent("SHENYU.COMMON.CALCEL")} + onOk={this.handleSubmit} + onCancel={handleCancel} + > + <Form onSubmit={this.handleSubmit} className="login-form"> + <FormItem label={getIntlContent("SHENYU.PLUGIN")} {...formItemLayout}> + {getFieldDecorator("name", { + rules: [ + { + required: true, + message: getIntlContent("SHENYU.PLUGIN.SELECT"), + }, + ], + initialValue: name, + })( + <Input + allowClear + placeholder={getIntlContent("SHENYU.PLUGIN.PLUGIN.NAME")} + disabled={disable} + />, + )} + </FormItem> + {data && data.length > 0 && ( + <> + <Divider> + {name} {getIntlContent("SHENYU.COMMON.SETTING")} + </Divider> + {data.map((eachField, index) => { + let fieldInitialValue = config + ? config[eachField.field] + : undefined; + // Add prefixes to prevent naming conflicts + let fieldName = `__${eachField.field}__`; + let dataType = eachField.dataType; + let required = ""; + let checkRule; + if (eachField.extObj) { + let extObj = JSON.parse(eachField.extObj); + required = extObj.required === "0" ? "" : extObj.required; + if (!fieldInitialValue) { + fieldInitialValue = extObj.defaultValue; + } + if (extObj.rule) { + checkRule = extObj.rule; + } + } + let rules = []; + if (required) { + rules.push({ + required: { required }, + message: getIntlContent("SHENYU.COMMON.PLEASEINPUT"), + }); + } + if (checkRule) { + rules.push({ + // eslint-disable-next-line no-eval + pattern: eval(checkRule), + message: `${getIntlContent( + "SHENYU.PLUGIN.RULE.INVALID", + )}:(${checkRule})`, + }); + } + if (dataType === 1) { + return ( + <FormItem + label={eachField.label} + name={fieldName} + {...formItemLayout} + key={index} + > + {getFieldDecorator(fieldName, { + rules, + initialValue: fieldInitialValue, + })( + <InputNumber + precision={0} + placeholder={eachField.label} + />, + )} + </FormItem> + ); + } else if (dataType === 3 && eachField.dictOptions) { + return ( + <FormItem + label={eachField.label} + name={fieldName} + {...formItemLayout} + key={index} + > + {getFieldDecorator(fieldName, { + rules, + initialValue: fieldInitialValue, + })( + <Select placeholder={eachField.label}> + {eachField.dictOptions.map((option) => { + return ( + <Option + key={option.dictValue} + value={option.dictValue} + > + {option.dictName} ({eachField.label}) + </Option> + ); + })} + </Select>, + )} + </FormItem> + ); + } else { + return ( + <FormItem + label={eachField.label} + name={fieldName} + {...formItemLayout} + key={index} + > + {getFieldDecorator(fieldName, { + rules, + initialValue: fieldInitialValue, + })(<Input allowClear placeholder={eachField.label} />)} + </FormItem> + ); + } + })} + <Divider /> + </> + )} + <FormItem + label={getIntlContent("SHENYU.PLUGIN.SORT")} + {...formItemLayout} + > + {getFieldDecorator("sort", { + rules: [ + { + required: true, + message: getIntlContent("SHENYU.PLUGIN.INPUTSORT"), + }, + ], + initialValue: sort, + })(<InputNumber precision={0} style={{ width: "100%" }} />)} + </FormItem> + + <FormItem + {...formItemLayout} + label={getIntlContent("SHENYU.SYSTEM.STATUS")} + > + {getFieldDecorator("enabled", { + initialValue: enabled, + valuePropName: "checked", + })(<Switch />)} + </FormItem> + </Form> + </Modal> + ); + } +} + +export default Form.create()(AddModal); diff --git a/src/routes/System/Plugin/index.js b/src/routes/System/NamespacePlugin/index.js similarity index 85% copy from src/routes/System/Plugin/index.js copy to src/routes/System/NamespacePlugin/index.js index ea442965..5543517f 100644 --- a/src/routes/System/Plugin/index.js +++ b/src/routes/System/NamespacePlugin/index.js @@ -31,23 +31,25 @@ import { import { connect } from "dva"; import { Link } from "dva/router"; import { resizableComponents } from "../../../utils/resizable"; -import AddModal from "./AddModal"; import { getCurrentLocale, getIntlContent } from "../../../utils/IntlUtils"; import AuthButton from "../../../utils/AuthButton"; import { refreshAuthMenus } from "../../../utils/AuthRoute"; -import { getUpdateModal, updatePluginsEnabled } from "../../../utils/plugin"; +import { + getUpdateModal, + updateNamespacePluginsEnabled, +} from "../../../utils/namespacePlugin"; const { Text } = Typography; const { Option } = Select; -@connect(({ plugin, resource, loading, global }) => ({ - plugin, +@connect(({ namespacePlugin, resource, loading, global }) => ({ + namespacePlugin, authMenu: resource.authMenu, language: global.language, - loading: loading.effects["plugin/fetch"], + loading: loading.effects["namespacePlugin/fetch"], })) -export default class Plugin extends Component { +export default class NamespacePlugin extends Component { components = resizableComponents; constructor(props) { @@ -55,6 +57,8 @@ export default class Plugin extends Component { this.state = { currentPage: 1, pageSize: 12, + // todo:[To be refactored with namespace] Temporarily hardcode + namespaceId: "649330b6-c2d7-4edc-be8e-8a54df9eb385", selectedRowKeys: [], name: "", enabled: null, @@ -68,14 +72,14 @@ export default class Plugin extends Component { componentDidMount() { this.query(); - this.initPluginColumns(); + this.initNamespacePluginColumns(); } componentDidUpdate() { const { language } = this.props; const { localeName } = this.state; if (language !== localeName) { - this.initPluginColumns(); + this.initNamespacePluginColumns(); this.changeLocale(language); } } @@ -98,10 +102,11 @@ export default class Plugin extends Component { }; currentQueryPayload = (override) => { - const { name, enabled, currentPage, pageSize } = this.state; + const { name, enabled, currentPage, pageSize, namespaceId } = this.state; return { name, enabled, + namespaceId, currentPage, pageSize, ...override, @@ -111,7 +116,7 @@ export default class Plugin extends Component { query = () => { const { dispatch } = this.props; dispatch({ - type: "plugin/fetch", + type: "namespacePlugin/fetch", payload: this.currentQueryPayload(), }); }; @@ -135,7 +140,9 @@ export default class Plugin extends Component { editClick = (record) => { const { dispatch } = this.props; getUpdateModal({ - pluginId: record.id, + id: record.id, + // todo:[To be refactored with namespace] Temporarily hardcode + namespaceId: "649330b6-c2d7-4edc-be8e-8a54df9eb385", dispatch, fetchValue: this.currentQueryPayload(), callback: (popup) => { @@ -154,7 +161,7 @@ export default class Plugin extends Component { resourceClick = (record) => { // code here... const { dispatch } = this.props; - const { name, role, sort, config, id, enabled } = record; + const { name, role, sort, config, pluginId, enabled } = record; dispatch({ type: "plugin/createPluginResource", payload: { @@ -162,7 +169,7 @@ export default class Plugin extends Component { role, sort, config, - id, + id: pluginId, enabled, }, callback: () => { @@ -188,9 +195,11 @@ export default class Plugin extends Component { const { selectedRowKeys } = this.state; if (selectedRowKeys && selectedRowKeys.length > 0) { dispatch({ - type: "plugin/delete", + type: "namespacePlugin/delete", payload: { list: selectedRowKeys, + // todo:[To be refactored with namespace] Temporarily hardcode + namespaceId: "649330b6-c2d7-4edc-be8e-8a54df9eb385", }, fetchValue: this.currentQueryPayload({ pageSize: 12, @@ -206,47 +215,15 @@ export default class Plugin extends Component { } }; - addClick = () => { - this.setState({ - popup: ( - <AddModal - disabled={false} - handleOk={(values) => { - const { dispatch } = this.props; - const { name, enabled, role, config, sort, file } = values; - dispatch({ - type: "plugin/add", - payload: { - name, - config, - role, - enabled, - sort, - file, - }, - fetchValue: this.currentQueryPayload(), - callback: () => { - this.closeModal(true); - refreshAuthMenus({ dispatch }); - }, - }); - }} - handleCancel={() => { - this.closeModal(); - }} - /> - ), - }); - }; - // 数据状态切换 - statusSwitch = ({ list, enabled, callback }) => { + statusSwitch = ({ list, enabled, namespaceId, callback }) => { const { dispatch } = this.props; - updatePluginsEnabled({ + updateNamespacePluginsEnabled({ list, dispatch, callback, enabled, + namespaceId, fetchValue: this.currentQueryPayload(), }); }; @@ -257,14 +234,18 @@ export default class Plugin extends Component { const { selectedRowKeys } = this.state; if (selectedRowKeys && selectedRowKeys.length > 0) { dispatch({ - type: "plugin/fetchItem", + type: "namespacePlugin/fetchItem", payload: { id: selectedRowKeys[0], + // todo:[To be refactored with namespace] Temporarily hardcode + namespaceId: "649330b6-c2d7-4edc-be8e-8a54df9eb385", }, callback: (user) => { this.statusSwitch({ list: selectedRowKeys, enabled: !user.enabled, + // todo:[To be refactored with namespace] Temporarily hardcode + namespaceId: "649330b6-c2d7-4edc-be8e-8a54df9eb385", callback: () => { this.setState({ selectedRowKeys: [] }); }, @@ -281,7 +262,7 @@ export default class Plugin extends Component { syncAllClick = () => { const { dispatch } = this.props; dispatch({ - type: "plugin/asyncAll", + type: "namespacePlugin/asyncAll", }); }; @@ -292,7 +273,7 @@ export default class Plugin extends Component { getCurrentLocale(this.state.localeName); } - initPluginColumns() { + initNamespacePluginColumns() { this.setState({ columns: [ { @@ -414,7 +395,11 @@ export default class Plugin extends Component { unCheckedChildren={getIntlContent("SHENYU.COMMON.CLOSE")} checked={text} onChange={(checked) => { - this.statusSwitch({ list: [row.id], enabled: checked }); + this.statusSwitch({ + list: [row.id], + enabled: checked, + namespaceId: row.namespaceId, + }); }} /> </AuthButton> @@ -431,7 +416,7 @@ export default class Plugin extends Component { render: (text, record) => { return ( <div className="optionParts"> - <AuthButton perms="system:plugin:edit"> + <AuthButton perms="system:namespacePlugin:edit"> <div className="edit" onClick={() => { @@ -441,7 +426,7 @@ export default class Plugin extends Component { {getIntlContent("SHENYU.SYSTEM.EDITOR")} </div> </AuthButton> - <AuthButton perms="system:plugin:resource"> + <AuthButton perms="system:namespacePlugin:resource"> <div className="edit" onClick={() => { @@ -460,8 +445,8 @@ export default class Plugin extends Component { } render() { - const { plugin, loading, authMenu } = this.props; - const { pluginList, total } = plugin; + const { namespacePlugin, loading, authMenu } = this.props; + const { namespacePluginList, total } = namespacePlugin; const { currentPage, pageSize, selectedRowKeys, name, enabled, popup } = this.state; const columns = this.state.columns.map((col, index) => ({ @@ -487,7 +472,7 @@ export default class Plugin extends Component { }; const flatAuthMenu = flatList({}, authMenu); - pluginList.forEach((p) => { + namespacePluginList.forEach((p) => { p.url = (flatAuthMenu[p.id] ?? {}).path; }); @@ -535,15 +520,15 @@ export default class Plugin extends Component { </Button> </Popconfirm> </AuthButton> - <AuthButton perms="system:plugin:add"> - <Button - style={{ marginLeft: 20 }} - type="primary" - onClick={this.addClick} - > - {getIntlContent("SHENYU.SYSTEM.ADDDATA")} - </Button> - </AuthButton> + {/* <AuthButton perms="system:plugin:add"> */} + {/* <Button */} + {/* style={{ marginLeft: 20 }} */} + {/* type="primary" */} + {/* onClick={this.addClick} */} + {/* > */} + {/* {getIntlContent("SHENYU.SYSTEM.ADDDATA")} */} + {/* </Button> */} + {/* </AuthButton> */} <AuthButton perms="system:plugin:modify"> <Button style={{ marginLeft: 20 }} @@ -572,7 +557,7 @@ export default class Plugin extends Component { bordered loading={loading} columns={columns} - dataSource={pluginList} + dataSource={namespacePluginList} rowSelection={rowSelection} pagination={{ total, diff --git a/src/routes/System/Plugin/index.js b/src/routes/System/Plugin/index.js index ea442965..08539917 100644 --- a/src/routes/System/Plugin/index.js +++ b/src/routes/System/Plugin/index.js @@ -277,14 +277,6 @@ export default class Plugin extends Component { } }; - // 同步插件数据 - syncAllClick = () => { - const { dispatch } = this.props; - dispatch({ - type: "plugin/asyncAll", - }); - }; - changeLocale(locale) { this.setState({ localeName: locale, @@ -544,16 +536,6 @@ export default class Plugin extends Component { {getIntlContent("SHENYU.SYSTEM.ADDDATA")} </Button> </AuthButton> - <AuthButton perms="system:plugin:modify"> - <Button - style={{ marginLeft: 20 }} - icon="reload" - type="primary" - onClick={this.syncAllClick} - > - {getIntlContent("SHENYU.PLUGIN.SYNCALLDATA")} - </Button> - </AuthButton> <AuthButton perms="system:plugin:disable"> <Button style={{ marginLeft: 20 }} diff --git a/src/services/api.js b/src/services/api.js index f9682cce..c793896d 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -1137,3 +1137,101 @@ export function updateDiscoveryUpstream(discoveryHandlerId, upstreams) { body: upstreams, }); } + +/* getAllNamespaces */ +export async function getAllNamespaces(params) { + return request(`${baseUrl}/namespace/findPageByQuery?${stringify(params)}`, { + method: `GET`, + }); +} +/* insertOrUpdateNamespace */ +export async function insertOrUpdateNamespace(params) { + return request(`${baseUrl}/namespace/insertOrUpdate`, { + method: `POST`, + body: { + ...params, + }, + }); +} + +/* findNamespace */ +export async function findNamespace(params) { + return request(`${baseUrl}/namespace/${params.id}`, { + method: `GET`, + }); +} + +/* deleteNamespace */ +export async function deleteNamespace(params) { + return request(`${baseUrl}/namespace/batch`, { + method: `DELETE`, + body: [...params.list], + }); +} + +/* findNamespacePlugin */ +export async function findNamespacePlugin(params) { + return request( + `${baseUrl}/namespacePlugin/id=${params.id}&namespaceId=${params.namespaceId}`, + { + method: `GET`, + }, + ); +} + +/* getAllNamespacePlugins */ +export async function getAllNamespacePlugins(params) { + // todo:[To be refactored with namespace] Temporarily hardcode + params.namespaceId = "649330b6-c2d7-4edc-be8e-8a54df9eb385"; + return request(`${baseUrl}/namespacePlugin?${stringify(params)}`, { + method: `GET`, + }); +} + +/* updatepluginEnabled */ +export async function updateNamespacePluginEnabled(params) { + return request(`${baseUrl}/namespacePlugin/enabled`, { + method: `POST`, + body: { + ids: params.list, + enabled: params.enabled, + namespaceId: params.namespaceId, + }, + }); +} + +/* updateNamespacePlugin */ +export async function updateNamespacePlugin(params) { + const formData = new FormData(); + formData.append("pluginId", params.pluginId); + if (params.config) formData.append("config", params.config); + formData.append("sort", params.sort); + formData.append("enabled", params.enabled); + formData.append("name", params.name); + formData.append("namespaceId", params.namespaceId); + return request( + `${baseUrl}/namespacePlugin/pluginId=${params.pluginId}&namespaceId=${params.namespaceId}`, + { + method: `PUT`, + body: formData, + }, + ); +} + +/* deletePlugin */ +export async function deleteNamespacePlugin(params) { + return request(`${baseUrl}/namespacePlugin/batch`, { + method: `DELETE`, + body: { + ids: [...params.list], + namespaceId: params.namespaceId, + }, + }); +} + +// sync all plugin +export async function asyncNamespacePlugin() { + return request(`${baseUrl}/namespacePlugin/syncPluginAll`, { + method: `POST`, + }); +} diff --git a/src/utils/namespacePlugin.js b/src/utils/namespacePlugin.js new file mode 100644 index 00000000..e45c4c3b --- /dev/null +++ b/src/utils/namespacePlugin.js @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from "react"; +import { refreshAuthMenus } from "./AuthRoute"; +import AddModal from "../routes/System/NamespacePlugin/AddModal"; + +export function getUpdateModal({ + id, + namespaceId, + dispatch, + fetchValue, + callback, + updatedCallback, + canceledCallback, +}) { + dispatch({ + type: "namespacePlugin/fetchItem", + payload: { + id, + namespaceId, + }, + callback: (plugin) => { + dispatch({ + type: "namespacePlugin/fetchByPluginId", + payload: { + pluginId: plugin.pluginId, + type: "3", + }, + callback: (pluginConfigList) => { + callback( + <AddModal + disabled={true} + {...plugin} + {...pluginConfigList} + handleOk={(values) => { + const { enabled, name, config, sort } = values; + dispatch({ + type: "namespacePlugin/update", + payload: { + config, + pluginId: plugin.pluginId, + enabled, + namespaceId, + sort, + name, + }, + fetchValue, + callback: () => { + if (updatedCallback) { + updatedCallback(values); + } + refreshAuthMenus({ dispatch }); + }, + }); + }} + handleCancel={canceledCallback} + />, + ); + }, + }); + }, + }); +} + +export function updateNamespacePluginsEnabled({ + list, + enabled, + namespaceId, + dispatch, + fetchValue, + callback, +}) { + dispatch({ + type: "namespacePlugin/updateEn", + payload: { + list, + enabled, + namespaceId, + }, + fetchValue, + callback: () => { + if (callback) { + callback(); + } + refreshAuthMenus({ dispatch }); + }, + }); +}