fix: 解决冲突
This commit is contained in:
commit
8b5bbea837
@ -1,11 +1,13 @@
|
|||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import WindowToggle from './components/WindowToggle';
|
import WindowToggle from './components/WindowToggle';
|
||||||
import WarningRecordList from './components/WarningRecordList';
|
import WarningRecordList from './components/WarningRecordList';
|
||||||
|
import { ConfigProvider } from '@zhst/meta';
|
||||||
import { IRecord } from '../WarningRecordCard';
|
import { IRecord } from '../WarningRecordCard';
|
||||||
import { VideoPlayerCardProps } from '../VideoPlayerCard';
|
import { VideoPlayerCardProps } from '../VideoPlayerCard';
|
||||||
import { ViewLargerImageModalRef } from '../ViewLargerImageModal';
|
import { ViewLargerImageModalRef } from '../ViewLargerImageModal';
|
||||||
|
|
||||||
interface RealTimeMonitorProps {
|
interface RealTimeMonitorProps {
|
||||||
|
prefixCls?: string
|
||||||
videoDataSource?: VideoPlayerCardProps[];
|
videoDataSource?: VideoPlayerCardProps[];
|
||||||
handleWindowClick?: (key?: string) => void;
|
handleWindowClick?: (key?: string) => void;
|
||||||
handleCloseButtonClick?: (key?: string) => void;
|
handleCloseButtonClick?: (key?: string) => void;
|
||||||
@ -34,7 +36,11 @@ interface RealTimeMonitorProps {
|
|||||||
|
|
||||||
export const RealTimeMonitor: React.FC<RealTimeMonitorProps> = (props) => {
|
export const RealTimeMonitor: React.FC<RealTimeMonitorProps> = (props) => {
|
||||||
|
|
||||||
const { videoDataSource,
|
const { ConfigContext } = ConfigProvider;
|
||||||
|
const { getPrefixCls } = useContext(ConfigContext);
|
||||||
|
const {
|
||||||
|
prefixCls: customizePrefixCls,
|
||||||
|
videoDataSource,
|
||||||
handleWindowClick,
|
handleWindowClick,
|
||||||
handleCloseButtonClick,
|
handleCloseButtonClick,
|
||||||
selectedWindowKey,
|
selectedWindowKey,
|
||||||
@ -45,9 +51,10 @@ export const RealTimeMonitor: React.FC<RealTimeMonitorProps> = (props) => {
|
|||||||
selectedRecordId,
|
selectedRecordId,
|
||||||
isRecordListLoading,
|
isRecordListLoading,
|
||||||
} = props
|
} = props
|
||||||
|
const componentName = getPrefixCls('biz-real-time-monitor', customizePrefixCls);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='zhst-biz-real-time-monitor' style={{ display: 'flex' }} >
|
<div className={componentName} style={{ display: 'flex' }} >
|
||||||
<WindowToggle
|
<WindowToggle
|
||||||
selectedWindowKey={selectedWindowKey}
|
selectedWindowKey={selectedWindowKey}
|
||||||
dataSource={videoDataSource}
|
dataSource={videoDataSource}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Card, Space, CardProps, Spin, Button } from 'antd';
|
import { Card, Space, CardProps, Spin, Button } from 'antd';
|
||||||
import { theme } from 'antd/lib';
|
import { theme } from 'antd/lib';
|
||||||
import { VideoPlayer, type VideoViewRef } from '@zhst/meta';
|
import { ConfigProvider, VideoPlayer, type VideoViewRef, } from '@zhst/meta';
|
||||||
import React, { useState, useEffect, ReactNode, useRef } from 'react';
|
import React, { useState, useEffect, ReactNode, useRef, useContext } from 'react';
|
||||||
import { CloseOutlined, LoadingOutlined } from '@ant-design/icons';
|
import { CloseOutlined, LoadingOutlined } from '@ant-design/icons';
|
||||||
import './index.less'
|
import './index.less'
|
||||||
export interface VideoPlayerCardProps {
|
export interface VideoPlayerCardProps {
|
||||||
|
prefixCls?: string;
|
||||||
windowKey?: string;
|
windowKey?: string;
|
||||||
selectedWindowKey?: string;
|
selectedWindowKey?: string;
|
||||||
showType?: 'video' | "image";
|
showType?: 'video' | "image";
|
||||||
@ -21,8 +22,11 @@ export interface VideoPlayerCardProps {
|
|||||||
|
|
||||||
export const VideoPlayerCard: React.FC<VideoPlayerCardProps> = (props) => {
|
export const VideoPlayerCard: React.FC<VideoPlayerCardProps> = (props) => {
|
||||||
|
|
||||||
const componentName = `zhst-biz-video-player-card`;
|
|
||||||
const { showType, imageKey, videoSrc, cardProps, isWindowLoading, errorReasonText, size, title, handleCloseButtonClick, handleWindowClick, windowKey, selectedWindowKey = '' } = props;
|
const { ConfigContext } = ConfigProvider;
|
||||||
|
const { getPrefixCls } = useContext(ConfigContext);
|
||||||
|
const { prefixCls: customizePrefixCls, showType, imgSrc, videoSrc, cardProps, isWindowLoading, errorReasonText, size, title, handleCloseButtonClick, handleWindowClick, windowKey, selectedWindowKey = '' } = props;
|
||||||
|
const componentName = getPrefixCls('biz-video-player-card', customizePrefixCls);
|
||||||
const [cardContent, setCardContent] = useState<JSX.Element | null>(null);
|
const [cardContent, setCardContent] = useState<JSX.Element | null>(null);
|
||||||
const { useToken } = theme
|
const { useToken } = theme
|
||||||
const { token } = useToken()
|
const { token } = useToken()
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React, { useImperativeHandle, useRef, useState, forwardRef } from 'react';
|
import React, { useImperativeHandle, useRef, useState, forwardRef, useContext } from 'react';
|
||||||
import { Modal, ModalProps, Space, SpaceProps } from 'antd';
|
import { Modal, ModalProps, Space, SpaceProps } from 'antd';
|
||||||
import theme from 'antd/lib/theme';
|
import theme from 'antd/lib/theme';
|
||||||
import { DownloadOutlined } from '@ant-design/icons';
|
import { DownloadOutlined } from '@ant-design/icons';
|
||||||
import { CropperImage } from '@zhst/meta'
|
import { ConfigProvider,CropperImage} from '@zhst/meta';
|
||||||
|
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
|
||||||
type ViewLargerImageModalParams = {
|
type ViewLargerImageModalParams = {
|
||||||
@ -27,6 +28,7 @@ export interface ViewLargerImageModalRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ViewLargerImageModalProps {
|
export interface ViewLargerImageModalProps {
|
||||||
|
prefixCls?: string
|
||||||
imgStyle?: React.CSSProperties;
|
imgStyle?: React.CSSProperties;
|
||||||
downloadImg?: (imageKey?: string) => void;
|
downloadImg?: (imageKey?: string) => void;
|
||||||
title?: string;
|
title?: string;
|
||||||
@ -38,8 +40,12 @@ export interface ViewLargerImageModalProps {
|
|||||||
export const ViewLargerImageModal = forwardRef<ViewLargerImageModalRef, ViewLargerImageModalProps>(
|
export const ViewLargerImageModal = forwardRef<ViewLargerImageModalRef, ViewLargerImageModalProps>(
|
||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
|
|
||||||
const componentName = `zhst-biz-view-warning-larger-image-modal`;
|
const { ConfigContext } = ConfigProvider;
|
||||||
const { modalProps, downloadImg, imgStyle, title = '预警大图', downloadText = '下载大图', spaceProps, } = props
|
const { getPrefixCls } = useContext(ConfigContext);
|
||||||
|
const { prefixCls: customizePrefixCls, modalProps, downloadImg, imgStyle, title = '预警大图', downloadText = '下载大图', spaceProps } = props
|
||||||
|
const componentName = getPrefixCls('biz-warning-larger-image', customizePrefixCls);
|
||||||
|
|
||||||
|
|
||||||
const { useToken } = theme
|
const { useToken } = theme
|
||||||
const { token } = useToken()
|
const { token } = useToken()
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
.zhst-biz-view-warning-larger-image-modal {
|
.zhst-biz-warning-larger-image {
|
||||||
font-family: MicrosoftYaHei;
|
font-family: MicrosoftYaHei;
|
||||||
|
|
||||||
.ant-modal-content {
|
.ant-modal-content {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { Card, Space, Divider, CardProps } from 'antd';
|
import { Card, Space, Divider, CardProps } from 'antd';
|
||||||
import { theme } from 'antd/lib';
|
import { theme } from 'antd/lib';
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { CropperImage } from '@zhst/meta'
|
import { ConfigProvider,CropperImage} from '@zhst/meta';
|
||||||
|
|
||||||
import './index.less'
|
import './index.less'
|
||||||
export interface IRecord {
|
export interface IRecord {
|
||||||
|
|
||||||
@ -65,6 +66,7 @@ export interface IRecord {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export interface WarningRecordCardProps {
|
export interface WarningRecordCardProps {
|
||||||
|
prefixCls?: string;
|
||||||
record?: IRecord;
|
record?: IRecord;
|
||||||
onRecordClick?: (record?: IRecord) => void;
|
onRecordClick?: (record?: IRecord) => void;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
@ -75,8 +77,13 @@ export interface WarningRecordCardProps {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const WarningRecordCard: React.FC<WarningRecordCardProps> = (props) => {
|
export const WarningRecordCard: React.FC<WarningRecordCardProps> = (props) => {
|
||||||
const componentName = `zhst-biz-warning-record-card`;
|
|
||||||
const { record, onRecordClick, style, cardProps, selectedRecordId, cardStyle, imgStyle } = props;
|
const { ConfigContext } = ConfigProvider;
|
||||||
|
const { getPrefixCls } = useContext(ConfigContext);
|
||||||
|
const { prefixCls: customizePrefixCls, record, onRecordClick, style, cardProps, selectedRecordId, cardStyle, imgStyle } = props;
|
||||||
|
const componentName = getPrefixCls('biz-warning-record-card', customizePrefixCls);
|
||||||
|
|
||||||
|
;
|
||||||
const { imageKey, id, warningType, warningInfo = [], cabietText, warningTime, warningTimestamp, warningTimeFormat = 'YYYY-MM-DD HH:mm:ss', odRect = [] } = record || {}
|
const { imageKey, id, warningType, warningInfo = [], cabietText, warningTime, warningTimestamp, warningTimeFormat = 'YYYY-MM-DD HH:mm:ss', odRect = [] } = record || {}
|
||||||
const formattedDate = warningTimestamp ? dayjs(warningTimestamp).format(warningTimeFormat) : '';
|
const formattedDate = warningTimestamp ? dayjs(warningTimestamp).format(warningTimeFormat) : '';
|
||||||
const warningTimeShow = warningTime ? warningTime : formattedDate
|
const warningTimeShow = warningTime ? warningTime : formattedDate
|
||||||
|
@ -2,5 +2,6 @@ export { default as AlgorithmConfigModal } from "./algorithmConfigModal";
|
|||||||
export { default as AlgorithmConfig } from "./algorithmConfig";
|
export { default as AlgorithmConfig } from "./algorithmConfig";
|
||||||
export { default as Login } from "./login";
|
export { default as Login } from "./login";
|
||||||
export { default as Password } from "./password";
|
export { default as Password } from "./password";
|
||||||
|
export { default as CenterLink } from "./centerLink";
|
||||||
export { default as SchemaFormModal } from "./algorithmConfig/components/schemaFormModal";
|
export { default as SchemaFormModal } from "./algorithmConfig/components/schemaFormModal";
|
||||||
export * from 'rc-util';
|
export * from 'rc-util';
|
1
packages/material/es/utils/index.d.ts
vendored
1
packages/material/es/utils/index.d.ts
vendored
@ -0,0 +1 @@
|
|||||||
|
export declare const createAElement: (url: string, isBlank: boolean) => void;
|
@ -0,0 +1,14 @@
|
|||||||
|
// 可应用于页面跳转以及文件下载
|
||||||
|
// 第一个参数:文件的下载路径/要跳转页面的路径(可携带参数)
|
||||||
|
// 第二个参数:是否新打开一个页面,true为新开一个页面,false是在当前页面进行操作;
|
||||||
|
export var createAElement = function createAElement(url, isBlank) {
|
||||||
|
var newLink = document.createElement('a');
|
||||||
|
newLink.className = 'create-link';
|
||||||
|
newLink.href = url;
|
||||||
|
if (isBlank) {
|
||||||
|
newLink.target = '_blank';
|
||||||
|
}
|
||||||
|
document.body.appendChild(newLink);
|
||||||
|
newLink.click();
|
||||||
|
document.body.removeChild(newLink);
|
||||||
|
};
|
@ -32,6 +32,7 @@ var src_exports = {};
|
|||||||
__export(src_exports, {
|
__export(src_exports, {
|
||||||
AlgorithmConfig: () => import_algorithmConfig.default,
|
AlgorithmConfig: () => import_algorithmConfig.default,
|
||||||
AlgorithmConfigModal: () => import_algorithmConfigModal.default,
|
AlgorithmConfigModal: () => import_algorithmConfigModal.default,
|
||||||
|
CenterLink: () => import_centerLink.default,
|
||||||
Login: () => import_login.default,
|
Login: () => import_login.default,
|
||||||
Password: () => import_password.default,
|
Password: () => import_password.default,
|
||||||
SchemaFormModal: () => import_schemaFormModal.default
|
SchemaFormModal: () => import_schemaFormModal.default
|
||||||
@ -41,12 +42,14 @@ var import_algorithmConfigModal = __toESM(require("./algorithmConfigModal"));
|
|||||||
var import_algorithmConfig = __toESM(require("./algorithmConfig"));
|
var import_algorithmConfig = __toESM(require("./algorithmConfig"));
|
||||||
var import_login = __toESM(require("./login"));
|
var import_login = __toESM(require("./login"));
|
||||||
var import_password = __toESM(require("./password"));
|
var import_password = __toESM(require("./password"));
|
||||||
|
var import_centerLink = __toESM(require("./centerLink"));
|
||||||
var import_schemaFormModal = __toESM(require("./algorithmConfig/components/schemaFormModal"));
|
var import_schemaFormModal = __toESM(require("./algorithmConfig/components/schemaFormModal"));
|
||||||
__reExport(src_exports, require("rc-util"), module.exports);
|
__reExport(src_exports, require("rc-util"), module.exports);
|
||||||
// Annotate the CommonJS export names for ESM import in node:
|
// Annotate the CommonJS export names for ESM import in node:
|
||||||
0 && (module.exports = {
|
0 && (module.exports = {
|
||||||
AlgorithmConfig,
|
AlgorithmConfig,
|
||||||
AlgorithmConfigModal,
|
AlgorithmConfigModal,
|
||||||
|
CenterLink,
|
||||||
Login,
|
Login,
|
||||||
Password,
|
Password,
|
||||||
SchemaFormModal,
|
SchemaFormModal,
|
||||||
|
1
packages/material/lib/utils/index.d.ts
vendored
1
packages/material/lib/utils/index.d.ts
vendored
@ -0,0 +1 @@
|
|||||||
|
export declare const createAElement: (url: string, isBlank: boolean) => void;
|
@ -0,0 +1,39 @@
|
|||||||
|
var __defProp = Object.defineProperty;
|
||||||
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||||
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||||
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||||
|
var __export = (target, all) => {
|
||||||
|
for (var name in all)
|
||||||
|
__defProp(target, name, { get: all[name], enumerable: true });
|
||||||
|
};
|
||||||
|
var __copyProps = (to, from, except, desc) => {
|
||||||
|
if (from && typeof from === "object" || typeof from === "function") {
|
||||||
|
for (let key of __getOwnPropNames(from))
|
||||||
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||||
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
};
|
||||||
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||||
|
|
||||||
|
// src/utils/index.ts
|
||||||
|
var utils_exports = {};
|
||||||
|
__export(utils_exports, {
|
||||||
|
createAElement: () => createAElement
|
||||||
|
});
|
||||||
|
module.exports = __toCommonJS(utils_exports);
|
||||||
|
var createAElement = (url, isBlank) => {
|
||||||
|
var newLink = document.createElement("a");
|
||||||
|
newLink.className = "create-link";
|
||||||
|
newLink.href = url;
|
||||||
|
if (isBlank) {
|
||||||
|
newLink.target = "_blank";
|
||||||
|
}
|
||||||
|
document.body.appendChild(newLink);
|
||||||
|
newLink.click();
|
||||||
|
document.body.removeChild(newLink);
|
||||||
|
};
|
||||||
|
// Annotate the CommonJS export names for ESM import in node:
|
||||||
|
0 && (module.exports = {
|
||||||
|
createAElement
|
||||||
|
});
|
@ -1,8 +1,8 @@
|
|||||||
import React,{useRef} from 'react'
|
// @ts-nocheck
|
||||||
|
import React,{useRef,useState} from 'react'
|
||||||
import TerminalForm from './components/TerminalForm'
|
import TerminalForm from './components/TerminalForm'
|
||||||
import WebTerminal from './components/WebTerminal'
|
import WebTerminal from './components/WebTerminal'
|
||||||
import './index.less';
|
import './index.less';
|
||||||
import style from 'packages/meta/src/badge/style';
|
|
||||||
|
|
||||||
interface CenterLinkProps{
|
interface CenterLinkProps{
|
||||||
websocketUrl:string; // websocket服务地址
|
websocketUrl:string; // websocket服务地址
|
||||||
@ -22,8 +22,8 @@ const materialName='center-link';
|
|||||||
|
|
||||||
const CenterLink:React.FC<CenterLinkProps>=(props:CenterLinkProps)=>{
|
const CenterLink:React.FC<CenterLinkProps>=(props:CenterLinkProps)=>{
|
||||||
const {
|
const {
|
||||||
websocketUrl,
|
|
||||||
ip,
|
ip,
|
||||||
|
websocketUrl,
|
||||||
token,
|
token,
|
||||||
terminalStyle,
|
terminalStyle,
|
||||||
onConnect,
|
onConnect,
|
||||||
@ -35,22 +35,23 @@ const CenterLink:React.FC<CenterLinkProps>=(props:CenterLinkProps)=>{
|
|||||||
style
|
style
|
||||||
}=props;
|
}=props;
|
||||||
const webRef=useRef(null);
|
const webRef=useRef(null);
|
||||||
|
const [ipUrl,setIpUrl]=useState<string>(ip);
|
||||||
|
|
||||||
// 处理开始连接服务器ip的事件
|
// 处理开始连接服务器ip的事件
|
||||||
const handleConnectClick=(values:any)=>{
|
const handleConnectClick=(values:any)=>{
|
||||||
const {ip}=values;
|
const {ip}=values;
|
||||||
if(ip&&token&&websocketUrl&&webRef.current){
|
setIpUrl((
|
||||||
webRef.current.connect();
|
)=>(ip))
|
||||||
}
|
|
||||||
onConnect&&onConnect(values);
|
onConnect&&onConnect(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={materialName} style={style}>
|
<div className={materialName} style={style}>
|
||||||
<TerminalForm onConnect={handleConnectClick} onExportLogs={onExportLogs} />
|
<TerminalForm ip={ipUrl} onConnect={handleConnectClick} onExportLogs={onExportLogs} />
|
||||||
<WebTerminal
|
<WebTerminal
|
||||||
ref={webRef}
|
ref={webRef}
|
||||||
|
ip={ipUrl}
|
||||||
websocketUrl={websocketUrl}
|
websocketUrl={websocketUrl}
|
||||||
ip={ip}
|
|
||||||
token={token}
|
token={token}
|
||||||
terminalStyle={terminalStyle}
|
terminalStyle={terminalStyle}
|
||||||
onOpen={onOpen}
|
onOpen={onOpen}
|
||||||
|
@ -1,20 +1,27 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Button, Form, Input,FormProps } from '@zhst/meta';
|
import { Button, Form, Input,FormProps } from '@zhst/meta';
|
||||||
import '../index.less';
|
import '../index.less';
|
||||||
|
|
||||||
const materialName='terminal-form';
|
const materialName='terminal-form';
|
||||||
|
|
||||||
export interface TerminalFormProps{
|
export interface TerminalFormProps{
|
||||||
|
ip:string; // ip地址
|
||||||
onConnect:FormProps['onFinish']; // 开始连接事件
|
onConnect:FormProps['onFinish']; // 开始连接事件
|
||||||
onExportLogs:()=>void; // 导出日志事件
|
onExportLogs:()=>void; // 导出日志事件
|
||||||
}
|
}
|
||||||
|
|
||||||
const TerminalForm:React.FC<TerminalFormProps>=(props:TerminalFormProps)=> {
|
const TerminalForm:React.FC<TerminalFormProps>=(props:TerminalFormProps)=> {
|
||||||
const {
|
const {
|
||||||
|
ip,
|
||||||
onConnect,
|
onConnect,
|
||||||
onExportLogs,
|
onExportLogs,
|
||||||
}=props;
|
}=props;
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
useEffect(()=>{
|
||||||
|
form.setFieldsValue({
|
||||||
|
ip,
|
||||||
|
});
|
||||||
|
},[])
|
||||||
return (
|
return (
|
||||||
<div className={materialName}>
|
<div className={materialName}>
|
||||||
<h1>连接中心服务器</h1>
|
<h1>连接中心服务器</h1>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
// @ts-nocheck
|
||||||
import React,{useRef,useEffect, useImperativeHandle,
|
import React,{useRef,useEffect, useImperativeHandle,
|
||||||
forwardRef,} from 'react';
|
forwardRef,} from 'react';
|
||||||
import { useWebSocket } from '@zhst/hooks';
|
|
||||||
import { Terminal } from 'xterm';
|
import { Terminal } from 'xterm';
|
||||||
import { FitAddon } from 'xterm-addon-fit';
|
import { FitAddon } from 'xterm-addon-fit';
|
||||||
import 'xterm/css/xterm.css';
|
import 'xterm/css/xterm.css';
|
||||||
@ -56,21 +55,10 @@ const WebTerminal:React.FC<WebTerminalProps&WebsocketOptions>=forwardRef((props:
|
|||||||
onMessage,
|
onMessage,
|
||||||
onError,
|
onError,
|
||||||
}=props;
|
}=props;
|
||||||
const { readyState, sendMessage, latestMessage, disconnect, connect }:WebsocketResult = useWebSocket(
|
const termRef = useRef<React.RefObject<HTMLDivElement>|null>(null);
|
||||||
`${websocketUrl}?ip=${ip}&Authorization=${token}`,{manual:true,reconnectLimit:0,onOpen,
|
const termClassRef=useRef<React.RefObject<HTMLDivElement>|null>(null)
|
||||||
onClose,
|
|
||||||
onMessage,
|
|
||||||
onError}
|
|
||||||
);
|
|
||||||
const termRef = useRef(null);
|
|
||||||
const termClassRef=useRef(null)
|
|
||||||
// const currLine=useRef(null);
|
// const currLine=useRef(null);
|
||||||
|
const wsRef=useRef<React.RefObject<HTMLDivElement>|null>(null);
|
||||||
useEffect(()=>{
|
|
||||||
if(termClassRef?.current){
|
|
||||||
termClassRef.current.write(latestMessage?.data+'\r\n\x1b[33m$\x1b[0m ');
|
|
||||||
}
|
|
||||||
},[latestMessage]);
|
|
||||||
|
|
||||||
// terminal初始化
|
// terminal初始化
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
@ -78,12 +66,12 @@ const WebTerminal:React.FC<WebTerminalProps&WebsocketOptions>=forwardRef((props:
|
|||||||
if(!termRef.current){
|
if(!termRef.current){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
termClassRef.current=new Terminal({
|
let term=new Terminal({
|
||||||
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
rows: Math.ceil(
|
rows: Math.ceil(
|
||||||
(termRef.current?.clientHeight -
|
(termRef.current.clientHeight -
|
||||||
150) /
|
150) /
|
||||||
14,
|
14,
|
||||||
),
|
),
|
||||||
@ -98,12 +86,12 @@ const WebTerminal:React.FC<WebTerminalProps&WebsocketOptions>=forwardRef((props:
|
|||||||
background: "#1a1a1d", //背景色
|
background: "#1a1a1d", //背景色
|
||||||
cursor: "help", //设置光标
|
cursor: "help", //设置光标
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
let term=termClassRef.current;
|
termClassRef.current=term;
|
||||||
term.open(termRef.current);
|
term.open(termRef.current);
|
||||||
term.focus(); // 光标聚集
|
term.focus(); // 光标聚集
|
||||||
term.promp=(_)=>{
|
term.promp=()=>{
|
||||||
term.write('\r\n\x1b[33m$\x1b[0m ');
|
term.write('\r\n\x1b[33m$\x1b[0m ');
|
||||||
}
|
}
|
||||||
const fitAddon=new FitAddon();
|
const fitAddon=new FitAddon();
|
||||||
@ -113,14 +101,44 @@ const WebTerminal:React.FC<WebTerminalProps&WebsocketOptions>=forwardRef((props:
|
|||||||
|
|
||||||
},[]);
|
},[]);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
// @ts-ignore
|
||||||
|
if(ip&&websocketUrl&&token){
|
||||||
|
const ws=new WebSocket(`${websocketUrl}?ip=${ip}&Authorization=${token}`);
|
||||||
|
wsRef.current=ws;
|
||||||
|
if(wsRef?.current){
|
||||||
|
wsRef.current.onopen=(event: WebSocketEventMap['open'], instance: WebSocket)=>{
|
||||||
|
if(onOpen){
|
||||||
|
onOpen(event,instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wsRef.current.onclose=(event: WebSocketEventMap['close'], instance: WebSocket)=>{
|
||||||
|
if(onClose){
|
||||||
|
onClose(event,instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wsRef.current.onmessage=(message: WebSocketEventMap['message'], instance: WebSocket)=>{
|
||||||
|
if(termClassRef.current){
|
||||||
|
termClassRef.current.write(message?.data+'\r\n\x1b[33m$\x1b[0m ');
|
||||||
|
}
|
||||||
|
if(onMessage){
|
||||||
|
onMessage(message,instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wsRef.current.onerror=(event: WebSocketEventMap['error'], instance: WebSocket)=>{
|
||||||
|
if(onError){
|
||||||
|
onError(event,instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
},[ip,websocketUrl,token])
|
||||||
|
|
||||||
// 自定义暴露给父组件的实例
|
// 自定义暴露给父组件的实例
|
||||||
useImperativeHandle(ref,()=>({
|
useImperativeHandle(ref,()=>(
|
||||||
readyState,
|
wsRef.current
|
||||||
sendMessage,
|
));
|
||||||
latestMessage,
|
|
||||||
disconnect,
|
|
||||||
connect
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={materialName}>
|
<div className={materialName}>
|
||||||
|
@ -5,8 +5,8 @@ const demo = () => {
|
|||||||
return (
|
return (
|
||||||
<CenterLink
|
<CenterLink
|
||||||
style={{width:'100%',height:'600px'}}
|
style={{width:'100%',height:'600px'}}
|
||||||
websocketUrl={'ws://10.0.0.7:50051/active'}
|
websocketUrl={'ws://127.0.0.1:50051/active'}
|
||||||
token={'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTM1OTI2NDYsImp0aSI6ImFkbWluIn0._mVU216h0q8va8bZ8PCKfGOKslYJWdRLFvLzUdvGDN4'}
|
token={'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTM4NDAxOTAsImp0aSI6ImFkbWluIn0.KAcgU8r-AIQ2GEu5g2o-YANstqisy5WT6rz5fbMjdcs'}
|
||||||
ip={'127.0.0.1'}
|
ip={'127.0.0.1'}
|
||||||
terminalStyle={{width:'100%',height:'calc(100% - 180px)'}}
|
terminalStyle={{width:'100%',height:'calc(100% - 180px)'}}
|
||||||
onConnect={(values)=>{
|
onConnect={(values)=>{
|
||||||
|
120
packages/meta/src/ButtonList/ButtonList.tsx
Normal file
120
packages/meta/src/ButtonList/ButtonList.tsx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import React, { useState, useMemo, ReactNode, useRef, useEffect, useContext } from 'react';
|
||||||
|
import { Button, Col, Dropdown, Row } from 'antd';
|
||||||
|
import { Gutter } from 'antd/es/grid/row';
|
||||||
|
import { ConfigContext } from '../config-provider';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
|
|
||||||
|
export interface IButtonItem {
|
||||||
|
key: string;
|
||||||
|
text: ReactNode;
|
||||||
|
onClick: () => void;
|
||||||
|
children?: Array<Omit<IButtonItem, 'weight'>>;
|
||||||
|
// 用于排序
|
||||||
|
weight?: number;
|
||||||
|
icon?: ReactNode
|
||||||
|
className?: string;
|
||||||
|
isCenter?: boolean;
|
||||||
|
};
|
||||||
|
export interface IButtonItemWithoutWeight extends Omit<IButtonItem, 'weight'> { }
|
||||||
|
export interface ButtonListProps {
|
||||||
|
prefixCls?: string;
|
||||||
|
buttons: Array<IButtonItem>
|
||||||
|
gutter?: Gutter;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ButtonList: React.FC<ButtonListProps> = (props) => {
|
||||||
|
|
||||||
|
const { getPrefixCls } = useContext(ConfigContext);
|
||||||
|
const { buttons, gutter, prefixCls: customizePrefixCls } = props
|
||||||
|
const componentName = getPrefixCls('button-list', customizePrefixCls);
|
||||||
|
const buttonListRef = useRef<HTMLDivElement>(null);
|
||||||
|
const centerButtonRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [activeKey, setActiveKey] = useState<string | null>(null);
|
||||||
|
const [buttonListWrapMarginLeft, setButtonListWrapMarginLeft] = useState(0)
|
||||||
|
|
||||||
|
const sortedButtons = useMemo(() => {
|
||||||
|
let buttonList = buttons.map((btn, index) => {
|
||||||
|
if (btn.weight === undefined) {
|
||||||
|
return { ...btn, weight: index }
|
||||||
|
} else {
|
||||||
|
return { ...btn, weight: btn.weight }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const newButtonList = buttonList?.sort((a, b) => a.weight - b.weight)
|
||||||
|
return newButtonList;
|
||||||
|
}, [buttons]);
|
||||||
|
|
||||||
|
const handleButtonClick = (key: string, item: IButtonItem) => {
|
||||||
|
setActiveKey(activeKey === key ? null : key);
|
||||||
|
if (!item.children) {
|
||||||
|
item.onClick();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 如果获取不到 需要居中的
|
||||||
|
if (!centerButtonRef) return
|
||||||
|
// 获取 buttonList 宽度
|
||||||
|
const buttonListWidth: number = buttonListRef.current?.offsetWidth as number;
|
||||||
|
// 获取 centerButton 宽度
|
||||||
|
const centerButtonWidth: number = centerButtonRef.current?.offsetWidth as number
|
||||||
|
// 获取 buttonList 相对视口左偏移量
|
||||||
|
const buttonListOffset: number = buttonListRef.current?.offsetLeft as number
|
||||||
|
// 获取 buttonList 中心点 距视口左边距离
|
||||||
|
const buttonListCenterOffset = buttonListOffset + buttonListWidth / 2
|
||||||
|
// 获取 centerButton 中心点 相对视口左偏移量
|
||||||
|
const centerButtonOffset: number = centerButtonRef.current?.offsetLeft as number + centerButtonWidth / 2
|
||||||
|
/*
|
||||||
|
计算 centerButton 修正距离
|
||||||
|
用 buttonList 中心点 距视口左边距离 - centerButton 中心点 相对视口左偏移量
|
||||||
|
作为 buttonListWrap marginLeft 修正距离
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 这里为什么要 * 2 由于 justify-content: center; marginLeft 需要 * 2才是 正确的值 具体原因 还在研究
|
||||||
|
const buttonListWrapMarginLeft = (buttonListCenterOffset - centerButtonOffset) * 2
|
||||||
|
setButtonListWrapMarginLeft(buttonListWrapMarginLeft)
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={componentName} ref={buttonListRef}>
|
||||||
|
<Row gutter={gutter} className={`${componentName}-list-wrap`} style={{ marginLeft: buttonListWrapMarginLeft }} >
|
||||||
|
{sortedButtons?.map((item) => (
|
||||||
|
<Col key={item.key} ref={item?.isCenter ? centerButtonRef : undefined} >
|
||||||
|
{item.children ? (
|
||||||
|
<Dropdown
|
||||||
|
menu={{
|
||||||
|
items: item.children.map((child) => ({
|
||||||
|
key: child.key,
|
||||||
|
label: <span>{child.text}</span>,
|
||||||
|
onClick: () => child.onClick(),
|
||||||
|
})),
|
||||||
|
}}
|
||||||
|
open={activeKey === item.key}
|
||||||
|
onOpenChange={(visible: boolean) => {
|
||||||
|
if (!visible) setActiveKey(null);
|
||||||
|
}}
|
||||||
|
trigger={['click']}
|
||||||
|
>
|
||||||
|
<Button type="text" icon={item.icon} onClick={() => handleButtonClick(item.key, item)}>{item.text}</Button>
|
||||||
|
</Dropdown>
|
||||||
|
) : (
|
||||||
|
<Button type="text"
|
||||||
|
icon={item.icon}
|
||||||
|
onClick={() => handleButtonClick(item.key, item)}
|
||||||
|
>
|
||||||
|
{item.text}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ButtonList;
|
62
packages/meta/src/ButtonList/demo/base.tsx
Normal file
62
packages/meta/src/ButtonList/demo/base.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import React, { useState, useMemo } from 'react';
|
||||||
|
import { Space } from 'antd'
|
||||||
|
import { DownloadOutlined } from '@ant-design/icons';
|
||||||
|
import { ButtonList } from '@zhst/meta'
|
||||||
|
import { ButtonListProps } from '../ButtonList';
|
||||||
|
import "./index.less"
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const [isPlay, setIsPlay] = useState(true);
|
||||||
|
|
||||||
|
const props: ButtonListProps = useMemo(() => {
|
||||||
|
return {
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
key: 'button1',
|
||||||
|
text: '一级按钮1',
|
||||||
|
onClick: () => console.log('一级按钮1被点击'),
|
||||||
|
children: [
|
||||||
|
{ key: 'subButton1-1', text: '二级按钮1-1', onClick: () => console.log('二级按钮1-1被点击'), icon: <DownloadOutlined /> },
|
||||||
|
{ key: 'subButton1-2', text: '二级按钮1-2', onClick: () => console.log('二级按钮1-2被点击') },
|
||||||
|
],
|
||||||
|
// icon:<DownloadOutlined />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'button2',
|
||||||
|
text: '一级按钮2',
|
||||||
|
onClick: () => console.log('一级按钮2被点击'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '播放',
|
||||||
|
text: isPlay ? '播放按钮' : '图片按钮',
|
||||||
|
onClick: () => { setIsPlay(!isPlay) },
|
||||||
|
// isCenter: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'button3',
|
||||||
|
text: '一级按钮3',
|
||||||
|
onClick: () => console.log('一级按钮3被点击'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'button4',
|
||||||
|
text: '一级按钮4',
|
||||||
|
onClick: () => console.log('一级按钮4被点击'),
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'button5',
|
||||||
|
text: '一级按钮5',
|
||||||
|
onClick: () => console.log('一级按钮5被点击'),
|
||||||
|
children: [
|
||||||
|
{ key: 'subButton5-1', text: '二级按钮5-1', onClick: () => console.log('二级按钮5-1被点击') },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}, [isPlay])
|
||||||
|
return (
|
||||||
|
<Space size={[8, 16]} direction="vertical">
|
||||||
|
<ButtonList {...props} />
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
5
packages/meta/src/ButtonList/demo/index.less
Normal file
5
packages/meta/src/ButtonList/demo/index.less
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#buttonlist-demo-base {
|
||||||
|
.dumi-default-previewer-demo>div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
15
packages/meta/src/ButtonList/index.less
Normal file
15
packages/meta/src/ButtonList/index.less
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
.zhst-button-list {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.ant-btn-text:hover{
|
||||||
|
background: none !important ;
|
||||||
|
color: #247fdb !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
19
packages/meta/src/ButtonList/index.md
Normal file
19
packages/meta/src/ButtonList/index.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
group: 通用
|
||||||
|
category: Components
|
||||||
|
subtitle: 按钮列表
|
||||||
|
title: ButtonList 按钮列表组件
|
||||||
|
---
|
||||||
|
|
||||||
|
# ButtonList 按钮列表组件
|
||||||
|
|
||||||
|
<code src="./demo/base.tsx"></code>
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
|
| ---- | ---- | ---- | ------ | ---- |
|
||||||
|
| buttons | 传入按钮列表 |ButtonItem[]| - | - |
|
||||||
|
| customButton | 传入自定义按钮 | ButtonItem | - | - |
|
||||||
|
| gutter | 控制间隙 | Gutter | - | - |
|
||||||
|
|
4
packages/meta/src/ButtonList/index.tsx
Normal file
4
packages/meta/src/ButtonList/index.tsx
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import ButtonList from './ButtonList'
|
||||||
|
|
||||||
|
|
||||||
|
export default ButtonList
|
@ -93,3 +93,4 @@ export { default as App } from './app';
|
|||||||
export type { AppProps } from './app';
|
export type { AppProps } from './app';
|
||||||
export { default as notification } from './notification';
|
export { default as notification } from './notification';
|
||||||
export type { ArgsProps as NotificationArgsProps } from './notification';
|
export type { ArgsProps as NotificationArgsProps } from './notification';
|
||||||
|
export { default as ButtonList } from './ButtonList'
|
||||||
|
@ -99,6 +99,7 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>((props, ref)
|
|||||||
const mergedStyle: React.CSSProperties = { ...segmented?.style, ...style };
|
const mergedStyle: React.CSSProperties = { ...segmented?.style, ...style };
|
||||||
|
|
||||||
return wrapCSSVar(
|
return wrapCSSVar(
|
||||||
|
// @ts-ignore
|
||||||
<RcSegmented
|
<RcSegmented
|
||||||
{...restProps}
|
{...restProps}
|
||||||
className={cls}
|
className={cls}
|
||||||
|
Loading…
Reference in New Issue
Block a user