You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@inlong.apache.org by do...@apache.org on 2021/07/13 04:38:23 UTC
[incubator-inlong] 03/10: feat(broker): broker complete
This is an automated email from the ASF dual-hosted git repository.
dockerzhang pushed a commit to branch new-web-client
in repository https://gitbox.apache.org/repos/asf/incubator-inlong.git
commit 47b69ec5c4a90fe9a0d2a4689a811fcb42235469
Author: zakwu <12...@qq.com>
AuthorDate: Fri Jun 19 15:44:50 2020 +0800
feat(broker): broker complete
---
web/src/components/Breadcrumb/index.tsx | 2 +-
web/src/components/Layout/index.less | 8 +-
web/src/components/Modalx/index.less | 17 +
web/src/components/Modalx/index.tsx | 49 +++
web/src/components/Tablex/index.tsx | 4 +-
web/src/components/Tablex/tableFilterHelper.ts | 32 ++
web/src/configs/menus/index.tsx | 5 +-
web/src/context/globalContext.ts | 1 +
web/src/pages/Broker/index.less | 0
web/src/pages/Broker/index.tsx | 482 +++++++++++++++++++++++++
web/src/pages/Issue/consumeGroupDetail.tsx | 16 +
web/src/router.tsx | 5 +-
web/src/routes/index.tsx | 4 +-
web/src/utils/index.ts | 12 +
14 files changed, 629 insertions(+), 8 deletions(-)
diff --git a/web/src/components/Breadcrumb/index.tsx b/web/src/components/Breadcrumb/index.tsx
index 4d41369..c4113bf 100644
--- a/web/src/components/Breadcrumb/index.tsx
+++ b/web/src/components/Breadcrumb/index.tsx
@@ -19,7 +19,7 @@ const BasicLayout: React.FC<BreadcrumbProps> = props => {
const breadcrumbNameMap = {} as any;
breadcrumbMap &&
breadcrumbMap.forEach((t: MenuDataItem) => {
- breadcrumbNameMap[t.pro_layout_parentKeys.join('/') + t.key] = t.name;
+ breadcrumbNameMap[t.key as string] = t.name;
});
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
if (appendParams && index === pathSnippets.length - 1) {
diff --git a/web/src/components/Layout/index.less b/web/src/components/Layout/index.less
index 9811a7d..4d0184e 100644
--- a/web/src/components/Layout/index.less
+++ b/web/src/components/Layout/index.less
@@ -13,9 +13,15 @@
// global css
.main-container {
background: #fff;
- padding: 20px;
+ padding: 10px 20px 20px 20px;
}
.search-wrapper {
margin-bottom: 20px;
+}
+
+.options-wrapper {
+ a {
+ margin-right: 8px;
+ }
}
\ No newline at end of file
diff --git a/web/src/components/Modalx/index.less b/web/src/components/Modalx/index.less
new file mode 100644
index 0000000..6b03518
--- /dev/null
+++ b/web/src/components/Modalx/index.less
@@ -0,0 +1,17 @@
+.psw-set {
+ border-top: 1px solid #ccc;
+ padding-top: 20px;
+
+ .pws-label {
+ float: left;
+ line-height: 32px;
+ }
+
+ .psw-input {
+ width: 300px;
+ }
+}
+
+.enhance {
+ color: red;
+}
\ No newline at end of file
diff --git a/web/src/components/Modalx/index.tsx b/web/src/components/Modalx/index.tsx
new file mode 100644
index 0000000..16a7831
--- /dev/null
+++ b/web/src/components/Modalx/index.tsx
@@ -0,0 +1,49 @@
+/**
+ * TABLE COMPONENT WITH SEARCH
+ */
+import { Modal, Input, Tooltip } from 'antd';
+import * as React from 'react';
+import { ModalProps } from 'antd/lib/modal';
+import { ReactElement, useEffect } from 'react';
+import './index.less';
+
+const { useState } = React;
+
+export interface OKProps {
+ e: React.MouseEvent<HTMLElement>;
+ psw: string;
+ params?: any;
+}
+
+type ComProps = {
+ context?: number;
+ children?: ReactElement;
+ onOk?: (p: OKProps) => {};
+ params?: any;
+};
+
+const Comp = (props: ComProps & Omit<ModalProps, 'onOk'>) => {
+ const { params } = props;
+ const [psw, setPsw] = useState('');
+ const onOk = (e: React.MouseEvent<HTMLElement>) => {
+ props.onOk && props.onOk({ e, psw, params });
+ };
+
+ return (
+ <>
+ <Modal {...props} className="textWrap" width="60%" onOk={onOk}>
+ {props.children}
+ <div className="psw-set">
+ <label className="pws-label">机器授权:</label>
+ <Input
+ className="psw-input"
+ placeholder="请输入机器授权字段,验证操作权限"
+ onChange={e => setPsw(e.target.value)}
+ />
+ </div>
+ </Modal>
+ </>
+ );
+};
+
+export default Comp;
diff --git a/web/src/components/Tablex/index.tsx b/web/src/components/Tablex/index.tsx
index dc5e904..6e311ad 100644
--- a/web/src/components/Tablex/index.tsx
+++ b/web/src/components/Tablex/index.tsx
@@ -21,6 +21,7 @@ interface ComProps extends TableProps<any> {
defaultSearchKey?: string;
isTruePagination?: boolean;
showSearch?: boolean;
+ searchWidth?: number;
}
const Comp = (props: ComProps) => {
@@ -32,6 +33,7 @@ const Comp = (props: ComProps) => {
defaultSearchKey,
isTruePagination,
showSearch = true,
+ searchWidth = 8,
} = props;
const [filterKey, setFilterKey] = useState(defaultSearchKey);
// 自动增加排序
@@ -83,7 +85,7 @@ const Comp = (props: ComProps) => {
<>
{showSearch && filterFnX && (
<Row gutter={20} className="mb10">
- <Col span={8}>
+ <Col span={searchWidth} style={{ padding: 0 }}>
<Tooltip title={filterKey}>
<Search
value={filterKey}
diff --git a/web/src/components/Tablex/tableFilterHelper.ts b/web/src/components/Tablex/tableFilterHelper.ts
new file mode 100644
index 0000000..0032e8f
--- /dev/null
+++ b/web/src/components/Tablex/tableFilterHelper.ts
@@ -0,0 +1,32 @@
+interface TableFilterHelperProp {
+ key: string;
+ targetArray: Array<any>;
+ srcArray: Array<any>;
+ filterList: Array<any>;
+ updateFunction?: (p: Array<any>) => void;
+}
+const tableFilterHelper = (p: TableFilterHelperProp): any[] => {
+ const { key, srcArray = [], filterList, updateFunction } = p;
+ const res: any[] = [];
+
+ if (key) {
+ srcArray.forEach(it => {
+ const tar = filterList.map(t => {
+ return it[t];
+ });
+ let isFilterRight = false;
+ tar.forEach(t => {
+ if ((t + '').indexOf(key) > -1) isFilterRight = true;
+ });
+ if (isFilterRight) {
+ res.push(it);
+ }
+ });
+ }
+
+ if (updateFunction) updateFunction(res);
+
+ return res;
+};
+
+export default tableFilterHelper;
diff --git a/web/src/configs/menus/index.tsx b/web/src/configs/menus/index.tsx
index 140f583..e689571 100644
--- a/web/src/configs/menus/index.tsx
+++ b/web/src/configs/menus/index.tsx
@@ -28,13 +28,14 @@ const menus: Route[] = [
name: '配置管理',
key: '/other',
icon: <SettingOutlined />,
+ path: '/other',
children: [
{
- path: '/hello',
+ path: '/broker',
name: 'Broker列表',
},
{
- path: '/user',
+ path: '/topic',
name: 'topic列表',
},
],
diff --git a/web/src/context/globalContext.ts b/web/src/context/globalContext.ts
index 34e7f06..0540e02 100644
--- a/web/src/context/globalContext.ts
+++ b/web/src/context/globalContext.ts
@@ -6,6 +6,7 @@ export interface GlobalContextProps {
setCluster?: Function;
breadMap?: BreadcrumbProps['breadcrumbMap'];
setBreadMap?: Function;
+ userInfo?: any;
}
export default React.createContext<GlobalContextProps>({});
diff --git a/web/src/pages/Broker/index.less b/web/src/pages/Broker/index.less
new file mode 100644
index 0000000..e69de29
diff --git a/web/src/pages/Broker/index.tsx b/web/src/pages/Broker/index.tsx
new file mode 100644
index 0000000..644f3e2
--- /dev/null
+++ b/web/src/pages/Broker/index.tsx
@@ -0,0 +1,482 @@
+import React, { useContext, useState } from 'react';
+import GlobalContext from '@/context/globalContext';
+import Breadcrumb from '@/components/Breadcrumb';
+import Table from '@/components/Tablex';
+import Modal, { OKProps } from '@/components/Modalx';
+import {
+ Form,
+ Select,
+ Button,
+ Spin,
+ Switch,
+ Input,
+ Row,
+ Col,
+ message,
+} from 'antd';
+import { useImmer } from 'use-immer';
+import { useRequest } from '@/hooks';
+import tableFilterHelper from '@/components/Tablex/tableFilterHelper';
+import { boolean2Chinese } from '@/utils';
+import './index.less';
+
+declare type BrokerData = any[];
+interface BrokerResultData {
+ acceptPublish: string;
+ acceptSubscribe: string;
+ brokerId: number;
+ brokerIp: string;
+ brokerPort: number;
+ brokerTLSPort: number;
+ brokerVersion: string;
+ enableTLS: boolean;
+ isAutoForbidden: boolean;
+ isBrokerOnline: string;
+ isConfChanged: string;
+ isConfLoaded: string;
+ isRepAbnormal: boolean;
+ manageStatus: string;
+ runStatus: string;
+ subStatus: string;
+ [key: string]: any;
+}
+
+const { Option } = Select;
+const OPTIONS = [
+ {
+ value: 'online',
+ name: '上线',
+ },
+ {
+ value: 'offline',
+ name: '下线',
+ },
+ {
+ value: 'reload',
+ name: '重载',
+ },
+ {
+ value: 'delete',
+ name: '删除',
+ },
+];
+const OPTIONS_VALUES = OPTIONS.map(t => t.value);
+const queryBroker = (data: BrokerResultData) => ({
+ url: '/api/op_query/admin_query_broker_run_status',
+ data: data,
+});
+
+const Broker: React.FC = () => {
+ // column config
+ const columns = [
+ {
+ title: 'BrokerID',
+ dataIndex: 'brokerId',
+ fixed: 'left',
+ },
+ {
+ title: 'BrokerIP',
+ dataIndex: 'brokerIp',
+ },
+ {
+ title: 'BrokerPort',
+ dataIndex: 'brokerPort',
+ },
+ {
+ title: '管理状态',
+ dataIndex: 'manageStatus',
+ },
+ {
+ title: '运行状态',
+ dataIndex: 'runStatus',
+ },
+ {
+ title: '运行子状态',
+ dataIndex: 'subStatus',
+ },
+ {
+ title: '可发布',
+ dataIndex: 'acceptPublish',
+ render: (t: string, r: BrokerResultData) => {
+ return (
+ <Switch
+ checked={t === 'true'}
+ onChange={e => onSwitchChange(e, r, 'acceptPublish')}
+ />
+ );
+ },
+ },
+ {
+ title: '可订阅',
+ dataIndex: 'acceptSubscribe',
+ render: (t: string, r: BrokerResultData) => {
+ return (
+ <Switch
+ checked={t === 'true'}
+ onChange={e => onSwitchChange(e, r, 'acceptSubscribe')}
+ />
+ );
+ },
+ },
+ {
+ title: '配置变更',
+ dataIndex: 'isConfChanged',
+ render: (t: string) => boolean2Chinese(t),
+ },
+ {
+ title: '变更加载',
+ dataIndex: 'isConfLoaded',
+ render: (t: string) => boolean2Chinese(t),
+ },
+ {
+ title: 'broker注册',
+ dataIndex: 'isBrokerOnline',
+ render: (t: string) => boolean2Chinese(t),
+ },
+ {
+ title: '上线',
+ dataIndex: 'isBrokerOnline',
+ render: (t: string) => boolean2Chinese(t),
+ },
+ {
+ title: 'TLS端口',
+ dataIndex: 'brokerTLSPort',
+ render: (t: string) => boolean2Chinese(t),
+ },
+ {
+ title: '启用TLS',
+ dataIndex: 'enableTLS',
+ render: (t: boolean) => boolean2Chinese(t),
+ },
+ {
+ title: '上报异常',
+ dataIndex: 'isRepAbnormal',
+ render: (t: boolean) => boolean2Chinese(t),
+ },
+ {
+ title: '自动屏蔽',
+ dataIndex: 'isAutoForbidden',
+ render: (t: boolean) => boolean2Chinese(t),
+ },
+ {
+ title: '操作',
+ dataIndex: 'brokerIp',
+ fixed: 'right',
+ width: 180,
+ render: (t: string, r: any) => {
+ return (
+ <span className="options-wrapper">
+ {OPTIONS.map(t => (
+ <a key={t.value} onClick={() => onOptionsChange(t.value, r)}>
+ {t.name}
+ </a>
+ ))}
+ </span>
+ );
+ },
+ },
+ ];
+ const { breadMap, userInfo } = useContext(GlobalContext);
+ const [modalParams, updateModelParams] = useImmer<any>({});
+ const [filterData, updateFilterData] = useImmer<any>({});
+ const [selectBroker, setSelectBroker] = useState<any>([]);
+ const [brokerList, updateBrokerList] = useImmer<BrokerData>([]);
+ const [form] = Form.useForm();
+ const [newBrokerForm] = Form.useForm();
+ // init query
+ const { data, loading, run } = useRequest<any, BrokerData>(queryBroker, {
+ onSuccess: data => {
+ updateBrokerList(d => {
+ Object.assign(d, data);
+ });
+ },
+ });
+ // render funcs
+ const renderBrokerOptions = () => {
+ const columns = [
+ {
+ title: 'Broker',
+ render: (t: string, r: BrokerResultData) => {
+ return `${r.brokerId}#${r.brokerIp}:${r.brokerPort}`;
+ },
+ },
+ {
+ title: 'BrokerIP',
+ dataIndex: 'brokerIp',
+ },
+ {
+ title: '管理状态',
+ dataIndex: 'manageStatus',
+ },
+ {
+ title: '运行状态',
+ dataIndex: 'runStatus',
+ },
+ {
+ title: '运行子状态',
+ dataIndex: 'subStatus',
+ },
+ {
+ title: '可发布',
+ render: (t: string) => boolean2Chinese(t),
+ },
+ {
+ title: '可订阅',
+ render: (t: string) => boolean2Chinese(t),
+ },
+ ];
+ const dataSource = data.filter((t: BrokerResultData) =>
+ modalParams.params.includes(t.brokerId)
+ );
+ return (
+ <Table
+ columns={columns}
+ dataSource={dataSource}
+ rowKey="brokerId"
+ ></Table>
+ );
+ };
+ const renderNewBroker = () => {
+ const brokerFormArr = [
+ {
+ name: 'brokerId',
+ defaultValue: '0',
+ },
+ {
+ name: 'numPartitions',
+ defaultValue: '3',
+ },
+ {
+ name: 'brokerIP',
+ defaultValue: '',
+ },
+ {
+ name: 'brokerPort',
+ defaultValue: '8123',
+ },
+ {
+ name: 'deleteWhen',
+ defaultValue: '0 0 6,18 * * ?',
+ },
+ {
+ name: 'deletePolicy',
+ defaultValue: 'delete,168h',
+ },
+ {
+ name: 'unflushThreshold',
+ defaultValue: '1000',
+ },
+ {
+ name: 'unflushInterval',
+ defaultValue: '10000',
+ },
+ {
+ name: 'acceptPublish',
+ defaultValue: 'true',
+ },
+ {
+ name: 'acceptSubscribe',
+ defaultValue: 'true',
+ },
+ ];
+
+ return (
+ <Form form={newBrokerForm}>
+ <Row gutter={24}>
+ {brokerFormArr.map((t, index) => (
+ <Col span={12} key={'brokerFormArr' + index}>
+ <Form.Item
+ labelCol={{ span: 12 }}
+ label={t.name}
+ name={t.name}
+ initialValue={t.defaultValue}
+ >
+ <Input />
+ </Form.Item>
+ </Col>
+ ))}
+ </Row>
+ </Form>
+ );
+ };
+ const renderBrokerStateChange = () => {
+ const { params } = modalParams;
+
+ return (
+ <div>
+ 请确认<span className="enhance">{params.option}</span> ID:{' '}
+ <span className="enhance">{params.id}</span> 的 Broker?
+ </div>
+ );
+ };
+
+ // events
+ const onOpenModal = (type: string, title: string, params?: any) => {
+ updateModelParams(m => {
+ m.type = type;
+ m.params = params;
+ Object.assign(m, {
+ params,
+ visible: type,
+ title,
+ onOk: (p: OKProps) => onModelOk(type, p),
+ onCancel: () =>
+ updateModelParams(m => {
+ m.visible = false;
+ }),
+ });
+ });
+ };
+ // table event
+ const onSwitchChange = (e: boolean, r: BrokerResultData, type: string) => {
+ const index = data.findIndex(
+ (t: BrokerResultData) => t.brokerId === r.brokerId
+ );
+ updateBrokerList(d => {
+ d[index][type] = e + '';
+ });
+ let option = '';
+ if (type === 'acceptPublish') {
+ option = e ? '发布' : '禁止发布';
+ } else if (type === 'acceptSubscribe') {
+ option = e ? '订阅' : '禁止订阅';
+ }
+
+ onOpenModal('brokerStateChange', `请确认操作`, { option, id: r.brokerId });
+ };
+ const onBrokerTableSelectChange = (p: any[], rows: any[]) => {
+ setSelectBroker(p);
+ };
+
+ // modal event
+ const onOptionsChange = (type: string, r?: BrokerResultData) => {
+ if (!r && !selectBroker.length) {
+ form.resetFields();
+ return message.error('批量操作至少选择一列!');
+ }
+ onOpenModal(
+ type,
+ `确认进行【${OPTIONS.find(t => t.value === type)?.name}】操作?`,
+ [r?.brokerId]
+ );
+ };
+ const onModelOk = (type: string, p: OKProps) => {
+ switch (type) {
+ case 'newBroker':
+ return newBroker(p);
+ default:
+ return brokerOptions(type, p);
+ }
+ };
+
+ const newBrokerQuery = useRequest<any, any>(
+ data => ({ url: '/api/op_modify/admin_add_broker_configure', ...data }),
+ { manual: true }
+ );
+ const newBroker = (p: OKProps) => {
+ const values = newBrokerForm.getFieldsValue();
+ newBrokerQuery.run({
+ data: {
+ ...values,
+ confModAuthToken: p.psw,
+ createUser: userInfo.userName,
+ },
+ });
+ };
+
+ const brokerOptionsQuery = useRequest<any, any>(
+ (url, data) => ({ url, ...data }),
+ { manual: true }
+ );
+ const brokerOptions = (type: string, p: OKProps) => {
+ const { params } = p;
+ brokerOptionsQuery.run(`/api/op_modify/admin_${type}_broker_configure`, {
+ data: {
+ brokerId: params ? params?.join(',') : selectBroker.join(','),
+ confModAuthToken: p.psw,
+ createUser: userInfo.userName,
+ },
+ });
+ };
+
+ return (
+ <Spin spinning={loading}>
+ <Breadcrumb breadcrumbMap={breadMap}></Breadcrumb>
+ <div className="main-container">
+ <div
+ className="search-wrapper"
+ style={{ float: 'right', marginRight: '-16px' }}
+ >
+ <Form form={form} layout={'inline'}>
+ <Form.Item label="批量操作" name="optionType">
+ <Select
+ style={{ width: 120 }}
+ onChange={(v: string) => onOptionsChange(v)}
+ placeholder="请选择操作"
+ >
+ {OPTIONS.map(t => (
+ <Option value={t.value} key={t.value}>
+ {t.name}
+ </Option>
+ ))}
+ </Select>
+ </Form.Item>
+ <Form.Item>
+ <Button
+ type="primary"
+ onClick={() => onOpenModal('newBroker', '新建Broker')}
+ style={{ margin: '0 10px 0 10px' }}
+ >
+ 新增
+ </Button>
+ <Button type="primary" onClick={() => run()}>
+ 刷新
+ </Button>
+ </Form.Item>
+ </Form>
+ </div>
+ <Table
+ rowSelection={{ onChange: onBrokerTableSelectChange }}
+ columns={columns}
+ dataSource={brokerList}
+ rowKey="brokerId"
+ searchPlaceholder="请输入关键字搜索"
+ searchWidth={12}
+ dataSourceX={filterData.list}
+ scroll={{ x: 2500 }}
+ filterFnX={value =>
+ tableFilterHelper({
+ key: value,
+ srcArray: data,
+ targetArray: filterData.list,
+ updateFunction: res =>
+ updateFilterData(filterData => {
+ filterData.list = res;
+ }),
+ filterList: [
+ 'brokerId',
+ 'brokerIp',
+ 'brokerPort',
+ 'runStatus',
+ 'subStatus',
+ 'manageStatus',
+ ],
+ })
+ }
+ ></Table>
+ </div>
+ <Modal {...modalParams}>
+ <div>
+ {modalParams.type &&
+ OPTIONS_VALUES.includes(modalParams.type) &&
+ renderBrokerOptions()}
+ {modalParams.type === 'newBroker' && renderNewBroker()}
+ {modalParams.type === 'brokerStateChange' &&
+ renderBrokerStateChange()}
+ </div>
+ </Modal>
+ </Spin>
+ );
+};
+
+export default Broker;
diff --git a/web/src/pages/Issue/consumeGroupDetail.tsx b/web/src/pages/Issue/consumeGroupDetail.tsx
index 9e54f42..8b2f562 100644
--- a/web/src/pages/Issue/consumeGroupDetail.tsx
+++ b/web/src/pages/Issue/consumeGroupDetail.tsx
@@ -2,6 +2,7 @@ import React, { useContext } from 'react';
import GlobalContext from '@/context/globalContext';
import Breadcrumb from '@/components/Breadcrumb';
import Table from '@/components/Tablex';
+import tableFilterHelper from '@/components/Tablex/tableFilterHelper';
import { Form, Input, Button, Spin } from 'antd';
import { useImmer } from 'use-immer';
import './index.less';
@@ -43,6 +44,7 @@ const ConsumeGroupDetail: React.FC = () => {
const { breadMap } = useContext(GlobalContext);
const [form] = Form.useForm();
const [formValues, updateFormValues] = useImmer<any>({});
+ const [filterData, updateFilterData] = useImmer<any>({});
const { data, loading, run } = useRequest<any, ConsumeGroupData>(
() =>
queryUser({
@@ -86,6 +88,20 @@ const ConsumeGroupDetail: React.FC = () => {
columns={columns}
dataSource={data?.list}
rowKey="brokerAddr"
+ searchPlaceholder="请输入 broker地址/分区ID 搜索"
+ dataSourceX={filterData.list}
+ filterFnX={value =>
+ tableFilterHelper({
+ key: value,
+ srcArray: data?.list,
+ targetArray: filterData.list,
+ updateFunction: res =>
+ updateFilterData(filterData => {
+ filterData.list = res;
+ }),
+ filterList: ['brokerAddr', 'partId'],
+ })
+ }
></Table>
</div>
</Spin>
diff --git a/web/src/router.tsx b/web/src/router.tsx
index 1b6a6e9..66e0cdc 100644
--- a/web/src/router.tsx
+++ b/web/src/router.tsx
@@ -14,10 +14,13 @@ import GlobalContext from '@/context/globalContext';
const App = () => {
const [cluster, setCluster] = useState();
const [breadMap, setBreadMap] = useState();
+ const [userInfo, setUserInfo] = useState({
+ userName: 'webapi',
+ });
return (
<GlobalContext.Provider
- value={{ cluster, setCluster, breadMap, setBreadMap }}
+ value={{ cluster, setCluster, breadMap, setBreadMap, userInfo }}
>
<Router>
<Layout>
diff --git a/web/src/routes/index.tsx b/web/src/routes/index.tsx
index 032a633..7cbd3d6 100644
--- a/web/src/routes/index.tsx
+++ b/web/src/routes/index.tsx
@@ -10,8 +10,8 @@ const routes: RouteProps[] = [
component: () => import('@/pages/Issue'),
},
{
- path: '/hello',
- component: () => import('@/pages/Other/Hello'),
+ path: '/broker',
+ component: () => import('@/pages/Broker'),
},
{
path: '/user',
diff --git a/web/src/utils/index.ts b/web/src/utils/index.ts
index 0c6378b..aadcc68 100644
--- a/web/src/utils/index.ts
+++ b/web/src/utils/index.ts
@@ -27,3 +27,15 @@ export const isEmptyParam = (value: any): boolean => {
// value为默认值
return !value;
};
+
+export const boolean2Chinese = (value: boolean | string): string => {
+ let v = false;
+ if (value === 'false') {
+ v = false;
+ } else if (value === 'true') {
+ v = true;
+ } else {
+ v = value as boolean;
+ }
+ return v === false ? '否' : '是';
+};