umi#router TypeScript Examples
The following examples show how to use
umi#router.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: 404.tsx From jetlinks-ui-antd with MIT License | 6 votes |
NoFoundPage: React.FC<{}> = () => (
<Result
status="404"
title="404"
subTitle="Sorry, the page you visited does not exist."
extra={
<Button type="primary" onClick={() => router.push('/')}>
Back Home
</Button>
}
/>
)
Example #2
Source File: index.tsx From jetlinks-ui-antd with MIT License | 6 votes |
ToolBar: React.FC<Props> = (props) => {
return (
<div>
<Button icon="plus" type="primary" onClick={() => router.push('/device/product/add')}>
新建
</Button>
</div>
);
}
Example #3
Source File: index.tsx From jetlinks-ui-antd with MIT License | 6 votes |
ToolBar: React.FC<Props> = (props) => {
return (
<div>
<Button icon="plus" type="primary" onClick={() => router.push('/device/instance/add')}>
新建
</Button>
<Button>
同步状态
</Button>
</div>
);
}
Example #4
Source File: SecurityLayout.tsx From jetlinks-ui-antd with MIT License | 5 votes |
SecurityLayout = (props: SecurityLayoutProps) => {
const { dispatch, settings } = props;
const [isReady, setIsReady] = useState(false);
const { children, loading } = props;
// You can replace it to your authentication rule (such as check token exists)
// 你可以把它替换成你自己的登录认证规则(比如判断 token 是否存在)
const isLogin = !!localStorage.getItem('x-access-token');
const token = getAccessToken();
const queryString = stringify({
redirect: window.location.href,
});
useEffect(() => {
setIsReady(true);
if (dispatch) {
if (token !== 'null') {
dispatch({
type: 'user/fetchCurrent',
});
}else{
router.push('/user/login');
}
}
}, []);
/**
* constructor
*/
useEffect(() => {
if (dispatch) {
dispatch({
type: 'settings/fetchConfig',
callback: () => {
if (settings.titleIcon) {
document.getElementById('title-icon')!.href = settings.titleIcon;
}
setIsReady(true);
},
});
}
}, [settings.title]);
const render = () => {
if (isLogin) {
initWebSocket();
}
if ((!isLogin && loading) || !isReady) {
return <PageLoading />;
}
if (!isLogin) {
// TODO 此处应使用注释的代码。但跳转存在问题,
// return <Redirect to={`/user/login?${queryString}`} />;
return <Redirect to="/user/login"></Redirect>;
}
return children;
};
return render();
}
Example #5
Source File: user.ts From jetlinks-ui-antd with MIT License | 5 votes |
UserModel: UserModelType = {
namespace: 'user',
state: {
currentUser: {},
},
effects: {
*fetch(_, { call, put }) {
const response = yield call(queryUsers);
yield put({
type: 'save',
payload: response,
});
},
*fetchCurrent(_, { call, put }) {
const response = yield call(queryCurrent);
if (response) {
// 取出login 后缓存的数据
const loginStorage = localStorage.getItem('hsweb-autz');
if (loginStorage) {
const tempLogin = JSON.parse(loginStorage);
tempLogin.permissions = response.result.permissions;
const autz = JSON.stringify(tempLogin);
autz && localStorage.setItem('hsweb-autz', autz);
} else {
const autz = JSON.stringify(response.result);
autz && localStorage.setItem('hsweb-autz', autz);
}
yield put({
type: 'saveCurrentUser',
payload: response.result.user,
});
} else {
router.push('/user/login');
}
reloadAuthorized();
},
},
reducers: {
saveCurrentUser(state, action) {
return {
...state,
// currentUser: action.payload || {},
currentUser: {
...state?.currentUser,
...action.payload
}
};
},
changeNotifyCount(
state = {
currentUser: {},
},
action,
) {
return {
...state,
currentUser: {
...state.currentUser,
notifyCount: action.payload.totalCount,
unreadCount: action.payload.unreadCount,
},
};
},
},
}
Example #6
Source File: NoticeIconView.tsx From jetlinks-ui-antd with MIT License | 5 votes |
render() {
const { currentUser, fetchingNotices, onNoticeVisibleChange } = this.props;
const noticeData = this.getNoticeData();
const unreadMsg = this.getUnreadData(noticeData);
return (
<NoticeIcon
className={styles.action}
count={currentUser && currentUser.unreadCount}
onItemClick={item => {
this.changeReadState(item as NoticeItem);
}}
loading={fetchingNotices}
clearText="当前标记为已读"
viewMoreText="查看更多"
onClear={this.handleNoticeClear}
onPopupVisibleChange={onNoticeVisibleChange}
onViewMore={() => router.push('/account/notification')}
clearClose
>
{/* <NoticeIcon.Tab
tabKey="notification"
count={unreadMsg.notification}
list={noticeData.notification}
title="通知"
emptyText="你已查看所有通知"
showViewMore
/> */}
<NoticeIcon.Tab
tabKey="read"
count={unreadMsg.unread}
list={noticeData.unread}
title="未读消息"
emptyText="您已读完所有消息"
showViewMore
/>
<NoticeIcon.Tab
tabKey="handle"
title="待办消息"
emptyText="暂无消息"
count={unreadMsg.handle}
list={noticeData.handle}
showViewMore
/>
</NoticeIcon>
);
}
Example #7
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Device = (props: Props) => {
const [visible, setVisible] = useState<boolean>(false);
const data = useContext(TenantContext);
const service = new Service('tenant');
const [online, setOnline] = useState(0);
const [offline, setOffline] = useState(0);
const getData = () => {
service.assets.deviceCount(encodeQueryParam({
terms: {
id$assets: JSON.stringify({
tenantId: data?.id,
assetType: 'device',
memberId: props.user,
}),
state: 'offline'
}
})).subscribe(resp => {
setOffline(resp)
});
service.assets.deviceCount(encodeQueryParam({
terms: {
id$assets: JSON.stringify({
tenantId: data?.id,
assetType: 'device',
memberId: props.user,
}),
state: 'online'
}
})).subscribe(resp => {
setOnline(resp)
})
};
useEffect(() => {
getData();
}, [props.user]);
return (
<List.Item style={{paddingRight: '10px'}}>
<Card
hoverable
className={styles.card}
actions={[
<Tooltip title="查看">
<Icon type="eye" onClick={() => router.push({
pathname: `/device/instance`,
search:'iop='+JSON.stringify({
terms: {
id$assets: {
tenantId: data?.id,
assetType: 'device',
memberId: props.user,
}
}
})
})}/>
</Tooltip>,
<Tooltip title="编辑">
<Icon type="edit" onClick={() => setVisible(true)}/>
</Tooltip>]}
>
<Card.Meta
avatar={<IconFont type="icon-device" style={{fontSize: 45}}/>}
title={<a>设备</a>}
/>
<div className={styles.cardInfo}>
<div>
<p>在线</p>
<p>{online}</p>
</div>
<div>
<p>离线</p>
<p>{offline}</p>
</div>
</div>
</Card>
{visible && (
<Edit
user={props.user}
data={data}
close={() => {
setVisible(false);
getData();
}}/>
)}
</List.Item>
)
}
Example #8
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
GroupList: React.FC<Props> = props => {
const service = new Service('device');
const [loading, setLoading] = useState<boolean>(false);
const [saveVisible, setSaveVisible] = useState<boolean>(false);
const [current, setCurrnet] = useState<any>({});
const [list, setList] = useState<any>({});
const [add, setAdd] = useState<boolean>(false);
const [searchParam, setSearchParam] = useState<any>({
pageSize: 10,
sorts: {
order: 'descend',
field: 'id',
},
});
const search = (params?: any) => {
setSearchParam(params);
setLoading(true);
const defaultTerms = { parentId$isnull: true };
service
.groupList(
encodeQueryParam({
...params,
terms: { ...defaultTerms, ...params?.terms },
}),
)
.subscribe(
d => setList(d),
() => message.error('刷新重试'),
() => setLoading(false),
);
};
useEffect(() => {
search(searchParam);
}, []);
const saveGroup = (item: GroupItem) => {
if (current.id) {
//编辑
service.saveOrUpdataGroup(item).subscribe(
() => message.success('保存成功'),
() => {},
() => {
setSaveVisible(false);
search(searchParam);
},
);
} else {
//新增
service.saveGroup(item).subscribe(
() => message.success('保存成功'),
() => {},
() => {
setSaveVisible(false);
search(searchParam);
},
);
}
};
return (
<PageHeaderWrapper title="设备分组">
<Card bordered={false} style={{ marginBottom: 10 }}>
<Row>
<Button
type="primary"
style={{ marginLeft: 8 }}
onClick={() => {
setSaveVisible(true);
setAdd(true);
setCurrnet({});
}}
>
<Icon type="plus" />
新建分组
</Button>
<span style={{ marginLeft: 20 }}>
<label>分组名称:</label>
<Input.Search
style={{ width: '20%' }}
placeholder="分组名称"
onSearch={value => {
search({
terms: {
name$LIKE: value,
},
pageSize: searchParam.pageSize,
});
}}
/>
</span>
</Row>
</Card>
<Card>
<ProTable
loading={loading}
dataSource={list?.data}
columns={[
{ title: '标识', dataIndex: 'id' },
{ title: '名称', dataIndex: 'name' },
{ title: '描述', dataIndex: 'description' },
{
title: '操作',
render: (_, record: any) => (
<>
<a
onClick={() => {
router.push({
pathname: '/device/tree/detail',
query: { id: record.id },
});
}}
>
查看
</a>
<Divider type="vertical" />
<a
onClick={() => {
setCurrnet(record);
setAdd(false);
setSaveVisible(true);
}}
>
编辑
</a>
<Divider type="vertical" />
<Popconfirm
title="确认删除此分组?"
onConfirm={() => {
service.removeGroup(record.id).subscribe(
() => message.success('删除成功!'),
() => message.error('删除失败'),
() => search(),
);
}}
>
<a>删除</a>
</Popconfirm>
</>
),
},
]}
rowKey="id"
onSearch={(params: any) => {
search(params);
}}
paginationConfig={list}
/>
</Card>
{saveVisible && (
<Save
flag={add}
data={current}
close={() => {
setSaveVisible(false);
}}
save={(item: GroupItem) => saveGroup(item)}
/>
)}
</PageHeaderWrapper>
);
}
Example #9
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
edgeDevice: React.FC<Props> = () => {
const [result, setResult] = useState({
data: [],
pageIndex: 0,
total: 0,
pageSize: 0,
});
const [loading, setLoading] = useState(false);
const [searchParam, setSearchParam] = useState({
pageSize: 8,
terms: {
'productId$edge-product': 1,
},
sorts: {
order: 'desc',
field: 'id',
},
});
const [saveVisible, setSaveVisible] = useState(false);
const [info, setInfo] = useState({});
const statusColor = new Map();
statusColor.set('online', 'green');
statusColor.set('offline', 'red');
statusColor.set('notActive', 'blue');
const handleSearch = (param?: any) => {
setLoading(true);
setSearchParam(param);
apis.edgeDevice.list(encodeQueryParam(param)).then(res => {
if (res.status === 200) {
setResult(res.result);
setLoading(false);
}
});
};
// const changeDeploy = (record: any) => {
// apis.deviceInstance
// .changeDeploy(record.id)
// .then(response => {
// if (response.status === 200) {
// message.success('操作成功');
// deviceIdList.splice(0, deviceIdList.length);
// handleSearch(searchParam);
// }
// })
// .catch(() => {
// });
// };
// const unDeploy = (record: any) => {
// apis.deviceInstance
// .unDeploy(record.id)
// .then(response => {
// if (response.status === 200) {
// message.success('操作成功');
// deviceIdList.splice(0, deviceIdList.length);
// handleSearch(searchParam);
// }
// })
// .catch(() => {
// });
// };
const onChange = (page: number, pageSize: number) => {
handleSearch({
pageIndex: page - 1,
pageSize,
terms: searchParam.terms,
});
};
useEffect(() => {
const query: any = getPageQuery();
if (query.hasOwnProperty('productId')) {
handleSearch({
terms: {
productId: query.productId,
},
pageSize: 10,
});
} else {
handleSearch(searchParam);
}
}, []);
return (
<PageHeaderWrapper title="设备管理">
<Card bordered={false}>
<div className={styles.tableList}>
<div>
<SearchForm
formItems={[
{
label: 'ID',
key: 'id',
type: 'string',
},
{
label: '名称',
key: 'name$LIKE',
type: 'string',
},
]}
search={(params: any) => {
setSearchParam(params);
handleSearch({
terms: { ...params, 'productId$edge-product': 1 },
sorts: {
order: 'desc',
field: 'id',
},
pageSize: 8,
});
}}
/>
</div>
<div className={styles.tableListOperator}>
<Button
icon="plus"
type="primary"
onClick={() => {
setSaveVisible(true);
setInfo({});
}}
>
新增
</Button>
</div>
</div>
</Card>
<br />
<div>
{result.data && (
<List<any>
rowKey="id"
loading={loading}
grid={{ gutter: 24, xl: 4, lg: 3, md: 3, sm: 2, xs: 1 }}
dataSource={(result || {}).data}
pagination={{
current: result?.pageIndex + 1,
total: result?.total,
pageSize: result?.pageSize,
showQuickJumper: true,
showSizeChanger: true,
hideOnSinglePage: true,
pageSizeOptions: ['8', '16', '40', '80'],
style: { marginTop: -20 },
showTotal: (total: number) =>
`共 ${total} 条记录 第 ${result.pageIndex + 1}/${Math.ceil(
result.total / result.pageSize,
)}页`,
onChange,
}}
renderItem={item => {
if (item && item.id) {
return (
<List.Item key={item.id}>
<Card
hoverable
bodyStyle={{ paddingBottom: 20, height: 200 }}
actions={[
<Tooltip key="seeProduct" title="查看">
<Icon
type="eye"
onClick={() => {
router.push(`/edge-gateway/device/detail/${item.id}`);
}}
/>
</Tooltip>,
<Tooltip key="update" title="编辑">
<Icon
type="edit"
onClick={() => {
setSaveVisible(true);
setInfo(item);
}}
/>
</Tooltip>,
]}
>
<Card.Meta
avatar={<Avatar size={40} src={img} />}
title={
<AutoHide title={item.name} style={{ width: '95%', fontWeight: 600 }} />
}
description={
<AutoHide
title={moment(item.createTime).format('YYYY-MM-DD HH:mm:ss')}
style={{ width: '95%' }}
/>
}
/>
<div>
<div
style={{
width: '100%',
display: 'flex',
marginTop: '10px',
justifyContent: 'space-',
}}
>
<div style={{ textAlign: 'center', width: '30%' }}>
<p>ID</p>
<AutoHide title={item.id} style={{ fontSize: 14, width: '95%' }} />
</div>
<div style={{ textAlign: 'center', width: '30%' }}>
<p>产品名称</p>
<p style={{ fontSize: 14 }}>{item.productName}</p>
</div>
<div style={{ textAlign: 'center', width: '30%' }}>
<p>状态</p>
<p style={{ fontSize: 14, fontWeight: 600 }}>
<Badge
color={statusColor.get(item.state?.value)}
text={item.state?.text}
/>
</p>
</div>
</div>
</div>
</Card>
</List.Item>
);
}
return '';
}}
/>
)}
</div>
{saveVisible && (
<Save
close={() => {
setSaveVisible(false);
}}
save={() => {
handleSearch(searchParam);
setSaveVisible(false);
}}
data={info}
/>
)}
</PageHeaderWrapper>
);
}
Example #10
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
edgeProduct: React.FC<Props> = props => {
const [result, setResult] = useState({
data: [],
pageIndex: 0,
total: 0,
pageSize: 0
});
const [searchParam, setSearchParam] = useState({ pageSize: 8, terms: {} });
const [saveVisible, setSaveVisible] = useState(false);
const [deviceCount, setDeviceCount] = useState({});
const [info, setInfo] = useState({});
const cardInfoTitle = {
fontSize: 14,
color: 'rgba(0, 0, 0, 0.85)'
};
const handleSearch = (params?: any) => {
setSearchParam(params);
apis.edgeProduct.list(encodeQueryParam(params)).then(res => {
if (res.status === 200) {
setResult(res.result)
}
})
};
const onChange = (page: number, pageSize: number) => {
handleSearch({
pageIndex: page - 1,
pageSize,
terms: searchParam.terms
});
};
useEffect(() => {
handleSearch(searchParam);
}, []);
useEffect(() => {
result.data?.map((item: any) => {
apis.deviceInstance.count(encodeQueryParam({ terms: { 'productId': item.id } }))
.then(res => {
if (res.status === 200) {
deviceCount[item.id] = String(res.result);
setDeviceCount({ ...deviceCount });
} else {
deviceCount[item.id] = '/';
setDeviceCount({ ...deviceCount });
}
}).catch();
});
}, [result]);
return (
<PageHeaderWrapper title="产品管理">
<Card bordered={false}>
<div className={styles.tableList}>
<div>
<SearchForm
formItems={[
{
label: 'ID',
key: 'id$LIKE',
type: 'string',
},
{
label: '名称',
key: 'name$LIKE',
type: 'string',
}
]}
search={(params: any) => {
setSearchParam(params);
handleSearch({
terms: params,
pageSize: 8
});
}}
/>
</div>
<div className={styles.tableListOperator}>
<Button
icon="plus"
type="primary"
onClick={() => {
setSaveVisible(true);
setInfo({});
}}>
新增
</Button>
</div>
</div>
</Card>
<br />
<div className={cardStyles.filterCardList}>
{result.data && (
<List<any>
rowKey="id"
loading={props.loading}
grid={{ gutter: 24, xl: 4, lg: 3, md: 3, sm: 2, xs: 1 }}
dataSource={(result || {}).data}
pagination={{
current: result?.pageIndex + 1,
total: result?.total,
pageSize: result?.pageSize,
showQuickJumper: true,
showSizeChanger: true,
hideOnSinglePage: true,
pageSizeOptions: ['8', '16', '40', '80'],
style: { marginTop: -20 },
showTotal: (total: number) =>
`共 ${total} 条记录 第 ${result.pageIndex + 1}/${Math.ceil(
result.total / result.pageSize,
)}页`,
onChange
}}
renderItem={item => {
if (item && item.id) {
return (
<List.Item key={item.id}>
<Card hoverable bodyStyle={{ paddingBottom: 20 }}
actions={[
<Tooltip key="seeProduct" title="查看">
<Icon
type="eye"
onClick={() => {
router.push(`/device/product/save/${item.id}`);
}}
/>
</Tooltip>,
<Tooltip key="update" title='编辑'>
<Icon
type="edit"
onClick={() => {
setSaveVisible(true);
setInfo(item);
}}
/>
</Tooltip>,
<Tooltip key="del" title='删除'>
<Popconfirm
title="删除此产品?"
onConfirm={() => {
apis.edgeProduct.remove(item.id).then(res => {
if (res.status === 200) {
handleSearch(searchParam);
message.success('删除成功!');
}
})
}}>
<Icon type="close" />
</Popconfirm>
</Tooltip>
]}
>
<Card.Meta
avatar={<Avatar size={40} src={item.photoUrl || img} />}
title={<AutoHide title={item.name} style={{ width: '95%', fontWeight: 600 }} />}
description={<AutoHide title={item.id} style={{ width: '95%' }} />}
/>
<div className={cardStyles.cardItemContent}>
<div className={cardStyles.cardInfo}>
<div style={{ textAlign: 'center', width: '30%' }}>
<p style={cardInfoTitle}>厂家</p>
<p style={{ fontSize: 14, fontWeight: 600 }}>
<AutoHide title={item.manufacturer} style={{ width: '95%' }} />
</p>
</div>
<div style={{ textAlign: 'center', width: '30%' }}>
<p style={cardInfoTitle}>型号</p>
<p style={{ fontSize: 14, fontWeight: 600 }}>
<AutoHide title={item.model} style={{ width: '95%' }} />
</p>
</div>
<div style={{ textAlign: 'center', width: '30%' }}>
<p style={cardInfoTitle}>设备数量</p>
<p style={{ fontSize: 14, fontWeight: 600 }}>
<Tooltip key="findDevice" title="点击查看设备">
<a onClick={() => {
router.push(`/edge-gateway/device?productId=${item.id}`);
}}
>{deviceCount[item.id]}</a>
</Tooltip>
</p>
</div>
</div>
</div>
</Card>
</List.Item>
);
}
return '';
}}
/>
)}
</div>
{
saveVisible && <Save
close={() => {
setSaveVisible(false);
}}
save={() => {
handleSearch(searchParam);
setSaveVisible(false);
}}
data={info}
/>
}
</PageHeaderWrapper>
)
}
Example #11
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
MediaDevice: React.FC<Props> = () => {
const service = new Service('media/device');
const [loading, setLoading] = useState<boolean>(false);
const [deviceUpdate, setDeviceUpdate] = useState<boolean>(false);
const [deviceData, setDeviceData] = useState<any>({});
const [result, setResult] = useState<any>({});
const [searchParam, setSearchParam] = useState(initState.searchParam);
const statusMap = new Map();
statusMap.set('online', 'success');
statusMap.set('offline', 'error');
statusMap.set('notActive', 'processing');
const streamMode = new Map();
streamMode.set('UDP', 'UDP');
streamMode.set('TCP_ACTIVE', 'TCP主动');
streamMode.set('TCP_PASSIVE', 'TCP被动');
useEffect(() => {
handleSearch(searchParam);
}, []);
const handleSearch = (params?: any) => {
setSearchParam(params);
setLoading(true);
service.query(encodeQueryParam(params)).subscribe(
data => setResult(data),
() => {
},
() => setLoading(false),
);
};
const columns: ColumnProps<any>[] = [
{
title: '国标设备编号',
dataIndex: 'id',
width: 200,
ellipsis: true,
fixed: 'left',
},
{
title: '设备名称',
dataIndex: 'name',
render: (record: any) => (record ? record : result.id),
ellipsis: true,
},
{
title: '信令传输',
dataIndex: 'transport',
width: 90,
ellipsis: true,
},
{
title: '流传输模式',
dataIndex: 'streamMode',
width: 110,
render: record => (record ? streamMode.get(record) : '/'),
ellipsis: true,
},
{
title: '通道数',
dataIndex: 'channelNumber',
width: 100,
ellipsis: true,
},
{
title: '设备状态',
dataIndex: 'state',
width: 110,
render: record =>
record ? <Badge status={statusMap.get(record.value)} text={record.text}/> : '/',
filters: [
{
text: '未启用',
value: 'notActive',
},
{
text: '离线',
value: 'offline',
},
{
text: '在线',
value: 'online',
},
],
filterMultiple: false,
},
{
title: '设备IP',
dataIndex: 'host',
ellipsis: true,
width: 150,
},
{
title: '设备端口',
dataIndex: 'port',
width: 90,
ellipsis: true,
},
{
title: '设备厂家',
dataIndex: 'manufacturer',
ellipsis: true,
},
{
title: '设备型号',
dataIndex: 'model',
ellipsis: true,
},
{
title: '固件版本',
dataIndex: 'firmware',
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
render: (text: any) => (text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : '/'),
sorter: true,
width: 180,
ellipsis: true,
},
{
title: '操作',
key: 'center',
fixed: 'right',
width: 260,
render: (record: any) => (
<Fragment>
<a
onClick={() => {
router.push(`/device/instance/save/${record.id}`);
}}
>
查看
</a>
<Divider type="vertical"/>
<a
onClick={() => {
setDeviceData(record);
setDeviceUpdate(true);
}}
>
编辑
</a>
<Divider type="vertical"/>
<a
onClick={() => {
router.push(`/media/device/channel/${record.id}`);
}}
>
查看通道
</a>
<Divider type="vertical"/>
{record.state.value === 'online' ? (
<a
onClick={() => {
setLoading(true);
service._sync(record.id).subscribe(
() => {
message.success('通道更新成功');
},
() => {
message.error('通道更新失败');
},
() => setLoading(false),
);
}}
>
更新通道
</a>
):(
<Popconfirm
placement="topRight"
title="确定删除此国标设备吗?"
onConfirm={() => {
setLoading(true);
service.remove(record.id).subscribe(
() => {
message.success('国标设备删除成功');
},
() => {
message.error('国标设备删除失败');
},
() => {
handleSearch(searchParam);
setLoading(false);
},
);
}}
>
<a>删除</a>
</Popconfirm>
)}
</Fragment>
),
},
];
return (
<PageHeaderWrapper title="国标设备">
<Card style={{marginBottom: 16, height: 92}}>
<div className={styles.tableList} style={{marginTop: -22}}>
<div>
<SearchForm
search={(params: any) => {
setSearchParam(params);
handleSearch({
terms: {...params},
pageSize: 10,
sorts: {field: 'id', order: 'desc'},
});
}}
formItems={[
{
label: '设备名称',
key: 'name$LIKE',
type: 'string',
},
]}
/>
</div>
</div>
</Card>
<Card>
<div className={styles.StandardTable}>
<ProTable
loading={loading}
dataSource={result?.data}
columns={columns}
rowKey="id"
scroll={{x: '150%'}}
onSearch={(params: any) => {
params.sorts = params.sorts.field ? params.sorts : {field: 'id', order: 'desc'};
handleSearch(params);
}}
paginationConfig={result}
/>
</div>
</Card>
{deviceUpdate && (
<DeviceUpdate
close={() => {
setDeviceUpdate(false);
handleSearch(searchParam);
}}
data={deviceData}
/>
)}
</PageHeaderWrapper>
);
}
Example #12
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Modbus = () => {
const [spinning, setSpinning] = useState<boolean>(false);
const [visible, setVisible] = useState<boolean>(false);
const [current, setCurrent] = useState<any>({});
const [searchParams, setSearchParams] = useState<any>({ pageSize: 10, pageIndex: 0 })
const [dataSource, setDataSource] = useState<any>({
data: []
})
const handleSearch = (params: any) => {
setSearchParams(params)
setSpinning(true)
apis.modbus.getChanelList(params).then(resp => {
if (resp.status === 200) {
setDataSource(resp.result)
}
setSpinning(false)
})
}
useEffect(() => {
handleSearch(searchParams)
}, [])
const statusMap = new Map();
statusMap.set('enabled', 'success');
statusMap.set('disabled', 'error');
const columns = [
{
title: '通道名称',
dataIndex: 'name',
key: 'name',
},
{
title: 'IP',
dataIndex: 'host',
key: 'host',
},
{
title: '端口',
dataIndex: 'port',
key: 'port',
},
{
title: '状态',
dataIndex: 'state',
key: 'state',
render: (record: any) =>
record ? <Badge status={statusMap.get(record.value)} text={record.text} /> : '',
},
{
title: '操作',
key: 'action',
align: 'center',
render: (text: any, record: any) => (
<span>
<a onClick={() => {
setCurrent(record)
setVisible(true)
}}>编辑</a>
<Divider type="vertical" />
<a onClick={() => {
router.push(`/network/modbus/${record.id}`)
}}>设备接入</a>
<Divider type="vertical" />
<Popconfirm title={`确认${record.state.value === 'enabled' ? '禁用' : '启用'}`}
onConfirm={() => {
const data = {
...record,
state: record.state.value === 'enabled' ? 'disabled' : 'enabled'
}
apis.modbus.updataChanel(data).then(resp => {
if (resp.status === 200) {
message.success('操作成功!')
handleSearch(searchParams)
}
})
}}
>
<a>{record.state.value === 'enabled' ? '禁用' : '启用'}</a>
</Popconfirm>
<Divider type="vertical" />
<Popconfirm title="确认删除" onConfirm={() => {
apis.modbus.removeChanel(record.id).then(resp => {
if (resp.status === 200) {
message.success('操作成功!')
handleSearch(searchParams)
}
})
}}>
<a>删除</a>
</Popconfirm>
</span>
),
}
];
const onTableChange = (
pagination: PaginationConfig
) => {
handleSearch({
pageIndex: Number(pagination.current) - 1,
pageSize: pagination.pageSize,
terms: searchParams?.terms
});
};
return (
<Spin spinning={spinning}>
<PageHeaderWrapper title="Modbus">
<Card bordered={false} style={{ marginBottom: 20 }}>
<SearchForm
search={(params: any) => {
if (params) {
const terms: any[] = []
Object.keys(params).forEach(key => {
if (params[key]) {
terms.push(
{
"terms": [
{
"column": "name",
"value": `%${params[key]}%`,
"termType": "like"
}
]
}
)
}
})
setSearchParams({ pageSize: 10, pageIndex: 0, terms })
handleSearch({ pageSize: 10, pageIndex: 0, terms })
} else {
setSearchParams({ pageSize: 10, pageIndex: 0 })
handleSearch({ pageSize: 10, pageIndex: 0 })
}
}}
formItems={[{
label: '通道名称',
key: 'name',
type: 'string',
}]}
/>
</Card>
<Card bordered={false}>
<div style={{ margin: '10px 0' }}><Button type="primary" onClick={() => {
setVisible(true)
setCurrent({})
}}>新增</Button></div>
<Table dataSource={dataSource?.data || []} rowKey='id' columns={columns}
onChange={onTableChange}
pagination={{
current: dataSource.pageIndex + 1,
total: dataSource.total,
pageSize: dataSource.pageSize || 10,
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
showTotal: (total: number) =>
`共 ${total} 条记录 第 ${dataSource.pageIndex + 1}/${Math.ceil(
dataSource.total / dataSource.pageSize,
)}页`,
}} />
</Card>
</PageHeaderWrapper>
{
visible && <Save close={() => {
setVisible(false)
handleSearch(searchParams)
}} data={current} />
}
</Spin>
)
}
Example #13
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
OrgChart = () => {
const [list, setList] = useState<any>({});
const [edit, setEdit] = useState<boolean>(false);
const [current, setCurrent] = useState<any>({});
const [autzVisible, setAutzVisible] = useState(false);
const [userVisible, setUserVisible] = useState(false);
const [parentId, setParentId] = useState(null);
const hitCenter = () => {
const orgChart = document.getElementsByClassName('orgchart-container')[0];
const {width} = orgChart.getBoundingClientRect();
orgChart.scrollLeft = width;
};
const handleSearch = () => {
apis.org.list(encodeQueryParam({paging: false, terms: {typeId: 'org'}})).then(resp => {
const data = {
id: null,
name: '机构管理',
title: '组织架构',
children: resp.result,
};
setList(data);
hitCenter();
});
};
useEffect(() => {
handleSearch();
}, []);
const saveData = (data: any) => {
if (data.id) {
apis.org
.saveOrUpdate(data)
.then(res => {
if (res.status === 200) {
message.success('保存成功');
}
})
.then(() => {
handleSearch();
});
} else {
apis.org
.add(data)
.then(res => {
if (res.status === 200) {
message.success('保存成功');
}
})
.then(() => {
handleSearch();
});
}
};
const remove = (id: string) => {
apis.org
.remove(id)
.then(resp => {
if (resp.status === 200) {
message.success('操作成功');
}
})
.finally(() => {
handleSearch();
});
};
const menu = (nodeData: any) => {
return nodeData.id === null ? (
<Menu>
<Menu.Item>
<a
target="_blank"
rel="noopener noreferrer"
onClick={() => {
setCurrent({});
setParentId(nodeData);
setEdit(true);
}}
>
添加下级
</a>
</Menu.Item>
</Menu>
) : (
<Menu>
<Menu.Item>
<a
target="_blank"
rel="noopener noreferrer"
onClick={() => {
setParentId(null);
setCurrent(nodeData);
setEdit(true);
}}
>
编辑
</a>
</Menu.Item>
<Menu.Item>
<a
target="_blank"
rel="noopener noreferrer"
onClick={() => {
setCurrent({});
setParentId(nodeData);
setEdit(true);
}}
>
添加下级
</a>
</Menu.Item>
<Menu.Item>
<a
target="_blank"
rel="noopener noreferrer"
onClick={() => {
setCurrent(nodeData);
setAutzVisible(true);
}}
>
权限分配
</a>
</Menu.Item>
<Menu.Item>
<a
target="_blank"
rel="noopener noreferrer"
onClick={() => {
setCurrent(nodeData);
setUserVisible(true);
}}
>
绑定用户
</a>
</Menu.Item>
<Menu.Item>
<a
target="_blank"
rel="noopener noreferrer"
onClick={() => {
router.push(`/system/org-chart/assets/${nodeData.id}/org`);
}}
>
资产分配
</a>
</Menu.Item>
<Menu.Item>
<a
target="_blank"
rel="noopener noreferrer"
onClick={() => {
remove(nodeData.id);
}}
>
删除
</a>
</Menu.Item>
</Menu>
);
};
return (
<PageHeaderWrapper title="机构管理">
<div className={styles.orgContainer}>
<OrganizationChart
datasource={list}
// draggable
// onClickNode={(node: any) => {
// message.success(JSON.stringify(node));
// }}
pan={true}
// zoom={true}
NodeTemplate={(nodeData: any) => (
<NodeTemplate data={nodeData.nodeData} action={menu(nodeData.nodeData)}/>
)}
/>
{edit && (
<Save
data={current}
close={() => {
setEdit(false);
}}
save={(item: any) => {
saveData(item);
setEdit(false);
}}
parentId={parentId}
/>
)}
{autzVisible && (
<Authorization
close={() => {
setAutzVisible(false);
setCurrent({});
}}
target={current}
targetType="org"
/>
)}
{userVisible && (
<BindUser
data={current}
close={() => {
setUserVisible(false);
}}
/>
)}
</div>
</PageHeaderWrapper>
);
}
Example #14
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Edit = (props: Props) => {
const service = new Service('tenant');
const initState: State = {
list: {},
};
const [list, setList] = useState(initState.list);
const [add, setAdd] = useState<boolean>(false);
const { data } = props;
const [cat, setCat] = useState<boolean>(false);
const [asset, setAsset] = useState();
const [selected, setSelected] = useState<any[]>([]);
const [tenant, setTenant] = useState<any[]>([]);
const initSearch = {
terms: {
id$assets: JSON.stringify({
tenantId: data?.id,
assetType: 'device',
memberId: props.user,
// not: true,
}),
},
pageIndex: 0,
pageSize: 10,
};
const [searchParam, setSearchParam] = useState<any>(initSearch);
useEffect(() => {
list.data?.map((item: any) => {
service.assets.members(data.id, 'device', item.id).subscribe(resp => {
tenant[item.id] = resp
.filter((item: any) => item.binding === true)
.map((i: any) => i.userName)
.join('、');
setTenant({ ...tenant });
});
});
}, [list]);
// let device = (tempSearch: any, datalist: any[]) => {
// return new Promise((resolve) => {
// service.assets.device(encodeQueryParam(tempSearch)).subscribe(res => {
// if (res.data.length > 0) {
// res.data.forEach((value: any) => {
// service.assets.members(data.id, 'device', value.id).subscribe(resp => {
// let str = '';
// resp.filter((item: any) => item.binding === true).map((i: any) => i.userName).forEach((i: string) => {
// str += i + '、'
// });
// datalist.push({
// id: value.id,
// name: value.name,
// tenant: str.substring(0, str.length - 1)
// });
// if (datalist.length == res.data.length) {
// resolve({
// pageIndex: res.pageIndex,
// pageSize: res.pageSize,
// total: res.total,
// data: datalist
// })
// }
// });
// })
// }else {
// resolve({
// pageIndex: res.pageIndex,
// pageSize: res.pageSize,
// total: res.total,
// data: []
// })
// }
// })
// })
// };
const handleSearch = (params: any) => {
const tempParam = { ...searchParam, ...params };
const defaultItem = searchParam.terms;
const tempTerms = params?.terms;
const terms = tempTerms ? { ...defaultItem, ...tempTerms } : initSearch;
let tempSearch: {};
if (tempTerms) {
tempParam.terms = terms;
tempSearch = tempParam;
} else {
tempSearch = initSearch;
}
setSearchParam(tempSearch);
service.assets.device(encodeQueryParam(tempSearch)).subscribe(res => {
if (res.data.length > 0) {
let datalist: any = [];
res.data.forEach((value: any) => {
datalist.push({
id: value.id,
name: value.name,
});
});
setList({
pageIndex: res.pageIndex,
pageSize: res.pageSize,
total: res.total,
data: datalist,
});
} else {
setList({
pageIndex: res.pageIndex,
pageSize: res.pageSize,
total: res.total,
data: [],
});
}
});
};
useEffect(() => {
handleSearch(searchParam);
}, []);
const rowSelection = {
onChange: (selectedRowKeys: any[], selectedRows: any[]) => {
setSelected(selectedRows);
},
getCheckboxProps: (record: any) => ({
name: record.name,
}),
};
const columns = [
{
title: 'ID',
dataIndex: 'id',
align: 'center',
},
{
title: '名称',
dataIndex: 'name',
align: 'center',
},
{
title: '租户名称',
ellipsis: true,
align: 'left',
width: 400,
render: (record: any) => (
<div
style={{ overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}
onClick={() => {
setAsset(record);
setCat(true);
}}
>
<span style={{ color: '#1890ff' }}>{tenant[record.id]}</span>
</div>
),
},
{
title: '操作',
align: 'center',
render: (_: string, record: any) => (
<Fragment>
<a
onClick={() => {
router.push(`/device/instance/save/${record.id}`);
}}
>
查看
</a>
</Fragment>
),
},
];
const unbind = () => {
service.assets
.unbind(data.id, [
{
assetIdList: selected.map(item => item.id),
assetType: 'device',
userId: props?.user,
},
])
.subscribe(() => {
message.success('解绑成功');
setSelected([]);
handleSearch(searchParam);
});
};
return (
<Drawer title="编辑设备资产" visible width="75VW" onClose={() => props.close()}>
<SearchForm
search={(params: any) => {
handleSearch({ terms: params });
}}
formItems={[
{
label: 'ID',
key: 'id$LIKE',
type: 'string',
},
{
label: '名称',
key: 'name$LIKE',
type: 'string',
},
]}
/>
<Button type="primary" style={{ marginBottom: 10 }} onClick={() => setAdd(true)}>
添加
</Button>
{selected.length > 0 && (
<Button
type="danger"
style={{ marginBottom: 10, marginLeft: 10 }}
onClick={() => {
unbind();
}}
>
{`解绑${selected.length}项`}
</Button>
)}
<ProTable
rowKey="id"
rowSelection={rowSelection}
columns={columns}
dataSource={list?.data || []}
onSearch={(searchData: any) => handleSearch(searchData)}
paginationConfig={list || {}}
/>
<div
style={{
position: 'absolute',
right: 0,
bottom: 0,
width: '100%',
borderTop: '1px solid #e9e9e9',
padding: '10px 16px',
background: '#fff',
textAlign: 'right',
}}
>
<Button
onClick={() => {
props.close();
}}
style={{ marginRight: 8 }}
>
关闭
</Button>
</div>
{add && (
<Add
user={props.user}
data={data}
close={() => {
setAdd(false);
handleSearch(searchParam);
}}
/>
)}
{cat && (
<User
asset={asset}
close={() => {
setCat(false);
handleSearch(searchParam);
}}
/>
)}
</Drawer>
);
}
Example #15
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
DeviceModel: React.FC<Props> = props => {
const { result } = props.deviceProduct;
const { dispatch, location } = props;
const initState: State = {
data: result,
searchParam: {
pageSize: 8,
terms: location?.query.iop && JSON.parse(location?.query.iop)?.terms,
sorts: { field: 'id', order: 'desc' },
},
saveVisible: false,
};
// 消息协议
const [protocolSupports, setProtocolSupports] = useState([]);
const [categoryList, setCategoryList] = useState([]);
const [searchParam, setSearchParam] = useState(initState.searchParam);
const [saveVisible, setSaveVisible] = useState(initState.saveVisible);
const [deviceCount, setDeviceCount] = useState({});
const [spinning, setSpinning] = useState(true);
const [basicInfo, setBasicInfo] = useState<any>();
const handleSearch = (params?: any) => {
setSearchParam(params);
dispatch({
type: 'deviceProduct/query',
payload: encodeQueryParam(params),
});
};
const deploy = (record: any) => {
dispatch({
type: 'deviceProduct/deploy',
payload: record.id,
callback: response => {
if (response.status === 200) {
message.success('操作成功');
handleSearch(searchParam);
}
},
});
};
const unDeploy = (record: any) => {
dispatch({
type: 'deviceProduct/unDeploy',
payload: record.id,
callback: response => {
if (response.status === 200) {
message.success('操作成功');
handleSearch(searchParam);
}
},
});
};
const handleDelete = (params: any) => {
dispatch({
type: 'deviceProduct/remove',
payload: params.id,
callback: response => {
if (response.status === 200) {
message.success('删除成功');
handleSearch(searchParam);
}
},
});
};
useEffect(() => {
apis.deviceProdcut
.deviceCategory({paging:false})
.then((response: any) => {
if (response.status === 200) {
setCategoryList(
response.result.map((item: any) => ({
id: item.id,
pId: item.parentId,
value: item.id,
title: item.name,
})),
);
}
})
.catch(() => {});
apis.deviceProdcut
.protocolSupport()
.then(response => {
if (response.status === 200) {
setProtocolSupports(response.result.map((i: any) => ({ id: i.id, name: i.name })));
}
})
.catch(() => {});
handleSearch(searchParam);
}, []);
useEffect(() => {
result.data?.map((item: any) => {
apis.deviceInstance
.count(encodeQueryParam({ terms: { productId: item.id } }))
.then(res => {
if (res.status === 200) {
deviceCount[item.id] = String(res.result);
setDeviceCount({ ...deviceCount });
} else {
deviceCount[item.id] = '/';
setDeviceCount({ ...deviceCount });
}
})
.catch();
});
setSpinning(false);
}, [result]);
const handleSave = (record: any) => {
dispatch({
type: 'deviceProduct/insert',
payload: record,
callback: response => {
if (response.status === 200) {
setSaveVisible(false);
message.success('保存成功');
router.push(`/device/product/save/${record.id}`);
}
},
});
};
const onChange = (page: number, pageSize: number) => {
handleSearch({
pageIndex: page - 1,
pageSize,
terms: searchParam.terms,
sorts: searchParam.sorts,
});
};
const onShowSizeChange = (current: number, size: number) => {
handleSearch({
pageIndex: current - 1,
pageSize: size,
terms: searchParam.terms,
sorts: searchParam.sorts,
});
};
const uploadProps = (item: any) => {
dispatch({
type: 'deviceProduct/insert',
payload: item,
callback: (response: any) => {
if (response.status === 200) {
message.success('导入成功');
handleSearch(searchParam);
}
},
});
};
/* const uploadProps: UploadProps = {
accept: '.json',
action: '/jetlinks/file/static',
headers: {
'X-Access-Token': getAccessToken(),
},
showUploadList: false,
onChange(info) {
if (info.file.status === 'done') {
const fileUrl = info.file.response.result;
request(fileUrl, {method: 'GET'}).then(e => {
if (e || e !== null) {
dispatch({
type: 'deviceProduct/insert',
payload: e,
callback: (response: any) => {
if (response.status === 200) {
message.success('导入成功');
handleSearch(searchParam);
}
},
});
}
}).catch(() => {
message.error('导入配置失败');
});
}
},
};*/
const cardInfoTitle = {
fontSize: 14,
color: 'rgba(0, 0, 0, 0.85)',
};
return (
<PageHeaderWrapper title="产品管理">
<Spin spinning={spinning}>
<Card bordered={false}>
<div>
<div>
<SearchForm
search={(params: any) => {
handleSearch({
terms: { ...params },
pageSize: 8,
sorts: searchParam.sorts,
});
}}
formItems={[
{
label: '产品名称',
key: 'name$LIKE',
type: 'string',
},
{
label: '所属品类',
key: 'classifiedId$LIKE',
type: 'treeSelect',
props: {
data: categoryList,
dropdownStyle: { maxHeight: 500 },
showSearch:true,
optionFilterProp:'children'
},
},
{
label: '产品类型',
key: 'deviceType$IN',
type: 'list',
props: {
data: [
{ id: 'device', name: '直连设备' },
{ id: 'childrenDevice', name: '网关子设备' },
{ id: 'gateway', name: '网关设备' },
],
mode: 'tags',
},
},
{
label: '消息协议',
key: 'messageProtocol$IN',
type: 'list',
props: {
data: protocolSupports,
mode: 'tags',
},
},
]}
/>
</div>
<div>
<Button
icon="plus"
type="primary"
onClick={() => {
router.push('/device/product/add');
}}
>
新建
</Button>
<Divider type="vertical" />
<Upload
action="/jetlinks/file/static"
headers={{
'X-Access-Token': getAccessToken(),
}}
showUploadList={false}
accept=".json"
beforeUpload={file => {
const reader = new FileReader();
reader.readAsText(file);
reader.onload = result => {
try {
uploadProps(JSON.parse(result.target.result));
} catch (error) {
message.error('文件格式错误');
}
};
}}
>
<Button>
<Icon type="upload" />
快速导入
</Button>
</Upload>
</div>
</div>
</Card>
<br />
<div className={cardStyles.filterCardList}>
{result.data && (
<List<any>
rowKey="id"
loading={props.loading}
grid={{ gutter: 24, xl: 4, lg: 3, md: 3, sm: 2, xs: 1 }}
dataSource={(result || {}).data}
pagination={{
current: result?.pageIndex + 1,
total: result?.total,
pageSize: result?.pageSize,
showQuickJumper: true,
showSizeChanger: true,
hideOnSinglePage: true,
pageSizeOptions: ['8', '16', '40', '80'],
style: { marginTop: -20 },
showTotal: (total: number) =>
`共 ${total} 条记录 第 ${result.pageIndex + 1}/${Math.ceil(
result.total / result.pageSize,
)}页`,
onChange,
onShowSizeChange,
}}
renderItem={item => {
if (item && item.id) {
return (
<List.Item key={item.id}>
<Card
hoverable
bodyStyle={{ paddingBottom: 20 }}
actions={[
<Tooltip key="seeProduct" title="查看">
<Icon
type="eye"
onClick={() => {
router.push(`/device/product/save/${item.id}`);
}}
/>
</Tooltip>,
<Tooltip key="update" title="编辑">
<Icon
type="edit"
onClick={() => {
setBasicInfo(item);
setSaveVisible(true);
}}
/>
</Tooltip>,
<Tooltip key="download" title="下载">
<Icon
type="download"
onClick={() => {
downloadObject(item, '产品');
}}
/>
</Tooltip>,
<Tooltip key="more_actions" title="">
<Dropdown
overlay={
<Menu>
<Menu.Item key="1">
<Popconfirm
placement="topRight"
title={
item.state !== 0
? '确定停用此组件吗?'
: '确定发布此组件吗?'
}
onConfirm={() => {
if (item.state === 0) {
deploy(item);
} else {
unDeploy(item);
}
}}
>
<Button
icon={item.state !== 0 ? 'close' : 'check'}
type="link"
>
{item.state !== 0 ? '停用' : '发布'}
</Button>
</Popconfirm>
</Menu.Item>
{item.state === 0 ? (
deviceCount[item.id] === '0' ? (
<Menu.Item key="2">
<Popconfirm
placement="topRight"
title="确定删除此组件吗?"
onConfirm={() => {
if (item.state === 0 && deviceCount[item.id] === '0') {
handleDelete(item);
} else {
message.error('产品以发布,无法删除');
}
}}
>
<Button icon="delete" type="link">
删除
</Button>
</Popconfirm>
</Menu.Item>
) : (
<Menu.Item key="2">
<Tooltip
placement="bottom"
title="该产品已绑定设备,无法删除"
>
<Button icon="stop" type="link">
删除
</Button>
</Tooltip>
</Menu.Item>
)
) : (
<Menu.Item key="2">
<Tooltip placement="bottom" title="该产品已发布,无法删除">
<Button icon="stop" type="link">
删除
</Button>
</Tooltip>
</Menu.Item>
)}
</Menu>
}
>
<Icon type="ellipsis" />
</Dropdown>
</Tooltip>,
]}
>
<Card.Meta
avatar={<Avatar size={40} src={item.photoUrl || productImg} />}
title={
<AutoHide title={item.name} style={{ width: '95%', fontWeight: 600 }} />
}
description={<AutoHide title={item.id} style={{ width: '95%' }} />}
/>
<div className={cardStyles.cardItemContent}>
<div className={cardStyles.cardInfo}>
<div style={{ width: '33%', textAlign: 'center' }}>
<Spin spinning={!deviceCount[item.id]}>
<p style={cardInfoTitle}>设备数量</p>
<p style={{ fontSize: 14, fontWeight: 600 }}>
<Tooltip key="findDevice" title="点击查看设备">
<a
onClick={() => {
router.push(`/device/instance?productId=${item.id}`);
}}
>
{numeral(deviceCount[item.id]).format('0,0')}
</a>
</Tooltip>
</p>
</Spin>
</div>
<div style={{ width: '33%', textAlign: 'center' }}>
<p style={cardInfoTitle}>发布状态</p>
<p style={{ fontSize: 14, fontWeight: 600 }}>
<Badge
color={item.state === 0 ? 'red' : 'green'}
text={item.state === 0 ? '未发布' : '已发布'}
/>
</p>
</div>
<div style={{ width: '33%', textAlign: 'center' }}>
<p style={cardInfoTitle}>产品类型</p>
<AutoHide
title={item.deviceType?.text}
style={{ fontSize: 14, fontWeight: 600, width: '95%' }}
/>
{/* <p style={{ fontSize: 14, fontWeight: 600 }}>{item.deviceType?.text}</p> */}
</div>
</div>
</div>
</Card>
</List.Item>
);
}
return '';
}}
/>
)}
</div>
</Spin>
{saveVisible && (
<Save
data={basicInfo}
close={() => {
setBasicInfo({});
setSaveVisible(false);
}}
save={item => handleSave(item)}
/>
)}
</PageHeaderWrapper>
);
}
Example #16
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Edit = (props: Props) => {
const service = new Service('tenant');
const initState: State = {
list: {},
};
const [list, setList] = useState(initState.list);
const [add, setAdd] = useState<boolean>(false);
const [cat, setCat] = useState<boolean>(false);
const [asset, setAsset] = useState();
const { data } = props;
const [selected, setSelected] = useState<any[]>([]);
const [tenant, setTenant] = useState<any[]>([]);
const initSearch = {
terms: {
id$assets: JSON.stringify({
tenantId: data?.id,
assetType: 'product',
memberId: props.user,
// not: true,
}),
},
pageIndex: 0,
pageSize: 10,
};
const [searchParam, setSearchParam] = useState<any>(initSearch);
// 获取设备资产和成员
// let product = (tempSearch: any) =>
// service.assets
// .product(encodeQueryParam(tempSearch))
// .pipe(
// mergeMap(result =>
// from(result.data).pipe(
// mergeMap((item: any) =>
// service.assets.members(data.id, 'product', item.id).pipe(
// map(j => ({
// ...item,
// tenant: j
// .filter((a: { binding: boolean }) => a.binding === true)
// .map((l: { userName: any }) => l.userName)
// .join('、'),
// })),
// ),
// ),
// toArray(),
// map(i => ({ ...result, data: i })),
// ),
// ),
// )
// .subscribe((data: any) => {
// setList(data);
// });
useEffect(() => {
list.data?.map((item: any) => {
service.assets.members(data.id, 'product', item.id).subscribe(resp => {
tenant[item.id] = resp
.filter((item: any) => item.binding === true)
.map((i: any) => i.userName)
.join('、');
setTenant({ ...tenant });
});
});
}, [list]);
const handleSearch = (params: any) => {
const tempParam = { ...searchParam, ...params };
const defaultItem = searchParam.terms;
const tempTerms = params?.terms;
const terms = tempTerms ? { ...defaultItem, ...tempTerms } : initSearch;
let tempSearch = {};
if (tempTerms) {
tempParam.terms = terms;
tempSearch = tempParam;
} else {
tempSearch = initSearch;
}
setSearchParam(tempSearch);
service.assets.product(encodeQueryParam(tempSearch)).subscribe(res => {
if (res.data.length > 0) {
let datalist: any = [];
res.data.forEach((value: any) => {
datalist.push({
id: value.id,
name: value.name,
photoUrl: value.photoUrl || productImg,
});
});
setList({
pageIndex: res.pageIndex,
pageSize: res.pageSize,
total: res.total,
data: datalist,
});
} else {
setList({
pageIndex: res.pageIndex,
pageSize: res.pageSize,
total: res.total,
data: [],
});
}
});
};
useEffect(() => {
handleSearch(searchParam);
}, []);
const rowSelection = {
onChange: (selectedRowKeys: any[], selectedRows: any[]) => {
setSelected(selectedRows);
},
getCheckboxProps: (record: any) => ({
name: record.name,
}),
};
const columns = [
{
title: 'ID',
dataIndex: 'id',
align: 'center',
},
{
title: '名称',
render: (record: any) => (
<div>
<Avatar shape="square" src={record.photoUrl || productImg} />
<span> {record.name}</span>
</div>
),
},
{
title: '租户名称',
ellipsis: true,
align: 'left',
width: 400,
render: (record: any) => (
<div
style={{ overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}
onClick={() => {
setAsset(record);
setCat(true);
}}
>
<span style={{ color: '#1890ff' }}>{tenant[record.id]}</span>
</div>
),
},
{
title: '操作',
align: 'center',
render: (_: string, record: any) => (
<Fragment>
<a
onClick={() => {
router.push(`/device/product/save/${record.id}`);
}}
>
查看
</a>
</Fragment>
),
},
];
const unbind = () => {
service.assets
.unbind(data.id, [
{
assetIdList: selected.map(item => item.id),
assetType: 'product',
userId: props?.user,
},
])
.subscribe(() => {
message.success('解绑成功');
handleSearch(searchParam);
setSelected([]);
});
};
return (
<Drawer title="编辑产品资产" visible width="75VW" onClose={() => props.close()}>
<SearchForm
search={(params: any) => {
handleSearch({ terms: params });
}}
formItems={[
{
label: 'ID',
key: 'id$LIKE',
type: 'string',
},
{
label: '名称',
key: 'name$LIKE',
type: 'string',
},
]}
/>
<Button type="primary" style={{ marginBottom: 10 }} onClick={() => setAdd(true)}>
添加
</Button>
{selected.length > 0 && (
<Button
type="danger"
style={{ marginBottom: 10, marginLeft: 10 }}
onClick={() => {
unbind();
}}
>
{`解绑${selected.length}项`}
</Button>
)}
<ProTable
rowKey="id"
rowSelection={rowSelection}
columns={columns}
dataSource={list?.data || []}
onSearch={(searchData: any) => handleSearch(searchData)}
paginationConfig={list || {}}
/>
<div
style={{
position: 'absolute',
right: 0,
bottom: 0,
width: '100%',
borderTop: '1px solid #e9e9e9',
padding: '10px 16px',
background: '#fff',
textAlign: 'right',
}}
>
<Button
onClick={() => {
props.close();
}}
style={{ marginRight: 8 }}
>
关闭
</Button>
</div>
{add && (
<Add
user={props.user}
data={data}
close={() => {
setAdd(false);
handleSearch(searchParam);
}}
/>
)}
{cat && (
<User
asset={asset}
close={() => {
setCat(false);
handleSearch(searchParam);
}}
/>
)}
</Drawer>
);
}
Example #17
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Product = (props: Props) => {
const [visible, setVisible] = useState<boolean>(false);
const data = useContext(TenantContext);
const service = new Service('tenant');
const [pub, setPub] = useState(0);
const [unPub, setUnPub] = useState(0);
const getData = () => {
service.assets
.productCount(
encodeQueryParam({
terms: {
id$assets: JSON.stringify({
tenantId: data?.id,
assetType: 'product',
memberId: props.user,
}),
state: 1,
},
}),
)
.subscribe(resp => {
setPub(resp);
});
service.assets
.productCount(
encodeQueryParam({
terms: {
id$assets: JSON.stringify({
tenantId: data?.id,
assetType: 'product',
memberId: props.user,
}),
state: 0,
},
}),
)
.subscribe(resp => {
setUnPub(resp);
});
};
useEffect(() => {
getData();
}, [props.user]);
return (
<List.Item style={{ paddingRight: '10px' }}>
<Card
hoverable
className={styles.card}
actions={[
<Tooltip title="查看">
<Icon
type="eye"
onClick={() =>
router.push({
pathname: `/device/product`,
search:
'iop=' +
JSON.stringify({
terms: {
id$assets: {
tenantId: data?.id,
assetType: 'product',
memberId: props.user,
// not: true,
},
},
}),
})
}
/>
</Tooltip>,
<Tooltip title="编辑">
<Icon type="edit" onClick={() => setVisible(true)} />
</Tooltip>,
]}
>
<Card.Meta
avatar={<IconFont type="icon-chanpin" style={{ fontSize: 45 }} />}
title={<a>产品</a>}
/>
<div className={styles.cardInfo}>
<div>
<p>已发布</p>
<p>{pub}</p>
</div>
<div>
<p>未发布</p>
<p>{unPub}</p>
</div>
</div>
</Card>
{visible && (
<Edit
data={data}
user={props.user}
close={() => {
setVisible(false);
getData();
}}
/>
)}
</List.Item>
);
}
Example #18
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Protocol = (props: Props) => {
const [visible, setVisible] = useState<boolean>(false);
const data = useContext(TenantContext);
const service = new Service('tenant');
const [deploy, setDeploy] = useState(0);
const [unDeploy, setUndeploy] = useState(0);
const getData = () => {
service.assets.protocolCount(encodeQueryParam({
terms: {
id$assets: JSON.stringify({
tenantId: data?.id,
assetType: 'protocol',
memberId: props.user,
}),
state: 1 // 已发布
}
})).subscribe(resp => {
setDeploy(resp)
});
service.assets.protocolCount(encodeQueryParam({
terms: {
id$assets: JSON.stringify({
tenantId: data?.id,
assetType: 'protocol',
memberId: props.user,
}),
state: 0 // 未发布
}
})).subscribe(resp => {
setUndeploy(resp);
// setActive(resp)
})
}
useEffect(() => {
getData();
}, [props.user]);
return (
<List.Item style={{ paddingRight: '10px' }}>
<Card
hoverable
className={styles.card}
actions={[
<Tooltip title="查看">
<Icon type="eye" onClick={() => router.push({
pathname: `/network/protocol`,
query: {
terms: {
id$assets: JSON.stringify({
tenantId: data?.id,
assetType: 'protocol',
memberId: props.user,
})
}
}
})} />
</Tooltip>,
<Tooltip title="编辑">
<Icon type="edit" onClick={() => setVisible(true)} />
</Tooltip>]}
>
<Card.Meta
avatar={<IconFont type="icon-anzhuangbaoguanli" style={{ fontSize: 45 }} />}
title={<a>协议</a>}
/>
<div className={styles.cardInfo}>
<div>
<p>已发布</p>
<p>{deploy}</p>
</div>
<div>
<p>未发布</p>
<p>{unDeploy}</p>
</div>
</div>
</Card>
{visible && (
<Edit
user={props.user}
data={data}
close={() => {
setVisible(false);
getData();
}} />
)}
</List.Item>
)
}
Example #19
Source File: userIndex.tsx From jetlinks-ui-antd with MIT License | 4 votes |
UserTenant = () => { const service = new Service('tenant'); const [loading, setLoading] = useState<boolean>(false); const [tloading, setTloading] = useState<boolean>(false); const [visible, setVisible] = useState<boolean>(false); const [current, setCurrent] = useState<Partial<TenantItem>>({}); const [list, setList] = useState<ListData<TenantItem>>(); const [searchParam, setSearchParam] = useState({ sorts: { field: 'id', order: 'desc', }, }); const handleSearch = (params: any) => { setTloading(true); setSearchParam(params); service.list(encodeQueryParam(params)).subscribe(data => { setList(data); setTloading(false); }); }; useEffect(() => { handleSearch(searchParam); }, []); const paginationProps: PaginationConfig = { showQuickJumper: true, pageSize: 5, total: list?.total || 0, showTotal: (total: number) => `共 ${total} 条记录 第 ${(searchParam.pageIndex || 0) + 1}/${Math.ceil( total / (searchParam.pageSize || 5), )}页`, onChange: (page, pageSize) => { handleSearch({ ...searchParam, pageIndex: page - 1, pageSize: 5 }); }, }; const extraContent = ( <div className={styles.extraContent}> <RadioGroup defaultValue="all" onChange={e => handleSearch(encodeQueryParam({ terms: { state: e.target.value } }))} > <RadioButton value="">全部</RadioButton> <RadioButton value="enabled">正常</RadioButton> <RadioButton value="disabled">禁用</RadioButton> </RadioGroup> {/* <Search className={styles.extraContentSearch} placeholder="请输入" onSearch={() => ({})} /> */} </div> ); const saveItem = (data: any) => { setLoading(true); service.create(data).subscribe( res => { if (res) { message.success('保存成功'); setLoading(false); setVisible(false); setCurrent({}); handleSearch(searchParam); } }, () => { message.error('保存失败'); }, ); }; const changeState = (item: any, state: string) => { setLoading(true); const data = item; data.state = state; service.update(data).subscribe(() => { setLoading(false); message.success('操作成功'); handleSearch(searchParam); }); }; return ( <PageHeaderWrapper> <Spin spinning={tloading}> <div className={styles.standardList}> <Card className={styles.listCard} bordered={false} title="租户列表" style={{ marginTop: 24 }} bodyStyle={{ padding: '0 32px 40px 32px' }} extra={extraContent} > <Button type="dashed" style={{ width: '100%', marginBottom: 8 }} onClick={() => { setVisible(true); setCurrent({}); }} > <PlusOutlined /> 添加 </Button> <List size="large" rowKey="id" pagination={paginationProps} dataSource={list?.data} renderItem={(item: TenantItem) => ( <List.Item actions={[ <a onClick={e => { e.preventDefault(); router.push(`/system/tenant/detail/${item.id}`); }} > 查看 </a>, <> {item.state.value === 'enabled' ? ( <Popconfirm title="确认禁用此租户?" onConfirm={() => { changeState(item, 'disabled'); }} > <a key="edit">禁用</a> </Popconfirm> ) : ( <Popconfirm title="确认禁用此租户?" onConfirm={() => { changeState(item, 'enabled'); }} > <a key="edit">启用</a> </Popconfirm> )} </>, ]} > <List.Item.Meta avatar={<Avatar src={item.photo || defaultImg} shape="square" size="large" />} title={<a>{item.name}</a>} description={item.description} /> <ListContent data={item} /> </List.Item> )} /> </Card> </div> </Spin> {visible && ( <Spin spinning={loading}> <Save close={() => { setVisible(false); }} save={(item: any) => saveItem(item)} data={current} /> </Spin> )} </PageHeaderWrapper> ); }
Example #20
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Login: React.FC<Props> = props => {
const { dispatch, settings, location: { query } } = props;
const token = getAccessToken();
const service = new Service('');
const [username, setUsername] = useState<string>('');
const [password, setPassword] = useState<string>('');
const [expires, setExpires] = useState<number>(3600000);
const [isReady, setIsReady] = useState(false);
const [captcha, setCaptcha] = useState<string>('');
const [captchaImg, setCaptchaImg] = useState<string>('');
const [code, setCode] = useState<string>("");
const [enable, setEnable] = useState<boolean>(false);
const [current, setCurrent] = useState<boolean>(false);
const handleSubmit = () => {
dispatch({
type: 'login/login',
payload: {
username,
password,
expires,
tokenType: 'default',
verifyKey: captcha,
verifyCode: code,
bindCode: query?.code
},
callback: () => { getCodeImg() }
});
};
const getCodeImg = () => {
if (enable) {
service.getCaptcha().subscribe((resp) => {
setCaptcha(resp.key);
setCaptchaImg(resp.base64);
});
}
}
useEffect(() => {
if (dispatch) {
dispatch({
type: 'settings/fetchConfig',
callback: () => {
const title = document.getElementById('sys-title');
const icon = document.getElementById('title-icon');
if (title && settings.title) {
title.textContent = settings.title;
}
if (icon && settings.titleIcon) {
icon.href = settings.titleIcon;
}
if (token!=='null') {
service.queryCurrent().subscribe((resp) => {
if (resp.status === 200) {
setCurrent(true)
} else {
setCurrent(false)
}
setIsReady(true);
})
} else {
setIsReady(true);
}
}
});
}
//判断是否开启验证码
service.captchaConfig().subscribe((resp) => {
if (resp) {
setEnable(resp.enabled)
if (resp.enabled) {
//获取验证码
service.getCaptcha().subscribe((resp) => {
setCaptcha(resp.key);
setCaptchaImg(resp.base64);
});
// getCodeImg();
} else {
//未开启验证码
}
}
});
}, [settings.title]);
const Login = () => {
const information = JSON.parse(localStorage.getItem('user-detail') || '{}');
if (current) {
return (
<div className={style.login}>
<div className={style.bg1} />
<div className={style.gyl}>
物联网平台
<div className={style.gy2}>MQTT TCP CoAP HTTP , 多消息协议适配 , 可视化规则引擎
</div>
</div>
<div className={style.box}>
<div className={style.box1} >
<div style={{ width: '100%', height: '30px' }}></div>
<div className={style.avatar}>
<Avatar size="small" className={style.avatarx} src={information.avatar || 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3273016457,3482023254&fm=26&gp=0.jpg'} alt="avatar" />
</div>
<input
onClick={() => {
let result = getQueryString(window.location.hash);
if (result &&
result.client_id !== undefined &&
result.response_type !== undefined &&
result.redirect_uri !== undefined &&
result.state !== undefined
) {
apis.login.oauth(result).then(res => {
if (res.status === 200) {
window.location.href = res.result;
}
});
} else {
router.replace('/');
}
}}
className={style.btn}
type="button"
name="登录"
value="登录"
/>
<div style={{ width: '100%', height: '30px' }}></div>
<input
onClick={() => {
localStorage.setItem('x-access-token', '');
setCurrent(false)
if (window.location.pathname !== '/user/login') {
router.replace({
pathname: '/user/login'
});
} else {
router.push('/user/login');
}
}}
className={style.btn}
type="button"
name="切换账号"
value="切换账号"
/>
<div style={{ width: '100%', height: '30px' }}></div>
</div>
</div>
</div>
)
} else {
return renderLogin()
}
}
const renderLogin = () => (
<div className={style.login}>
<div className={style.bg1} />
<div className={style.gyl}>
物联网平台
<div className={style.gy2}>MQTT TCP CoAP HTTP , 多消息协议适配 , 可视化规则引擎
</div>
</div>
{/* style={{ height: enable ? '387px' : '330px' }} */}
<div className={style.box}>
<div className={style.box1} >
<div className={style.header}>用户登录</div>
<div className={style.item}>
<div className={style.userLabel}>用户名</div>
<input
style={{ borderStyle: 'none none solid none' }}
onChange={e => setUsername(e.target.value)}
value={username}
type="text"
/>
</div>
<div className={style.item} onKeyUp={e => { if (e.keyCode === 13 && !enable) { handleSubmit(); } }}>
<div className={style.userLabel}>
密<span style={{ marginLeft: '1em' }} />码
</div>
<input
style={{ borderStyle: 'none none solid none' }}
onChange={e => setPassword(e.target.value)}
value={password}
type="password"
/>
</div>
{
enable ? <div className={style.item}>
<div className={style.userLabel}>验证码</div>
<input onKeyUp={e => { if (e.keyCode === 13) { handleSubmit(); } }}
style={{ borderStyle: 'none none solid none' }}
onChange={e => setCode(e.target.value)}
value={code}
type="text"
/>
<div className={style.code} onClick={() => { getCodeImg(); }}><img src={captchaImg} className={style.code_img} /></div>
</div> : <div></div>
}
<div className={style.remember}>
<div className={style.remember_box}>
<input
type="checkbox"
checked={expires === -1}
onChange={() => {
setExpires(expires === -1 ? 3600000 : -1);
}}
/>
<div className={style.text}>记住我</div>
</div>
</div>
<input
onClick={() => {
handleSubmit();
}}
className={style.btn}
type="button"
name="登录"
value="登录"
/>
</div>
</div>
</div>
)
// return isReady ? renderLogin() : <Spin spinning={isReady} />;
return isReady ? Login() : <Spin spinning={isReady} />;
}
Example #21
Source File: request.ts From jetlinks-ui-antd with MIT License | 4 votes |
errorHandler = (error: { response: Response }): Response | undefined => {
const {
// response: { status },
response,
} = error;
// if (response && response.status) {
// const errorText = codeMessage[response.status] || response.statusText;
// const { status, url } = response;
// notification.error({
// message: `请求错误 ${status}: ${url}`,
// description: errorText,
// });
if (response) {
if (response.status === 401) {
notification.error({
key: 'error',
message: '未登录或登录已过期,请重新登录。',
});
localStorage.removeItem('x-access-token');
// const { redirect } = getPageQuery();
setTimeout(() => router.push('/user/login'), 300);
// if (window.location.pathname !== '/user/login' && !redirect) {
// router.replace({
// pathname: '/user/login',
// search: stringify({
// // redirect: window.location.href,
// }),
// });
// } else {
// }
} else if (response.status === 400) {
response.text().then(resp => {
if (resp) {
notification.error({
key: 'error',
message: JSON.parse(resp).message,
});
} else {
response.json().then((res: any) => {
notification.error({
key: 'error',
message: `请求错误:${res.message}`,
});
});
}
});
return response;
} else if (response.status === 500) {
response
.json()
.then((res: any) => {
notification.error({
key: 'error',
message: `${res.message}`,
});
})
.catch(() => {
notification.error({
key: 'error',
message: response.statusText,
});
});
} else if (response.status === 504) {
notification.error({
key: 'error',
message: '服务器错误',
});
// router.push('/user/login');
} else if (response.status === 403) {
response.json().then((res: any) => {
notification.error({
key: 'error',
message: `${res.message}`,
});
});
// router.push('/exception/403');
// return;
} else if (response.status === 404) {
if (window.location.hash.search('/user/login') != -1) {
notification.error({
key: 'error',
message: '404 错误',
});
return response;
}
// console.log(status, '状态');
router.push('/exception/404');
} else {
notification.error({
key: 'error',
message: '服务器内部错误',
});
}
} else {
notification.error({
key: 'error',
message: '服务器内部错误,请检测您的配置',
});
}
return response;
// } else if (!response) {
// notification.error({
// description: '您的网络发生异常,无法连接服务器',
// message: '网络异常',
// });
// }
// return response;
}
Example #22
Source File: DeviceTree.tsx From jetlinks-ui-antd with MIT License | 4 votes |
DeviceTree: React.FC<Props> = props => {
const {
location: { query },
} = props;
const initialState: State = {
data: [],
deviceData: {},
saveVisible: false,
bindVisible: false,
detailVisible: false,
current: {},
parentId: query.id,
deviceIds: [],
device: {},
};
const reducer = (state: State, action: any) => {
switch (action.type) {
case 'operation':
return { ...state, ...action.payload };
default:
throw new Error();
}
};
const [state, dispatch] = useReducer(reducer, initialState);
const [add, setAdd] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);
const [deviceLoading, setDeviceLoading] = useState<boolean>(true);
const {
data,
deviceData,
saveVisible,
bindVisible,
detailVisible,
current,
parentId,
deviceIds,
device,
} = state;
const service = new Service('device');
const [searchParam, setSearchParam] = useState({
pageSize: 10,
sorts: {
order: 'descend',
field: 'alarmTime',
},
});
const [searchValue, setSearchValue] = useState('');
const search = (param?: any) => {
setDeviceLoading(true);
const defaultTerms = {
terms: {
id: query.id,
},
paging: false,
};
service
.groupTree(
encodeQueryParam({
...defaultTerms,
terms: { ...defaultTerms.terms, ...param?.terms },
}),
)
.subscribe(
resp => {
dispatch({
type: 'operation',
payload: {
data: resp,
},
});
searchDevice(resp[0], searchParam);
dispatch({
type: 'operation',
payload: {
current: resp[0],
parentId: resp[0].id,
},
});
},
() => { },
() => {
setLoading(true);
},
);
};
useEffect(() => {
search();
}, []);
const saveGroup = (item: GroupItem) => {
if (add) {
service.saveGroup({ ...item, parentId }).subscribe(
() => message.success('添加成功'),
() => { },
() => {
dispatch({ type: 'operation', payload: { saveVisible: false, parentId: null } });
search();
},
);
} else {
service.saveOrUpdataGroup(item).subscribe(
() => message.success('添加成功'),
() => { },
() => {
dispatch({ type: 'operation', payload: { saveVisible: false, parentId: null } });
search();
},
);
}
};
const searchDevice = (item: GroupItem | any, params?: any) => {
service
.groupDevice(
encodeQueryParam({
pageSize: 10,
...params,
terms: {
'id$dev-group': item.id,
name$LIKE: item.searchValue,
},
}),
)
.subscribe(
resp => {
dispatch({
type: 'operation',
payload: {
deviceData: resp,
// deviceIds: resp.data.map((item: any) => item.id),
},
});
},
() => { },
() => {
setDeviceLoading(false);
},
);
};
const bindDevice = () => {
if (deviceIds.length > 0) {
service.bindDevice(parentId!, deviceIds).subscribe(
() => message.success('绑定成功'),
() => message.error('绑定失败'),
() => {
dispatch({
type: 'operation',
payload: {
bindVisible: false,
deviceIds: []
},
});
searchDevice({ id: parentId });
},
);
} else {
message.error('未选择需要绑定的设备')
}
};
const unbindDevice = (deviceId: string[]) => {
service.unbindDevice(parentId!, deviceId).subscribe(
() => message.success('解绑成功'),
() => message.error('解绑失败'),
() => {
dispatch({
type: 'operation',
payload: {
bindVisible: false,
},
});
searchDevice({ id: parentId });
},
);
};
const unbindAll = () => {
if (selectedRowKeys.length > 0) {
service.unbind(parentId!, selectedRowKeys).subscribe(
() => message.success('解绑成功'),
() => message.error('解绑失败'),
() => {
dispatch({
type: 'operation',
payload: {
bindVisible: false,
},
});
searchDevice({ id: parentId });
setSelectedRowKeys([]);
},
);
} else {
service.unbindAll(parentId!).subscribe(
() => message.success('解绑成功'),
() => message.error('解绑失败'),
() => {
dispatch({
type: 'operation',
payload: {
bindVisible: false,
},
});
searchDevice({ id: parentId });
},
);
}
};
const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
const rowSelection = {
selectedRowKeys,
onChange: (selectedRowKeys: string[] | any[]) => {
setSelectedRowKeys(selectedRowKeys);
},
};
return (
<PageHeaderWrapper
onBack={() => router.push('/device/tree')}
title={<>{data[0] ? data[0].name : null}</>}
>
{loading && (
<Card>
<Row gutter={24}>
<Col span={8}>
<Table
title={() => (
<>
分组
<span style={{ marginLeft: 20, marginRight: 10 }}>
<Input.Search
style={{ width: '60%' }}
placeholder="请输入输入名称"
// onChange={() => search()}
onSearch={value => {
if (value) {
const tempData = data.filter(
(item: any) => item.name.indexOf(value) > -1,
);
dispatch({ type: 'operation', payload: { data: tempData } });
} else {
search();
}
}}
/>
</span>
<Button
type="primary"
onClick={() => {
setAdd(true);
dispatch({
type: 'operation',
payload: {
saveVisible: true,
parentId: query.id,
// current:null,
},
});
}}
>
新增
</Button>
</>
)}
onRow={item => {
return {
onClick: () => {
searchDevice(item, searchParam);
setDeviceLoading(true);
dispatch({
type: 'operation',
payload: { current: item, parentId: item.id },
});
},
};
}}
bordered={false}
pagination={false}
dataSource={data}
size="small"
defaultExpandedRowKeys={data[0] && [data[0].id]}
rowKey={(item: any) => item.id}
columns={[
{
title: '序号',
dataIndex: 'id',
// width:200,
ellipsis: true,
render: (record) => (
<Tooltip title={record}>
<span>{record}</span>
</Tooltip>
)
},
{ title: '名称', dataIndex: 'name', ellipsis: true },
{
title: '操作',
width: 100,
align: 'center',
render: (_, record) => (
<Fragment>
<Icon
type="edit"
onClick={() => {
setAdd(false);
dispatch({
type: 'operation',
payload: {
current: record,
saveVisible: true,
},
});
}}
/>
{
record.level < 6 &&
<>
<Divider type="vertical" />
<Tooltip title="新增子分组">
<Icon
type="plus"
onClick={() => {
setAdd(true);
dispatch({
type: 'operation',
payload: {
parentId: record.id,
saveVisible: true,
},
});
}}
/>
</Tooltip>
</>
}
<Divider type="vertical" />
<Tooltip title="删除">
<Popconfirm
title="确认删除分组?"
onConfirm={() => {
service.removeGroup(record.id).subscribe(
() => message.success('删除成功!'),
() => message.error('删除失败'),
() => search(),
);
}}
>
<Icon type="close" />
</Popconfirm>
</Tooltip>
{/* <Divider type="vertical" />
<Tooltip title="绑定设备">
<Icon type="apartment" onClick={() => {
dispatch({
type: 'operation',
payload: {
bindVisible: true,
parentId: record.id
}
})
}} />
</Tooltip> */}
</Fragment>
),
},
]}
/>
</Col>
<Col span={16}>
<ProTable
loading={deviceLoading}
title={() => (
<Fragment>
{current.name}
<span style={{ marginLeft: 20 }}>
<Input.Search
style={{ width: '30%' }}
placeholder="输入名称后自动查询"
onSearch={value => {
setSearchValue(value);
searchDevice({
id: parentId,
searchValue: value,
})
}}
/>
</span>
<Popconfirm title="确认解绑?" onConfirm={() => unbindAll()}>
<Button style={{ marginLeft: 10 }} type="danger">
解绑{selectedRowKeys.length > 0 ? `${selectedRowKeys.length}项` : '全部'}
</Button>
</Popconfirm>
<Button
style={{ marginLeft: 10 }}
onClick={() => {
dispatch({
type: 'operation',
payload: {
bindVisible: true,
parentId: parentId,
},
});
}}
>
绑定设备
</Button>
</Fragment>
)}
dataSource={deviceData?.data}
paginationConfig={deviceData}
size="small"
rowKey="id"
rowSelection={rowSelection}
onSearch={(params: any) => {
searchDevice({ id: parentId, searchValue: searchValue }, params);
}}
columns={[
{ title: 'ID', dataIndex: 'id' },
{ title: '名称', dataIndex: 'name' },
{ title: '产品名称', dataIndex: 'productName' },
{ title: '状态', dataIndex: 'state', render: (text: any) => text.text },
{
title: '操作',
render: (_: any, record: any) => (
<Fragment>
<a
onClick={() => {
dispatch({
type: 'operation',
payload: {
device: record,
detailVisible: true,
},
});
}}
>
详情
</a>
<Divider type="vertical" />
<a
onClick={() => {
unbindDevice([record.id]);
}}
>
解绑
</a>
</Fragment>
),
},
]}
/>
</Col>
</Row>
</Card>
)}
{saveVisible && (
<Save
data={current}
flag={add}
close={() => {
dispatch({ type: 'operation', payload: { saveVisible: false, current: {} } });
setAdd(false);
}}
save={(item: GroupItem) => saveGroup(item)}
/>
)}
{bindVisible && (
<Modal
title="绑定设备"
visible
width="80vw"
onCancel={() => {
dispatch({
type: 'operation',
payload: {
deviceIds: [],
bindVisible: false
},
});
}}
onOk={() => {
bindDevice();
}}
>
<ChoiceDevice
parentId={parentId}
save={(item: any[]) => {
dispatch({
type: 'operation',
payload: {
deviceIds: item,
},
});
}}
/>
</Modal>
)}
{detailVisible && (
<Drawer
visible
width="80vw"
title="设备详情"
onClose={() => {
dispatch({
type: 'operation',
payload: {
detailVisible: false,
},
});
}}
>
<DeviceInfo
location={{
pathname: `/device/instance/save/${device.id}`,
search: '',
hash: '',
query: {},
state: undefined,
}}
/>
</Drawer>
)}
</PageHeaderWrapper>
);
}
Example #23
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Save: React.FC<Props> = props => {
const initState: State = {
protocolSupports: [],
protocolTransports: [],
organizationList: [],
configName: '',
configForm: [],
classified: [],
classifiedData: {},
defaultMetadata: '{"events":[],"properties":[],"functions":[],"tags":[]}',
};
const systemVersion = localStorage.getItem('system-version');
const {getFieldDecorator, setFieldsValue} = props.form;
// 消息协议
const [protocolSupports, setProtocolSupports] = useState(initState.protocolSupports);
// 消息协议
const [organizationList, setOrganizationList] = useState(initState.organizationList);
// 传输协议
const [protocolTransports, setProtocolTransports] = useState(initState.protocolTransports);
const [classified, setClassified] = useState(initState.classified);
const [classifiedData, setClassifiedData] = useState(initState.classifiedData);
//默认物模型
const [defaultMetadata, setDefaultMetadata] = useState(initState.defaultMetadata);
const [photoUrl, setPhotoUrl] = useState(props.data?.photoUrl);
const [classifiedVisible, setClassifiedVisible] = useState(false);
const [storagePolicy, setStoragePolicy] = useState<any[]>([]);
const [checkStorage, setCheckStorage] = useState<any>({});
const onMessageProtocolChange = (value: string) => {
// 获取链接协议
apis.deviceProdcut
.protocolTransports(value)
.then(e => {
if (e.status === 200) {
setProtocolTransports(e.result);
}
})
.catch(() => {
});
};
const getDefaultModel = (id: string, transport: string) => {
apis.deviceProdcut
.getDefaultModel(id, transport)
.then(res => {
if (res.status === 200) {
if (res.result === '{}') {
setDefaultMetadata('{"events":[],"properties":[],"functions":[],"tags":[]}');
} else {
setDefaultMetadata(res.result);
}
} else {
setDefaultMetadata('{"events":[],"properties":[],"functions":[],"tags":[]}');
}
})
.catch(() => {
setDefaultMetadata('{"events":[],"properties":[],"functions":[],"tags":[]}');
});
};
useEffect(() => {
apis.deviceProdcut
.protocolSupport()
.then(e => {
if (e.status === 200) {
setProtocolSupports(e.result);
}
})
.catch(() => {
});
apis.deviceProdcut
.deviceCategoryTree(encodeQueryParam({paging: false, sorts: {field: 'id', order: 'desc'}}))
.then((response: any) => {
if (response.status === 200) {
setClassified(response.result);
}
})
.catch(() => {
});
apis.deviceProdcut
.queryOrganization()
.then((res: any) => {
if (res.status === 200) {
let orgList: any = [];
res.result.map((item: any) => {
orgList.push({id: item.id, pId: item.parentId, value: item.id, title: item.name});
});
setOrganizationList(orgList);
}
})
.catch(() => {
});
// if (systemVersion === 'pro') {
apis.deviceProdcut.storagePolicy().then(res => {
if (res.status === 200) {
setStoragePolicy(res.result);
}
});
// }
if (props.data && props.data.messageProtocol) {
onMessageProtocolChange(props.data.messageProtocol);
}
}, []);
const basicForm: FormItemConfig[] = [
{
label: '产品ID',
key: 'id',
styles: {
lg: {span: 8},
md: {span: 12},
sm: {span: 24},
},
options: {
initialValue: props.data?.id,
rules: [
{required: true, message: '请输入产品ID'},
{max: 64, message: '产品ID不超过64个字符'},
{
pattern: new RegExp(/^[0-9a-zA-Z_\-]+$/, 'g'),
message: '产品ID只能由数字、字母、下划线、中划线组成',
},
],
},
component: <Input placeholder="请输入产品ID " disabled={!!props.data?.id}/>,
},
{
label: '产品名称',
key: 'name',
options: {
rules: [
{required: true, message: '请输入产品名称'},
{max: 200, message: '产品名称不超过200个字符'},
],
initialValue: props.data?.name,
},
styles: {
xl: {span: 8},
lg: {span: 8},
md: {span: 12},
sm: {span: 24},
},
component: <Input style={{width: '100%'}} maxLength={200} placeholder="请输入"/>,
},
{
label: '所属品类',
key: 'classifiedId',
options: {
rules: [{required: true, message: '请选择所属品类'}],
},
styles: {
xl: {span: 8},
lg: {span: 8},
md: {span: 12},
sm: {span: 24},
},
component: (
<Cascader
fieldNames={{label: 'name', value: 'id', children: 'children'}}
options={classified}
popupVisible={false}
onChange={value => {
if (value.length === 0) {
setClassifiedData({});
}
}}
onClick={() => {
setClassifiedVisible(true);
}}
placeholder="点击选择品类"
/>
),
},
{
label: '所属机构',
key: 'orgId',
options: {
initialValue: props.data?.orgId,
},
styles: {
xl: {span: 8},
lg: {span: 10},
md: {span: 24},
sm: {span: 24},
},
component: (
<TreeSelect
allowClear
treeDataSimpleMode
showSearch
placeholder="所属机构"
treeData={organizationList}
treeNodeFilterProp="title"
searchPlaceholder="根据机构名称模糊查询"
/>
),
},
{
label: '消息协议',
key: 'messageProtocol',
options: {
rules: [{required: true, message: '请选择消息协议'}],
initialValue: props.data?.messageProtocol,
},
styles: {
xl: {span: 8},
lg: {span: 8},
md: {span: 12},
sm: {span: 24},
},
component: (
<Select
placeholder="请选择"
showSearch
optionFilterProp='label'
onChange={(value: string) => {
onMessageProtocolChange(value);
}}
>
{protocolSupports.map(e => (
<Select.Option value={e.id} key={e.id} label={e.name}>
{e.name}
</Select.Option>
))}
</Select>
),
},
{
label: '传输协议',
key: 'transportProtocol',
options: {
rules: [{required: true, message: '请选择传输协议'}],
initialValue: props.data?.transportProtocol,
},
styles: {
xl: {span: 8},
lg: {span: 10},
md: {span: 24},
sm: {span: 24},
},
component: (
<Select
placeholder="请选择"
showSearch
optionFilterProp='label'
onChange={(value: string) => {
if (
value !== '' &&
value !== undefined &&
props.form.getFieldsValue().messageProtocol !== '' &&
props.form.getFieldsValue().messageProtocol !== undefined
) {
getDefaultModel(props.form.getFieldsValue().messageProtocol, value);
}
}}
>
{protocolTransports.map(e => (
<Select.Option value={e.id} key={e.id} label={e.name}>
{e.name}
</Select.Option>
))}
</Select>
),
},
{
label: (
<span>
存储策略
<Tooltip
title={
checkStorage.description
? checkStorage.description
: '使用指定的存储策略来存储设备数据'
}
>
<Icon type="question-circle-o"/>
</Tooltip>
</span>
),
key: 'storePolicy',
options: {},
styles: {
xl: {span: 8},
lg: {span: 10},
md: {span: 24},
sm: {span: 24},
},
component: (
<Select
onChange={e => setCheckStorage(storagePolicy.find(i => i.id === e))}
placeholder="请选择"
>
{storagePolicy.map(e => (
<Select.Option value={e.id} key={e.id}>
{e.name}
</Select.Option>
))}
</Select>
),
},
{
label: '设备类型',
key: 'deviceType',
options: {
rules: [{required: true, message: '请选择设备类型'}],
initialValue:
typeof props.data?.deviceType === 'string'
? props.data?.deviceType
: (props.data?.deviceType || {}).value,
},
styles: {
lg: {span: 8},
md: {span: 12},
sm: {span: 24},
},
component: (
<Radio.Group>
<Radio value="device">直连设备</Radio>
<Radio value="childrenDevice">网关子设备</Radio>
<Radio value="gateway">网关设备</Radio>
</Radio.Group>
),
},
{
label: '描述',
key: 'describe',
styles: {
xl: {span: 24},
lg: {span: 24},
md: {span: 24},
sm: {span: 24},
},
options: {
initialValue: props.data?.describe,
},
component: <Input.TextArea rows={4} placeholder="请输入描述"/>,
},
];
const saveData = () => {
const {form} = props;
form.validateFields((err, fileValue) => {
if (err) return;
if (!fileValue.orgId) {
fileValue.orgId = '';
}
const protocol: Partial<ProtocolItem> =
protocolSupports.find(i => i.id === fileValue.messageProtocol) || {};
apis.deviceProdcut
.saveDeviceProduct({
state: 0,
...fileValue,
photoUrl,
metadata: defaultMetadata, //'{"events":[],"properties":[],"functions":[],"tags":[]}',
protocolName: protocol.name,
classifiedId: classifiedData.id,
classifiedName: classifiedData.name,
})
.then((response: any) => {
if (response.status === 200) {
message.success('保存成功');
router.push(`/device/product/save/${response.result.id}`);
}
})
.catch(() => {
});
});
};
const uploadProps: UploadProps = {
action: '/jetlinks/file/static',
headers: {
'X-Access-Token': getAccessToken(),
},
showUploadList: false,
onChange(info) {
if (info.file.status === 'done') {
setPhotoUrl(info.file.response.result);
message.success('上传成功');
}
},
};
return (
<PageHeaderWrapper>
<Card title="基本信息" bordered={false}>
<div className={styles.right}>
<Spin spinning={false}>
<div className={styles.baseView}>
<div className={styles.left}>
<Form labelCol={{span: 5}} wrapperCol={{span: 16}}>
<Row gutter={16}>
{basicForm.map(item => (
<Col key={item.key}>
<Form.Item label={item.label}>
{getFieldDecorator(item.key, item.options)(item.component)}
</Form.Item>
</Col>
))}
{/* {(systemVersion === 'pro' ? basicForm : basicForm.filter(i => i.key !== 'storePolicy')).map(item => (
<Col key={item.key}>
<Form.Item label={item.label}>
{getFieldDecorator(item.key, item.options)(item.component)}
</Form.Item>
</Col>
))} */}
</Row>
</Form>
</div>
<div className={styles.right}>
<>
<div className={styles.avatar_title}>图标</div>
<div className={styles.avatar}>
<Avatar size={144} src={photoUrl || props.data?.photoUrl || productImg}/>
</div>
<Upload {...uploadProps} showUploadList={false}>
<div className={styles.button_view}>
<Button>
<UploadOutlined/>
更换图片
</Button>
</div>
</Upload>
</>
</div>
</div>
<div
style={{
position: 'absolute',
right: 0,
bottom: 0,
height: 32,
lineHeight: 4,
width: '100%',
borderTop: '1px solid #e9e9e9',
paddingRight: 16,
background: '#fff',
textAlign: 'right',
}}
>
<Button
onClick={() => {
router.push(`/device/product`);
}}
style={{marginRight: 8}}
>
返回
</Button>
<Button
onClick={() => {
saveData();
}}
type="primary"
>
保存
</Button>
</div>
</Spin>
</div>
</Card>
{classifiedVisible && (
<Classified
choice={(item: any) => {
const categoryId = item.categoryId;
setFieldsValue({classifiedId: categoryId});
setClassifiedData(item);
setClassifiedVisible(false);
}}
close={() => {
setClassifiedVisible(false);
}}
data={classifiedData}
/>
)}
</PageHeaderWrapper>
);
}
Example #24
Source File: Detail.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Detail: React.FC<Props> = props => {
const initState: State = {
basicInfo: {},
saveVisible: false,
config: [],
orgInfo: {},
deviceCount: 0,
spinning: true,
units: {},
};
const {
dispatch,
location: { pathname },
} = props;
const [events, setEvents] = useState<any[]>([]);
const [functions, setFunctions] = useState<any[]>([]);
const [properties, setProperties] = useState<any[]>([]);
const [tags, setTags] = useState<any[]>([]);
const [basicInfo, setBasicInfo] = useState(initState.basicInfo);
const [saveVisible, setSaveVisible] = useState(initState.saveVisible);
const [config, setConfig] = useState(initState.config);
const [orgInfo] = useState(initState.orgInfo);
const [deviceCount, setDeviceCount] = useState(initState.deviceCount);
const [spinning, setSpinning] = useState(initState.spinning);
const [units, setUnits] = useState(initState.units);
const [updateVisible, setUpdateVisible] = useState(false);
const [productId, setProductId] = useState('');
const handleSearch = (id?: string) => {
const list = pathname.split('/');
const temp = id || list[list.length - 1];
dispatch({
type: 'deviceProduct/queryById',
payload: temp,
callback: (r: any) => {
if (r.status === 200) {
const data = r.result;
data.orgName = orgInfo[data.orgId];
setBasicInfo(data);
setSpinning(false);
if (data.metadata) {
const metadata = JSON.parse(data.metadata);
setEvents(metadata.events);
setFunctions(metadata.functions);
setProperties(metadata.properties);
setTags(metadata.tags);
}
// apis.deviceProdcut
// .protocolConfiguration(data.messageProtocol, data.transportProtocol)
// .then(resp => {
// setConfig(resp.result);
// });
apis.deviceProdcut.productConfiguration(data.id).then(resp => {
setConfig(resp.result);
});
}
},
});
apis.deviceInstance
.count(encodeQueryParam({ terms: { productId: id } }))
.then(res => {
if (res.status === 200) {
setDeviceCount(res.result);
}
})
.catch();
};
useEffect(() => {
apis.deviceProdcut
.queryOrganization()
.then(res => {
if (res.status === 200) {
res.result.map((e: any) => (orgInfo[e.id] = e.name));
}
})
.catch(() => {});
apis.deviceProdcut
.units()
.then((response: any) => {
if (response.status === 200) {
setUnits(response.result);
}
})
.catch(() => {});
if (pathname.indexOf('save') > 0) {
const list = pathname.split('/');
handleSearch(list[list.length - 1]);
setProductId(list[list.length - 1]);
}
}, []);
const saveData = (item?: any) => {
let data: Partial<DeviceProduct>;
const metadata = JSON.stringify({ events, properties, functions, tags });
// TODO 这个地方有疑惑,set数据之后此处数据还是未更新。原因待查
if (item) {
data = { ...item, metadata };
} else {
data = { ...basicInfo, metadata };
}
apis.deviceProdcut
.saveOrUpdate(data)
.then(r => {
if (r.status === 200) {
setSaveVisible(false);
message.success('保存成功');
const list = pathname.split('/');
handleSearch(list[list.length - 1]);
}
})
.catch(() => {});
};
const updateData = (type: string, item: any, onlySave: boolean) => {
let metadata = JSON.stringify({ events, properties, functions, tags });
if (type === 'event') {
metadata = JSON.stringify({ events: item, properties, functions, tags });
} else if (type === 'properties') {
metadata = JSON.stringify({ events, properties: item, functions, tags });
} else if (type === 'function') {
metadata = JSON.stringify({ events, properties, functions: item, tags });
} else if (type === 'tags') {
metadata = JSON.stringify({ events, properties, functions, tags: item });
}
const data = { ...basicInfo, metadata };
apis.deviceProdcut
.saveOrUpdate(data)
.then((re: any) => {
if (re.status === 200) {
message.success('保存成功');
if (!onlySave) {
deploy(data);
}
}
})
.catch(() => {})
.finally(() => setBasicInfo({ ...data, state: onlySave ? basicInfo.state : 1 }));
};
const deploy = (record: any) => {
setSpinning(true);
dispatch({
type: 'deviceProduct/deploy',
payload: record.id,
callback: response => {
if (response.status === 200) {
basicInfo.state = 1;
setBasicInfo({ ...basicInfo });
message.success('操作成功');
setSpinning(false);
}
},
});
};
const unDeploy = (record: any) => {
setSpinning(true);
dispatch({
type: 'deviceProduct/unDeploy',
payload: record.id,
callback: response => {
if (response.status === 200) {
basicInfo.state = 0;
setBasicInfo(basicInfo);
message.success('操作成功');
setSpinning(false);
}
},
});
};
const updateInfo = (onlySave: boolean, item?: any) => {
apis.deviceProdcut
.update(item, basicInfo.id)
.then((response: any) => {
if (response.status === 200) {
message.success('配置信息修改成功');
setUpdateVisible(false);
const list = pathname.split('/');
handleSearch(list[list.length - 1]);
if (!onlySave) {
// deploy(basicInfo)
setSpinning(true);
dispatch({
type: 'deviceProduct/deploy',
payload: basicInfo.id,
callback: res => {
if (res.status === 200) {
basicInfo.state = 0;
setBasicInfo(basicInfo);
message.success('操作成功');
setSpinning(false);
const list = pathname.split('/');
handleSearch(list[list.length - 1]);
}
},
});
}
}
})
.catch(() => {});
};
const content = (
<div style={{ marginTop: 30 }}>
<Descriptions column={4}>
<Descriptions.Item label="设备数量">
<div>
{numeral(deviceCount).format('0,0')}
<a
style={{ marginLeft: 10 }}
onClick={() => {
router.push(`/device/instance?productId=${basicInfo.id}`);
}}
>
查看
</a>
</div>
</Descriptions.Item>
</Descriptions>
</div>
);
const titleInfo = (
<Row>
<div>
<span>产品:{basicInfo.name}</span>
<Badge
style={{ marginLeft: 20 }}
color={basicInfo.state === 1 ? 'green' : 'red'}
text={basicInfo.state === 1 ? '已发布' : '未发布'}
/>
{basicInfo.state === 1 ? (
<Popconfirm
title="确认停用?"
onConfirm={() => {
unDeploy(basicInfo);
}}
>
<a style={{ fontSize: 12, marginLeft: 20 }}>停用</a>
</Popconfirm>
) : (
<Popconfirm
title="确认发布?"
onConfirm={() => {
deploy(basicInfo);
}}
>
<a style={{ fontSize: 12, marginLeft: 20 }}>发布</a>
</Popconfirm>
)}
</div>
</Row>
);
const action = (
<Tooltip title="编辑产品信息后请重新应用配置">
<Popconfirm
title="确认重新应用该配置?"
onConfirm={() => {
deploy(basicInfo);
}}
>
<Button icon="sync" type="primary">
应用配置
</Button>
</Popconfirm>
</Tooltip>
);
return (
<Spin tip="加载中..." spinning={spinning}>
<PageHeaderWrapper title={titleInfo} content={content} extra={action}>
<Card>
<Tabs>
<Tabs.TabPane tab="产品信息" key="info">
<Descriptions
style={{ marginBottom: 20 }}
bordered
column={3}
title={
<span>
产品信息
<Button
icon="edit"
style={{ marginLeft: 20 }}
type="link"
onClick={() => setSaveVisible(true)}
>
编辑
</Button>
</span>
}
>
<Descriptions.Item label="产品ID" span={1}>
{basicInfo.id}
</Descriptions.Item>
{/*<Descriptions.Item label="产品名称" span={1}>
{basicInfo.name}
</Descriptions.Item>*/}
<Descriptions.Item label="所属品类" span={1}>
{basicInfo.classifiedName}
</Descriptions.Item>
<Descriptions.Item label="所属机构" span={1}>
{basicInfo.orgName}
</Descriptions.Item>
<Descriptions.Item label="消息协议" span={1}>
{basicInfo.protocolName || basicInfo.protocolId}
</Descriptions.Item>
<Descriptions.Item label="链接协议" span={1}>
{basicInfo.transportProtocol}
</Descriptions.Item>
<Descriptions.Item label="设备类型" span={1}>
{(basicInfo.deviceType || {}).text}
</Descriptions.Item>
<Descriptions.Item label="说明" span={3}>
{basicInfo.describe}
</Descriptions.Item>
</Descriptions>
{config && config.length > 0 && (
<div style={{ width: '100%' }}>
<Descriptions
title={
<span>
配置
<Button
icon="edit"
style={{ marginLeft: 20 }}
type="link"
onClick={() => setUpdateVisible(true)}
>
编辑
</Button>
{/* <Button
style={{ marginLeft: 10 }}
type="link"
onClick={() => setUpdateVisible(true)}
>
应用配置
</Button> */}
</span>
}
></Descriptions>
{config.map((i: any) => (
<div style={{ marginBottom: '20px' }} key={i.name}>
<h3>{i.name}</h3>
<Descriptions bordered column={2} title="">
{i.properties &&
i.properties.map((item: any) => (
<Descriptions.Item
label={
item.description ? (
<div>
<span style={{ marginRight: '10px' }}>{item.name}</span>
<Tooltip title={item.description}>
<Icon type="question-circle-o" />
</Tooltip>
</div>
) : (
item.name
)
}
span={1}
key={item.property}
>
{basicInfo.configuration
? item.type.type === 'password'
? basicInfo.configuration[item.property]?.length > 0
? '••••••'
: null
: basicInfo.configuration[item.property]
: null}
</Descriptions.Item>
))}
</Descriptions>
</div>
))}
</div>
)}
</Tabs.TabPane>
<Tabs.TabPane tab="物模型" key="metadata">
<Definition
basicInfo={basicInfo}
eventsData={events}
functionsData={functions}
propertyData={properties}
tagsData={tags}
unitsData={units}
saveEvents={(data: any, onlySave: boolean) => {
setEvents(data);
updateData('event', data, onlySave);
}}
saveFunctions={(data: any, onlySave: boolean) => {
setFunctions(data);
updateData('function', data, onlySave);
}}
saveProperty={(data: any[], onlySave: boolean) => {
setProperties(data);
updateData('properties', data, onlySave);
}}
saveTags={(data: any[], onlySave: boolean) => {
setTags(data);
updateData('tags', data, onlySave);
}}
update={() => handleSearch(productId)}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="告警设置" key="metadata1">
<Alarm
target="product"
productId={basicInfo.id}
productName={basicInfo.name}
targetId={basicInfo.id}
metaData={basicInfo.metadata}
name={basicInfo.name}
/>
</Tabs.TabPane>
</Tabs>
</Card>
{saveVisible && (
<Save
data={basicInfo}
close={() => setSaveVisible(false)}
save={(item: any) => {
setBasicInfo(item);
saveData(item);
}}
/>
)}
{updateVisible && (
<Configuration
data={basicInfo}
configuration={config}
close={() => {
setUpdateVisible(false);
}}
save={(onlySave: boolean, item: any) => {
updateInfo(onlySave, item);
}}
/>
)}
</PageHeaderWrapper>
</Spin>
);
}
Example #25
Source File: index-backups.tsx From jetlinks-ui-antd with MIT License | 4 votes |
DeviceModel: React.FC<Props> = props => {
const { result } = props.deviceProduct;
const initState: State = {
data: result,
searchParam: { pageSize: 10 },
saveVisible: false,
};
const [searchParam, setSearchParam] = useState(initState.searchParam);
const [saveVisible, setSaveVisible] = useState(initState.saveVisible);
const [filterData, setFilterData] = useState({});
const { dispatch } = props;
const handleSearch = (params?: any) => {
setSearchParam(params);
dispatch({
type: 'deviceProduct/query',
payload: encodeQueryParam(params),
});
};
const deploy = (record: any) => {
dispatch({
type: 'deviceProduct/deploy',
payload: record.id,
callback: response => {
if (response.status === 200) {
message.success('操作成功');
handleSearch(searchParam);
}
},
});
};
const unDeploy = (record: any) => {
dispatch({
type: 'deviceProduct/unDeploy',
payload: record.id,
callback: response => {
if (response.status === 200) {
message.success('操作成功');
handleSearch(searchParam);
}
},
});
};
const handleDelete = (params: any) => {
dispatch({
type: 'deviceProduct/remove',
payload: params.id,
callback: response => {
if (response.status === 200) {
message.success('删除成功');
handleSearch(searchParam);
}
},
});
};
const columns: ColumnProps<DeviceProduct>[] = [
{
title: 'ID',
dataIndex: 'id',
width: '250px',
},
{
title: '产品名称',
dataIndex: 'name',
},
{
title: '类型',
dataIndex: 'deviceType',
width: '150px',
align: 'center',
render: (text: any) => (text || {}).text,
sorter: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: '200px',
align: 'center',
render: (text: any) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
sorter: true,
defaultSortOrder: 'descend',
},
{
title: '发布状态',
dataIndex: 'state',
align: 'center',
filters: [{
value: '0',
text: '未发布'
}, {
value: '1',
text: '已发布'
}],
render: (text: any) => {
const color = text === 0 ? 'red' : 'green';
const status = text === 0 ? '未发布' : '已发布';
return <Badge color={color} text={status} />;
},
},
{
title: '操作',
width: '300px',
align: 'center',
render: (record: DeviceProduct) => (
<Fragment>
<a
onClick={() => {
router.push(`/device/product/save/${record.id}`);
}}
>
查看
</a>
<Divider type="vertical" />
{record.state === 0 ? (
<span>
<Popconfirm
title="确认发布?"
onConfirm={() => {
deploy(record);
}}
>
<a>发布</a>
</Popconfirm>
<Divider type="vertical" />
<Popconfirm title="确定删除?" onConfirm={() => handleDelete(record)}>
<a>删除</a>
</Popconfirm>
</span>
) : (
<Popconfirm
title="确认停用"
onConfirm={() => {
unDeploy(record);
}}
>
<a>停用</a>
</Popconfirm>
)}
<Divider type="vertical" />
<a
onClick={() => {
downloadObject(record, '产品');
}}
>
下载配置
</a>
<Divider type="vertical" />
<a
onClick={() => {
router.push(`/device/instance?productId=${record.id}`);
}}
>
查看设备
</a>
</Fragment>
),
},
];
useEffect(() => {
handleSearch(searchParam);
}, []);
const handeSave = (record: any) => {
dispatch({
type: 'deviceProduct/insert',
payload: record,
callback: response => {
if (response.status === 200) {
setSaveVisible(false);
message.success('保存成功');
router.push(`/device/product/save/${record.id}`);
}
},
});
};
const onTableChange = (
pagination: PaginationConfig,
filters: any,
sorter: SorterResult<DeviceProduct>
) => {
const tempFilter = converObjectKey(filters, { state: 'state$IN' });
setFilterData(tempFilter);
handleSearch({
pageIndex: Number(pagination.current) - 1,
pageSize: pagination.pageSize,
terms: { ...searchParam, ...tempFilter },
sorts: sorter,
});
};
const uploadProps: UploadProps = {
accept: '.json',
action: '/jetlinks/file/static',
headers: {
'X-Access-Token': getAccessToken(),
},
showUploadList: false,
onChange(info) {
if (info.file.status === 'done') {
const fileUrl = info.file.response.result;
request(fileUrl, { method: 'GET' }).then(e => {
if (e || e !== null) {
dispatch({
type: 'deviceProduct/insert',
payload: e,
callback: (response: any) => {
if (response.status === 200) {
message.success('导入成功');
handleSearch(searchParam);
}
},
});
}
}).catch(() => {
message.error('导入配置失败');
});
}
},
};
// 消息协议
const [protocolSupports, setProtocolSupports] = useState([]);
useEffect(() => {
apis.deviceProdcut
.protocolSupport()
.then(response => {
if (response.status === 200) {
setProtocolSupports(response.result.map((i: any) => ({ id: i.id, name: i.name })));
}
})
.catch(() => { });
}, []);
return (
<PageHeaderWrapper title="产品管理">
<Card bordered={false}>
<div className={styles.tableList}>
<div>
<SearchForm
search={(params: any) => {
handleSearch({
terms: { ...params, ...filterData },
pageSize: 10,
sorts: searchParam.sorts
});
}}
formItems={[{
label: '产品名称',
key: 'name$LIKE',
type: 'string',
},
{
label: '设备类型',
key: 'deviceType',
type: 'list',
props: {
data: [
{ id: 'gateway', name: '网关' },
{ id: 'device', name: '设备' }
]
}
},
{
label: '消息协议',
key: 'messageProtocol',
type: 'list',
props: {
data: protocolSupports
}
},]}
/>
</div>
<div className={styles.tableListOperator}>
<Button icon="plus" type="primary" onClick={() => setSaveVisible(true)}>
新建
</Button>
<Divider type="vertical" />
<Upload {...uploadProps}>
<Button>
<Icon type="upload" /> 导入配置
</Button>
</Upload>
</div>
<div className={styles.StandardTable}>
<Table
loading={props.loading}
dataSource={(result || {}).data}
columns={columns}
rowKey='id'
onChange={onTableChange}
pagination={{
current: result.pageIndex + 1,
total: result.total,
pageSize: result.pageSize,
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
showTotal: (total: number) => (
`共 ${total} 条记录 第 ${
result.pageIndex + 1
}/${
Math.ceil(result.total / result.pageSize)
}页`
),
}}
/>
</div>
</div>
</Card>
{saveVisible && <Save close={() => setSaveVisible(false)} save={item => handeSave(item)} />}
</PageHeaderWrapper>
);
}
Example #26
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
DeviceInstancePage: React.FC<Props> = props => {
const { result } = props.deviceInstance;
const { dispatch, location } = props;
const map = new Map();
map.set('id', 'id$like');
map.set('name', 'name$like');
map.set('orgId', 'orgId$in');
map.set('devTag', 'id$dev-tag');
map.set('devBind', 'id$dev-bind$any');
map.set('devProd', 'productId$dev-prod-cat');
map.set('productId', 'productId');
const initState: State = {
data: result,
searchParam: {
pageSize: 10,
terms: location?.query?.terms,
sorts: {
order: 'desc',
field: 'id',
},
},
addVisible: false,
currentItem: {},
processVisible: false,
importLoading: false,
action: '',
deviceCount: {
notActiveCount: 0,
offlineCount: 0,
onlineCount: 0,
deviceTotal: 0,
loading: true,
},
productList: [],
deviceIdList: [],
};
const [searchParam, setSearchParam] = useState(initState.searchParam);
const [addVisible, setAddVisible] = useState(initState.addVisible);
const [currentItem, setCurrentItem] = useState(initState.currentItem);
const [importLoading, setImportLoading] = useState(initState.importLoading);
const [action, setAction] = useState(initState.action);
const [productList, setProductList] = useState(initState.productList);
const [product, setProduct] = useState<string>();
const [deviceCount, setDeviceCount] = useState(initState.deviceCount);
const [deviceImport, setDeviceImport] = useState(false);
const [deviceExport, setDeviceExport] = useState(false);
const [deviceIdList, setDeviceIdLIst] = useState(initState.deviceIdList);
const statusMap = new Map();
statusMap.set('在线', 'success');
statusMap.set('离线', 'error');
statusMap.set('未激活', 'processing');
statusMap.set('online', 'success');
statusMap.set('offline', 'error');
statusMap.set('notActive', 'processing');
const handleSearch = (params?: any) => {
setSearchParam(params);
dispatch({
type: 'deviceInstance/query',
payload: encodeQueryParam(params),
});
};
const delelteInstance = (record: any) => {
apis.deviceInstance
.remove(record.id)
.then(response => {
if (response.status === 200) {
message.success('操作成功');
deviceIdList.splice(0, deviceIdList.length);
handleSearch(searchParam);
}
})
.catch(() => {});
};
const changeDeploy = (record: any) => {
apis.deviceInstance
.changeDeploy(record.id)
.then(response => {
if (response.status === 200) {
message.success('操作成功');
deviceIdList.splice(0, deviceIdList.length);
handleSearch(searchParam);
}
})
.catch(() => {});
};
const unDeploy = (record: any) => {
apis.deviceInstance
.unDeploy(record.id)
.then(response => {
if (response.status === 200) {
message.success('操作成功');
deviceIdList.splice(0, deviceIdList.length);
handleSearch(searchParam);
}
})
.catch(() => {});
};
const columns: ColumnProps<DeviceInstance>[] = [
{
title: 'ID',
dataIndex: 'id',
},
{
title: '设备名称',
dataIndex: 'name',
ellipsis: true,
},
{
title: '产品名称',
dataIndex: 'productName',
ellipsis: true,
},
{
title: '注册时间',
dataIndex: 'registryTime',
width: '200px',
render: (text: any) => (text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : '/'),
sorter: true,
},
{
title: '状态',
dataIndex: 'state',
width: '90px',
render: record =>
record ? <Badge status={statusMap.get(record.value)} text={record.text} /> : '',
filters: [
{
text: '未启用',
value: 'notActive',
},
{
text: '离线',
value: 'offline',
},
{
text: '在线',
value: 'online',
},
],
filterMultiple: false,
},
{
title: '说明',
dataIndex: 'describe',
width: '15%',
ellipsis: true,
},
{
title: '操作',
width: '200px',
align: 'center',
render: (record: any) => (
<Fragment>
<a
onClick={() => {
router.push(`/device/instance/save/${record.id}`);
}}
>
查看
</a>
<Divider type="vertical" />
<a
onClick={() => {
setCurrentItem(record);
setAddVisible(true);
}}
>
编辑
</a>
<Divider type="vertical" />
{record.state?.value === 'notActive' ? (
<span>
<Popconfirm
title="确认启用?"
onConfirm={() => {
changeDeploy(record);
}}
>
<a>启用</a>
</Popconfirm>
<Divider type="vertical" />
<Popconfirm
title="确认删除?"
onConfirm={() => {
delelteInstance(record);
}}
>
<a>删除</a>
</Popconfirm>
</span>
) : (
<Popconfirm
title="确认禁用设备?"
onConfirm={() => {
unDeploy(record);
}}
>
<a>禁用</a>
</Popconfirm>
)}
</Fragment>
),
},
];
const stateCount = (productId: string) => {
const map = {
notActiveCount: 0,
offlineCount: 0,
onlineCount: 0,
deviceTotal: 0,
loading: true,
};
apis.deviceInstance
.count(
encodeQueryParam({
terms: {
state: 'notActive',
productId,
...location?.query?.terms,
...(location?.query.iop && JSON.parse(location?.query.iop)?.terms),
},
}),
)
.then(res => {
if (res.status === 200) {
map.notActiveCount = res.result;
setDeviceCount({ ...map });
}
})
.catch();
apis.deviceInstance
.count(
encodeQueryParam({
terms: {
state: 'offline',
productId,
...location?.query?.terms,
...(location?.query.iop && JSON.parse(location?.query.iop)?.terms),
},
}),
)
.then(res => {
if (res.status === 200) {
map.offlineCount = res.result;
setDeviceCount({ ...map });
}
})
.catch();
apis.deviceInstance
.count(
encodeQueryParam({
terms: {
state: 'online',
productId,
...location?.query?.terms,
...(location?.query.iop && JSON.parse(location?.query.iop)?.terms),
},
}),
)
.then(res => {
if (res.status === 200) {
map.onlineCount = res.result;
setDeviceCount({ ...map });
}
})
.catch();
apis.deviceInstance
.count(
encodeQueryParam({
terms: {
productId,
...location?.query?.terms,
...(location?.query.iop && JSON.parse(location?.query.iop)?.terms),
},
}),
)
.then(res => {
if (res.status === 200) {
map.deviceTotal = res.result;
map.loading = false;
setDeviceCount({ ...map });
}
})
.catch();
};
useEffect(() => {
// 获取下拉框数据
apis.deviceProdcut
.queryNoPagin(
encodeQueryParam({
paging: false,
}),
)
.then(e => {
setProductList(e.result);
})
.catch(() => {});
const query: any = getPageQuery();
if (query.hasOwnProperty('productId')) {
const { productId } = query;
setProduct(productId);
handleSearch({
terms: {
productId: query.productId,
},
pageSize: 10,
});
stateCount(productId);
} else if (location?.query) {
let key = Object.keys(location?.query)[0];
let params = {};
params[map.get(key)] = location?.query[key];
handleSearch({
terms: { ...params, ...(location?.query.iop && JSON.parse(location?.query.iop)?.terms) },
pageSize: 10,
sorts: searchParam.sorts,
});
stateCount('');
} else {
handleSearch(searchParam);
stateCount('');
}
}, []);
const onTableChange = (
pagination: PaginationConfig,
filters: any,
sorter: SorterResult<DeviceInstance>,
) => {
let { terms } = searchParam;
if (filters.state) {
if (terms) {
terms.state = filters.state[0];
} else {
terms = {
state: filters.state[0],
};
}
}
handleSearch({
pageIndex: Number(pagination.current) - 1,
pageSize: pagination.pageSize,
terms,
sorts: sorter,
});
};
const [processVisible, setProcessVisible] = useState(false);
const [api, setAPI] = useState<string>('');
const getSearchParam = () => {
const data = encodeQueryParam(searchParam);
let temp = '';
Object.keys(data).forEach((i: string) => {
if (data[i] && i !== 'pageSize' && i !== 'pageIndex') {
temp += `${i}=${data[i]}&`;
}
});
return encodeURI(temp.replace(/%/g, '%'));
};
// 激活全部设备
const startImport = () => {
setProcessVisible(true);
const activeAPI = `/jetlinks/device-instance/deploy?${getSearchParam()}:X_Access_Token=${getAccessToken()} `;
setAPI(activeAPI);
setAction('active');
};
const startSync = () => {
setProcessVisible(true);
const syncAPI = `/jetlinks/device-instance/state/_sync/?${getSearchParam()}:X_Access_Token=${getAccessToken()}`;
setAPI(syncAPI);
setAction('sync');
};
const activeDevice = () => {
Modal.confirm({
title: `确认激活全部设备`,
okText: '确定',
okType: 'primary',
cancelText: '取消',
onOk() {
startImport();
},
});
};
const syncDevice = () => {
Modal.confirm({
title: '确定同步设备真实状态?',
okText: '确定',
okType: 'primary',
cancelText: '取消',
onOk() {
// 同步设备
startSync();
},
});
};
const onDeviceProduct = (value: string) => {
let { terms } = searchParam;
if (terms) {
terms.productId = value;
} else {
terms = {
productId: value,
};
}
handleSearch({
pageIndex: searchParam.pageIndex,
pageSize: searchParam.pageSize,
terms,
sorts: searchParam.sorter,
});
stateCount(value);
};
const rowSelection = {
onChange: (selectedRowKeys: any) => {
setDeviceIdLIst(selectedRowKeys);
},
};
const _delete = (deviceId: any[]) => {
Modal.confirm({
title: `确认删除选中设备`,
okText: '确定',
okType: 'primary',
cancelText: '取消',
onOk() {
apis.deviceInstance
._delete(deviceId)
.then(response => {
if (response.status === 200) {
message.success('成功删除选中设备');
deviceIdList.splice(0, deviceIdList.length);
handleSearch(searchParam);
}
})
.catch(() => {});
},
});
};
const _unDeploy = (deviceId: any[]) => {
Modal.confirm({
title: `确认注销选中设备`,
okText: '确定',
okType: 'primary',
cancelText: '取消',
onOk() {
apis.deviceInstance
._unDeploy(deviceId)
.then(response => {
if (response.status === 200) {
message.success('成功注销选中设备');
deviceIdList.splice(0, deviceIdList.length);
handleSearch(searchParam);
}
})
.catch(() => {});
},
});
};
const _deploy = (deviceId: any[]) => {
Modal.confirm({
title: `确认激活选中设备`,
okText: '确定',
okType: 'primary',
cancelText: '取消',
onOk() {
apis.deviceInstance
._deploy(deviceId)
.then(response => {
if (response.status === 200) {
message.success('成功激活选中设备');
deviceIdList.splice(0, deviceIdList.length);
handleSearch(searchParam);
}
})
.catch(() => {});
},
});
};
const Info: FC<{
title: React.ReactNode;
value: React.ReactNode;
}> = ({ title, value }) => (
<div>
<span>{title}</span>
<p style={{ fontSize: '26px' }}>{value}</p>
</div>
);
const menu = (
<Menu>
<Menu.Item key="1">
<Button
icon="download"
type="default"
onClick={() => {
setDeviceExport(true);
}}
>
批量导出设备
</Button>
</Menu.Item>
<Menu.Item key="2">
<Button
icon="upload"
onClick={() => {
setDeviceImport(true);
}}
>
批量导入设备
</Button>
</Menu.Item>
{deviceIdList.length > 0 && (
<Menu.Item key="3">
<Button
icon="delete"
onClick={() => {
_delete(deviceIdList);
}}
>
删除选中设备
</Button>
</Menu.Item>
)}
{deviceIdList.length > 0 && (
<Menu.Item key="6">
<Button
icon="stop"
onClick={() => {
_unDeploy(deviceIdList);
}}
>
注销选中设备
</Button>
</Menu.Item>
)}
{deviceIdList.length > 0 ? (
<Menu.Item key="4">
<Button icon="check-circle" type="danger" onClick={() => _deploy(deviceIdList)}>
激活选中设备
</Button>
</Menu.Item>
) : (
<Menu.Item key="4">
<Button icon="check-circle" type="danger" onClick={() => activeDevice()}>
激活全部设备
</Button>
</Menu.Item>
)}
<Menu.Item key="5">
<Button icon="sync" type="danger" onClick={() => syncDevice()}>
同步设备状态
</Button>
</Menu.Item>
</Menu>
);
return (
<PageHeaderWrapper title="设备管理">
<div className={styles.standardList}>
<Card bordered={false} style={{ height: 95 }}>
<Spin spinning={deviceCount.loading}>
<Row>
<Col sm={7} xs={24}>
<Select
placeholder="选择产品"
showSearch
optionFilterProp='label'
allowClear
style={{ width: '70%', marginTop: 7 }}
value={product}
onChange={(value: string) => {
let key = Object.keys(location?.query)[0];
let params = {};
if (location?.query) {
params[key] = location?.query[key];
}
params['productId'] = value;
router.push({ pathname: `/device/instance`, query: params });
setProduct(() => value);
setDeviceCount({ loading: true });
onDeviceProduct(value);
}}
>
{productList?.map(item => (
<Select.Option key={item.id} label={item.name}>{item.name}</Select.Option>
))}
</Select>
</Col>
<Col sm={4} xs={24}>
<Info title="全部设备" value={numeral(deviceCount.deviceTotal).format('0,0')} />
</Col>
<Col sm={4} xs={24}>
<Info
title={<Badge status={statusMap.get('online')} text="在线" />}
value={numeral(deviceCount.onlineCount).format('0,0')}
/>
</Col>
<Col sm={4} xs={24}>
<Info
title={<Badge status={statusMap.get('offline')} text="离线" />}
value={numeral(deviceCount.offlineCount).format('0,0')}
/>
</Col>
<Col sm={4} xs={24}>
<Info
title={<Badge status={statusMap.get('notActive')} text="未启用" />}
value={numeral(deviceCount.notActiveCount).format('0,0')}
/>
</Col>
<Col sm={1} xs={24}>
<Tooltip title="刷新">
<Icon
type="sync"
style={{ fontSize: 20 }}
onClick={() => {
setDeviceCount({ loading: true });
stateCount(product);
}}
/>
</Tooltip>
</Col>
</Row>
</Spin>
</Card>
<br />
<Card bordered={false}>
<div className={styles.tableList}>
<div className={styles.tableListForm}>
<Search
type={'device-instance'}
search={(params: any) => {
if (Object.keys(params).length === 0) {
deviceIdList.splice(0, deviceIdList.length);
}
if (product) {
params.productId = product;
}
params.state = searchParam.terms?.state;
handleSearch({ terms: params, pageSize: 10, sorts: searchParam.sorts });
}}
/>
</div>
<div className={styles.tableListOperator}>
<Button
icon="plus"
type="primary"
onClick={() => {
setCurrentItem({});
setAddVisible(true);
}}
>
添加设备
</Button>
<Divider type="vertical" />
<Dropdown overlay={menu}>
<Button icon="menu">
其他批量操作
<Icon type="down" />
</Button>
</Dropdown>
</div>
<div className={styles.StandardTable}>
<Table
loading={props.loading}
columns={columns}
dataSource={(result || {}).data}
rowKey="id"
onChange={onTableChange}
rowSelection={{
type: 'checkbox',
...rowSelection,
}}
pagination={{
current: result.pageIndex + 1,
total: result.total,
pageSize: result.pageSize,
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
showTotal: (total: number) =>
`共 ${total} 条记录 第 ${result.pageIndex + 1}/${Math.ceil(
result.total / result.pageSize,
)}页`,
}}
/>
</div>
</div>
</Card>
{addVisible && (
<Save
data={currentItem}
close={() => {
setAddVisible(false);
setCurrentItem({});
}}
/>
)}
{(processVisible || importLoading) && (
<Process
api={api}
action={action}
closeVisible={() => {
setProcessVisible(false);
setImportLoading(false);
handleSearch(searchParam);
}}
/>
)}
{deviceImport && (
<Import
productId={product}
close={() => {
setDeviceImport(false);
handleSearch(searchParam);
}}
/>
)}
{deviceExport && (
<Export
productId={product}
searchParam={searchParam}
close={() => {
setDeviceExport(false);
handleSearch(searchParam);
}}
/>
)}
</div>
</PageHeaderWrapper>
);
}
Example #27
Source File: index-backups.tsx From jetlinks-ui-antd with MIT License | 4 votes |
DeviceInstancePage: React.FC<Props> = props => {
const { result } = props.deviceInstance;
const initState: State = {
data: result,
searchParam: { pageSize: 10 },
addVisible: false,
currentItem: {},
processVisible: false,
importLoading: false,
action: '',
};
const [searchParam, setSearchParam] = useState(initState.searchParam);
const [addVisible, setAddvisible] = useState(initState.addVisible);
const [currentItem, setCurrentItem] = useState(initState.currentItem);
const [importLoading, setImportLoading] = useState(initState.importLoading);
const [action, setAction] = useState(initState.action);
const { dispatch } = props;
const statusMap = new Map();
statusMap.set('在线', 'success');
statusMap.set('离线', 'error');
statusMap.set('未激活', 'processing');
const handleSearch = (params?: any) => {
setSearchParam(params);
dispatch({
type: 'deviceInstance/query',
payload: encodeQueryParam(params),
});
};
const delelteInstance = (record: any) => {
apis.deviceInstance
.remove(record.id)
.then(response => {
if (response.status === 200) {
message.success('操作成功');
handleSearch(searchParam);
}
})
.catch(() => {});
};
const changeDeploy = (record: any) => {
apis.deviceInstance
.changeDeploy(record.id)
.then(response => {
if (response.status === 200) {
message.success('操作成功');
handleSearch(searchParam);
}
})
.catch(() => {});
};
const unDeploy = (record: any) => {
apis.deviceInstance
.unDeploy(record.id)
.then(response => {
if (response.status === 200) {
message.success('操作成功');
handleSearch(searchParam);
}
})
.catch(() => {});
};
const columns: ColumnProps<DeviceInstance>[] = [
{
title: 'ID',
dataIndex: 'id',
},
{
title: '设备名称',
dataIndex: 'name',
},
{
title: '产品名称',
dataIndex: 'productName',
},
{
title: '注册时间',
dataIndex: 'registryTime',
width: '200px',
render: (text: any) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
sorter: true,
defaultSortOrder: 'descend',
},
{
title: '状态',
dataIndex: 'state',
render: record =>
record ? <Badge status={statusMap.get(record.text)} text={record.text} /> : '',
},
{
title: '描述',
dataIndex: 'describe',
},
{
title: '操作',
width: '200px',
align: 'center',
render: (record: any) => (
<Fragment>
<a
onClick={() => {
router.push(`/device/instance/save/${record.id}`);
}}
>
查看
</a>
<Divider type="vertical" />
<a
onClick={() => {
setCurrentItem(record);
setAddvisible(true);
}}
>
编辑
</a>
<Divider type="vertical" />
{record.state?.value === 'notActive' ? (
<span>
<Popconfirm
title="确认激活?"
onConfirm={() => {
changeDeploy(record);
}}
>
<a>激活</a>
</Popconfirm>
<Divider type="vertical" />
<Popconfirm
title="确认删除?"
onConfirm={() => {
delelteInstance(record);
}}
>
<a>删除</a>
</Popconfirm>
</span>
) : (
<Popconfirm
title="确认注销设备?"
onConfirm={() => {
unDeploy(record);
}}
>
<a>注销</a>
</Popconfirm>
)}
</Fragment>
),
},
];
useEffect(() => {
handleSearch(searchParam);
}, []);
const saveDeviceInstance = (item: any) => {
dispatch({
type: 'deviceInstance/update',
payload: encodeQueryParam(item),
callback: (response:any) => {
if (response.status === 200) {
message.success('保存成功');
setAddvisible(false);
router.push(`/device/instance/save/${item.id}`);
}
},
});
};
const onTableChange = (
pagination: PaginationConfig,
filters: any,
sorter: SorterResult<DeviceInstance>,
) => {
handleSearch({
pageIndex: Number(pagination.current) - 1,
pageSize: pagination.pageSize,
terms: searchParam.terms,
sorts: sorter,
});
};
const [processVisible, setProcessVisible] = useState(false);
const [api, setAPI] = useState<string>('');
const getSearchParam = () => {
const data = encodeQueryParam(searchParam);
let temp = '';
Object.keys(data).forEach((i: string) => {
if (data[i] && i !== 'pageSize' && i !== 'pageIndex') {
temp += `${i}=${data[i]}&`;
}
});
return encodeURI(temp.replace(/%/g, '%'));
};
// 激活全部设备
const startImport = () => {
// let dt = 0;
setProcessVisible(true);
const activeAPI = `/jetlinks/device-instance/deploy?${getSearchParam()}:X_Access_Token=${getAccessToken()} `;
setAPI(activeAPI);
setAction('active');
};
const startSync = () => {
setProcessVisible(true);
const syncAPI = `/jetlinks/device-instance/state/_sync/?${getSearchParam()}:X_Access_Token=${getAccessToken()}`;
setAPI(syncAPI);
setAction('sync');
};
const activeDevice = () => {
Modal.confirm({
title: `确认激活全部设备`,
okText: '确定',
okType: 'primary',
cancelText: '取消',
onOk() {
startImport();
},
});
};
const syncDevice = () => {
Modal.confirm({
title: '确定同步设备真实状态?',
okText: '确定',
okType: 'primary',
cancelText: '取消',
onOk() {
// 同步设备
startSync();
},
});
};
const [uploading, setUploading] = useState(false);
const exportDevice = () => {
const formElement = document.createElement('form');
formElement.style.display = 'display:none;';
formElement.method = 'post';
formElement.action = `/jetlinks/device-instance/export?:X_Access_Token=${getAccessToken()}`;
const params = encodeQueryParam(searchParam);
Object.keys(params).forEach((key: string) => {
const inputElement = document.createElement('input');
inputElement.type = 'hidden';
inputElement.name = key;
inputElement.value = params[key];
formElement.appendChild(inputElement);
});
document.body.appendChild(formElement);
formElement.submit();
document.body.removeChild(formElement);
};
const uploadProps: UploadProps = {
accept: '.xlsx, .xls',
action: '/jetlinks/file/static',
headers: {
'X-Access-Token': getAccessToken(),
},
showUploadList: false,
onChange(info) {
if (info.file.status === 'done') {
setUploading(false);
const fileUrl = info.file.response.result;
const url = `/jetlinks/device-instance/import?fileUrl=${fileUrl}&:X_Access_Token=${getAccessToken()}`;
setAPI(url);
setAction('import');
setImportLoading(true);
}
if (info.file.status === 'uploading') {
setUploading(true);
}
},
};
return (
<PageHeaderWrapper title="设备管理">
<Spin spinning={uploading} tip="上传中...">
<Card bordered={false}>
<div className={styles.tableList}>
<div className={styles.tableListForm}>
<Search
search={(params: any) => {
setSearchParam(params);
handleSearch({ terms: params, pageSize: 10 });
}}
/>
</div>
<div className={styles.tableListOperator}>
<Button
icon="plus"
type="primary"
onClick={() => {
setCurrentItem({});
setAddvisible(true);
}}
>
新建
</Button>
<Divider type="vertical" />
<Button href={template} download="设备模版" icon="download">
下载模版
</Button>
<Divider type="vertical" />
<Button icon="download" type="default" onClick={() => exportDevice()}>
导出设备
</Button>
<Divider type="vertical" />
<Upload {...uploadProps}>
<Button icon="upload">导入设备</Button>
</Upload>
<Divider type="vertical" />
<Button icon="check-circle" type="danger" onClick={() => activeDevice()}>
激活全部设备
</Button>
<Divider type="vertical" />
<Button icon="sync" type="danger" onClick={() => syncDevice()}>
同步设备状态
</Button>
</div>
<div className={styles.StandardTable}>
<Table
loading={props.loading}
columns={columns}
dataSource={(result || {}).data}
rowKey="id"
onChange={onTableChange}
pagination={{
current: result.pageIndex + 1,
total: result.total,
pageSize: result.pageSize,
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
showTotal: (total: number) =>
`共 ${total} 条记录 第 ${result.pageIndex + 1}/${Math.ceil(
result.total / result.pageSize,
)}页`,
}}
/>
</div>
</div>
</Card>
{addVisible && (
<Save
data={currentItem}
close={() => {
setAddvisible(false);
setCurrentItem({});
}}
save={(item: any) => {
saveDeviceInstance(item);
}}
/>
)}
{(processVisible || importLoading) && (
<Process
api={api}
action={action}
closeVisible={() => {
setProcessVisible(false);
setImportLoading(false);
handleSearch(searchParam);
}}
/>
)}
</Spin>
</PageHeaderWrapper>
);
}
Example #28
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Editor: React.FC<Props> = props => {
const {
location: { pathname },
} = props;
const initState: State = {
activeKey: 'info',
data: {},
logs: {},
orgInfo: {},
config: {},
spinning: true,
};
const [activeKey, setActiveKey] = useState(initState.activeKey);
const [data, setData] = useState(initState.data);
const [id, setId] = useState();
const [config, setConfig] = useState(initState.config);
const [orgInfo] = useState(initState.orgInfo);
const [spinning, setSpinning] = useState(initState.spinning);
const [tableList, setTableList] = useState();
const [events, setEvents] = useState<any[]>([]);
const [functions, setFunctions] = useState<any[]>([]);
const [properties, setProperties] = useState<any[]>([]);
const [tags, setTags] = useState<any[]>([]);
const [units, setUnits] = useState({});
const [deviceId, setDeviceId] = useState<string | null>();
let deviceStatus: any;
const tabList = [
{
key: 'info',
tab: '实例信息',
},
{
key: 'metadata',
tab: '物模型',
},
{
key: 'log',
tab: '日志管理',
},
{
key: 'alarm',
tab: '告警设置',
},
{
key: 'visualization',
tab: '可视化',
},
{
key: 'shadow',
tab: '设备影子',
},
];
const subscribeDeviceState = (deviceData: any, deviceId: string) => {
deviceStatus && deviceStatus.unsubscribe();
deviceStatus = getWebsocket(
`instance-editor-info-status-${deviceId}`,
`/dashboard/device/status/change/realTime`,
{
deviceId: deviceId,
},
).subscribe((resp: any) => {
const { payload } = resp;
if(payload.value.type === 'online'){
deviceData.state = { value: 'online', text: '在线' }
} else if(payload.value.type === 'notActive'){
deviceData.state = { value: 'notActive', text: '未启用' }
}else if(payload.value.type === 'offline'){
deviceData.state = {
value: 'offline',
text: '离线'
}
}
if (payload.value.type === 'online') {
deviceData.onlineTime = payload.timestamp;
} else {
deviceData.offlineTime = payload.timestamp;
}
setData({ ...deviceData });
});
};
const getInfo = (deviceId: string) => {
setSpinning(true);
setDeviceId(deviceId);
apis.deviceInstance
.info(deviceId)
.then((response: any) => {
if (response.status === 200) {
const deviceData = response.result;
if (deviceData.orgId) {
deviceData.orgName = orgInfo[deviceData.orgId];
}
setData({ ...deviceData });
if (deviceData.metadata) {
const metadata = JSON.parse(deviceData.metadata);
setEvents(metadata.events);
setFunctions(metadata.functions);
setProperties(metadata.properties);
setTags(metadata.tags);
}
subscribeDeviceState(deviceData, deviceId);
if (deviceData.metadata) {
const deriveMetadata = JSON.parse(deviceData.metadata);
if (
(deriveMetadata.functions || []).length > 0 &&
deviceData.state?.value !== 'notActive'
) {
tabList.splice(2, 0, {
key: 'functions',
tab: '设备功能',
});
}
}
if (deviceData.deviceType.value === 'gateway') {
tabList.push({
key: 'gateway',
tab: '子设备管理',
});
}
if (deviceData.state?.value !== 'notActive') {
tabList.splice(1, 0, {
key: 'status',
tab: '运行状态',
});
}
// apis.deviceProdcut.protocolConfiguration(deviceData.protocol, deviceData.transport)
// .then(resp => {
// setConfig(resp.result);
// }).catch();
apis.deviceProdcut
.deviceConfiguration(deviceData.id)
.then(resp => {
setConfig(resp.result);
})
.catch();
setTableList(tabList);
setSpinning(false);
}
})
.catch(() => {
setSpinning(false);
message.error('产品物模型数据错误');
});
};
const statusMap = new Map();
statusMap.set('online', <Badge status="success" text={'在线'} />);
statusMap.set('offline', <Badge status="error" text={'离线'} />);
statusMap.set('notActive', <Badge status="processing" text={'未启用'} />);
useEffect(() => {
apis.deviceProdcut
.queryOrganization()
.then(res => {
if (res.status === 200) {
res.result.map((e: any) => (orgInfo[e.id] = e.name));
}
})
.catch(() => {});
apis.deviceProdcut
.units()
.then((response: any) => {
if (response.status === 200) {
setUnits(response.result);
}
})
.catch(() => {});
return () => {
deviceStatus && deviceStatus.unsubscribe();
};
}, []);
useEffect(() => {
setActiveKey('info');
if (pathname.indexOf('save') > 0) {
const list = pathname.split('/');
getInfo(list[list.length - 1]);
setId(list[list.length - 1]);
}
setTableList(tabList);
}, [window.location.hash]);
const disconnectDevice = (deviceId?: string) => {
setSpinning(true);
apis.deviceInstance
.disconnectDevice(deviceId)
.then(response => {
if (response.status === 200) {
message.success('断开连接成功');
data.state = { value: 'offline', text: '离线' };
setData(data);
setSpinning(false);
} else {
message.error('断开连接失败');
setSpinning(false);
}
})
.catch();
};
const changeDeploy = (deviceId?: string) => {
setSpinning(true);
apis.deviceInstance
.changeDeploy(deviceId)
.then(response => {
if (response.status === 200) {
message.success('操作成功');
data.state = { value: 'offline', text: '离线' };
setData(data);
setSpinning(false);
} else {
message.error('操作失败');
setSpinning(false);
}
})
.catch(() => {});
};
const updateData = (type: string, item: any) => {
let metadata = JSON.stringify({ events, properties, functions, tags });
if (type === 'event') {
metadata = JSON.stringify({ events: item, properties, functions, tags });
} else if (type === 'properties') {
metadata = JSON.stringify({ events, properties: item, functions, tags });
} else if (type === 'function') {
metadata = JSON.stringify({ events, properties, functions: item, tags });
} else if (type === 'tags') {
metadata = JSON.stringify({ events, properties, functions, tags: item });
}
apis.deviceInstance
.saveOrUpdateMetadata(data.id, metadata)
.then((re: any) => {
if (re.status === 200) {
message.success('保存成功');
}
})
.catch(() => {})
.finally(() => {
getInfo(deviceId!);
});
};
const action = (
<Tooltip title="刷新">
<Icon
type="sync"
style={{ fontSize: 20 }}
onClick={() => {
getInfo(deviceId!);
}}
/>
</Tooltip>
);
const info = {
info: (
<Info
data={data}
configuration={config}
refresh={() => {
getInfo(data.id);
}}
/>
),
metadata: (
<Definition
basicInfo={data}
eventsData={events}
functionsData={functions}
propertyData={properties}
tagsData={tags}
unitsData={units}
saveEvents={(data: any) => {
setEvents(data);
updateData('event', data);
}}
saveFunctions={(data: any) => {
setFunctions(data);
updateData('function', data);
}}
saveProperty={(data: any[]) => {
setProperties(data);
updateData('properties', data);
}}
saveTags={(data: any[]) => {
setTags(data);
updateData('tags', data); //handleSearch()
}}
update={() => {
getInfo(data.id);
}}
/>
),
status: (
<Status
device={data}
refresh={() => {
getInfo(data.id);
}}
/>
),
functions: <Functions device={data} />,
log: <Log deviceId={id} />,
debugger: <Debugger />,
gateway: <Gateway deviceId={id} loading={false} />,
alarm: (
<Alarm
target="device"
productId={data.productId}
productName={data.productName}
targetId={data.id}
metaData={data.metadata}
name={data.name}
/>
),
shadow: <Shadow deviceId={data.id} />,
visualization: (
<Visualization
type="device"
target={data.id}
name={data.name}
productId={data.productId}
metaData={data.metadata}
/>
),
};
const content = (
<div style={{ marginTop: 30 }}>
<Descriptions column={4}>
<Descriptions.Item label="ID">{id}</Descriptions.Item>
<Descriptions.Item label="产品">
<div>
{data.productName}
<a
style={{ marginLeft: 10 }}
onClick={() => {
router.push(`/device/product/save/${data.productId}`);
}}
>
查看
</a>
</div>
</Descriptions.Item>
</Descriptions>
</div>
);
const deviceStateStyle = {
style: {
fontSize: 12,
marginLeft: 20,
},
};
const titleInfo = (
<Row>
<div>
<span style={{ paddingRight: 20 }}>设备:{data.name}</span>
{statusMap.get(data.state?.value)}
{data.state?.value === 'online' ? (
<Popconfirm
title="确认让此设备断开连接?"
onConfirm={() => {
disconnectDevice(data.id);
}}
>
<a {...deviceStateStyle}>断开连接</a>
</Popconfirm>
) : data.state?.value === 'notActive' ? (
<Popconfirm
title="确认启动此设备?"
onConfirm={() => {
changeDeploy(data.id);
}}
>
<a {...deviceStateStyle}>启动设备</a>
</Popconfirm>
) : (
<span />
)}
</div>
</Row>
);
const extra = <div className={styles.moreInfo} />;
return (
<Spin tip="加载中..." spinning={spinning}>
<PageHeaderWrapper
className={styles.instancePageHeader}
style={{ marginTop: 0, backgroundColor: '#F0F2F5', paddingBottom: 10 }}
onBack={() => window.history.back()}
title={titleInfo}
extra={action}
content={content}
extraContent={extra}
tabList={tableList}
tabActiveKey={activeKey}
onTabChange={(key: string) => {
if (!data.metadata) {
message.error('请检查产品物模型');
return;
}
setActiveKey(key);
}}
>
{info[activeKey]}
</PageHeaderWrapper>
</Spin>
);
}
Example #29
Source File: gateway.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Gateway: React.FC<Props> = (props) => {
const initState: State = {
data: {},
searchParam: { pageSize: 10 },
currentItem: {},
spinning: false,
bindVisible: false,
addVisible: false,
};
const [searchParam, setSearchParam] = useState(initState.searchParam);
const [data, setData] = useState(initState.data);
const [spinning, setSpinning] = useState(initState.spinning);
const [currentItem, setCurrentItem] = useState(initState.currentItem);
const [addVisible, setAddVisible] = useState(initState.addVisible);
const [bindVisible, setBindVisible] = useState(initState.bindVisible);
const handleSearch = (params?: any) => {
setSearchParam(params);
apis.deviceInstance.list(encodeQueryParam(params))
.then((response: any) => {
if (response.status === 200) {
setData(response.result);
}
setSpinning(false);
},
).catch(() => {
});
};
useEffect(() => {
setSpinning(true);
handleSearch({
pageSize: 10,
terms: {
parentId: props.deviceId,
},
});
}, []);
const changeDeploy = (record: any) => {
setSpinning(true);
apis.deviceInstance
.changeDeploy(record.id)
.then(response => {
if (response.status === 200) {
message.success('操作成功');
handleSearch(searchParam);
} else {
setSpinning(false);
}
})
.catch(() => {
});
};
const unDeploy = (record: any) => {
setSpinning(true);
apis.deviceInstance
.unDeploy(record.id)
.then(response => {
if (response.status === 200) {
message.success('操作成功');
handleSearch(searchParam);
} else {
setSpinning(false);
}
})
.catch(() => {
});
};
const unBindGateway = (id: string, deviceId: string) => {
setSpinning(true);
apis.deviceGateway.unBind(id, deviceId)
.then(response => {
if (response.status === 200) {
message.success('解绑成功');
handleSearch(searchParam);
} else {
setSpinning(false);
}
}).catch(() => {
});
};
const statusMap = new Map();
statusMap.set('online', 'success');
statusMap.set('offline', 'error');
statusMap.set('notActive', 'processing');
const columns: ColumnProps<DeviceInstance>[] = [
{
title: 'ID',
dataIndex: 'id',
},
{
title: '设备名称',
dataIndex: 'name',
},
{
title: '产品名称',
dataIndex: 'productName',
},
{
title: '注册时间',
dataIndex: 'registryTime',
width: '200px',
render: (text: any) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
sorter: true,
defaultSortOrder: 'descend',
},
{
title: '状态',
dataIndex: 'state',
render: record =>
record ? <Badge status={statusMap.get(record.value)} text={record.text}/> : '',
},
{
title: '操作',
width: '200px',
align: 'center',
render: (record: any) => (
<Fragment>
<a
onClick={() => {
router.push(`/device/instance/save/${record.id}`);
}}
>
查看
</a>
<Divider type="vertical"/>
<a
onClick={() => {
setCurrentItem(record);
setAddVisible(true);
}}
>
编辑
</a>
<Divider type="vertical"/>
{record.state?.value === 'notActive' ? (
<Popconfirm
title="确认激活?"
onConfirm={() => {
changeDeploy(record);
}}
>
<a>激活</a>
</Popconfirm>
) : (
<Popconfirm
title="确认注销设备?"
onConfirm={() => {
unDeploy(record);
}}
>
<a>注销</a>
</Popconfirm>
)}
<Divider type="vertical"/>
<Popconfirm
title="确认解绑?"
onConfirm={() => {
unBindGateway(props.deviceId, record.id);
}}
>
<a>解绑</a>
</Popconfirm>
</Fragment>
),
},
];
const onTableChange = (
pagination: PaginationConfig,
filters: any,
sorter: SorterResult<DeviceInstance>,
) => {
handleSearch({
pageIndex: Number(pagination.current) - 1,
pageSize: pagination.pageSize,
terms: searchParam.terms,
sorts: sorter,
});
};
const saveDeviceInstance = (item: any) => {
setSpinning(true);
apis.deviceInstance.saveOrUpdate(item)
.then((response: any) => {
if (response.status === 200) {
message.success('保存成功');
handleSearch(searchParam);
} else {
setSpinning(false);
}
}).catch(() => {
});
};
const insert = (deviceData: any) => {
setSpinning(true);
apis.deviceGateway.bind(props.deviceId, deviceData).then(response => {
if (response.status === 200) {
message.success('保存成功');
handleSearch(searchParam);
} else {
setSpinning(false);
}
}).catch(() => {
});
};
const action = (
<Button type="primary" icon="plus" onClick={() => setBindVisible(true)}>
绑定子设备
</Button>
);
return (
<div>
<Spin spinning={spinning}>
<Card style={{ marginBottom: 20 }} title="子设备列表" extra={action}>
<div className={styles.tableListForm}>
<Search
search={(params: any) => {
setSearchParam(params);
params.parentId = props.deviceId;
handleSearch({ terms: params, pageSize: 10 });
}}
/>
</div>
<Table
loading={props.loading}
columns={columns}
dataSource={data?.data}
rowKey="id"
onChange={onTableChange}
pagination={{
current: data.pageIndex + 1,
total: data.total,
pageSize: data.pageSize,
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
showTotal: (total: number) =>
`共 ${total} 条记录 第 ${data.pageIndex + 1}/${Math.ceil(
data.total / data.pageSize,
)}页`,
}}
/>
</Card>
{addVisible && (
<Save
data={currentItem}
close={() => {
setAddVisible(false);
setCurrentItem({});
}}
save={(item: any) => {
setAddVisible(false);
saveDeviceInstance(item);
}}
/>
)}
{bindVisible && (
<Bind selectionType='checkbox'
close={() => {
setBindVisible(false);
}}
save={(item: any) => {
setBindVisible(false);
insert(item);
}}
/>
)}
</Spin>
</div>
);
}