diff --git a/config/proxy.ts b/config/proxy.ts index 0fb4e5d02..d1180f473 100644 --- a/config/proxy.ts +++ b/config/proxy.ts @@ -23,7 +23,7 @@ export default { test: { '/jetlinks': { // target: 'http://192.168.3.89:8848/', - target: 'http://2.jetlinks.org:9010/', + target: 'http://192.168.3.89:9010/', changeOrigin: true, pathRewrite: { '^/jetlinks': '' }, }, @@ -31,7 +31,7 @@ export default { pre: { '/jetlinks': { // target: 'http://192.168.3.89:8848/', - target: 'http://2.jetlinks.org:9010/', + target: 'http://192.168.3.89:9010/', changeOrigin: true, pathRewrite: { '^/jetlinks': '' }, }, diff --git a/config/routes.ts b/config/routes.ts index 00a626988..62269567b 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -479,13 +479,13 @@ export const routes = [ name: '云云对接', icon: 'cloud', version: 'pro', - authority: ['dueros-product', 'aliyun-bridge', 'onenet-product', 'ctwing-product', 'admin'], + authority: ['dueros-product', 'aliyun-bridge', 'onenet-product', 'ctwing-product'], routes: [ { path: '/cloud/duer', name: 'DuerOS', version: 'pro', - authority: ['dueros-product', 'admin'], + authority: ['dueros-product'], icon: 'cloud', component: './cloud/dueros', }, @@ -493,7 +493,7 @@ export const routes = [ path: '/cloud/aliyun', name: '阿里云', version: 'pro', - authority: ['aliyun-bridge', 'admin'], + authority: ['aliyun-bridge'], icon: 'aliyun', component: './cloud/aliyun', }, @@ -501,7 +501,7 @@ export const routes = [ path: '/cloud/onenet', name: '移动OneNet', version: 'pro', - authority: ['onenet-product', 'admin'], + authority: ['onenet-product'], icon: 'mobile', component: './cloud/onenet', }, @@ -509,7 +509,7 @@ export const routes = [ path: '/cloud/ctwing', name: '电信CTWing', version: 'pro', - authority: ['ctwing-product', 'admin'], + authority: ['ctwing-product'], icon: 'phone', component: './cloud/ctwing', } @@ -520,13 +520,13 @@ export const routes = [ name: '国标网关', icon: 'youtube', version: 'pro', - authority: ['gb28181-gateway','media-channel','media-server','media-stream','admin'], + authority: ['gb28181-gateway','media-channel','media-server','media-stream','gb28181-cascade'], routes: [ { path: '/media/basic', name: '基本配置', version: 'pro', - authority: ['gb28181-gateway','media-server','admin'], + authority: ['gb28181-gateway','media-server'], icon: 'video-camera', component: './media/basic', }, @@ -550,7 +550,7 @@ export const routes = [ path: '/media/device', name: '国标设备', version: 'pro', - authority: ['media-device','media-stream','admin'], + authority: ['media-device'], icon: 'gateway', component: './media/device', }, @@ -567,10 +567,18 @@ export const routes = [ path: '/media/reveal', name: '分屏展示', version: 'pro', - authority: ['media-stream','admin'], + authority: ['media-stream'], icon: 'appstore', component: './media/reveal', }, + { + path: '/media/cascade', + name: '国标级联', + version: 'pro', + authority: ['gb28181-cascade'], + icon: 'cloud-upload', + component: './media/cascade', + }, ] }, { diff --git a/src/pages/device/instance/Search/index.tsx b/src/pages/device/instance/Search/index.tsx index 625f9363f..98493811e 100644 --- a/src/pages/device/instance/Search/index.tsx +++ b/src/pages/device/instance/Search/index.tsx @@ -2,10 +2,13 @@ import React, { useEffect, useState } from 'react'; import Form, { FormComponentProps } from 'antd/lib/form'; import { Button, Col, Input, Row, Select, TreeSelect } from 'antd'; import apis from '@/services'; +import { router } from 'umi'; import SearchTags from "@/pages/device/instance/Search/tags/tags"; +import { getPageQuery } from '@/utils/utils'; interface Props extends FormComponentProps { search: Function; + location: Location } interface State { @@ -34,9 +37,41 @@ const Search: React.FC = props => { const [categoryList, setCategoryList] = useState([]); const [bindList, setBindList] = useState([]); + const mapType = new Map(); + mapType.set('id$like', 'id'); + mapType.set('name$like', 'name'); + mapType.set('orgId$in', 'orgId'); + mapType.set('id$dev-tag', 'devTag'); + mapType.set('id$dev-bind$any', 'devBind'); + mapType.set('productId$dev-prod-cat', 'devProd'); + useEffect(() => { setParameterType('id$like'); - form.setFieldsValue({ parameter: 'id$like' }); + const query: any = getPageQuery(); + if (query && query !== {}) { + mapType.forEach((value, key) => { + let k = Object.keys(query)[0] + if(value === k){ + form.setFieldsValue({ parameter: key }); + if (key === 'orgId$in') { + form.setFieldsValue({value: query[k].split(",")}) + } else if (key === 'id$dev-tag') { + let v = JSON.parse(query[k]) + let displayData: any[] = []; + v.map((item: any) => { + displayData.push(`${item.key}=${item.value}`); + }); + setFieldsValue({ 'value': displayData.join(';') }); + }else if (key === 'id$dev-bind$any') { + form.setFieldsValue({value: query[k].split(",")}) + }else{ + form.setFieldsValue({ value: query[k] }); + } + } + }); + }else{ + form.setFieldsValue({ parameter: 'id$like' }); + } apis.deviceProdcut.queryOrganization() .then(res => { @@ -82,8 +117,16 @@ const Search: React.FC = props => { } else if (data.parameter === 'id$dev-tag') { data.value = tagsData.length > 0 ? JSON.stringify(tagsData) : undefined; } else if (data.parameter === 'id$dev-bind$any') { - data.value = JSON.stringify(data.value).replace(/[\[\]"]/g, '') + data.value = JSON.stringify(data.value).replace(/[\[\]"]/g, '') + } + let params = {} + params[mapType.get(data.parameter)] = data.value + params['productId'] = getPageQuery().productId + if(getPageQuery().productId){ + params['productId'] = getPageQuery().productId + map['productId'] = getPageQuery().productId } + router.push({ pathname: `/device/instance`, query: params }) map[data.parameter] = data.value; props.search(map); }; diff --git a/src/pages/device/instance/editor/detail/functions.tsx b/src/pages/device/instance/editor/detail/functions.tsx index 920764bea..971259971 100644 --- a/src/pages/device/instance/editor/detail/functions.tsx +++ b/src/pages/device/instance/editor/detail/functions.tsx @@ -1,8 +1,18 @@ -import React, { useEffect, useState } from 'react'; -import { Button, Card, Divider, Form, Input, Select, Spin } from 'antd'; +import React, {useEffect, useState} from 'react'; +import {Button, Card, Divider, Form, Input, Select, Spin} from 'antd'; import apis from '@/services'; +import {FormComponentProps} from "antd/lib/form"; +import AceEditor from "react-ace"; +import 'ace-builds/src-noconflict/mode-json'; +import 'ace-builds/src-noconflict/mode-json5'; +import 'ace-builds/src-noconflict/mode-hjson'; +import 'ace-builds/src-noconflict/mode-jsoniq'; +import 'ace-builds/src-noconflict/snippets/json'; +import 'ace-builds/src-noconflict/ext-language_tools'; +import 'ace-builds/src-noconflict/ext-searchbox'; +import 'ace-builds/src-noconflict/theme-eclipse'; -interface Props { +interface Props extends FormComponentProps { device: any } @@ -10,107 +20,139 @@ interface State { propertiesData: any[]; functionsSelectList: any[]; functionsInfo: any; - debugData: string; - logs: string; - functionId: string; spinning: boolean; } const Functions: React.FC = (props) => { - const initState: State = { - propertiesData: [], - functionsSelectList: [], - functionsInfo: {}, - debugData: '', - logs: '', - functionId: '', - spinning: false, - }; + const { + form: {getFieldDecorator, setFieldsValue}, + form, + } = props; - const [functionsSelectList] = useState(initState.functionsSelectList); - const [functionsInfo, setFunctionsInfo] = useState(initState.functionsInfo); - const [debugData, setDebugData] = useState(initState.debugData); - const [logs, setLogs] = useState(initState.logs); - const [functionId, setFunctionId] = useState(initState.functionId); - const [spinning, setSpinning] = useState(initState.spinning); + const initState: State = { + propertiesData: [], + functionsSelectList: [], + functionsInfo: {}, + spinning: false, + }; - useEffect(() => { - const { functions } = JSON.parse(props.device.metadata); - const map = {}; - functions.forEach((item: any) => { - map[item.id] = item; - functionsSelectList.push({item.name}); - }); - setFunctionsInfo(map); - }, []); + const [functionsSelectList] = useState(initState.functionsSelectList); + const [functionsInfo, setFunctionsInfo] = useState(initState.functionsInfo); + const [spinning, setSpinning] = useState(initState.spinning); - const debugFunction = () => { - setSpinning(true); - apis.deviceInstance - .invokedFunction(props.device.id, functionId, JSON.parse(debugData)) - .then(response => { - const tempResult = response?.result; - if (response.status === 200) { - typeof tempResult === 'object' ? setLogs(JSON.stringify(tempResult)) : setLogs(tempResult); - // setLogs(tempResult); - } - setSpinning(false); - }).catch(() => { - setLogs(`调试错误`); + useEffect(() => { + const {functions} = JSON.parse(props.device.metadata); + const map = {}; + functions.forEach((item: any) => { + map[item.id] = item; + functionsSelectList.push({item.name}); }); - }; + setFunctionsInfo(map); + }, []); - return ( -
- - -
- - - - - { - setDebugData(e.target.value); - }} - placeholder="参数必须JSON格式" - /> - -
- - - -
+ const debugFunction = () => { + setSpinning(true); - - - -
-
-
-
- ); -}; + form.validateFields((err, fileValue) => { + if (err) return; -export default Functions; + localStorage.setItem(`function-debug-data-${props.device.id}-${fileValue.functionId}`, fileValue.functionData); + + apis.deviceInstance + .invokedFunction(props.device.id, fileValue.functionId, JSON.parse(fileValue.functionData)) + .then(response => { + const tempResult = response?.result; + if (response.status === 200) { + typeof tempResult === 'object' ? + setFieldsValue({logs: JSON.stringify(tempResult)}) : + setFieldsValue({logs: tempResult}) + } + setSpinning(false); + }).catch(() => { + setFieldsValue({logs: '调试错误'}); + }); + }); + }; + + return ( +
+ + +
+ + {getFieldDecorator('functionId', { + rules: [ + {required: true, message: '请选择设备功能'}, + ], + })()} + + + {getFieldDecorator('functionData', { + rules: [ + {required: true, message: '请输入功能参数'}, + ], + })( + + )} + +
+ + + +
+ + + {getFieldDecorator('logs', {})( + + )} + +
+
+
+
+ ); + } +; + +export default Form.create()(Functions); diff --git a/src/pages/device/instance/index.tsx b/src/pages/device/instance/index.tsx index f9ab2ec96..97a1b821c 100644 --- a/src/pages/device/instance/index.tsx +++ b/src/pages/device/instance/index.tsx @@ -62,6 +62,15 @@ const DeviceInstancePage: React.FC = 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, }, @@ -320,6 +329,12 @@ const DeviceInstancePage: React.FC = props => { 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, pageSize: 10, sorts: searchParam.sorts }); + stateCount(''); } else { handleSearch(searchParam); stateCount(''); @@ -568,6 +583,13 @@ const DeviceInstancePage: React.FC = props => { + )} + + + + + {getFieldDecorator('name', { + initialValue: props.data?.name, + rules: [ + {required: true, message: '请输入级联名称'}, + {max: 200, message: '级联名称不超过200个字符'} + ], + })()} + + + + + {getFieldDecorator('description', { + initialValue: props.data.description, + })()} + + + + +
信令服务配置
+
+ { + sipConfigs.map((item: any, index: number) => { + return ( +
+
+ 服务: {index + 1} +
+
+
+ + + + {getFieldDecorator(`sipConfigs[${index}].clusterNodeId`, { + initialValue: item.clusterNodeId || undefined, + rules: [{required: true, message: '请输入本地服务ID'}], + })( + , + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].name`, { + initialValue: item.name || undefined, + rules: [ + {required: true, message: '请输入名称'}, + {max: 200, message: '级联名称不超过200个字符'} + ], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].sipId`, { + initialValue: item.sipId || undefined, + rules: [{required: true, message: '请输入SIP ID'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].domain`, { + initialValue: item.domain || undefined, + rules: [{required: true, message: '请输入SIP 域'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].remoteAddress`, { + initialValue: item.remoteAddress || undefined, + rules: [{required: true, message: '请输入SIP HOST'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].remotePort`, { + initialValue: item.remotePort || undefined, + rules: [{required: true, message: '请输入SIP PORT'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].localSipId`, { + initialValue: item.localSipId || undefined, + rules: [{required: true, message: '请输入SIP本地ID'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].localAddress`, { + initialValue: item.localAddress || undefined, + rules: [{required: true, message: '请输入SIP本地地址'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].port`, { + initialValue: item.port || undefined, + rules: [{required: true, message: '请输入SIP本地端口'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].user`, { + initialValue: item.user || undefined, + rules: [{required: true, message: '请输入用户'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].password`, { + initialValue: item.password || undefined, + rules: [{required: true, message: '请输入接入密码'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].manufacturer`, { + initialValue: item.manufacturer || undefined, + rules: [{required: true, message: '请输入厂商'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].model`, { + initialValue: item.model || undefined, + rules: [{required: true, message: '请输入型号'}], + })( + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].firmware`, { + initialValue: item.firmware || undefined, + rules: [{required: true, message: '请输入版本号'}], + })()} + + + + + {getFieldDecorator(`sipConfigs[${index}].registerInterval`, { + initialValue: item.registerInterval || 3600, + rules: [{required: true, message: '请输入注册间隔(秒)'}], + })()} + + + + + {getFieldDecorator(`sipConfigs[${index}].keepaliveInterval`, { + initialValue: item.keepaliveInterval || 60, + rules: [{required: true, message: '请输入心跳周期(秒)'}], + })()} + + + + + {getFieldDecorator(`sipConfigs[${index}].transport`, { + initialValue: item.transport || 'UDP', + rules: [{required: true, message: '请选择传输协议'}], + })( + + UDP + TCP + + )} + + + + + {getFieldDecorator(`sipConfigs[${index}].charset`, { + initialValue: item.charset || 'gb2312', + rules: [{required: true, message: '请选择字符集'}], + })( + + GB2312 + UTF-8 + + )} + + + +
+ {/*
+ + { + sipConfigs.splice(index, 1); + setSipConfigs([...sipConfigs]); + }} + /> + +
*/} +
+
+ ) + }) + } + {/**/} + + + + ) +}; + +export default Form.create()(Save); diff --git a/src/pages/media/cascade/service.ts b/src/pages/media/cascade/service.ts new file mode 100644 index 000000000..356a722d0 --- /dev/null +++ b/src/pages/media/cascade/service.ts @@ -0,0 +1,85 @@ +import BaseService from "@/services/crud"; +import request from "@/utils/request"; +import {defer, from} from "rxjs"; +import {filter, map} from "rxjs/operators"; + +class Service extends BaseService { + + public _enabled = (cascadeId: string) => defer( + () => from(request(`/jetlinks/media/gb28181-cascade/${cascadeId}/_enabled`, { + method: 'POST', + })).pipe( + filter(resp => resp.status === 200), + map(resp => resp.result) + )); + + public _disabled = (cascadeId: string) => defer( + () => from(request(`/jetlinks/media/gb28181-cascade/${cascadeId}/_disabled`, { + method: 'POST', + })).pipe( + filter(resp => resp.status === 200), + map(resp => resp.result) + )); + + public saveCascade = (data: any) => defer( + () => from(request(`/jetlinks/media/gb28181-cascade/`, { + method: 'PATCH', + data: data + })) + .pipe( + filter(resp => resp.status === 200), + map(resp => resp.result) + )); + + public deviceChannelNoPaging = (params: any) => defer( + () => from(request(`/jetlinks/media/channel/_query/no-paging?paging=false`, { + method: 'GET', + params + })) + .pipe( + filter(resp => resp.status === 200), + map(resp => resp.result) + )); + + public deviceChannel = (params: any) => defer( + () => from(request(`/jetlinks/media/channel/_query`, { + method: 'GET', + params + })) + .pipe( + filter(resp => resp.status === 200), + map(resp => resp.result) + )); + + public _bind = (cascadeId: string, channelList: any[]) => defer( + () => from(request(`/jetlinks/media/gb28181-cascade/${cascadeId}/channels/_bind`, { + method: 'POST', + data: channelList + })) + .pipe( + filter(resp => resp.status === 200), + map(resp => resp.result) + )); + + public _unbind = (cascadeId: string, channelList: any[]) => defer( + () => from(request(`/jetlinks/media/gb28181-cascade/${cascadeId}/channels/_unbind`, { + method: 'POST', + data: channelList + })) + .pipe( + filter(resp => resp.status === 200), + map(resp => resp.result) + )); + + public clusterNodes = () => defer( + () => from(request(`/jetlinks/cluster/nodes`, { + method: 'GET', + })) + .pipe( + filter(resp => resp.status === 200), + map(resp => resp.result) + )); + +} + +export default Service; diff --git a/src/pages/media/device/channel/edit/index.tsx b/src/pages/media/device/channel/edit/index.tsx index ecf3b2fc0..4e81ac86c 100644 --- a/src/pages/media/device/channel/edit/index.tsx +++ b/src/pages/media/device/channel/edit/index.tsx @@ -71,12 +71,12 @@ const Update: React.FC = props => { initialValue: props.data.address, })()} - - {getFieldDecorator('others.ptzType', { + + {getFieldDecorator('ptzType', { rules: [ {required: true, message: '请选择云台类型'} ], - initialValue: props.data.others?.ptzType || 0, + initialValue: props.data?.ptzType.value || 0, })( , )} - + {/* {getFieldDecorator('mediaServerId', { rules: [{required: true, message: '请选择流媒体服务'}], initialValue: data?.mediaServerId, @@ -138,7 +139,7 @@ const Save: React.FC = props => { ))} , )} - + */} @@ -206,11 +207,11 @@ const Save: React.FC = props => { rules: [ {required: true, message: '请选择字符集'} ], - initialValue: data?.sipConfig?.charset || "GBK", + initialValue: data?.sipConfig?.charset || "gb2312", })( - GB2312 - UTF-8 + GB2312 + UTF-8 )} diff --git a/src/pages/media/media-server/save/index.tsx b/src/pages/media/media-server/save/index.tsx index eeae44b7e..f54cb5e4f 100644 --- a/src/pages/media/media-server/save/index.tsx +++ b/src/pages/media/media-server/save/index.tsx @@ -1,4 +1,4 @@ -import {Button, Input, InputNumber, message, Select, Spin} from "antd"; +import {Button, Col, Input, InputNumber, message, Row, Select, Spin} from "antd"; import React, {useEffect, useState} from "react"; import Service from "../service"; import Form from "antd/es/form"; @@ -48,12 +48,15 @@ const Save: React.FC = props => { const saveData = () => { form.validateFields((err, fileValue) => { - if (err) return; + if (err) { + setLoading(false); + return; + } //todo 统一界面,后期有需求就开放多网关和流媒体服务 fileValue.id = id; - service.saveMediaServer(fileValue).subscribe((data) => { + service.saveMediaServer(fileValue).subscribe(() => { message.success('保存成功'); }, () => { @@ -80,7 +83,7 @@ const Save: React.FC = props => { {required: true, message: '请输入公网 Host'} ], initialValue: configuration.publicHost, - })()} + })()} {getFieldDecorator('configuration.apiHost', { @@ -88,47 +91,57 @@ const Save: React.FC = props => { {required: true, message: '请输入API Host'} ], initialValue: configuration.apiHost, - })()} - - - {getFieldDecorator('configuration.apiPort', { - rules: [ - {required: true, message: '请输入API端口'} - ], - initialValue: configuration.apiPort, - })()} - - - {getFieldDecorator('configuration.rtpPort', { - rules: [ - {required: true, message: '请输入RTP端口'} - ], - initialValue: configuration.rtpPort, - })()} - - - {getFieldDecorator('configuration.httpPort', { - rules: [ - {required: true, message: '请输入HTTP端口'} - ], - initialValue: configuration.httpPort, - })()} - - - {getFieldDecorator('configuration.rtmpPort', { - rules: [ - {required: true, message: '请输入RTMP端口'} - ], - initialValue: configuration.rtmpPort, - })()} + })()} + + + + {getFieldDecorator('configuration.apiPort', { + rules: [ + {required: true, message: '请输入API端口'} + ], + initialValue: configuration.apiPort, + })()} + + + + + {getFieldDecorator('configuration.rtpPort', { + rules: [ + {required: true, message: '请输入RTP端口'} + ], + initialValue: configuration.rtpPort, + })()} + + + + + {getFieldDecorator('configuration.httpPort', { + rules: [ + {required: true, message: '请输入HTTP端口'} + ], + initialValue: configuration.httpPort, + })()} + + + + + {getFieldDecorator('configuration.rtmpPort', { + rules: [ + {required: true, message: '请输入RTMP端口'} + ], + initialValue: configuration.rtmpPort, + })()} + + + {getFieldDecorator('configuration.formats', { rules: [ {required: true, message: '请选择流媒体格式'} ], initialValue: configuration.formats, - })( FLV MP4 HLS diff --git a/src/pages/system/org/index.tsx b/src/pages/system/org/index.tsx index 3340397d3..708f26574 100644 --- a/src/pages/system/org/index.tsx +++ b/src/pages/system/org/index.tsx @@ -73,11 +73,11 @@ const OrgList: React.FC = props => { type: 'org/insert', payload: encodeQueryParam(item), callback: (response: any) => { - if (response) { + if (response.status === 200) { message.success('保存成功'); - setSaveVisible(false); - handleSearch(searchParam); } + setSaveVisible(false); + handleSearch(searchParam); }, }); }; diff --git a/src/pages/system/permission/component/ProTable.tsx b/src/pages/system/permission/component/ProTable.tsx index c55484e71..48e0322dd 100644 --- a/src/pages/system/permission/component/ProTable.tsx +++ b/src/pages/system/permission/component/ProTable.tsx @@ -14,6 +14,7 @@ interface Props { rowSelection?: any; onRow?: any; scroll?: any; + expandedRowRender?: any; } const ProTable = (props: Props) => { const { loading, dataSource, columns, rowKey, onSearch, paginationConfig } = props; @@ -41,15 +42,15 @@ const ProTable = (props: Props) => { // rowKey={rowKey} onChange={onTableChange} pagination={typeof paginationConfig === "boolean" ? paginationConfig : { - current: paginationConfig.pageIndex + 1, - total: paginationConfig.total, - pageSize: paginationConfig.pageSize, + current: paginationConfig?.pageIndex + 1 || 0, + total: paginationConfig?.total || 0, + pageSize: paginationConfig?.pageSize || 0, showQuickJumper: true, showSizeChanger: true, pageSizeOptions: ['10', '20', '50', '100'], showTotal: (total: number) => - `共 ${total} 条记录 第 ${paginationConfig.pageIndex + 1}/${Math.ceil( - paginationConfig.total / paginationConfig.pageSize, + `共 ${total} 条记录 第 ${paginationConfig?.pageIndex + 1}/${Math.ceil( + paginationConfig?.total / paginationConfig?.pageSize, )}页`, }} />