Merge branch 'develop'

# Conflicts:
#	deploy/build.sh
This commit is contained in:
NICE CODE BY DEV 2024-05-15 09:18:11 +08:00
commit 2310cac125
146 changed files with 3826 additions and 1504 deletions

4
.vscode/launch.json vendored
View File

@ -2,9 +2,9 @@
"configurations": [
{
"type": "chrome",
"name": "http://localhost:8000/metas/big-image-preview",
"name": "lambo",
"request": "launch",
"url": "http://localhost:8000/metas/big-image-preview"
"url": "http://localhost:8000/metas"
}
]
}

View File

@ -1,159 +0,0 @@
---
hero:
title: lambo
description: 致力于提升前端开发效率与规范
actions:
- text: 快速上手
link: /bizs
features:
- title: biz
emoji: 🍑
description: 业务库
- title: hooks
emoji: 💎
description: hooks
- title: func
emoji: 🌈
description: 常用函数库
- title: meta
emoji: ☀️
description: 原子组件库
- title: constants
emoji: 🈶️
description: 静态定义库
- title: request
emoji: 🥣
description: 网络请求库
- title: types
emoji: 🈸
description: typescript 声明库
- title: material
emoji: 🥱
description: 物料库
- title: cli
emoji: 🐔
description: 脚手架
---
## 目录结构
<Tree>
<ul>
<li>
docs
<small>全局文档</small>
<ul>
<li>
index.md
<small>这是首页文档</small>
</li>
</ul>
</li>
<li>
packages
<small>组件包目录</small>
<ul>
<li>
biz
<small>业务组件</small>
</li>
<li>
func
<small>函数库</small>
</li>
<li>
hooks
<small>hooks</small>
</li>
<li>
constants
<small>静态枚举值定义</small>
</li>
<li>
meta
<small>元组件</small>
</li>
<li>
request
<small>请求库</small>
</li>
<li>
types
<small>类型定义库</small>
</li>
<li>
material
<small>物料库</small>
</li>
<li>
cli
<small>脚手架</small>
</li>
</ul>
</li>
<li>
src
<small>这是 src 文件夹</small>
<ul>
<li>
index.md
<small>这是 index.md</small>
</li>
</ul>
</li>
<li>
.dumirc.ts
<small>文档配置</small>
</li>
<li>
package.json
<small>这是 package.json</small>
</li>
</ul>
</Tree>
## 本文档食用说明
目前在进行中的项目为:@zhst/bizs、@zhst/hooks、@zhst/meta、@zhst/func..
bizs: 基于@zhst/hooks、@zhst/meta、@zhst/func 开发,基本贴近于业务。<Badge>doing</Badge>
meta基于 antd 开发,作为公司的定制化原子组件。<Badge>doing</Badge>
hooks基于 ahooks、@zhst/func 定制化二次开发。<Badge>doing</Badge>
func基于 lodash-es 定制化二次开发 (由于 utils 包名被使用了)<Badge>doing</Badge>
## 后续构思
想做一个,基于智慧视通开发场景和业务场景的前端技术流程化方案,希望它能渗透到整个研发的所有流程中。
比如代码规范、git 提交规范、物料库、基于 electron 的前端工具客户端可集成物料库、图片上传小工具、api 自动生成...
:::info{title=@zhst/lint}
lint 工具库包含eslint-config、eslint-plugin、commit-lint
:::
:::info{title=@zhst/metarial}
物料库,可以直接通过 clone npm 仓库的形式生成模板页面,页面没有任何依赖,一个页面就是一个项目。
:::
:::info{title=@zhst/app}
基于 electron 的前端客户端工具初期功能构思方案有文件上传、git 仓库管理、物料库可视化页面一键生成
:::
:::info{title=@zhst/autoapi}
接口一键生成工具
:::
:::info{title=@types/zhst}
类型定义库
:::
:::info{title=@zhst/constants}
静态变量枚举库
:::
:::info{title=@zhst/cli}
基于物料库的脚手架,可以直接通过可视化界面搭建项目,偏向于 lowcode+ 思维
:::

View File

@ -51,7 +51,6 @@
"prettier --parser=typescript --write"
]
},
"dependencies": {},
"devDependencies": {
"@changesets/cli": "^2.27.1",
"@commitlint/cli": "^17.1.2",

View File

@ -1,5 +1,200 @@
# @zhst/biz
## 0.21.5
### Patch Changes
- fix: 修改 zhst/meta
- Updated dependencies
- @zhst/meta@0.20.3
## 0.21.4
### Patch Changes
- zhst/biz、zhst/meta、zhst/material: 修改 od、修改 boxSelectTree
- Updated dependencies
- @zhst/meta@0.20.2
## 0.21.3
### Patch Changes
- zhst/biz: fix: 边框颜色主题色,实时监控窗口有图片和选中,预警记录文字溢出
## 0.21.2
### Patch Changes
- Updated dependencies
- @zhst/meta@0.20.1
## 0.21.1
### Patch Changes
- Updated dependencies
- @zhst/meta@0.20.0
## 0.21.0
### Minor Changes
- feat: 修复之前发版错乱问题
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.13.0
- @zhst/func@0.15.0
- @zhst/icon@0.5.0
- @zhst/meta@0.19.0
## 0.20.1
### Patch Changes
- Updated dependencies
- @zhst/func@0.14.1
- @zhst/hooks@0.12.1
- @zhst/meta@0.18.1
## 0.20.0
### Minor Changes
- feat: 重新发版
### Patch Changes
- fix: zhst/biz 优化实时监控布局
## 0.19.1
### Patch Changes
- zhst/biz: 实时监控预警记录添加滚动条
## 0.19.0
### Minor Changes
- fix: 修复适配问题
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.11.0
- @zhst/func@0.13.0
- @zhst/icon@0.3.0
- @zhst/meta@0.17.0
## 0.18.8
### Patch Changes
- Updated dependencies
- @zhst/func@0.12.0
- @zhst/hooks@0.10.4
- @zhst/meta@0.16.4
## 0.18.7
### Patch Changes
- @zhst/func@0.11.3
- @zhst/hooks@0.10.3
- @zhst/meta@0.16.3
## 0.18.6
### Patch Changes
- biz 优化无限滚动组件、boxselectTree 组件material 修改算法编辑模块
## 0.18.5
### Patch Changes
- zhst/biz 优化 boxselecttree
## 0.18.4
### Patch Changes
- biz-transfer 修改删除时透出
## 0.18.3
### Patch Changes
- zhst/biz - 修改穿梭框事件透出
## 0.18.2
### Patch Changes
- @zhst/func@0.11.2
- @zhst/hooks@0.10.2
- @zhst/meta@0.16.2
## 0.18.1
### Patch Changes
- @zhst/func@0.11.1
- @zhst/hooks@0.10.1
- @zhst/meta@0.16.1
## 0.18.0
### Minor Changes
- zhst/biz:新增摘要列表-无限滚动组件
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.10.0
- @zhst/func@0.11.0
- @zhst/meta@0.16.0
## 0.17.0
### Minor Changes
- 视频添加 OD 框,查看大图首次点击修复
### Patch Changes
- Updated dependencies
- @zhst/meta@0.15.0
- @zhst/func@0.10.2
- @zhst/hooks@0.9.2
## 0.16.1
### Patch Changes
- zhst/biz: fix: 在业务层控制窗口切换状态
## 0.16.0
### Minor Changes
- zhst/meta 大图圈选组件点击选不上 bug 修复,attach 遮挡底部框事件阻止修复
### Patch Changes
- Updated dependencies
- @zhst/meta@0.14.0
## 0.15.0
### Minor Changes
- biz: 视频播放首图添加 od 框
## 0.14.0
### Minor Changes

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/biz",
"version": "0.14.0",
"version": "0.21.5",
"description": "业务库",
"keywords": [
"business",
@ -47,6 +47,7 @@
"antd": "^5.12.5",
"classnames": "^2.5.1",
"dayjs": "^1.11.10",
"rc-util": "^5.38.1"
"rc-util": "^5.38.1",
"react-infinite-scroll-component": "^6.1.0"
}
}

View File

@ -223,11 +223,6 @@ const BigImageModal: React.FC<BigImageModalProps, BigModalRef> = forwardRef((pro
]
}
// TODO: 页面初始化
useEffect(() => {
}, [dataSource]);
// 暴露 ref 实例
useImperativeHandle(ref, () => ({
ref,

View File

@ -17,7 +17,7 @@
:global {
i:hover {
color: #f0f0f0 !important;
color: #fff !important;
}
}

View File

@ -29,7 +29,7 @@ const Navigation: React.FC<{
className
)}
>
<Button type="text" disabled={disabled} onClick={onClick}>
<Button type="text" disabled={disabled} shape='circle' onClick={onClick}>
<IconFont size={28} color={color} icon={prev ? 'icon-qiehuanzuo' : 'icon-qiehuanyou'} />
</Button>
</div>

View File

@ -1,5 +1,5 @@
import React, { useContext } from 'react';
import WindowToggle from './components/WindowToggle';
import React, { useContext, useRef } from 'react';
import WindowToggle, { ISize } from './components/WindowToggle';
import WarningRecordList from './components/WarningRecordList';
import { ConfigProvider } from '@zhst/meta';
import { IRecord } from '../WarningRecordCard';
@ -32,6 +32,10 @@ interface RealTimeMonitorProps {
cardStyle?: React.CSSProperties;
imgStyle?: React.CSSProperties;
largeImageTitle?: string;
size: ISize;
setSize: React.Dispatch<React.SetStateAction<ISize>>
maxRecordCount?: number;
warningImgStyle?: React.CSSProperties;
}
export const RealTimeMonitor: React.FC<RealTimeMonitorProps> = (props) => {
@ -50,16 +54,24 @@ export const RealTimeMonitor: React.FC<RealTimeMonitorProps> = (props) => {
onRecordClick,
selectedRecordId,
isRecordListLoading,
size,
setSize,
maxRecordCount,
warningImgStyle,
} = props
const toggleRef = useRef<HTMLDivElement>()
const componentName = getPrefixCls('biz-real-time-monitor', customizePrefixCls);
return (
<div className={componentName} style={{ display: 'flex' }} >
<WindowToggle
toggleRef={toggleRef}
selectedWindowKey={selectedWindowKey}
dataSource={videoDataSource}
handleWindowClick={handleWindowClick}
handleCloseButtonClick={handleCloseButtonClick}
size={size}
setSize={setSize}
/>
<WarningRecordList
dataSource={warningDataSource}
@ -69,6 +81,9 @@ export const RealTimeMonitor: React.FC<RealTimeMonitorProps> = (props) => {
viewLargerImageModalRef={viewLargerImageModalRef}
isRecordListLoading={isRecordListLoading}
recordListTitle="监控预警记录"
maxHeight={toggleRef.current?.offsetHeight}
maxRecordCount={maxRecordCount}
imgStyle={warningImgStyle}
/>
</div>
);

View File

@ -4,6 +4,7 @@ import { ViewLargerImageModalRef } from '../../../ViewLargerImageModal';
import WarningRecordCard from '../../../WarningRecordCard';
import ViewLargerImageModal from '../../../ViewLargerImageModal';
import { Empty, Space, Spin } from 'antd';
import { pxToRem } from '@zhst/func'
import { LoadingOutlined } from '@ant-design/icons';
import "./index.less"
@ -28,6 +29,8 @@ interface WarningRecordListProps {
cardStyle?: React.CSSProperties;
imgStyle?: React.CSSProperties;
largeImageTitle?: string;
maxHeight?: number;
maxRecordCount?: number;
}
const WarningRecordList: React.FC<WarningRecordListProps> = (props) => {
@ -43,11 +46,12 @@ const WarningRecordList: React.FC<WarningRecordListProps> = (props) => {
style,
cardStyle,
imgStyle,
largeImageTitle
largeImageTitle,
maxHeight,
maxRecordCount = 10
} = props
return (
<div className='zhst-biz-warning-record-list' style={style}>
<div className='zhst-biz-warning-record-list' style={{ maxHeight: `${pxToRem(`${maxHeight}`)}`, ...style }} >
<div className='header'>{recordListTitle}</div>
<div className='body'>
{
@ -58,14 +62,14 @@ const WarningRecordList: React.FC<WarningRecordListProps> = (props) => {
: (dataSource?.length) > 0 ?
<Space direction='vertical' size={10} >
{dataSource?.map((record, index) => {
if (index > 2) return
if (index > maxRecordCount - 1) return
return (<WarningRecordCard
key={record?.id}
record={record}
onRecordClick={(record) => { onRecordClick?.(record) }}
selectedRecordId={selectedRecordId}
cardStyle={{ width: 300, height: 264, ...cardStyle }}
imgStyle={{ width: 280, height: 169, ...imgStyle }}
cardStyle={{ ...cardStyle }}
imgStyle={{ ...imgStyle }}
/>)
}
)}

View File

@ -2,19 +2,21 @@
display: flex;
flex-direction: column;
border-left: solid 1px #00000026;
width: 320px;
min-width: 320px;
.header {
width: 100%;
height: 48px;
background-color: #EFF2F4;
padding: 10px 20px;
padding: 0 20px;
box-sizing: border-box;
line-height: 48px;
}
.body {
padding: 10px;
overflow: hidden;
overflow-y: auto;
overflow-x: hidden;
flex: 1;
}
}

View File

@ -1,35 +1,35 @@
import React, { useState } from 'react';
import React from 'react';
import VideoPlayerCard from '../../../VideoPlayerCard';
import { VideoPlayerCardProps } from '../../../VideoPlayerCard';
import { Segmented } from 'antd';
import { Row, Col, Segmented, theme } from 'antd';
import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
import { theme } from 'antd/lib';
import { pxToRem } from '@zhst/func'
import './index.less'
type Size = 'large' | 'small'
export type ISize = 'large' | 'small'
interface WindowToggleProps {
dataSource?: VideoPlayerCardProps[];
handleWindowClick?: (key?: string) => void;
handleCloseButtonClick?: (key?: string) => void;
selectedWindowKey?: string;
size: ISize;
setSize: React.Dispatch<React.SetStateAction<ISize>>
toggleRef: React.MutableRefObject<any>
}
export const WindowToggle: React.FC<WindowToggleProps> = (props) => {
const { dataSource = [], handleWindowClick, handleCloseButtonClick, selectedWindowKey } = props
const [size, setSize] = useState<Size>("large");
const { dataSource = [], handleWindowClick, handleCloseButtonClick, selectedWindowKey, size = "large", setSize, toggleRef } = props
const { useToken } = theme
const { token } = useToken()
const getLabelStyle = (isSelected: boolean) => ({
padding: "0 11px", background: "#fff",
padding: `0 ${pxToRem("11px")}`, background: "#fff",
...(isSelected ? { background: token.colorPrimary, color: '#fff' } : {}),
});
return (
<div className='zhst-biz-window-toggle'>
<div className='zhst-biz-window-toggle' ref={toggleRef}>
{/* 切换按钮 */}
<div className='header'>
<Segmented
@ -44,25 +44,52 @@ export const WindowToggle: React.FC<WindowToggleProps> = (props) => {
const { windowKey } = dataSource[0]
handleWindowClick?.(windowKey)
}
setSize(value as Size)
setSize(value as ISize)
}}
/>
</div>
<div className='body'>
{
dataSource?.map((item, index) => {
if (size === "large" && index > 0) return
return (
<VideoPlayerCard
key={item.windowKey}
selectedWindowKey={selectedWindowKey}
size={size} {...item}
handleWindowClick={handleWindowClick}
handleCloseButtonClick={handleCloseButtonClick}
/>)
})
}
<Row gutter={[0, 20]} style={{ width: "100%" }} > {/* 设置栅格间距 */}
{
size === "large" ?
<>
{
dataSource?.map((item, index) => { // 仅显示前四个元素,即两行两列
if (index > 0) return null
return (
<Col xs={24} sm={24} md={24} lg={24} xl={24} key={item.windowKey}>
<VideoPlayerCard
key={""}
selectedWindowKey={selectedWindowKey}
size={size}
handleWindowClick={handleWindowClick}
handleCloseButtonClick={handleCloseButtonClick}
{...item}
/>
</Col>)
})
}
</>
: <>
{dataSource?.map((item) => {
return (
<Col xs={24} sm={12} md={12} lg={12} xl={12} className='sm-card' key={item.windowKey}>
<VideoPlayerCard
key={item.windowKey}
selectedWindowKey={selectedWindowKey}
size={size}
handleWindowClick={handleWindowClick}
handleCloseButtonClick={handleCloseButtonClick}
{...item}
/>
</Col>
);
})}</>
}
</Row>
</div>
</div>
);

View File

@ -29,17 +29,16 @@
}
.body {
flex: 1;
width: 100%;
background-color: #E5EAEC;
padding: 10px;
padding: 20px;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
>div {
margin: 10px;
.sm-card:nth-child(odd) {
padding-right: 10px;
}
.sm-card:nth-child(even) {
padding-left: 10px;
}
}
}

View File

@ -1,2 +1,4 @@
import WindowToggle from './WindowToggle'
export default WindowToggle
export default WindowToggle
import type { ISize } from './WindowToggle';
export type { ISize };

View File

@ -3,6 +3,7 @@ import React, { useState } from 'react';
import { IRecord, RealTimeMonitor, VideoPlayerCardProps, useViewLargerImageModal } from '@zhst/biz';
import { videoData, warningData } from './mock';
import { Space } from 'antd';
import { pxToRem } from '@zhst/func';
import dayjs from 'dayjs'
import './index.less'
@ -23,6 +24,8 @@ export default () => {
windowKey: 'forth-window',
}
]
// 控制窗口切换
const [size, setSize] = useState<"large" | "small">('large')
const [videoDataSource, setVideoDataSource] = useState<VideoPlayerCardProps[]>(initialVideoDataSource);
const [warningDataSource, setWarningDataSource] = useState<IRecord[]>();
const [selectedWindowKey, setSelectedWindowKey] = useState<string | undefined>('first-window');
@ -34,7 +37,6 @@ export default () => {
const handleWindowClick = (key?: string) => {
setSelectedWindowKey(key)
}
const clearWindowData = (key?: string) => {
// 当关闭窗口时 也要刷新 右侧 预警记录接口 不要忘记
setVideoDataSource((pre) => {
@ -47,16 +49,13 @@ export default () => {
return newVideoDataSource
})
}
const handleDownloadImg = (imageKey?: string) => {
console.log(imageKey)
// 可以调用 下面 方法关闭弹窗
// viewLargerImageModalRef.current?.handleCancel()
}
const onRecordClick = (record?: IRecord) => {
// 点击的时候把数据 拿过来处理一下传给大图弹框
const { imageKey, warningType, boxId, position, cabietId, warningTime, warningTimestamp, warningTimeFormat = 'YYYY-MM-DD HH:mm:ss' } = record || {}
const { imageKey, warningType, boxId, position, cabietId, warningTime, warningTimestamp, warningTimeFormat = 'YYYY-MM-DD HH:mm:ss', odRect = [] } = record || {}
const formattedDate = warningTimestamp ? dayjs(warningTimestamp).format(warningTimeFormat) : '';
const warningTimeShow = warningTime ? warningTime : formattedDate
//用于渲染右侧的 信息
@ -68,8 +67,7 @@ export default () => {
{ label: '柜子ID', value: cabietId },
]
// 调用这个方法打开弹框
viewLargerImageModalRef?.current?.show({ imageKey: imageKey, warningData: warningData })
viewLargerImageModalRef?.current?.show({ imageKey: imageKey, warningData: warningData, odRect: odRect })
setSelectedRecordId(record?.id)
}
@ -89,7 +87,8 @@ export default () => {
// 模拟 视频数据请求
setTimeout(() => {
// 对后端返回数据进行处理 组装一套符合属性的 数据
const newVideoData: VideoPlayerCardProps = { imageKey: videoData.imageKey, title: videoData.title, }
// videoSrc : videoData.videoSrc
const newVideoData: VideoPlayerCardProps = { imageKey: videoData.imageKey, title: videoData.title, odRect: videoData.odRect }
setVideoDataSource((pre) => {
const newVideoDataSource: VideoPlayerCardProps[] = pre.map((item) => {
// 传给 选中的视频窗口
@ -131,6 +130,7 @@ export default () => {
warningInfo: [`盒子${o.boxId}`, `位置${o.position}`, `柜子ID${o.cabietId}`],
// cabietText: `柜子ID: ${o.cabietId}`,
warningTimestamp: o.warningTimestamp,
odRect: o.odRect
}
})
setWarningDataSource(newWarningDataSource)
@ -151,7 +151,12 @@ export default () => {
selectedRecordId={selectedRecordId}
viewLargerImageModalRef={viewLargerImageModalRef}
isRecordListLoading={isRecordListLoading}
recordListTitle="监控预警记录" />
recordListTitle="监控预警记录"
size={size}
setSize={setSize}
warningImgStyle={{ width: `${pxToRem("280px")}`, height: `${pxToRem("169px")}` }}
/>
<button onClick={() => { mockData() }}></button>
</Space>
)

View File

@ -1,7 +1,14 @@
export const videoData = {
imageKey: 'https://i.yourimageshare.com/lRHiD2UnAT.png',
// videoSrc: 'ws://10.0.0.7:9033/flv/File/test/test_h264_1.mp4.flv?ip=127.0.0.1',
title: `盒子1 点位1`
videoSrc: 'ws://10.0.0.7:9033/flv/File/test/test_h264_1.mp4.flv?ip=127.0.0.1',
title: `盒子1 点位1`,
odRect: [{
"id": "456",
"x": 0.1519352,
"y": 0.2965385,
"w": 0.05185461,
"h": 0.24698898,
}]
}
export const warningData = [
@ -15,6 +22,13 @@ export const warningData = [
// warningTime: "2023-03-01 ",
warningTimestamp: Date.now(),
// warningTimeFormat:"YYYY-MM-DD"
odRect: [{
"id": "456",
"x": 0.1519352,
"y": 0.2965385,
"w": 0.05185461,
"h": 0.24698898,
}]
},
{
imageKey: 'https://i.yourimageshare.com/lRHiD2UnAT.png',
@ -26,6 +40,13 @@ export const warningData = [
// warningTime: "2023-03-01 ",
warningTimestamp: Date.now(),
// warningTimeFormat:"YYYY-MM-DD"
odRect: [{
"id": "456",
"x": 0.1519352,
"y": 0.2965385,
"w": 0.05185461,
"h": 0.24698898,
}]
},
{
imageKey: 'https://i.yourimageshare.com/lRHiD2UnAT.png',
@ -37,6 +58,13 @@ export const warningData = [
// warningTime: "2023-03-01 ",
warningTimestamp: Date.now(),
// warningTimeFormat:"YYYY-MM-DD"
odRect: [{
"id": "456",
"x": 0.1519352,
"y": 0.2965385,
"w": 0.05185461,
"h": 0.24698898,
}]
},
{
imageKey: 'https://i.yourimageshare.com/lRHiD2UnAT.png',
@ -48,6 +76,13 @@ export const warningData = [
// warningTime: "2023-03-01 ",
warningTimestamp: Date.now(),
// warningTimeFormat:"YYYY-MM-DD"
odRect: [{
"id": "456",
"x": 0.1519352,
"y": 0.2965385,
"w": 0.05185461,
"h": 0.24698898,
}]
},
{
imageKey: 'https://i.yourimageshare.com/lRHiD2UnAT.png',
@ -59,6 +94,13 @@ export const warningData = [
// warningTime: "2023-03-01 ",
warningTimestamp: Date.now(),
// warningTimeFormat:"YYYY-MM-DD"
odRect: [{
"id": "456",
"x": 0.1519352,
"y": 0.2965385,
"w": 0.05185461,
"h": 0.24698898,
}]
}
]

View File

@ -1,6 +1,5 @@
import { Card, Space, CardProps, Spin, Button } from 'antd';
import { theme } from 'antd/lib';
import { ConfigProvider, VideoPlayer, type VideoViewRef, } from '@zhst/meta';
import { Card, Space, CardProps, Spin, Button, theme } from 'antd';
import { ConfigProvider, VideoPlayer, CropperImage, type VideoViewRef, } from '@zhst/meta';
import React, { useState, useEffect, ReactNode, useRef, useContext } from 'react';
import { CloseOutlined, LoadingOutlined } from '@ant-design/icons';
import './index.less'
@ -18,44 +17,56 @@ export interface VideoPlayerCardProps {
title?: string | ReactNode
handleCloseButtonClick?: (key?: string) => void;
handleWindowClick?: (key?: string) => void;
odRect?: {
id: string;
x: number;
y: number;
w: number;
h: number;
selectAble?: boolean;
}[]
[key: string]: any
}
export const VideoPlayerCard: React.FC<VideoPlayerCardProps> = (props) => {
const { ConfigContext } = ConfigProvider;
const { getPrefixCls } = useContext(ConfigContext);
const { prefixCls: customizePrefixCls, showType, imageKey, videoSrc, cardProps, isWindowLoading, errorReasonText, size, title, handleCloseButtonClick, handleWindowClick, windowKey, selectedWindowKey = '' } = props;
const { prefixCls: customizePrefixCls, showType, imageKey, videoSrc, cardProps, isWindowLoading, errorReasonText, size, title, handleCloseButtonClick, handleWindowClick, windowKey, selectedWindowKey = '', odRect = [] } = props;
const componentName = getPrefixCls('biz-video-player-card', customizePrefixCls);
const [cardContent, setCardContent] = useState<JSX.Element | null>(null);
const { useToken } = theme
const { token } = useToken()
const videoRef = useRef<VideoViewRef>(null)
const odRectDefault = odRect?.map(rect => ({
...rect,
selectAble: rect.hasOwnProperty('selectAble') ? rect.selectAble : false
}));
const selectedBorderStyle = {
border: `2px solid ${token.colorPrimary}`, boxShadow: " 0px 2px 9px 0px rgba(0,0,0,0.16)"
}
const cardStyle: React.CSSProperties = {
...(size === 'large' ? { height: 931 } : { height: 456, cursor: 'pointer' }),
...(size === 'small' && selectedWindowKey === windowKey ? selectedBorderStyle : {})
};
const videoPlayerCardStyle = size === 'small' ? { width: "calc(50% - 20px)" } : { flex: 1 }
useEffect(() => {
if (!isWindowLoading && (videoSrc || imageKey)) {
let contentElement: JSX.Element | null = null;
if (videoSrc) {
contentElement = (
<VideoPlayer ref={videoRef} url={videoSrc} />
<VideoPlayer ref={videoRef} url={videoSrc} showOD odList={odRectDefault} />
);
videoRef.current?.setShowCrop(true)
} else if (imageKey) {
contentElement = (
<img
alt="首帧图"
src={imageKey}
style={{ width: "100%", height: "100%", display: 'block' }}
/>
<div style={{ width: "100%", height: "100%" }}>
<CropperImage
// editAble={true}
selectAble={false}
odList={odRectDefault}
url={imageKey}
/>
</div>
);
}
setCardContent(contentElement);
@ -63,10 +74,10 @@ export const VideoPlayerCard: React.FC<VideoPlayerCardProps> = (props) => {
setCardContent(null)
}
}, [showType, imageKey, videoSrc, isWindowLoading]);
return (
<div className={componentName} onClick={() => { handleWindowClick?.(windowKey) }} style={videoPlayerCardStyle}>
<div className={componentName} onClick={() => { handleWindowClick?.(windowKey) }} >
<Card
className={`${size === 'large' ? `${componentName}-large` : `${componentName}-small`}`}
title={
<Space style={{ width: "100%", justifyContent: "space-between" }}>
<div>{title}</div>
@ -77,7 +88,7 @@ export const VideoPlayerCard: React.FC<VideoPlayerCardProps> = (props) => {
</div>
</Space>}
style={{ display: "flex", flexDirection: "column", borderRadius: 4, overflow: "hidden", ...cardStyle }}
bodyStyle={{ flex: 1 }}
styles={{ body: { flex: 1 } }}
{...cardProps}
>
{cardContent ? (
@ -85,7 +96,7 @@ export const VideoPlayerCard: React.FC<VideoPlayerCardProps> = (props) => {
{cardContent}
</>
) : (
<div style={{ backgroundColor: '#000', height: '100%', display: 'flex', padding: '20px', boxSizing: 'border-box' }}>
<div className={`${componentName}-error`} >
{
isWindowLoading ?
<div style={{ flex: 1, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>

View File

@ -7,7 +7,7 @@ const mockVideoPlayerCardProps: VideoPlayerCardProps = {
showType: 'image',
videoSrc: 'ws://10.0.0.7:9033/flv/File/test/test_h264_1.mp4.flv?ip=127.0.0.1',
// 如果需要在没有视频时显示图片封面
// imageKey: 'https://i.yourimageshare.com/lRHiD2UnAT.png',
// imageKey : 'https://i.yourimageshare.com/lRHiD2UnAT.png',
size: 'small',
};

View File

@ -1,4 +1,21 @@
.zhst-biz-video-player-card {
&-large {
height: 931px;
}
&-small {
height: 456px;
cursor: pointer
}
&-error {
background-color: #000;
height: 100%;
display: flex;
padding: 20px;
box-sizing: border-box;
}
.ant-card-head {
padding: 0 20px;
}

View File

@ -1,8 +1,7 @@
import React, { useImperativeHandle, useRef, useState, forwardRef, useContext } from 'react';
import { Modal, ModalProps, Space, SpaceProps } from 'antd';
import theme from 'antd/lib/theme';
import { Modal, ModalProps, Space, SpaceProps, theme } from 'antd';
import { DownloadOutlined } from '@ant-design/icons';
import { ConfigProvider,CropperImage} from '@zhst/meta';
import { ConfigProvider, CropperImage } from '@zhst/meta';
import './index.less'
@ -44,8 +43,6 @@ export const ViewLargerImageModal = forwardRef<ViewLargerImageModalRef, ViewLarg
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 { token } = useToken()
const [open, setOpen] = useState<boolean>(false);
@ -85,14 +82,13 @@ export const ViewLargerImageModal = forwardRef<ViewLargerImageModalRef, ViewLarg
{...modalProps}
>
<Space size={0} {...spaceProps}>
<div className={`${componentName}-left-img`}>
<div style={{ width: 789, height: 444, ...imgStyle }}>
<CropperImage
// editAble={true}
odList={odRectDefault}
url={imageKey}
/>
</div>
<div className={`${componentName}-left-img`} style={{ ...imgStyle }}>
<CropperImage
// editAble={false}
// selectAble={false}
odList={odRectDefault}
url={imageKey}
/>
</div>
<div className='right-context'>
{warningData?.map(({ label, value }) => (
@ -101,7 +97,7 @@ export const ViewLargerImageModal = forwardRef<ViewLargerImageModalRef, ViewLarg
{value}
</div>
))}
{imageKey && downloadImg && <div className='img-download' style={{ color: token.colorPrimary }} onClick={() => downloadImg?.(imageKey)} ><DownloadOutlined /><span style={{ paddingLeft: 3 }}>{downloadText}</span></div>}
{imageKey && downloadImg && <div className='img-download' style={{ color: token.colorPrimary }} onClick={() => downloadImg?.(imageKey)} ><DownloadOutlined /><span className='img-download-text'>{downloadText}</span></div>}
</div>
</Space>
</Modal>

View File

@ -17,7 +17,7 @@ const backEndData = [
warningTimestamp: Date.now(),
// warningTimeFormat:"YYYY-MM-DD"
odRect: [{
// "id": "456",
"id": "456",
"x": 0.6519352,
"y": 0.2965385,
"w": 0.05185461,
@ -35,7 +35,7 @@ const backEndData = [
warningTimestamp: Date.now(),
// warningTimeFormat:"YYYY-MM-DD"
odRect: [{
// "id": "456",
"id": "456",
"x": 0.1519352,
"y": 0.2965385,
"w": 0.05185461,
@ -71,7 +71,6 @@ export default () => {
const viewLargerImageModalRef = useViewLargerImageModal()
const handleDownloadImg = () => {
console.log('download')
// 可以调用 下面 方法关闭弹窗
// viewLargerImageModalRef.current?.handleCancel()
}

View File

@ -1,6 +1,11 @@
.zhst-biz-warning-larger-image {
font-family: MicrosoftYaHei;
&-left-img {
width: 789px;
height: 444px;
}
.ant-modal-content {
padding: 0;
height: 492px;
@ -52,6 +57,10 @@
position: absolute;
bottom: 0;
cursor: pointer;
.img-download-text {
padding-left: 3px;
}
}
}

View File

@ -19,7 +19,7 @@ title: ViewLargerImageModal 查看大图弹窗
| --- | --- | --- | --- | --- |
| show() |通过 ref 用于开启弹窗 可以将点击的 记录传给弹窗| | | |
| handleCancel() | 通过 ref 用于关闭弹窗 | | | |
| imageKey | 图片地址 |string | | |
| imageKey | 图片地址 |string | | |
| contextData | 大图显示的数据 | | | |
| imgStyle | 用于修改图片样式 | | | |
| downloadImg | 传入下载图片的方法 | | | |

View File

@ -1,9 +1,7 @@
import { Card, Space, Divider, CardProps } from 'antd';
import { theme } from 'antd/lib';
import { Card, Space, Divider, CardProps, theme } from 'antd';
import React, { useContext } from 'react';
import dayjs from 'dayjs';
import { ConfigProvider,CropperImage} from '@zhst/meta';
import { ConfigProvider, CropperImage } from '@zhst/meta';
import './index.less'
export interface IRecord {
@ -55,7 +53,7 @@ export interface IRecord {
od框
*/
odRect?: {
id?: string;
id: string;
x: number;
y: number;
w: number;
@ -77,13 +75,11 @@ export interface WarningRecordCardProps {
};
export const WarningRecordCard: React.FC<WarningRecordCardProps> = (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 formattedDate = warningTimestamp ? dayjs(warningTimestamp).format(warningTimeFormat) : '';
const warningTimeShow = warningTime ? warningTime : formattedDate
@ -99,42 +95,40 @@ export const WarningRecordCard: React.FC<WarningRecordCardProps> = (props) => {
const selectedCardStyle: React.CSSProperties = {
...(selectedRecordId === record?.id ? selectedBorderStyle : {})
};
const handleClick = () => {
onRecordClick?.(record);
};
return (
<div className={componentName} key={id} onClick={handleClick} style={style}>
<Card
cover={
<div style={{ width: 336, height: 203, ...imgStyle }}>
<CropperImage
// editAble={true}
odList={odRectDefault}
url={imageKey}
/>
</div>
}
style={{ width: 356, height: 302, padding: 10, borderRadius: 4, ...selectedCardStyle, ...cardStyle }}
className={`${componentName}-card`}
style={{ ...selectedCardStyle, ...cardStyle }}
{...cardProps}
>
<div className={`${componentName}-left-context`}>
<div className={`${componentName}-left-context-warning-type`}>{warningType}</div>
<Space size={0} split={<Divider type="vertical" />}>
{warningInfo?.map((item, index) => (
<div key={index} className="info-item">
{item}
</div>
))}
</Space>
<div className={`${componentName}-left-context-warning-time`}>{warningTimeShow}</div>
<div className={`${componentName}-card-img`} style={{ ...imgStyle }}>
<CropperImage
// 无法触发 图片点击时间需要 加 selectAble
selectAble={false}
odList={odRectDefault}
url={imageKey}
/>
</div>
<div className={`${componentName}-cabietInfo`} >{cabietText}</div>
<div style={{ display: 'flex' }}>
<div className={`${componentName}-left-context`}>
<div className={`${componentName}-left-context-warning-type`}>{warningType}</div>
<Space size={0} split={<Divider type="vertical" />}>
{warningInfo?.map((item, index) => (
<div key={index} className="info-item">
{item}
</div>
))}
</Space>
<div className={`${componentName}-left-context-warning-time`}>{warningTimeShow}</div>
</div>
<div className={`${componentName}-cabietInfo`} >{cabietText}</div>
</div>
</Card>
</div>
);

View File

@ -16,7 +16,7 @@ const backEndData = [
warningTimestamp: Date.now(),
// warningTimeFormat:"YYYY-MM-DD"
odRect: [{
// "id": "123",
"id": "123",
"x": 0.5519352,
"y": 0.2965385,
"w": 0.05185461,
@ -34,7 +34,7 @@ const backEndData = [
warningTimestamp: Date.now(),
// warningTimeFormat:"YYYY-MM-DD"
odRect: [{
// "id": "456",
"id": "456",
"x": 0.1519352,
"y": 0.2965385,
"w": 0.05185461,

View File

@ -1,24 +1,32 @@
.zhst-biz-warning-record-card {
cursor: pointer;
.ant-card-body {
padding: 0;
font-family: MicrosoftYaHei;
line-height: 19px;
display: flex;
margin-top: 10px;
}
.ant-card-bordered {
border: 2px solid #f0f0f0;
}
&-cover-img img {
width: 336px;
height: 203px;
border-radius: 0,
&-card {
border-radius: 4px;
max-width: 380px;
&-img {
width: 356px ;
height: 203px;
margin-bottom: 10px;
}
.ant-card-body {
padding: 10px;
font-family: MicrosoftYaHei;
line-height: 19px;
display: flex;
flex-direction: column;
}
}
&-left-context {
flex: 1;

View File

@ -16,7 +16,7 @@ title: WarningRecordCard 预警记录卡片
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| imageKey | 图片src | string | - | - |
| imageKey | 图片src | string | - | - |
| id | 数据的唯一id 用于key 传值| string | - | - |
| warningType | 预警类型 | string | - | - |
| warningInfo | 盒子 点位 柜子 等信息 | string[] | - | - |

View File

@ -1,13 +1,18 @@
import React, { FC } from 'react';
import React, { FC, useContext } from 'react';
import { Tabs, TabsProps } from 'antd'
import { ConfigProvider } from '@zhst/meta';
import BoxPanel from './components/boxPanel';
import type { BoxPanelProps } from './components/boxPanel';
import './index.less'
import classNames from 'classnames';
export interface BoxSelectTreeProps extends BoxPanelProps {
onTabChange?: (e: any) => void
tabsProps?: TabsProps
prefixCls?: string;
}
const { ConfigContext } = ConfigProvider
const BoxSelectTree: FC<BoxSelectTreeProps> = (props) => {
const {
data,
@ -28,69 +33,52 @@ const BoxSelectTree: FC<BoxSelectTreeProps> = (props) => {
customImport,
showOptions,
extraBtns,
prefixCls: customizePrefixCls
} = props
const { getPrefixCls } = useContext(ConfigContext);
const componentName = getPrefixCls('biz-box-select-tree', customizePrefixCls);
const items: TabsProps['items'] = [
{
key: '1',
label: <div style={{ textAlign:'center', width: '160px' }} ></div>,
children: (
<BoxPanel
searchInputProps={searchInputProps}
boxDataSource={boxDataSource}
treeProps={treeProps}
data={data}
onCreate={onCreate}
onCreateSubmit={onCreateSubmit}
onBoxBatchDelete={onBoxBatchDelete}
onBoxDelete={onBoxDelete}
onSearch={onSearch}
onItemCheck={onItemCheck}
onItemSelect={onItemSelect}
showOptions={showOptions}
customImport={customImport}
extraBtns={extraBtns}
onClockClick={onClockClick}
onImport={onImport}
/>
)
label: <div className={classNames(componentName + '-tab')} style={{ textAlign:'center' }} ></div>,
},
{
key: '2',
label: <div style={{ textAlign:'center', width: '160px' }} ></div>,
children: (
<BoxPanel
searchInputProps={searchInputProps}
boxDataSource={boxDataSource}
treeProps={treeProps}
data={data}
onCreate={onCreate}
onCreateSubmit={onCreateSubmit}
onBoxBatchDelete={onBoxBatchDelete}
onBoxDelete={onBoxDelete}
onSearch={onSearch}
onItemCheck={onItemCheck}
onItemSelect={onItemSelect}
showOptions={showOptions}
customImport={customImport}
extraBtns={extraBtns}
onClockClick={onClockClick}
onImport={onImport}
/>
)
label: <div className={classNames(componentName + '-tab')} style={{ textAlign:'center' }} ></div>,
},
];
return (
<Tabs
defaultActiveKey="1"
centered
items={items}
onChange={onTabChange}
tabBarGutter={0}
indicator={{ size: (origin) => origin, align: 'center' }}
{...tabsProps}
/>
<div className={componentName}>
<Tabs
defaultActiveKey="1"
centered
items={items}
onChange={onTabChange}
tabBarGutter={0}
indicator={{ size: (origin) => origin, align: 'center' }}
{...tabsProps}
/>
<BoxPanel
searchInputProps={searchInputProps}
boxDataSource={boxDataSource}
treeProps={treeProps}
data={data}
onCreate={onCreate}
onCreateSubmit={onCreateSubmit}
onBoxBatchDelete={onBoxBatchDelete}
onBoxDelete={onBoxDelete}
onSearch={onSearch}
onItemCheck={onItemCheck}
onItemSelect={onItemSelect}
showOptions={showOptions}
customImport={customImport}
extraBtns={extraBtns}
onClockClick={onClockClick}
onImport={onImport}
/>
</div>
);
};

View File

@ -0,0 +1,36 @@
.zhst-biz-box-select-tree-panel {
&-search {
display: flex;
padding: 0 12px;
&-input {
margin-right: 4px;
}
&-btns {
flex: none;
}
}
&-btns {
padding: 6px 12px;
display: flex;
justify-content: space-between;
&-common {
padding: 4px 8px;
}
&-import {
padding: 4px 8px;
}
&-divider {
margin: 8px 0;
}
}
&-tree {
padding: 6px 0;
}
}

View File

@ -1,11 +1,14 @@
import React, { FC, useState, useRef } from 'react';
import React, { FC, useState, useRef, useContext } from 'react';
import{ Button, Divider, Input, Space, TreeDataNode } from 'antd'
import { ModalForm, ModalFormProps, ProFormInstance, ProFormText } from '@ant-design/pro-components'
import { ConfigProvider } from '@zhst/meta';
import { ClockCircleOutlined, CloseCircleOutlined, DiffOutlined, FolderAddOutlined, ImportOutlined, SwitcherOutlined } from '@ant-design/icons'
import type { TreeProps, InputProps } from 'antd';
import type { BoxTreeProps } from '../../../tree';
import TreeTransferModal from '../../../treeTransferModal'
import BoxTree from '../../../tree';
import './index.less'
import classNames from 'classnames';
export interface BoxPanelProps {
searchInputProps?: InputProps
@ -26,8 +29,11 @@ export interface BoxPanelProps {
onCreate?: () => void
customImport?: any
extraBtns?: any
prefixCls?: string;
}
const { ConfigContext } = ConfigProvider
const BoxPanel: FC<BoxPanelProps> = (props) => {
const {
searchInputProps,
@ -46,8 +52,12 @@ const BoxPanel: FC<BoxPanelProps> = (props) => {
onBatch,
onCreate,
boxDataSource,
prefixCls: customizePrefixCls,
customImport
} = props
const { getPrefixCls } = useContext(ConfigContext);
const componentName = getPrefixCls('biz-box-select-tree-panel', customizePrefixCls);
const [isTreeCheckable, setIsTreeCheckable] = useState(false)
const [targetItems, setTargetItems] = useState<TreeDataNode[]>([]);
const [boxChoiceOpen, setBoxChoiceOpen] = useState(false)
@ -104,7 +114,7 @@ const BoxPanel: FC<BoxPanelProps> = (props) => {
}
return (
<div style={{ padding: '0 16px' }}>
<div className={componentName}>
{/* 盒子选择弹框 */}
<TreeTransferModal
open={boxChoiceOpen}
@ -118,102 +128,109 @@ const BoxPanel: FC<BoxPanelProps> = (props) => {
onTreeCheck={onTreeCheck} // 树check选中事件
onItemDelete={onItemDelete} // 右侧点击删除事件
/>
<Space size={12} direction='vertical' style={{ width: '100%' }}>
<Space size={4} style={{ width: '100%', justifyContent: 'space-between' }} >
<Input size='middle' onChange={(e) => onSearch?.(e)} placeholder='请输入盒子名称' {...searchInputProps} />
{customImport || (
<>
<Button type="text" onClick={() => onBatch?.() || handleCheckable()} icon={isTreeCheckable ? <SwitcherOutlined /> : <DiffOutlined />} />
<Button type="text" onClick={() => onClockClick?.()} icon={<ClockCircleOutlined />} />
</>
)}
</Space>
{/* 是否显示操作按钮 */}
{showOptions && (
<>
<Space align='center'>
<Button type='text' style={{ padding: '4px 8px' }} onClick={() => onImport?.()} icon={<ImportOutlined />} ></Button>
<Divider type="vertical" style={{ margin: '8px 0' }} />
{onCreate ?
(
<Button onClick={onCreate} type='text' style={{ padding: '4px 8px' }} icon={<FolderAddOutlined />} ></Button>
) : (
<ModalForm<{
name: string
boxList?: any[]
}>
width={'600px'}
open={onCreate ? false : undefined}
formRef={createFormRef}
title="新建组"
modalProps={{ destroyOnClose: true }}
layout='horizontal'
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
trigger={<Button type='text' style={{ padding: '4px 8px' }} icon={<FolderAddOutlined />} ></Button>}
submitter={{
searchConfig: {
submitText: '确定',
resetText: '取消',
},
}}
onFinish={onCreateSubmit}
>
<ProFormText
rules={[
{
required: true,
max: 20,
},
{
pattern: /^[^\s]*$/g,
message: '禁止输入空格'
}
]}
fieldProps={{ showCount: true }}
width="md"
name="name"
label="盒子组名称"
placeholder="请输入盒子名称"
/>
<ProFormText
width="md"
name="boxList"
label="盒子选择"
fieldProps={{
readOnly: true,
value: `已选择${createFormRef.current?.getFieldValue('boxList')?.length || 0}个盒子`,
suffix: (
<Space>
<a onClick={() => {
createFormRef.current?.setFieldValue('boxList', null)
onBoxChoiceReset()
}} ></a>
<a onClick={() => setBoxChoiceOpen(true)}></a>
</Space>
)
}}
/>
</ModalForm>
)
}
<Divider type="vertical" style={{ margin: '8px 0' }} />
{/* @ts-ignore */}
<Button danger type='text' style={{ padding: '4px 8px' }} icon={<CloseCircleOutlined />} disabled={treeProps?.checkedKeys?.length <= 0} onClick={onBoxBatchDelete} ></Button>
</Space>
<Divider style={{ margin: 0 }} />
</>
)}
{extraBtns}
<BoxTree
treeCheckable={isTreeCheckable}
data={data}
onItemSelect={onItemSelect}
onItemCheck={onItemCheck}
onItemDelete={onBoxDelete}
{...treeProps}
<div className={classNames(componentName + '-search')}>
<Input
className={classNames(componentName + '-search-input')}
size='middle'
onChange={(e) => onSearch?.(e)}
placeholder='请输入盒子名称'
{...searchInputProps}
/>
</Space>
{customImport || (
<div
className={classNames(componentName + '-search-btns')}
>
<Button type="text" onClick={() => onBatch?.() || handleCheckable()} icon={isTreeCheckable ? <SwitcherOutlined /> : <DiffOutlined />} />
<Button type="text" onClick={() => onClockClick?.()} icon={<ClockCircleOutlined />} />
</div>
)}
</div>
{/* 是否显示操作按钮 */}
{showOptions && (
<>
<div className={classNames(componentName + '-btns')}>
<Button className={classNames(componentName + '-btns-common')} type='text' onClick={() => onImport?.()} icon={<ImportOutlined />} ></Button>
<Divider className={classNames(componentName + '-btns-divider')} type="vertical" />
{onCreate ?
(
<Button className={classNames(componentName + '-btns-common')} onClick={onCreate} type='text' icon={<FolderAddOutlined />} ></Button>
) : (
<ModalForm<{
name: string
boxList?: any[]
}>
className={classNames(componentName + '-create-modal')}
open={onCreate ? false : undefined}
formRef={createFormRef}
title="新建组"
modalProps={{ destroyOnClose: true }}
layout='horizontal'
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
trigger={<Button type='text' className={classNames(componentName + '-btns-common')} icon={<FolderAddOutlined />} ></Button>}
submitter={{
searchConfig: {
submitText: '确定',
resetText: '取消',
},
}}
onFinish={onCreateSubmit}
>
<ProFormText
rules={[
{
required: true,
max: 20,
},
{
pattern: /^[^\s]*$/g,
message: '禁止输入空格'
}
]}
fieldProps={{ showCount: true }}
width="md"
name="name"
label="盒子组名称"
placeholder="请输入盒子名称"
/>
<ProFormText
width="md"
name="boxList"
label="盒子选择"
fieldProps={{
readOnly: true,
value: `已选择${createFormRef.current?.getFieldValue('boxList')?.length || 0}个盒子`,
suffix: (
<Space>
<a onClick={() => {
createFormRef.current?.setFieldValue('boxList', null)
onBoxChoiceReset()
}} ></a>
<a onClick={() => setBoxChoiceOpen(true)}></a>
</Space>
)
}}
/>
</ModalForm>
)
}
<Divider className={classNames(componentName + '-btns-divider')} type="vertical" />
{/* @ts-ignore */}
<Button className={classNames(componentName + '-btns-common')} danger type='text' icon={<CloseCircleOutlined />} disabled={treeProps?.checkedKeys?.length <= 0} onClick={onBoxBatchDelete} ></Button>
</div>
<Divider style={{ margin: 0 }} />
</>
)}
{extraBtns}
<BoxTree
className={classNames(componentName + '-tree')}
treeCheckable={isTreeCheckable}
data={data}
onItemSelect={onItemSelect}
onItemCheck={onItemCheck}
onItemDelete={onBoxDelete}
{...treeProps}
/>
</div>
)
}

View File

@ -56,6 +56,7 @@ const demo = () => {
addonBefore: (
<Select
value={searchType}
dropdownMatchSelectWidth={false}
onChange={_type => {
setSearchType(_type)
setSearchVal('')

View File

@ -0,0 +1,5 @@
.zhst-biz-box-select-tree {
&-tab {
width: 160px;
}
}

View File

@ -17,4 +17,5 @@ export { default as ViewLargerImageModal, useViewLargerImageModal } from './View
export type { VideoPlayerCardProps } from './VideoPlayerCard'
export { default as VideoPlayerCard } from './VideoPlayerCard'
export { default as RealTimeMonitor } from './RealTimeMonitor'
export { default as InfiniteList } from './infiniteList'
export type { InfiniteListProps, InfiniteListRefProps } from './infiniteList'

View File

@ -0,0 +1,129 @@
/**
* Created by jiangzhixiong
*/
import React, { forwardRef, ReactNode, useContext, useImperativeHandle, useRef } from 'react'
import { ConfigProvider } from '@zhst/meta';
import { Divider, Flex } from 'antd';
import classNames from 'classnames';
import InfiniteScroll from 'react-infinite-scroll-component';
import { SearchCard, SearchCardProps } from './components';
import './index.less'
import { Idata } from './components/SearchCard';
const { ConfigContext } = ConfigProvider
export interface InfiniteListProps {
type?: 'custom' | 'auto'
prefixCls?: string;
height?: number;
itemRender?: (data?: any) => React.ReactNode
loading?: boolean; //
data: Idata[];
targetId?: string; // 滚动列表 ID
loadMore?: (data?: any) => any;
params?: {
[key: string]: any;
}
hasMore: boolean;
endMessage?: ReactNode
loadingMessage?: ReactNode
onItemClick?: (data: any) => void;
searchCardProps?: SearchCardProps
}
export interface InfiniteListRefProps {
}
const InfiniteList = forwardRef<InfiniteListRefProps, InfiniteListProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
height,
type = 'auto',
loadingMessage = <p style={{ textAlign: 'center' }}>...</p>,
targetId = 'scrollableDiv',
itemRender,
hasMore,
onItemClick,
loadMore,
data = [],
endMessage = <Divider plain>...🤐</Divider>,
searchCardProps
} = props
const { getPrefixCls } = useContext(ConfigContext);
const componentName = getPrefixCls('biz-infinite-list', customizePrefixCls);
const listRef = useRef<HTMLDivElement>(null);
useImperativeHandle(ref, () => ({
}))
return (
<div
id={targetId}
className={classNames(componentName)}
ref={listRef}
style={{
height,
overflow: 'auto',
padding: 12
}}
>
{/* {loading ? (
<p>...</p>
) : (
<Flex wrap='wrap' gap="small" className={classNames(componentName + 'items')}>
{data?.list?.map((item) => (
itemRender?.(item) || (
<div className={classNames(componentName + 'items-item')}>
<SearchCard data={item} />
</div>
)
))}
</Flex>
)} */}
<InfiniteScroll
dataLength={data.length}
next={type === 'auto' ? loadMore! : () => {}}
hasMore={hasMore}
loader={loadingMessage}
endMessage={endMessage}
scrollableTarget={targetId}
>
<Flex wrap='wrap' gap="small" className={classNames(componentName + 'items')}>
{data?.map((item, idx) => (
itemRender?.(item) || (
<div
key={idx}
className={classNames(componentName + 'items-item')}
onClick={() => {
onItemClick?.(item)
}}
>
<SearchCard
id={idx + 1}
data={item}
width="184px"
{...searchCardProps}
/>
</div>
)
))}
</Flex>
</InfiniteScroll>
{/* <div style={{ marginTop: 8 }}>
{!noMore && (
<Button onClick={loadMore} disabled={loadingMore}>
{loadingMore ? '加载中...' : '点击加载更多'}
</Button>
)}
{noMore && <span></span>}
</div> */}
</div>
)
})
export default InfiniteList

View File

@ -0,0 +1,105 @@
/**
* Created by jiangzhixiong on 2024/04/28
*/
import React, { forwardRef, useContext, useImperativeHandle } from 'react'
import { ConfigProvider, EMPTY_BASE64 } from '@zhst/meta'
import { Flex, Image } from 'antd';
import './index.less'
const { ConfigContext } = ConfigProvider
export interface Idata {
id?: string | number;
url?: string;
sort?: number;
title?: string;
subtitle?: string;
}
export interface SearchCardProps extends Idata {
prefixCls?: string;
data?: Idata
width?: string;
height?: string;
onCreateTxt?: string;
onCreate?: (data: any) => void;
onAddTxt?: string;
onAdd?: (data: any) => void;
onRemoveTxt?: string;
onRemove?: (data: any) => void;
customOptionRender?: React.ReactNode
}
export interface SearchCardRefProps {
}
const SearchCard = forwardRef<SearchCardRefProps, SearchCardProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
url,
id,
title,
subtitle,
sort,
data,
onCreate,
onCreateTxt = '创建检索',
onAddTxt = '添加目标',
onRemoveTxt = '移除轨迹',
onAdd,
onRemove,
customOptionRender,
width = '184px',
height = '100%'
} = props
const { getPrefixCls } = useContext(ConfigContext)
const componentName = getPrefixCls('biz-search-card', customizePrefixCls);
const stopBumble = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, fn?: ((data: Idata) => void), data?: Idata) => {
e.stopPropagation()
fn?.(data!)
}
useImperativeHandle(ref, () => ({
}))
return (
<div
className={componentName}
style={{
width,
height
}}
>
<div className={`${componentName}-main`}>
<i className={`${componentName}-main-num`}>{id || sort}</i>
<Image
className={`${componentName}-main-img`}
src={url || data?.url}
height={'240px'}
preview={false}
fallback={EMPTY_BASE64}
/>
<Flex align='center' justify='space-between' className={`${componentName}-main-opt`}>
{customOptionRender || (
<>
<a onClick={(e) => stopBumble(e, onCreate, data)}>{onCreateTxt}</a>
|
<a onClick={(e) => stopBumble(e, onAdd, data)}>{onAddTxt}</a>
|
<a onClick={(e) => stopBumble(e, onRemove, data)}>{onRemoveTxt}</a>
</>
)}
</Flex>
</div>
<div className={`${componentName}-footer`}>
<p className={`${componentName}-footer-tit`}>{title || data?.title}</p>
<p className={`${componentName}-footer-subtitle`}>{subtitle || data?.subtitle}</p>
</div>
</div>
)
})
export default SearchCard

View File

@ -0,0 +1,81 @@
.zhst-biz-search-card {
border: 2px solid transparent;
transition: .1s ease-in all;
cursor: pointer;
&:hover {
border: 2px solid #09f;
.zhst-biz-search-card-main-opt {
display: flex;
}
}
&-main {
position: relative;
&-num {
position: absolute;
right: 2px;
top: 2px;
width: 20px;
height: 20px;
line-height: 20px;
font-style: normal;
text-align: center;
font-size: 12px;
color: rgb(153, 153, 153);
background-color: rgba(255, 255, 255, 75%);
z-index: 1;
border-radius: 3px;
}
&-img {
width: 100%;
height: 240px;
}
&-opt {
display: none;
position: absolute;
padding: 6px 3px;
left: 0;
width: 100%;
bottom: 0;
font-size: 12px;
background-color: #09f;
color: #fff;
box-sizing: border-box;
transition: .2s ease-in all;
a {
color: #fff;
&:hover {
opacity: 0.9;
}
}
}
}
&-footer {
padding: 6px;
font-size: 12px;
text-align: center;
&-tit {
margin: 0;
line-height: 20px;
}
&-subtitle {
margin: 0;
line-height: 20px;
transition: .1s ease-in all;
&:hover {
color: #09f;
}
}
}
}

View File

@ -0,0 +1,6 @@
/**
* Created by jiangzhixiong on 2024/04/28
*/
export { default as SearchCard } from './SearchCard'
export type { SearchCardProps, SearchCardRefProps } from './SearchCard'

View File

@ -0,0 +1,50 @@
import React, { useEffect, useState } from 'react'
import { InfiniteList } from '@zhst/biz'
export default () => {
const [data, setData] = useState([])
const [loading, setLoading] = useState(false)
const loadMoreData = () => {
if (loading) {
return;
}
setLoading(true);
fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo')
.then((res) => res.json())
.then((body) => {
let res = body.results.map(o => {
return {
title: o.name.first,
subtitle: o.name.last,
url: o.picture.large
}
})
setData([...data, ...res]);
setLoading(false);
})
.catch(() => {
setLoading(false);
});
};
useEffect(() => {
loadMoreData();
}, []);
return (
<InfiniteList
loading={loading}
loadMore={loadMoreData}
height={300}
hasMore={data.length < 100}
data={data}
onItemClick={_data => console.log('item点击', _data)}
searchCardProps={{
onAdd: (_data) => console.log('新增', _data),
onCreate: (_data) => console.log('创建', _data),
onRemove: (_data) => console.log('删除', _data),
}}
/>
)
}

View File

@ -0,0 +1,58 @@
import React, { useEffect, useState } from 'react'
import { InfiniteList } from '@zhst/biz'
import { Button, Input, Space } from 'antd'
export default () => {
const [data, setData] = useState([])
const [loading, setLoading] = useState(false)
const [params, setParams] = useState({})
const loadMoreData = (params?: { name: string; age?: number; sex: string; tel: number }) => {
if (loading) {
return;
}
setLoading(true);
fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo')
.then((res) => res.json())
.then((body) => {
let res = body.results.map(o => {
return {
title: o.name.first,
subtitle: o.name.last,
url: o.picture.large
}
})
setData([...data, ...res]);
setLoading(false);
})
.catch(() => {
setLoading(false);
});
};
useEffect(() => {
loadMoreData();
}, []);
return (
<Space direction='vertical'>
<Space>
<Input placeholder='名称' onChange={(e) => setParams(pre => ({ ...pre, name: e.target.value }))} style={{ width: '120px' }} />
<Input placeholder='年龄' onChange={(e) => setParams(pre => ({ ...pre, age: e.target.value }))} style={{ width: '120px' }} />
<Input placeholder='性别' onChange={(e) => setParams(pre => ({ ...pre, sex: e.target.value }))} style={{ width: '120px' }} />
<Input placeholder='手机号' onChange={(e) => setParams(pre => ({ ...pre, tel: e.target.value }))} style={{ width: '120px' }} />
<Button onClick={() => loadMoreData(params)}></Button>
</Space>
<InfiniteList
loading={loading}
loadMore={() => loadMoreData(params)}
height={300}
hasMore={data.length < 100}
data={data}
type="custom"
loadingMessage={<Button onClick={() => loadMoreData(params)}></Button>}
onItemClick={data => console.log('item点击', data)}
/>
</Space>
)
}

View File

@ -0,0 +1,72 @@
import React, { useEffect, useState } from 'react'
import { InfiniteList } from '@zhst/biz'
import { Button, Input, Space } from 'antd'
interface IData { name: string; age?: number; sex: string; tel: number }
export default () => {
const [data, setData] = useState<any>([])
const [loading, setLoading] = useState(false)
const [params, setParams] = useState<IData>({
name: '',
tel: 123,
age: 12,
sex: '男'
})
const loadMoreData = (params?: IData) => {
if (loading) {
return;
}
setLoading(true);
fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo', {
})
.then((res) => res.json())
.then((body) => {
setData([...data, ...body.results]);
setLoading(false);
})
.catch(() => {
setLoading(false);
});
};
useEffect(() => {
loadMoreData();
}, []);
return (
<Space direction='vertical'>
<Space>
<Input placeholder='名称' onChange={(e) => setParams(pre => ({ ...pre, name: e.target.value }))} style={{ width: '120px' }} />
<Input placeholder='年龄' onChange={(e) => setParams(pre => ({ ...pre, age: Number(e.target.value) }))} style={{ width: '120px' }} />
<Input placeholder='性别' onChange={(e) => setParams(pre => ({ ...pre, sex: e.target.value }))} style={{ width: '120px' }} />
<Input placeholder='手机号' onChange={(e) => setParams(pre => ({ ...pre, tel: Number(e.target.value) }))} style={{ width: '120px' }} />
<Button onClick={() => loadMoreData(params)}></Button>
</Space>
<InfiniteList
loading={loading}
loadMore={() => loadMoreData(params)}
height={300}
hasMore={data.length < 100}
data={data}
type="custom"
loadingMessage={<Button onClick={() => loadMoreData(params)}></Button>}
itemRender={() => {
return (
<div
style={{
width: '200px',
height: '100px',
border: '1px solid #000',
boxSizing: 'border-box'
}}>
</div>
)
}}
onItemClick={data => console.log('item点击', data)}
/>
</Space>
)
}

View File

@ -0,0 +1,17 @@
const resultData = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13'];
export function getLoadMoreList(nextId: string | undefined, limit: number): Promise<any> {
let start = 0;
const end = start + limit;
const list = resultData.slice(start, end);
const nId = resultData.length >= end ? resultData[end] : undefined;
return new Promise((resolve) => {
setTimeout(() => {
resolve({
list,
nextId: nId,
isNoMore: list.length > 20
});
}, 1000);
});
}

View File

@ -0,0 +1,5 @@
.zhst-biz-infinite-list {
&::-webkit-scrollbar {
display: none;
}
}

View File

@ -0,0 +1,38 @@
---
category: Components
title: infiniteList 无限滚动列表
toc: content
group:
title: 数据展示
---
无限滚动列表
## 代码演示
<code src="./demo/basic.tsx">基本用法</code>
<code src="./demo/custom.tsx">手动触发</code>
<code src="./demo/customItem.tsx">自定义子元素</code>
## API
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| data | 数据源 | Idata[] | [] | - |
| loading | 数据源 | Array[] | [] | - |
| data | 数据源 | Array[] | [] | - |
| data | 数据源 | Array[] | [] | - |
| data | 数据源 | Array[] | [] | - |
| data | 数据源 | Array[] | [] | - |
| data | 数据源 | Array[] | [] | - |
## Idata
```js
interface Idata {
id?: string | number;
url?: string; // 链接
title?: string; // 标题
subtitle?: string; // 副标题
}
```

View File

@ -0,0 +1,5 @@
import InfinityList from './InfiniteList'
export type { InfiniteListProps, InfiniteListRefProps } from './InfiniteList'
export default InfinityList

View File

@ -2,6 +2,8 @@ import React, { forwardRef, useContext, useImperativeHandle, useRef } from 'reac
import { Button, Modal, ModalProps, Select, SelectProps, Space, theme } from 'antd';
import { ConfigProvider, CropperImage, Scanner, CropperImageProps, CropperImageRefProps } from '@zhst/meta'
import { IconFont } from '@zhst/icon'
import classNames from 'classnames';
import './index.less'
export interface ODModalProps extends ModalProps {
prefixCls?: string;
@ -95,7 +97,7 @@ const ODModal = forwardRef<ODModalRefProps, ODModalProps>((props, ref) => {
onCancel={onCancel}
onOk={onOk}
{...modalProps}
className={componentName}
rootClassName={componentName}
>
<Space size={12} direction='vertical' align='center' style={{ textAlign: 'center' }}>
<Scanner style={{ width: odWidth, height: odHeight }} visible={scanning}>
@ -118,7 +120,7 @@ const ODModal = forwardRef<ODModalRefProps, ODModalProps>((props, ref) => {
<div>
<Select
style={{ width: '90px' }}
className={classNames(componentName + '-type')}
defaultValue={selectDefaultValue}
value={selectedType}
options={selectOptions}
@ -129,7 +131,7 @@ const ODModal = forwardRef<ODModalRefProps, ODModalProps>((props, ref) => {
<Button disabled={handSelectDisable} size='small' type="link" onClick={onHandSelect}><IconFont icon="icon-shoudongkuangxuan" /> </Button>
<Button disabled={autoSelectDisable} size='small' type="link" onClick={onAutoSelect}><IconFont icon="icon-zidong" /> </Button>
<Button disabled={resetDisable} size='small' type="link" onClick={onReset}><IconFont icon="icon-zhongzhi3" /> </Button>
{showRotateButton && <IconFont onIconClick={onRotate} styles={{ marginLeft: '6px' }} color={token.colorPrimary} icon="icon-xuanzhuan1" />}
{showRotateButton && <IconFont className={classNames(componentName + '-rotate')} onIconClick={onRotate} color={token.colorPrimary} icon="icon-xuanzhuan1" />}
</Space>
)}
</Space>

View File

@ -69,11 +69,8 @@ const demo = () => {
autoSelectDisable={paintType === 'auto'}
onCropEnd={_cropData => console.log('结束绘制:', _cropData)}
onReset={() => console.log('重置')}
onRotate={() => console.log('旋转', odModalRef.current.rotateTo(90))}
onRotate={() => console.log('旋转', odModalRef.current?.rotateTo?.(90))}
onTypeSelect={(val) => {
if (val === 1) {
}
setSelectedType(val)
}}
/>

View File

@ -0,0 +1,9 @@
.zhst-od-modal {
&-type {
width: 90px;
}
&-rotate {
margin-left: 6px;
}
}

View File

@ -1,6 +1,5 @@
import React, { FC, useState } from 'react';
import { Tree, Badge, TreeDataNode, Space, TreeProps } from 'antd';
import theme from 'antd/es/theme'
import { Tree, Badge, TreeDataNode, Space, TreeProps, theme } from 'antd';
import { CloseOutlined, EditOutlined, SettingOutlined } from '@ant-design/icons'
import { ModalForm, ProFormText } from '@ant-design/pro-components';
import './index.less'

View File

@ -1,10 +1,7 @@
import React, { useState } from 'react';
import { Button, Card, Flex, Input, InputProps, Tree } from 'antd';
import theme from 'antd/es/theme'
import { TransferProps, TreeDataNode, TreeProps } from 'antd';
import { Button, Card, ConfigProvider, theme, Flex, Input, InputProps, TransferProps, TreeDataNode, TreeProps, Tree } from 'antd';
import './index.less'
import { DeleteOutlined, DoubleRightOutlined, SearchOutlined } from '@ant-design/icons';
import { getAllRootKeyById } from './treeTransferHelper';
const componentName = 'zhst-biz-treeTransfer'
@ -16,7 +13,7 @@ export interface TreeTransferProps {
checkedKeys: string[];
onTreeSelect?: TreeProps['onSelect']
onTreeCheck?: TreeProps['onCheck']
onItemDelete?: (key: string, info?: { root: TreeDataNode[], keys: string[] }) => void
onItemDelete?: (key: string, info?: TreeDataNode) => void
onChange?: TransferProps['onChange'];
onOk?: (data: any) => void;
onReset?: () => void;
@ -56,20 +53,29 @@ const TreeTransfer: React.FC<TreeTransferProps> = ({
<Card
className={`${componentName}-left_card`}
title={<div style={{ textAlign: 'center' }} ></div>}
bodyStyle={{ padding: 12 }}
>
<Input prefix={<SearchOutlined />} onChange={e => setKeyWords(e.target.value)} placeholder='请输入设备名称' {...searchInputProps} />
<Tree
style={{ marginTop: '6px' }}
height={420}
blockNode
checkable
checkedKeys={checkedKeys}
treeData={findNodesWithKeyword(keyWords, dataSource)}
onCheck={(keys, info) => onTreeCheck?.(keys, info)}
onSelect={(keys, info) => onTreeSelect?.(keys, info)}
{...treeProps}
/>
<ConfigProvider
theme={{
components: {
Tree: {
colorBgContainer: '#FCFCFC'
}
}
}}
>
<Tree
className={`${componentName}-left_card-tree`}
height={420}
blockNode
checkable
checkedKeys={checkedKeys}
treeData={findNodesWithKeyword(keyWords, dataSource)}
onCheck={(keys, info) => onTreeCheck?.(keys, info)}
onSelect={(keys, info) => onTreeSelect?.(keys, info)}
{...treeProps}
/>
</ConfigProvider>
</Card>
</div>
<DoubleRightOutlined/>
@ -77,8 +83,6 @@ const TreeTransfer: React.FC<TreeTransferProps> = ({
<Card
className={`${componentName}-right_card`}
title={<div style={{ textAlign: 'center' }}></div>}
bodyStyle={{ padding: 0 }}
>
<div
className={`${componentName}-right_card__items`}
@ -99,8 +103,8 @@ const TreeTransfer: React.FC<TreeTransferProps> = ({
{item.title as any}
<div style={{ float: 'right' }}>
<DeleteOutlined onClick={() => {
const { root, keys } = getAllRootKeyById(item.key as string, dataSource)
onItemDelete?.(item.key as string, { root, keys })
// const { root, keys } = getAllRootKeyById(item.key as string, dataSource)
onItemDelete?.(item.key as string, item)
}} />
</div>
</div>

View File

@ -22,9 +22,9 @@ const App: React.FC = () => {
* @param key
* @param param1
*/
const onItemDelete = (key: any, { keys }: any) => {
const onItemDelete = (key: any, { keys = [] }: any) => {
setCheckedKeys(pre => {
const newKeys = pre.filter(_key => !keys.includes(_key))
const newKeys = pre.filter(_key => _key !== key)
console.log('newKeys', newKeys, keys)
return newKeys
})

View File

@ -25,11 +25,48 @@ export const boxDataSource: TreeDataNode[] = [
{ key: '0-1-3-3', title: '分组0-1-3-3', isLeaf: true },
],
},
{
key: '0-1-4',
title: '分组0-1-4',
isLeaf: false,
children: [
{ key: '0-1-4-1', title: '分组0-1-3-1', isLeaf: true },
{ key: '0-1-4-2', title: '分组0-1-3-2', isLeaf: true },
{ key: '0-1-4-3', title: '分组0-1-3-3', isLeaf: true },
],
},
{
key: '0-1-5',
title: '分组0-1-5',
isLeaf: false,
children: [
{ key: '0-1-5-1', title: '分组0-1-3-1', isLeaf: true },
{ key: '0-1-5-2', title: '分组0-1-3-2', isLeaf: true },
{ key: '0-1-5-3', title: '分组0-1-3-3', isLeaf: true },
],
},
],
},
{ key: '0-2', title: '分组0-2', isLeaf: false, checkable: false, },
{ key: '0-3', title: '分组0-3', isLeaf: false, checkable: false, },
{ key: '0-4', title: '分组0-4', isLeaf: false, checkable: false, },
{
key: '0-4',
title: '分组0-4',
isLeaf: false,
checkable: false,
children: [
{
key: '0-4-5',
title: '分组0-4-5',
isLeaf: false,
children: [
{ key: '0-4-5-1', title: '分组0-1-3-1', isLeaf: true },
{ key: '0-4-5-2', title: '分组0-1-3-2', isLeaf: true },
{ key: '0-4-5-3', title: '分组0-1-3-3', isLeaf: true },
],
},
]
},
{ key: '0-5', title: '分组0-4', isLeaf: false, checkable: false, },
{ key: '0-6', title: '分组0-4', isLeaf: false, checkable: false, },
];

View File

@ -2,15 +2,19 @@
&-left {
&_card {
width: 500px;
height: 522px;
min-height: 544px;
background-color: #FCFCFC;
&-tree {
margin-top: 6px;
}
}
}
&-right {
&_card {
width: 300px;
height: 522px;
min-height: 544px;
background-color: #FCFCFC;
&__items {
@ -18,6 +22,7 @@
width: 100%;
height: calc(100% - 105px);
overflow-y: scroll;
max-height: 422px;
&::-webkit-scrollbar {
display: none;
@ -27,6 +32,7 @@
margin: 0;
padding: 4px 12px;
cursor: pointer;
transition: 0.3s ease-out all;
}
}

View File

@ -1,6 +1,7 @@
import React, { FC, useState } from 'react';
import { Modal, ModalProps, Radio, RadioGroupProps, Select, SelectProps, TransferProps, TreeDataNode, TreeProps } from 'antd';
import TreeTransfer from '../treeTransfer';
import { TreeTransferProps } from '../treeTransfer'
export interface TreeTransferModalProps {
dataSource: TreeDataNode[]
@ -9,7 +10,7 @@ export interface TreeTransferModalProps {
checkedKeys: string[];
onTreeSelect?: TreeProps['onSelect']
onTreeCheck?: TreeProps['onCheck']
onItemDelete?: (key: string, info?: { root: TreeDataNode[], keys: string[] }) => void
onItemDelete?: TreeTransferProps['onItemDelete']
onChange?: TransferProps['onChange'];
onOk?: (data: any) => void;
onReset?: () => void;

View File

@ -2,11 +2,12 @@ import React, { useState } from 'react';
import { TreeTransferModal } from '@zhst/biz';
import { Button, TreeDataNode } from 'antd';
import { TreeProps } from 'antd/lib';
import { boxDataSource } from './mock'
import { boxDataSource, boxGroupDataSource } from './mock'
const App: React.FC = () => {
const [targetItems, setTargetItems] = useState<TreeDataNode[]>([]);
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
const [type, setType] = useState<string>('1');
const [open, setOpen] = useState(true)
const onTreeCheck: TreeProps['onCheck'] = (keys: any, info) => {
@ -47,14 +48,17 @@ const App: React.FC = () => {
<TreeTransferModal
open={open}
onCancel={() => setOpen(false)}
onRadioChange={(e) => console.log('radioChange', e)} // 顶部 radio 事件
dataSource={boxDataSource} // 数据源
onRadioChange={(e) => setType(e.target.value)} // 顶部 radio 事件
dataSource={type === '1' ? boxDataSource : boxGroupDataSource} // 数据源
targetItems={targetItems} // 右侧选中项
checkedKeys={checkedKeys} // 左侧选中
onReset={onReset} // 重置按钮事件
onOk={onOk} // 确定按钮事件
onTreeCheck={onTreeCheck} // 树check选中事件
onItemDelete={onItemDelete} // 右侧点击删除事件
radioProps={{
value: type
}}
/>
</div>
)

View File

@ -3,33 +3,74 @@ import { TreeDataNode } from "antd";
export const boxDataSource: TreeDataNode[] = [
{
key: '0-0',
title: '分组0-0',
title: '全部盒子',
value: 0,
isLeaf: false,
checkable: false,
children: [
{
key: '0-1',
title: '盒子组-1',
value: 1,
isLeaf: false,
children: [
{ key: '0-1-0', value: '10', title: '盒子1-0', isLeaf: true, },
{ key: '0-1-1', value: '11', title: '盒子1-1', isLeaf: true, },
{ key: '0-1-2', value: '12', title: '盒子1-2', isLeaf: true, },
],
},
{ key: '0-2', value: '2', title: '盒子组-2', isLeaf: false, checkable: false, },
{ key: '0-3', value: '3', title: '盒子组-3', isLeaf: false, checkable: false, },
{
key: '0-4',
title: '盒子组-4',
value: '4',
isLeaf: false,
children: [
{ key: '0-4-10', value: '10', title: '盒子1-0', isLeaf: true, },
{ key: '0-4-11', value: '11', title: '盒子1-1', isLeaf: true, },
{ key: '0-4-12', value: '12', title: '盒子1-2', isLeaf: true, },
],
},
{ key: '0-5', value: '5', title: '盒子组-4', isLeaf: false, checkable: false, },
{ key: '0-6', value: '6', title: '盒子组-4', isLeaf: false, checkable: false, },
]
},
];
export const boxGroupDataSource: TreeDataNode[] = [
{
key: '0',
title: '盒子组-0',
value: '0',
isLeaf: false,
checkable: false,
},
{
key: '0-1',
title: '分组0-1',
key: '1',
value: '1',
title: '盒子组-1',
isLeaf: false,
children: [
{ key: '0-1-0', title: '分组0-1-0', isLeaf: true, checkable: false },
{ key: '0-1-1', title: '分组0-1-1', isLeaf: true, checkable: false },
{ key: '0-1-2', title: '分组0-1-2', isLeaf: true, checkable: false },
{
key: '0-1-3',
title: '分组0-1-3',
isLeaf: false,
children: [
{ key: '0-1-3-1', title: '分组0-1-3-1', isLeaf: true },
{ key: '0-1-3-2', title: '分组0-1-3-2', isLeaf: true },
{ key: '0-1-3-3', title: '分组0-1-3-3', isLeaf: true },
],
},
{ key: '0-1-10', value: '10', title: '盒子1-0', isLeaf: true, },
{ key: '0-1-11', value: '11', title: '盒子1-1', isLeaf: true, },
{ key: '0-1-12', value: '12', title: '盒子1-2', isLeaf: true, },
],
},
{ key: '0-2', title: '分组0-2', isLeaf: false, checkable: false, },
{ key: '0-3', title: '分组0-3', isLeaf: false, checkable: false, },
{ key: '0-4', title: '分组0-4', isLeaf: false, checkable: false, },
{ key: '0-5', title: '分组0-4', isLeaf: false, checkable: false, },
{ key: '0-6', title: '分组0-4', isLeaf: false, checkable: false, },
{ key: '2', value: '2', title: '盒子组-2', isLeaf: false, checkable: false, },
{
key: '3',
title: '盒子组-3',
value: '3',
isLeaf: false,
children: [
{ key: '3-10', value: '10', title: '盒子1-0', isLeaf: true, },
{ key: '3-11', value: '11', title: '盒子1-1', isLeaf: true, },
{ key: '3-12', value: '12', title: '盒子1-2', isLeaf: true, },
],
},
{ key: '4', value: '4', title: '盒子组-4', isLeaf: false, checkable: false, },
{ key: '5', value: '4', title: '盒子组-5', isLeaf: false, checkable: false, },
];

View File

@ -2,7 +2,12 @@ import { defineConfig } from 'father';
export default defineConfig({
// more father config: https://github.com/umijs/father/blob/master/docs/config.md
esm: { output: 'es' },
cjs: { output: 'lib' },
// umd: { output: 'dist' }
esm: {
output: 'es',
ignores: ['**/demo/*', 'src/**/demo/*']
},
cjs: {
output: 'lib',
ignores: ['**/demo/*', 'src/**/demo/*']
},
});

View File

@ -1,5 +1,94 @@
# @zhst/utils
## 0.15.0
### Minor Changes
- feat: 修复之前发版错乱问题
### Patch Changes
- Updated dependencies
- @zhst/request@0.15.0
## 0.14.1
### Patch Changes
- zhst/func: 修改 pxToRem 算法
- @zhst/request@0.14.1
## 0.14.0
### Minor Changes
- feat: 重新发版
### Patch Changes
- Updated dependencies
- @zhst/request@0.14.0
## 0.13.0
### Minor Changes
- fix: 修复适配问题
### Patch Changes
- Updated dependencies
- @zhst/request@0.13.0
## 0.12.0
### Minor Changes
- feat: zhst/func 添加 pxToRem 方法
### Patch Changes
- @zhst/request@0.12.4
## 0.11.3
### Patch Changes
- Updated dependencies
- @zhst/request@0.12.3
## 0.11.2
### Patch Changes
- Updated dependencies
- @zhst/request@0.12.2
## 0.11.1
### Patch Changes
- Updated dependencies
- @zhst/request@0.12.1
## 0.11.0
### Minor Changes
- zhst/biz:新增摘要列表-无限滚动组件
### Patch Changes
- Updated dependencies
- @zhst/request@0.12.0
## 0.10.2
### Patch Changes
- Updated dependencies
- @zhst/request@0.11.0
## 0.10.1
### Patch Changes

View File

@ -1,43 +0,0 @@
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
/**
* Created by jiangzhixiong on 2024/03/20
*/
import React, { useState } from 'react';
import { getValueByUrl } from '@zhst/func';
import { Input, Button, Space } from 'antd';
var demo = function demo() {
var _useState = useState(null),
_useState2 = _slicedToArray(_useState, 2),
url = _useState2[0],
setUrl = _useState2[1];
var _useState3 = useState(null),
_useState4 = _slicedToArray(_useState3, 2),
keyword = _useState4[0],
setKeyword = _useState4[1];
var _useState5 = useState(null),
_useState6 = _slicedToArray(_useState5, 2),
outputVal = _useState6[0],
setOutPutVal = _useState6[1];
var handleClick = function handleClick() {
var val = getValueByUrl(keyword, url);
setOutPutVal(val);
};
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Space, null, "\u94FE\u63A5\uFF1A", /*#__PURE__*/React.createElement(Input, {
onChange: function onChange(e) {
return setUrl(e.target.value);
}
}), "\u83B7\u53D6\u5B57\u6BB5\uFF1A", /*#__PURE__*/React.createElement(Input, {
onChange: function onChange(e) {
return setKeyword(e.target.value);
}
}), /*#__PURE__*/React.createElement(Button, {
onClick: handleClick
}, "\u63D0\u4EA4")), /*#__PURE__*/React.createElement("p", null, "\u8F93\u51FA\uFF1A", outputVal));
};
export default demo;

View File

@ -71,4 +71,19 @@ export var getValueByUrl = function getValueByUrl(key, str) {
result = new URLSearchParams(str.indexOf('?') > -1 ? str : "?".concat(str)).get(key);
}
return result;
};
/**
* 行内px rem
* @param value px像素
* @param rootFontSize 根元素大小: 默认16px
*/
export var pxToRem = function pxToRem(value, rootFontSize) {
var fontSize = rootFontSize || 80 || parseFloat(document.documentElement.style.fontSize);
var valueArr = value.split(' ');
return valueArr.filter(function (o) {
return o;
}).map(function (val) {
return parseFloat(val) / fontSize + 'rem';
}).join(' ');
};

View File

@ -1,48 +0,0 @@
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/string/demo/getValueByUrl.tsx
var getValueByUrl_exports = {};
__export(getValueByUrl_exports, {
default: () => getValueByUrl_default
});
module.exports = __toCommonJS(getValueByUrl_exports);
var import_react = __toESM(require("react"));
var import_func = require("@zhst/func");
var import_antd = require("antd");
var demo = () => {
const [url, setUrl] = (0, import_react.useState)(null);
const [keyword, setKeyword] = (0, import_react.useState)(null);
const [outputVal, setOutPutVal] = (0, import_react.useState)(null);
const handleClick = () => {
let val = (0, import_func.getValueByUrl)(keyword, url);
setOutPutVal(val);
};
return /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement(import_antd.Space, null, "链接:", /* @__PURE__ */ import_react.default.createElement(import_antd.Input, { onChange: (e) => setUrl(e.target.value) }), "获取字段:", /* @__PURE__ */ import_react.default.createElement(import_antd.Input, { onChange: (e) => setKeyword(e.target.value) }), /* @__PURE__ */ import_react.default.createElement(import_antd.Button, { onClick: handleClick }, "提交")), /* @__PURE__ */ import_react.default.createElement("p", null, "输出:", outputVal));
};
var getValueByUrl_default = demo;

View File

@ -22,7 +22,8 @@ __export(string_exports, {
cutStr: () => cutStr,
getStrLength: () => getStrLength,
getValueByUrl: () => getValueByUrl,
isUrl: () => isUrl
isUrl: () => isUrl,
pxToRem: () => pxToRem
});
module.exports = __toCommonJS(string_exports);
var getStrLength = function(str) {
@ -74,10 +75,16 @@ var getValueByUrl = (key, str) => {
}
return result;
};
var pxToRem = (value, rootFontSize) => {
const fontSize = rootFontSize || 80;
const valueArr = value.split(" ");
return valueArr.filter((o) => o).map((val) => parseFloat(val) / fontSize + "rem").join(" ");
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
cutStr,
getStrLength,
getValueByUrl,
isUrl
isUrl,
pxToRem
});

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/func",
"version": "0.10.1",
"version": "0.15.0",
"description": "函数合集",
"keywords": [
"hooks"

View File

@ -0,0 +1,25 @@
/**
* Created by jiangzhixiong on 2024/03/20
*/
import React, { useState } from 'react'
import { pxToRem } from '@zhst/func'
import { Input } from 'antd'
const demo = () => {
const [outputVal, setOutPutVal ] = useState<any>(null)
const handleChange = (e: { target: { value: string } }) => {
const value = pxToRem(e.target.value)
setOutPutVal(value)
}
return (
<div>
<Input onChange={handleChange} />
<p>{outputVal}</p>
</div>
)
}
export default demo

View File

@ -0,0 +1,131 @@
// @ts-nocheck
// !! 参考代码,不投入使用
/**
*
*/
const screenAdapt = () => {
window.global = window;
(function () {
if ($(window).width() >= 1920) {
$(window).width() && $('body').css('width', $(window).width());
$(window).height() && $('body').css('height', $(window).height());
} else {
$(window).width() && $('body').css('width', "1920px");
var ratio = $(window).width() / (1920 || $('body').width());
$('body').css({
transform: "scale(" + ratio + ")",
transformOrigin: "left top",
backgroundSize: "100%",
height: "1080px"
});
}
$('head').append('<meta name="viewport" content=""/>');
//监听页面是否发生改变
$(window, document).resize(function () {
resize();
})
function resize () {
if (window.screen.display == 2) { // 等比缩放高度铺满
resizeCenter();
} else if (window.screen.display == 3) { //全屏铺满
resizeFull();
} else if (window.screen.display == 4) { //等比缩放高度铺满并且可以左右移动
resizeHeight();
} else { // 等比缩放宽度铺满
resizeWidth();
}
}
function resizeWidth () {
window.location.reload()
if ($(window).width() >= 1920) {
var ratio = $(window).width() / ($(window).width() || $('body').width());
$(window).height() && $('body').css('height', $(window).height());
} else {
$('body').css('height', "1080px");
}
var ratio = $(window).width() / (1920 || $('body').width());
$('body').css({
transform: "scale(" + ratio + ")",
transformOrigin: "left top",
backgroundSize: "100%"
});
}
function resizeCenter () {
if (!window.screen.height || !window.screen.width) return resizeCenterBak();
var ratio = $(window).height() / window.screen.height;
$('body').css({
transform: "scale(" + ratio + ")",
transformOrigin: "left top",
backgroundSize: 100 * (window.screen.width / $(window).width() * ratio) + "%" + ' 100%',
backgroundPosition: ($(window).width() - $('body').width() * ratio) / 2 + "px top",
marginLeft: ($(window).width() - $('body').width() * ratio) / 2
});
}
function resizeHeight () { //
if (!window.screen.height || !window.screen.width) return resizeCenterBak();
var ratio = $(window).height() / window.screen.height;
$('body').css({
transform: "scale(" + ratio + ")",
transformOrigin: "left top",
backgroundSize: 100 * (window.screen.width / $(window).width() * ratio) + "%" + ' 100%',
backgroundPosition: ($(window).width() - $('body').width() * ratio) / 2 + "px top",
// marginLeft: ($(window).width() - $('body').width() * ratio) / 2
});
$('html').css({
overflowX: 'scroll',
})
}
function resizeFull () {
if (!window.screen.height || !window.screen.width) return resizeFullBak();
var ratioX = $(window).width() / window.screen.width;
var ratioY = $(window).height() / window.screen.height;
$('body').css({
transform: "scale(" + ratioX + ", " + ratioY + ")",
transformOrigin: "left top",
backgroundSize: "100% 100%",
});
}
function resizeCenterBak () {
var ratioX = $(window).width() / $('body').width();
var ratioY = $(window).height() / $('body').height();
var ratio = Math.min(ratioX, ratioY);
$('body').css({
transform: "scale(" + ratio + ")",
transformOrigin: "left top",
backgroundSize: (1 / ratioX) * 100 * ratio + "%",
backgroundPosition: ($(window).width() - $('body').width() * ratio) / 2 + "px top",
marginLeft: ($(window).width() - $('body').width() * ratio) / 2
});
}
function resizeFullBak () {
var ratioX = $(window).width() / $('body').width();
var ratioY = $(window).height() / $('body').height();
$('body').css({
transform: "scale(" + ratioX + ", " + ratioY + ")",
transformOrigin: "left top",
backgroundSize: "100% " + ratioY * 100 + "%",
});
}
})();
}

View File

@ -21,3 +21,4 @@ const value3 = getValueByUrl('a', 'a=123&token=asdfasdfsdf')
```
<code src="./demo/getValueByUrl.tsx">调试台</code>
<code src="./demo/pxTorem.tsx">px 转 rem</code>

View File

@ -78,3 +78,15 @@ export const getValueByUrl = (key: string, str: string) => {
return result
}
/**
* px rem
* @param value px像素
* @param rootFontSize 根元素大小: 默认16px
*/
export const pxToRem = (value: string, rootFontSize?: number) => {
const fontSize = rootFontSize || 80 || parseFloat(document.documentElement.style.fontSize)
const valueArr = value.split(' ')
return valueArr.filter(o => o).map(val => ((parseFloat(val) / fontSize) + 'rem')).join(' ')
}

View File

@ -0,0 +1,151 @@
// @ts-nocheck
// !! 开发中
// 手动添加mate标签
const addMeta = (name: string, content: string) => {
const meta = document.createElement('meta');
meta.content = content;
meta.name = name;
document.getElementsByTagName('head')[0].appendChild(meta);
};
const autoResize = (opt: {
el: HTMLDivElement
targetWidth?: number
targetHeight?: number
}) => {
const { targetHeight = 1080, targetWidth = 1920, el } = opt
let targetRatio = targetWidth / targetHeight; // 宽高比率 (宽 / 高) - 默认16 /9
// 当前设备(浏览器)的宽度
let clientWidth =
document.documentElement.clientWidth || document.body.clientWidth;
let clientHeight =
document.documentElement.clientHeight || document.body.clientHeight;
// 当前宽高比例
// let currentRatio = clientWidth / clientHeight;
if (clientWidth >= 1920) {
el.style.width = `${clientWidth}px`
el.style.height = `${clientHeight}px`
} else {
el.style.width = '1920px'
// let currentRatio =
}
addMeta(
'viewport',
'width=' + clientWidth + ',initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover',
);
const resizeWidth = () => {
if ($(window).width() >= 1920) {
var ratio = $(window).width() / ($(window).width() || $('body').width());
$(window).height() && $('body').css('height', $(window).height());
} else {
$('body').css('height', "1080px");
}
var ratio = $(window).width() / (1920 || $('body').width());
$('body').css({
transform: "scale(" + ratio + ")",
transformOrigin: "left top",
backgroundSize: "100%"
});
}
function resizeCenter () {
if (!window.screen.height || !window.screen.width) return resizeCenterBak();
var ratio = $(window).height() / window.screen.height;
$('body').css({
transform: "scale(" + ratio + ")",
transformOrigin: "left top",
backgroundSize: 100 * (window.screen.width / $(window).width() * ratio) + "%" + ' 100%',
backgroundPosition: ($(window).width() - $('body').width() * ratio) / 2 + "px top",
marginLeft: ($(window).width() - $('body').width() * ratio) / 2
});
}
function resizeHeight () { //
if (!window.screen.height || !window.screen.width) return resizeCenterBak();
var ratio = $(window).height() / window.screen.height;
$('body').css({
transform: "scale(" + ratio + ")",
transformOrigin: "left top",
backgroundSize: 100 * (window.screen.width / $(window).width() * ratio) + "%" + ' 100%',
backgroundPosition: ($(window).width() - $('body').width() * ratio) / 2 + "px top",
});
$('html').css({
overflowX: 'scroll',
})
}
function resizeFull () {
if (!window.screen.height || !window.screen.width) return resizeFullBak();
var ratioX = $(window).width() / window.screen.width;
var ratioY = $(window).height() / window.screen.height;
$('body').css({
transform: "scale(" + ratioX + ", " + ratioY + ")",
transformOrigin: "left top",
backgroundSize: "100% 100%",
});
}
function resizeCenterBak () {
var ratioX = $(window).width() / $('body').width();
var ratioY = $(window).height() / $('body').height();
var ratio = Math.min(ratioX, ratioY);
$('body').css({
transform: "scale(" + ratio + ")",
transformOrigin: "left top",
backgroundSize: (1 / ratioX) * 100 * ratio + "%",
backgroundPosition: ($(window).width() - $('body').width() * ratio) / 2 + "px top",
marginLeft: ($(window).width() - $('body').width() * ratio) / 2
});
}
function resizeFullBak () {
var ratioX = $(window).width() / $('body').width();
var ratioY = $(window).height() / $('body').height();
$('body').css({
transform: "scale(" + ratioX + ", " + ratioY + ")",
transformOrigin: "left top",
backgroundSize: "100% " + ratioY * 100 + "%",
});
}
if (window.screen.display === 2) { // 等比缩放高度铺满
resizeCenter();
} else if (window.screen.display === 3) { //全屏铺满
resizeFull();
} else if (window.screen.display === 4) { //等比缩放高度铺满并且可以左右移动
resizeHeight();
} else { // 等比缩放宽度铺满
resizeWidth();
}
// if (currentRatio > targetRatio) {
// let scaleRatio = clientHeight / targetHeight; // 参照高度进行缩放(屏幕很宽的情况下)
// const transform = `scale(${scaleRatio}) translateX(-50%) left: 50%`;
// console.log('transform', transform)
// // el.style.transform =
// } else {
// // 4.开始缩放网页
// let scaleRatio = clientWidth / targetWidth; // 参照宽度进行缩放(默认情况下)
// console.log('scaleRatio', scaleRatio)
// el.style.transform = `scale(${scaleRatio})`;
// }
}
export default autoResize

View File

@ -2,7 +2,12 @@ import { defineConfig } from 'father';
export default defineConfig({
// more father config: https://github.com/umijs/father/blob/master/docs/config.md
esm: { output: 'es' },
cjs: { output: 'lib' },
// umd: { output: 'dist' }
esm: {
output: 'es',
ignores: ['**/demo/*', 'src/**/demo/*']
},
cjs: {
output: 'lib',
ignores: ['**/demo/*', 'src/**/demo/*']
},
});

View File

@ -1,5 +1,87 @@
# @zhst/hooks
## 0.13.0
### Minor Changes
- feat: 修复之前发版错乱问题
### Patch Changes
- Updated dependencies
- @zhst/func@0.15.0
## 0.12.1
### Patch Changes
- Updated dependencies
- @zhst/func@0.14.1
## 0.12.0
### Minor Changes
- feat: 重新发版
### Patch Changes
- Updated dependencies
- @zhst/func@0.14.0
## 0.11.0
### Minor Changes
- fix: 修复适配问题
### Patch Changes
- Updated dependencies
- @zhst/func@0.13.0
## 0.10.4
### Patch Changes
- Updated dependencies
- @zhst/func@0.12.0
## 0.10.3
### Patch Changes
- @zhst/func@0.11.3
## 0.10.2
### Patch Changes
- @zhst/func@0.11.2
## 0.10.1
### Patch Changes
- @zhst/func@0.11.1
## 0.10.0
### Minor Changes
- zhst/biz:新增摘要列表-无限滚动组件
### Patch Changes
- Updated dependencies
- @zhst/func@0.11.0
## 0.9.2
### Patch Changes
- @zhst/func@0.10.2
## 0.9.1
### Patch Changes

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/hooks",
"version": "0.9.1",
"version": "0.13.0",
"description": "hooks合集",
"keywords": [
"hooks"

View File

@ -2,6 +2,12 @@ import { defineConfig } from 'father';
export default defineConfig({
// more father config: https://github.com/umijs/father/blob/master/docs/config.md
esm: { output: 'es' },
cjs: { output: 'lib' },
esm: {
output: 'es',
ignores: ['**/demo/*', 'src/**/demo/*']
},
cjs: {
output: 'lib',
ignores: ['**/demo/*', 'src/**/demo/*']
},
});

View File

@ -1,5 +1,23 @@
# @zhst/icon
## 0.5.0
### Minor Changes
- feat: 修复之前发版错乱问题
## 0.4.0
### Minor Changes
- feat: 重新发版
## 0.3.0
### Minor Changes
- fix: 修复适配问题
## 0.2.0
### Minor Changes

View File

@ -1,8 +0,0 @@
import React from 'react';
import { IconFont } from '@zhst/icon';
var demo = function demo() {
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(IconFont, {
icon: "icon-daoru1"
}));
};
export default demo;

View File

@ -1,33 +0,0 @@
import React from 'react';
var iconJson = require("../font/iconfont.json");
import { IconFont } from '@zhst/icon';
import "./index.less";
import { message } from '@zhst/meta';
var demo = function demo() {
var iconArr = iconJson['glyphs'];
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("ul", {
className: 'demo-ul'
}, iconArr.map(function (item) {
var font_class = item.font_class,
name = item.name;
var fontName = "icon-".concat(font_class);
return /*#__PURE__*/React.createElement("li", {
className: 'demo-li'
}, /*#__PURE__*/React.createElement(IconFont, {
styles: {
marginBottom: 20
},
icon: fontName,
size: 32,
onIconClick: function onIconClick() {
navigator.clipboard.writeText(fontName);
message.success("\u590D\u5236".concat(fontName, "\u6210\u529F"));
}
}), /*#__PURE__*/React.createElement("div", {
className: "demo-li-name"
}, name), /*#__PURE__*/React.createElement("div", {
className: "demo-li-name"
}, fontName));
})));
};
export default demo;

View File

@ -1,20 +0,0 @@
.demo {
&-ul {
list-style-type: none;
display: flex;
flex-wrap: wrap;
}
&-li {
display: flex;
flex-direction: column;
align-items: center;
height: 150px;
width: 150px;
&-name {
color: #666;
font-size: 12px;
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/icon",
"version": "0.2.0",
"version": "0.5.0",
"description": "图标库",
"keywords": [
"icon",

View File

@ -1,5 +1,253 @@
# @zhst/material
## 0.17.4
### Patch Changes
- fix: 修改 zhst/meta
- Updated dependencies
- @zhst/meta@0.20.3
- @zhst/biz@0.21.5
## 0.17.3
### Patch Changes
- zhst/biz、zhst/meta、zhst/material: 修改 od、修改 boxSelectTree
- Updated dependencies
- @zhst/meta@0.20.2
- @zhst/biz@0.21.4
## 0.17.2
### Patch Changes
- Updated dependencies
- @zhst/biz@0.21.3
## 0.17.1
### Patch Changes
- fix: 修改 cropperImage 渲染问题
- Updated dependencies
- @zhst/meta@0.20.1
- @zhst/biz@0.21.2
## 0.17.0
### Minor Changes
- fix: cropperImage 透出矩形坐标
### Patch Changes
- Updated dependencies
- @zhst/meta@0.20.0
- @zhst/biz@0.21.1
## 0.16.0
### Minor Changes
- feat: 修复之前发版错乱问题
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.13.0
- @zhst/func@0.15.0
- @zhst/meta@0.19.0
- @zhst/biz@0.21.0
## 0.15.1
### Patch Changes
- Updated dependencies
- @zhst/func@0.14.1
- @zhst/biz@0.20.1
- @zhst/hooks@0.12.1
- @zhst/meta@0.18.1
## 0.15.0
### Patch Changes
- Updated dependencies
- @zhst/biz@0.19.2
## 0.14.1
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.12.0
- @zhst/func@0.14.0
- @zhst/meta@0.18.0
- @zhst/biz@0.20.0
## 0.14.0
### Minor Changes
- fix: 修复适配问题
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.11.0
- @zhst/func@0.13.0
- @zhst/meta@0.17.0
- @zhst/biz@0.19.0
## 0.13.4
### Patch Changes
- Updated dependencies
- @zhst/func@0.12.0
- @zhst/biz@0.18.8
- @zhst/hooks@0.10.4
- @zhst/meta@0.16.4
## 0.13.3
### Patch Changes
- zhst/material: 修改 theme 组件引用方式
## 0.13.2
### Patch Changes
- zhst/material: 修改 antd 依赖版本
## 0.13.1
### Patch Changes
- zhst/material: 修复引用 antd/theme 失败
## 0.13.0
### Minor Changes
- zhst/material: 添加盒子列表加载更多
## 0.12.7
### Patch Changes
- @zhst/func@0.11.3
- @zhst/biz@0.18.7
- @zhst/hooks@0.10.3
- @zhst/meta@0.16.3
## 0.12.6
### Patch Changes
- biz 优化无限滚动组件、boxselectTree 组件material 修改算法编辑模块
- Updated dependencies
- @zhst/biz@0.18.6
## 0.12.5
### Patch Changes
- Updated dependencies
- @zhst/biz@0.18.5
## 0.12.4
### Patch Changes
- Updated dependencies
- @zhst/biz@0.18.4
## 0.12.3
### Patch Changes
- Updated dependencies
- @zhst/biz@0.18.3
## 0.12.2
### Patch Changes
- zhst/request 修改报错提示逻辑、zhst/material - 删除算法配置路数
- @zhst/func@0.11.2
- @zhst/biz@0.18.2
- @zhst/hooks@0.10.2
- @zhst/meta@0.16.2
## 0.12.1
### Patch Changes
- @zhst/func@0.11.1
- @zhst/biz@0.18.1
- @zhst/hooks@0.10.1
- @zhst/meta@0.16.1
## 0.12.0
### Minor Changes
- zhst/biz:新增摘要列表-无限滚动组件
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.10.0
- @zhst/func@0.11.0
- @zhst/meta@0.16.0
- @zhst/biz@0.18.0
## 0.11.0
### Minor Changes
- 视频添加 OD 框,查看大图首次点击修复
### Patch Changes
- Updated dependencies
- @zhst/meta@0.15.0
- @zhst/biz@0.17.0
- @zhst/func@0.10.2
- @zhst/hooks@0.9.2
## 0.10.4
### Patch Changes
- Updated dependencies
- @zhst/biz@0.16.1
## 0.10.3
### Patch Changes
- Updated dependencies
- @zhst/meta@0.14.0
- @zhst/biz@0.16.0
## 0.10.2
### Patch Changes
- material 修改找不到包的问题
## 0.10.1
### Patch Changes
- Updated dependencies
- @zhst/biz@0.15.0
## 0.10.0
### Minor Changes

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/material",
"version": "0.10.0",
"version": "0.17.4",
"description": "物料库",
"keywords": [
"business",
@ -43,7 +43,7 @@
"@zhst/meta": "workspace:^",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
"antd": "^5.14.2",
"antd": "^5.12.5",
"classnames": "^2.5.1",
"rc-util": "^5.38.1"
}

View File

@ -1,17 +1,19 @@
import React, { forwardRef } from 'react';
import { Flex, Image } from "antd";
import React, { forwardRef, ReactNode, useContext, useImperativeHandle } from 'react';
import { Button, Flex, Image } from "antd";
import theme from 'antd/es/theme'
import { CropperImage } from '@zhst/meta'
import { CropperImage, ConfigProvider } from '@zhst/meta'
import type { CropperImageProps } from '@zhst/meta'
import { AlgorithmConfigImg, ErrorImage } from '../utils/base64Images'
import AlgorithmTable from './components/algorithmTable'
import TimeTemplateTable from './components/timeTemplateTable';
import { AlgorithmTableProps } from './components/algorithmTable/AlgorithmTable';
import { TimeTemplateTableProps } from './components/timeTemplateTable/TimeTemplateTable';
import classNames from 'classnames';
import './index.less'
const { useToken } = theme
const Title = (props: any) => <h2 style={{ margin: '18px 16px', fontSize: '14px', color: 'rgba(0, 0, 0, 0.88)' }} >{props.children}</h2>
const Title = (props: any) => <h2 style={{ color: 'rgba(0, 0, 0, 0.88)' }} {...props}>{props.children}</h2>
export interface AlgorithmConfigProps {
onAddAlgorithm?: () => void
@ -49,42 +51,71 @@ export interface AlgorithmConfigProps {
type: AlgorithmTableProps<any>['tableType']
title?:string; // boxList列表的属性名称【点位列表、盒子列表】
onSelect?: (key: string, info?: any) => void
/**
* /
*/
showLoadMoreButton?: boolean
/**
*
*/
onLoadMoreButtonClick?: () => void;
/**
*
*/
customLoadMoreButton: ReactNode
/**
*
*/
customBatchCenterContent: ReactNode
prefixCls?: string;
}
export interface AlgorithmConfigRef {
}
const AlgorithmConfig = forwardRef<AlgorithmConfigRef, AlgorithmConfigProps>((props) => {
const { ConfigContext } = ConfigProvider
const AlgorithmConfig = forwardRef<AlgorithmConfigRef, AlgorithmConfigProps>((props, ref) => {
const {
algorithmTableDataSource = [],
timeTemplateDataSource = [],
boxList = [],
drawListener,
cropperImageProps = {},
algorithmTableProps,
timeTemplateTableProps,
selectedKey,
boxList = [],
type = 'multiple',
rowKey = 'id',
onSelect, title='盒子名称',
showLoadMoreButton,
onLoadMoreButtonClick,
customBatchCenterContent,
customLoadMoreButton,
prefixCls: customizePrefixCls
} = props
const { getPrefixCls } = useContext(ConfigContext);
const componentName = getPrefixCls('material-algo', customizePrefixCls);
const { token } = useToken()
// @ts-ignore
const { type: cropType } = cropperImageProps
// useImperativeHandle(ref, () => ({
// }))
useImperativeHandle(ref, () => ({
}))
return (
<Flex style={{ border: `1px solid ${token.colorBorder}`, backgroundColor: token.colorBgBase }}>
<div title={title} style={{ width: '13.9%' }}>
<Title>{title}</Title>
<div style={{ borderTop: `1px solid ${token.colorBorder}` }}>
<Flex className={componentName} style={{ border: `1px solid ${token.colorBorder}`, backgroundColor: token.colorBgBase }}>
<div className={classNames(`${componentName}-left`)} title={title} style={{ position: 'relative', width: '13.9%' }}>
<Title className={classNames(`${componentName}-title`)}>{title}</Title>
<div className={classNames(`${componentName}-left-list`)} style={{ borderTop: `1px solid ${token.colorBorder}` }}>
{boxList.map(item => {
return (
<p
key={item.id}
onClick={() => onSelect?.(item.id, item)}
// @ts-ignore
key={item[rowKey]}
// @ts-ignore
onClick={() => onSelect?.(item[rowKey], item)}
style={{
margin: 0,
padding: `${token.paddingXXS}px ${token.paddingLG}px`,
@ -93,12 +124,27 @@ const AlgorithmConfig = forwardRef<AlgorithmConfigRef, AlgorithmConfigProps>((pr
color: selectedKey === item[rowKey] ? token.colorPrimary : token.colorText,
// @ts-ignore
backgroundColor: selectedKey === item[rowKey] ? token.blue1 : token.colorBgBase,
transition: '0cancelDraw.2s ease'
transition: '.2s ease all',
}}
onMouseEnter={(e: any) => {
e.target.style.backgroundColor = token.colorPrimaryBg
e.target.style.color = token.colorPrimary
}}
onMouseLeave={(e: any) => {
// @ts-ignore
if (selectedKey === item[rowKey]) return
e.target.style.color = token.colorText
e.target.style.backgroundColor = null
}}
>{item.name}</p>
)
})}
</div>
{showLoadMoreButton && (
<div style={{ width: '100%', padding: '6px', position: 'absolute', left: '0', bottom: '0', boxSizing: 'border-box' }}>
{customLoadMoreButton || <Button onClick={onLoadMoreButtonClick} block></Button>}
</div>
)}
</div>
<div style={{ boxSizing: 'border-box', width: '46.3%', textAlign: 'center', borderLeft: `1px solid ${token.colorBorder}`, borderRight: `1px solid ${token.colorBorder}` }}>
{/* 单个配置 */}
@ -109,7 +155,7 @@ const AlgorithmConfig = forwardRef<AlgorithmConfigRef, AlgorithmConfigProps>((pr
{...cropperImageProps}
/>
) : (
<div style={{ padding: '84px' }}>
<div className={classNames(`${componentName}-middle-cont`)}>
<Image
width={'62.5%'}
src={AlgorithmConfigImg}
@ -117,19 +163,21 @@ const AlgorithmConfig = forwardRef<AlgorithmConfigRef, AlgorithmConfigProps>((pr
fallback={ErrorImage}
/>
<h2></h2>
<ul style={{ display: 'inline-block', paddingLeft: 0, width: '51.8%', listStyle: 'none', textAlign: 'left', color: token.colorTextLabel }}>
<li>· </li>
<li>· </li>
<li>· 线线</li>
<li>· </li>
</ul>
{customBatchCenterContent || (
<ul style={{ display: 'inline-block', paddingLeft: 0, width: '51.8%', listStyle: 'none', textAlign: 'left', color: token.colorTextLabel }}>
<li>· </li>
<li>· </li>
<li>· 线线</li>
<li>· </li>
</ul>
)}
</div>
)
}
</div>
<div style={{ width: '39.8%' }} >
<div>
<Title></Title>
<Title className={classNames(`${componentName}-title`)}></Title>
<div style={{ padding: `${token.paddingMD}px ${token.paddingSM}px`, borderTop: `1px solid ${token.colorBorder}`, borderBottom: `1px solid ${token.colorBorder}` }}>
<TimeTemplateTable
dataSource={timeTemplateDataSource}
@ -138,7 +186,7 @@ const AlgorithmConfig = forwardRef<AlgorithmConfigRef, AlgorithmConfigProps>((pr
</div>
</div>
<div>
<Title></Title>
<Title className={classNames(`${componentName}-title`)}></Title>
<div style={{ padding: `${token.paddingMD}px ${token.paddingSM}px`, borderTop: `1px solid ${token.colorBorder}` }}>
<AlgorithmTable
dataSource={algorithmTableDataSource}

View File

@ -1,16 +1,20 @@
import React from 'react';
import React, { useContext } from 'react';
import { DeleteFilled, EditFilled, ImportOutlined, PlusCircleFilled } from '@ant-design/icons';
import type { ParamsType, ProColumns, ProTableProps } from '@ant-design/pro-components';
import {
ProTable,
} from '@ant-design/pro-components';
import { Popconfirm, Select, Space, Switch } from 'antd';
import theme from 'antd/es/theme';
import { ConfigProvider } from '@zhst/meta'
import theme from 'antd/es/theme'
import { AnyObject } from 'antd/es/_util/type';
import { SelectProps } from 'antd/lib';
import SchemaFormModal from '../schemaFormModal';
import classNames from 'classnames';
import './index.less'
const { useToken } = theme
const { ConfigContext } = ConfigProvider
export interface AlgorithmTableProps<DataSource, Params extends ParamsType = ParamsType, ValueType = "text"> extends ProTableProps<DataSource, Params, ValueType> {
onAddAlgorithm?: (id?: string, record?: any) => void
@ -40,7 +44,11 @@ const AlgorithmTable= <DataSource extends AnyObject = AnyObject>(
onDraw,
tableType = 'multiple',
sortList = [],
prefixCls: customizePrefixCls
} = props
const { getPrefixCls } = useContext(ConfigContext);
const componentName = getPrefixCls('material-algo-algoTable', customizePrefixCls);
const { token } = useToken()
@ -53,21 +61,21 @@ const AlgorithmTable= <DataSource extends AnyObject = AnyObject>(
title: '运行周期',
dataIndex: 'runCycle',
valueType: 'select',
width: 80,
valueEnum: {
1: { text: '黑夜' },
0: { text: '白天' },
},
},
{
title: '算力占用',
dataIndex: 'powerOccupy',
},
// {
// title: '算力占用',
// dataIndex: 'powerOccupy',
// },
{
title: '操作',
key: 'option',
valueType: 'option',
fixed: true,
fixed: 'right',
width: '120px',
render: (_DOM, record) => [
<Switch value={record.status} onChange={_status => onItemSwitch?.(_status, record.id, record)} />,
<a onClick={() => onDraw?.(record.id, record)} style={{ display: tableType === 'single' ? 'block' : 'none' }} href="#"><ImportOutlined /></a>,
@ -88,29 +96,34 @@ const AlgorithmTable= <DataSource extends AnyObject = AnyObject>(
];
return (
<ProTable<DataSource>
columns={columns}
bordered
scroll={{ y: 240 }}
dataSource={[]}
headerTitle={(
<Space size={16}>
<Select
value={selectedKey}
style={{ width: 320 }}
onChange={onSortSelect}
options={sortList}
/>
<PlusCircleFilled onClick={() => onAddAlgorithm?.()} style={{ fontSize: '24px', color: token.colorPrimary, cursor: 'pointer' }} />
</Space>
)}
toolbar={undefined}
rowKey="id"
search={false}
options={false}
pagination={false}
{...props}
/>
<div className={componentName}>
<Space className={classNames(`${componentName}-top`)} size={16}>
<Select
className={classNames(`${componentName}-top-select`)}
value={selectedKey}
onChange={onSortSelect}
options={sortList}
/>
<PlusCircleFilled className={classNames(`${componentName}-top-plus`)} onClick={() => onAddAlgorithm?.(selectedKey)} style={{ color: token.colorPrimary, cursor: 'pointer' }} />
</Space>
<ProTable<DataSource>
columns={columns}
bordered
scroll={{ y: 240, x: 600 }}
dataSource={[]}
cardProps={{
bodyStyle: {
padding: 0
}
}}
toolbar={undefined}
rowKey="id"
search={false}
options={false}
pagination={false}
{...props}
/>
</div>
)
}

View File

@ -0,0 +1,13 @@
.zhst-material-algo-algoTable {
&-top {
margin-bottom: 12px;
&-select {
width: 320px;
}
&-plus {
font-size: 24px;
}
}
}

View File

@ -1,10 +1,11 @@
import React from 'react';
import type { ParamsType, ProColumns, ProTableProps } from '@ant-design/pro-components';
import {
ProTable,
} from '@ant-design/pro-components';
import { InputNumber } from 'antd';
import { AnyObject } from 'antd/es/_util/type';
import { InputNumber } from 'antd';
export interface TimeTemplateTableProps<DataSource, Params extends ParamsType = ParamsType, ValueType = "text"> extends ProTableProps<DataSource, Params, ValueType> {
onItemBlur?: (value?: number | string, id?: any, record?: any) => void,
@ -31,10 +32,11 @@ const TimeTemplateTable = <DataSource extends AnyObject = AnyObject>(
title: '布控星期',
dataIndex: 'arrangeDay',
},
{
title: '算力占用',
dataIndex: 'powerOccupy',
},
// TODO: 暂时先注释后续在做这个功能
// {
// title: '算力占用',
// dataIndex: 'powerOccupy',
// },
{
title: '配置路数',
key: 'option',
@ -46,6 +48,11 @@ const TimeTemplateTable = <DataSource extends AnyObject = AnyObject>(
return (
<ProTable<DataSource>
columns={columns}
cardProps={{
bodyStyle: {
padding: 0
}
}}
bordered
scroll={{ y: 95 }}
toolbar={undefined}

View File

@ -8,7 +8,7 @@ const algorithmTableDataSource: any = []
const timeTemplateDataSource: any = []
const boxListData: any[] | (() => any[]) = []
for (let i = 0; i < 5; i += 1) {
for (let i = 0; i < 100; i += 1) {
algorithmTableDataSource.push({
id: String(i),
templateName: '算法模板' + (i + 1),

View File

@ -0,0 +1,21 @@
.zhst-material-algo {
&-title {
margin: 18px 16px;
font-size: 14px;
}
&-left {
&-list {
padding-bottom: 36px;
max-height: 612px;
overflow-y: scroll;
box-sizing: 'border-box';
}
}
&-middle {
&-cont {
padding: 84px;
}
}
}

View File

@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { AlgorithmConfigModal } from '@zhst/material';
import { Button } from 'antd';
const algorithmTableDataSource: any = []
const timeTemplateDataSource: any = []
@ -26,7 +27,7 @@ for (let i = 0; i < 5; i += 1) {
}
const demo = () => {
const [open, setOpen] = useState(true)
const [open, setOpen] = useState(false)
const [algorithmTableList, setAlgorithmTableList] = useState(algorithmTableDataSource)
const [timeTemplateData, setTimeTemplateData] = useState(timeTemplateDataSource)
const [boxList, setBoxList] = useState(boxListData)
@ -34,7 +35,8 @@ const demo = () => {
const [algorithmSelectedKey, setAlgorithmSelectedKey] = useState('1')
return (
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
<div>
<Button onClick={() => setOpen(true)}></Button>
<AlgorithmConfigModal
title="批量算法设置"
width={1500}

View File

@ -47,12 +47,12 @@ const Password: FC<PasswordProps> = (props) => {
<Form.Item
name="rePassword"
label="确认密码"
dependencies={['password']}
dependencies={['newPassword']}
rules={[
{ required: true, message: '请再次输入密码!' },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
if (!value || getFieldValue('newPassword') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('两次输入的密码不一致!'));

View File

@ -1,5 +1,161 @@
# @zhst/utils
## 0.20.3
### Patch Changes
- fix: 修改 zhst/meta
- Updated dependencies
- @zhst/meta@0.20.3
## 0.20.2
### Patch Changes
- zhst/biz、zhst/meta、zhst/material: 修改 od、修改 boxSelectTree
- Updated dependencies
- @zhst/meta@0.20.2
## 0.20.1
### Patch Changes
- fix: 修改 cropperImage 渲染问题
- Updated dependencies
- @zhst/meta@0.20.1
## 0.20.0
### Minor Changes
- fix: cropperImage 透出矩形坐标
### Patch Changes
- Updated dependencies
- @zhst/meta@0.20.0
## 0.19.0
### Minor Changes
- feat: 修复之前发版错乱问题
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.13.0
- @zhst/func@0.15.0
- @zhst/icon@0.5.0
- @zhst/meta@0.19.0
## 0.18.1
### Patch Changes
- Updated dependencies
- @zhst/func@0.14.1
- @zhst/hooks@0.12.1
- @zhst/meta@0.18.1
## 0.18.0
### Minor Changes
- feat: 重新发版
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.12.0
- @zhst/func@0.14.0
- @zhst/icon@0.4.0
- @zhst/meta@0.18.0
## 0.17.0
### Minor Changes
- fix: 修复适配问题
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.11.0
- @zhst/func@0.13.0
- @zhst/icon@0.3.0
- @zhst/meta@0.17.0
## 0.16.4
### Patch Changes
- Updated dependencies
- @zhst/func@0.12.0
- @zhst/hooks@0.10.4
- @zhst/meta@0.16.4
## 0.16.3
### Patch Changes
- @zhst/func@0.11.3
- @zhst/hooks@0.10.3
- @zhst/meta@0.16.3
## 0.16.2
### Patch Changes
- @zhst/func@0.11.2
- @zhst/hooks@0.10.2
- @zhst/meta@0.16.2
## 0.16.1
### Patch Changes
- @zhst/func@0.11.1
- @zhst/hooks@0.10.1
- @zhst/meta@0.16.1
## 0.16.0
### Minor Changes
- zhst/biz:新增摘要列表-无限滚动组件
### Patch Changes
- Updated dependencies
- @zhst/hooks@0.10.0
- @zhst/func@0.11.0
- @zhst/meta@0.16.0
## 0.15.0
### Minor Changes
- 视频添加 OD 框,查看大图首次点击修复
### Patch Changes
- Updated dependencies
- @zhst/meta@0.15.0
- @zhst/func@0.10.2
- @zhst/hooks@0.9.2
## 0.14.0
### Minor Changes
- zhst/meta 大图圈选组件点击选不上 bug 修复,attach 遮挡底部框事件阻止修复
### Patch Changes
- Updated dependencies
- @zhst/meta@0.14.0
## 0.13.0
### Minor Changes

View File

@ -17,8 +17,7 @@ import React, { useEffect, useState, useRef, useImperativeHandle } from 'react';
import classNames from 'classnames';
import { get, pick, isNull, generateImg, dataURLToBlob, getTransforms, addEventListenerWrapper, getFileByRect } from '@zhst/func';
import Align from 'rc-align';
import { Button, Empty } from '..';
import { IconFont as Icon } from '@zhst/icon';
import { Empty, AttachImage } from '..';
import { Cropper, Viewer, EVENT_VIEWER_TRANSFORM_CHANGE, EVENT_VIEWER_READY, EVENT_CROP_START, EVENT_CROP_END } from "../ImageEditor";
import BtnGroup from "./components/BtnGroup";
import "./index.less";
@ -480,16 +479,6 @@ export var BigImagePreview = /*#__PURE__*/React.forwardRef(function (props, ref)
}
};
// ============================= attact img =========================
var _useState17 = useState(0),
_useState18 = _slicedToArray(_useState17, 2),
selectAttachImgIndex = _useState18[0],
setSelectAttachImgIndex = _useState18[1];
var _useState19 = useState(false),
_useState20 = _slicedToArray(_useState19, 2),
isZoomin = _useState20[0],
setIsZoomin = _useState20[1];
// ============================== Ref ===============================
useImperativeHandle(ref, function () {
return {
@ -544,46 +533,10 @@ export var BigImagePreview = /*#__PURE__*/React.forwardRef(function (props, ref)
setShowCrop: setShowCrop,
cropType: cropType,
selectAlgorithmVersion: selectAlgorithmVersion
}))), (attachImg === null || attachImg === void 0 ? void 0 : attachImg.length) && !showCrop && /*#__PURE__*/React.createElement("div", {
className: classNames("".concat(componentName, "-attach"), isZoomin && "".concat(componentName, "-attach--zoomin"), "".concat(componentName, "-attach--fixed"), isZoomin && "".concat(componentName, "-attach--zoomin--fixed"))
}, /*#__PURE__*/React.createElement("div", {
className: classNames("".concat(componentName, "-attach__tab"))
}, showAttachImgLabel ? attachImg === null || attachImg === void 0 ? void 0 : attachImg.map(function (_ref6, index) {
var label = _ref6.label;
return /*#__PURE__*/React.createElement("div", {
key: index,
className: classNames("".concat(componentName, "-attach__tab-item"), selectAttachImgIndex === index && "".concat(componentName, "-attach__tab-item--select")),
onMouseEnter: function onMouseEnter() {
setSelectAttachImgIndex(index);
}
}, label);
}) : null), /*#__PURE__*/React.createElement("div", {
className: classNames("".concat(componentName, "-attach__scale"))
}, /*#__PURE__*/React.createElement(Button, {
type: "text"
//绝对定位下onClick事件失效采用onMouseDown
,
onMouseDown: function onMouseDown(e) {
//如果是左键执行
if (e.button == 0) {
setIsZoomin(function (pre) {
return !pre;
});
}
},
style: {
color: '#fff'
}
}, /*#__PURE__*/React.createElement(Icon, {
styles: {
display: 'flex'
},
icon: isZoomin ? 'icon-cancle_fullscreen' : 'icon-fullscreen'
}))), /*#__PURE__*/React.createElement("img", {
draggable: "false",
className: classNames("".concat(componentName, "-attach__img"), "".concat(componentName, "-attach__img--fixed")),
src: get(attachImg, "".concat(selectAttachImgIndex, ".url"), '')
})), (showScore || score) && /*#__PURE__*/React.createElement("div", {
}))), (attachImg === null || attachImg === void 0 ? void 0 : attachImg.length) && !showCrop && /*#__PURE__*/React.createElement(AttachImage, {
showAttachImgLabel: showAttachImgLabel,
data: attachImg
}), (showScore || score) && /*#__PURE__*/React.createElement("div", {
style: {
bottom: 20
},

View File

@ -101,8 +101,7 @@ var CompareImage = /*#__PURE__*/forwardRef(function (props, ref) {
type: "primary",
shape: "circle",
style: {
width: '56px',
height: '56px'
width: '56px'
},
icon: /*#__PURE__*/React.createElement(IconFont, {
icon: "icon-qiehuanzuo",
@ -116,8 +115,7 @@ var CompareImage = /*#__PURE__*/forwardRef(function (props, ref) {
type: "primary",
shape: "circle",
style: {
width: '56px',
height: '56px'
width: '56px'
},
icon: /*#__PURE__*/React.createElement(IconFont, {
icon: "icon-qiehuanyou",

View File

@ -123,6 +123,8 @@
box-sizing: border-box;
&__btn {
width: 56px;
height: 56px;
opacity: 0.4;
pointer-events: all;

View File

@ -30,31 +30,32 @@ export default {
_this$eventHandleList = this.eventHandleList,
eventHandleList = _this$eventHandleList === void 0 ? [] : _this$eventHandleList,
options = this.options;
//图片事件
// 鼠标滚轮事件
var scaleAble = get(options, 'scaleAble', true);
if (scaleAble) {
var handleWhele = addEventListenerWrapper(canvas, EVENT_WHEEL, this.onWheel.bind(this));
eventHandleList.push(handleWhele);
var handleWheel = addEventListenerWrapper(canvas, EVENT_WHEEL, this.onWheel.bind(this));
eventHandleList.push(handleWheel);
}
// 鼠标 - 拖拽事件
var dragAble = get(options, 'dragAble', true);
if (dragAble) {
var handleDragStart = addEventListenerWrapper(canvas, EVENT_POINTER_DOWN, this.onDragStart.bind(this));
eventHandleList.push(addEventListenerWrapper);
eventHandleList.push(handleDragStart);
var handleDragMove = addEventListenerWrapper(element.ownerDocument, EVENT_POINTER_MOVE, this.onDragMove.bind(this));
eventHandleList.push(handleDragMove);
EVENT_POINTER_UP.trim().split(REGEXP_SPACES).forEach(function (eventName) {
var handleDragEnd = addEventListenerWrapper(element.ownerDocument, eventName, _this.onDragEnd.bind(_this));
EVENT_POINTER_UP.trim().split(REGEXP_SPACES).forEach(function (_eventName) {
var handleDragEnd = addEventListenerWrapper(element.ownerDocument, _eventName, _this.onDragEnd.bind(_this));
eventHandleList.push(handleDragEnd);
});
}
//rect事件
var handleClick = addEventListenerWrapper(canvas, EVENT_CLICK, this.onClick.bind(this));
eventHandleList.push(handleClick);
// const handleLeveal = addEventListenerWrapper(canvas, EVENT_LEAVEL, this.onLeavel.bind(this));
// eventHandleList.push(handleLeveal);
// const handleEnter = addEventListenerWrapper(canvas, EVENT_ENTER, this.onEnter.bind(this));
// eventHandleList.push(handleEnter);
// 鼠标 - 点击事件
var selectAble = get(options, 'selectAble', true);
if (selectAble) {
var handleClick = addEventListenerWrapper(canvas, EVENT_CLICK, this.onClick.bind(this));
eventHandleList.push(handleClick);
}
},
unbind: function unbind() {
var eventHandleList = this.eventHandleList;
@ -67,7 +68,7 @@ export default {
}
}
},
/* 图片事件 */onWheel: function onWheel(event, cropBox) {
/* 鼠标滚轮事件 */onWheel: function onWheel(event, cropBox) {
var _this2 = this;
event.stopPropagation();
event.preventDefault();
@ -97,6 +98,7 @@ export default {
})
}, cropBox);
},
// 鼠标拖拽 - 开始拖拽
onDragStart: function onDragStart(event) {
event.stopPropagation();
// This line is required for preventing page zooming in iOS browsers
@ -119,6 +121,7 @@ export default {
this.action = ACTION_DRAG;
addClass(this.canvas, CLASS_MOVE);
},
// 鼠标拖拽 - 拖拽中
onDragMove: function onDragMove(event) {
event.stopPropagation();
@ -147,6 +150,7 @@ export default {
this.pointer.startX = this.pointer.endX;
this.pointer.startY = this.pointer.endY;
},
// 鼠标拖拽 - 停止拖拽
onDragEnd: function onDragEnd(event) {
event.stopPropagation();
var action = this.action;
@ -158,14 +162,7 @@ export default {
this.point = null;
removeClass(this.canvas, CLASS_MOVE);
},
/* rect事件 */
// onLeavel(event) {
// const pointerCenter = this.windowToCanvasAxis(event);
// this.highlightShape(pointerCenter);
// },
// onEnter(event) {
// this.highlightShape(null);
// },
// 鼠标点击
onClick: function onClick(event) {
event.stopPropagation();
var pointerCenter = this.windowToCanvasAxis(event);

View File

@ -13,7 +13,12 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
//@ts-nocheck
import * as turf from '@turf/turf';
import { AXIS_TYPE_ORIGIN, AXIS_TYPE_CANVAS, AXIS_TYPE_IMAGE } from "./constants";
export function rectToPolygon(axisRect) {
var polygon = turf.polygon([[[setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)], [setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y, -2)], [setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y2, -2)], [setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y2, -2)], [setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)]]]);
return polygon;
}
//设置数据的精度
//accuracy 表示精度 以原点为中心向左为正,向右为负,
@ -42,17 +47,14 @@ export default {
targetTransform: {
translateX: 0,
translateY: 0,
scale: 0,
scale: 1,
rotate: 0
// rotate: 90,
},
windowToCanvasAxis: function windowToCanvasAxis(event) {
var _this$canvas$getBound = this.canvas.getBoundingClientRect(),
x = _this$canvas$getBound.x,
y = _this$canvas$getBound.y;
return {
x: event.pageX - x,
y: event.pageY - y,
x: event.offsetX,
y: event.offsetY,
__AXIS_TYPE__: AXIS_TYPE_CANVAS
};
},
@ -76,7 +78,8 @@ export default {
var targetTransform = this.targetTransform;
var translateX = targetTransform.translateX,
translateY = targetTransform.translateY,
scale = targetTransform.scale;
_targetTransform$scal = targetTransform.scale,
scale = _targetTransform$scal === void 0 ? 1 : _targetTransform$scal;
var axis = _objectSpread(_objectSpread({
x: translateX + x * scale,
y: translateY + y * scale

View File

@ -75,7 +75,14 @@ var Viewer = (_dec = Mixin(Render, Event, Shape, Helper), _dec(_class = /*#__PUR
}, {
key: "build",
value: function build() {
var _this$options = this.options,
_this$options$width = _this$options.width,
width = _this$options$width === void 0 ? 300 : _this$options$width,
_this$options$height = _this$options.height,
height = _this$options$height === void 0 ? 150 : _this$options$height;
var canvas = document.createElement('canvas');
canvas.width = width || canvas.width;
canvas.height = height || canvas.height;
addClass(canvas, CLASS_CANVAS);
this.element.appendChild(canvas);
this.canvas = canvas;

View File

@ -65,7 +65,6 @@ export default {
});
},
initCanvas: function initCanvas() {
if (!this.image) return;
//通过样式设置 不依赖父元素的prosition
var element = this.element,
canvas = this.canvas,
@ -83,7 +82,7 @@ export default {
var fitTransform = this.calcFitScreen();
this.targetTransform = Object.assign({}, this.targetTransform, fitTransform);
dispatchEvent(this.element, EVENT_VIEWER_TRANSFORM_CHANGE, cloneDeep(this.targetTransform));
//产品需求fitscale 是minscale
//产品需求fitscale 是 minscale
var _options$fitScaleAsMi = options.fitScaleAsMinScale,
fitScaleAsMinScale = _options$fitScaleAsMi === void 0 ? false : _options$fitScaleAsMi;
if (fitScaleAsMinScale) {
@ -106,8 +105,9 @@ export default {
};
loop();
},
// 绘制画布
renderCanvas: function renderCanvas(_ctx) {
if (!this.image || !this.canvas) return;
if (!this.canvas) return;
var containerData = this.containerData,
canvas = this.canvas,
targetTransform = this.targetTransform,
@ -128,14 +128,14 @@ export default {
ctx.setTransform(scale, 0, 0, scale, translateX, translateY);
// ctx.setTransform(scale, 0, 0, scale, translateX, translateY);
//旋转
var centerX = this.image.width / 2;
var centerY = this.image.height / 2;
var centerX = this.image ? this.image.width / 2 : canvas.width;
var centerY = this.image ? this.image.height / 2 : canvas.height;
ctx.translate(centerX, centerY);
ctx.rotate(rotate / 180 * Math.PI);
ctx.translate(-centerX, -centerY);
//图片
ctx.drawImage(this.image, 0, 0);
this.image && ctx.drawImage(this.image, 0, 0);
ctx.restore();
//画图形
ctx.save();

View File

@ -14,13 +14,9 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
import { isNil, isArray, isFunction } from '@zhst/func';
import * as turf from '@turf/turf';
import booleanPointInPolygon from '@turf/boolean-point-in-polygon';
import { setNumberAccuracy } from "./helper";
import { rectToPolygon } from "./helper";
import { SHAPE_TYPE_RECT, SHAPE_TYPE_CIRCLE, EVENT_SHAPE_SELECT } from "./constants";
import { dispatchEvent } from "../utils";
function rectToPolygon(axisRect) {
var polygon = turf.polygon([[[setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)], [setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y, -2)], [setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y2, -2)], [setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y2, -2)], [setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)]]]);
return polygon;
}
export default {
//store
shapeList: [],
@ -47,20 +43,20 @@ export default {
this.changeZoonAble(true);
}
},
//method
addShape: function addShape(shap) {
//method:添加矩形
addShape: function addShape(shape) {
var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : SHAPE_TYPE_RECT;
this.color = shap.color ? shap.color : '';
if (isNil(shap) || this.disableAdd) return;
this.color = shape.color ? shape.color : '';
if (isNil(shape) || this.disableAdd) return;
var _this$shapeList = this.shapeList,
preShapeList = _this$shapeList === void 0 ? [] : _this$shapeList;
var shapList = isArray(shap) ? shap : [shap];
shapList = shapList.map(function (v) {
var _shapeList = isArray(shape) ? shape : [shape];
_shapeList = _shapeList.map(function (v) {
return _objectSpread(_objectSpread({}, v), {}, {
__SHAPE_TYPE__: type
});
});
this.shapeList = [].concat(_toConsumableArray(preShapeList), _toConsumableArray(shapList));
this.shapeList = [].concat(_toConsumableArray(preShapeList), _toConsumableArray(_shapeList));
},
//
setSelectShapId: function setSelectShapId(id) {
@ -214,9 +210,12 @@ export default {
}
}
},
// 绘制矩形
renderRect: function renderRect(ctx, shape, type) {
//算rect
var axisRect = this.imgRectAxisToCanvasAxisRect(shape);
var axisRect = this.imgRectAxisToCanvasAxisRect(_objectSpread(_objectSpread({}, shape), {}, {
image: ctx.canvas
}));
var rect = {
x: axisRect.x2 > axisRect.x ? axisRect.x : axisRect.x2,
y: axisRect.y2 > axisRect.y ? axisRect.y : axisRect.y2,

View File

@ -1,4 +1,5 @@
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@ -18,15 +19,14 @@ import { noop, get, addEventListenerWrapper, dataURLToBlob, nextTick, toRealNumb
import Align from 'rc-align';
import { useLatest, useUpdateEffect, useFullscreen, useUnmount } from '@zhst/hooks';
import classNames from 'classnames';
import download from 'downloadjs';
import { Button, message } from '..';
import { message } from '..';
import { IconFont } from '@zhst/icon';
import { Cropper, EVENT_CROP_START, EVENT_CROP_END } from "../ImageEditor";
import { Cropper, EVENT_CROP_START, EVENT_CROP_END, Viewer } from "../ImageEditor";
import FlvPlayer, { FLV_EVENT } from "./components/FlvPlayer";
import Range from "./components/Progress";
import Loading from "./components/Loading";
import { CROP_TYPE } from "../utils/constants";
import { getShowStatus } from "./videoPlayerHelper";
import { downloadFrame, getShowStatus } from "./videoPlayerHelper";
import "./index.less";
var componentName = "zhst-image__video-view";
var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
@ -42,6 +42,16 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
adjustY: true
}
} : _props$screenshotButt,
_props$autoPlay = props.autoPlay,
autoPlay = _props$autoPlay === void 0 ? false : _props$autoPlay,
odList = props.odList,
showOD = props.showOD,
_props$width = props.width,
width = _props$width === void 0 ? '100%' : _props$width,
_props$height = props.height,
height = _props$height === void 0 ? '532px' : _props$height,
_props$backgroundColo = props.backgroundColor,
backgroundColor = _props$backgroundColo === void 0 ? '#333' : _props$backgroundColo,
_props$screenshotButt2 = props.screenshotButtonRender,
screenshotButtonRender = _props$screenshotButt2 === void 0 ? function () {
return /*#__PURE__*/React.createElement("div", {
@ -53,7 +63,8 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
}, "\u56DE\u8C03DOM");
} : _props$screenshotButt2,
onCropChange = props.onCropChange,
defaultNormalizationRect = props.defautlNormalizationRect;
defaultNormalizationRect = props.defautlNormalizationRect,
playerProps = props.playerProps;
// ========================== 播放 =========================
//实例参数
var containerRef = useRef(null); //容器ref
@ -257,6 +268,8 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
console.error(e);
}
});
// 重新加载
var _reload = /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
var oldTime;
@ -309,6 +322,8 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
return _ref.apply(this, arguments);
};
}();
// 进度条操作
var seek = function seek(v) {
if (videoInsRef.current && isVideoLoadFinished) {
setPlayTime(parseFloat(v));
@ -317,6 +332,7 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
message.warning('待视频加载完,才可操作进度条');
}
};
// ========================== 视频opt bar =========================
var _useFullscreen = useFullscreen(containerRef, {
pageFullscreen: true
@ -354,17 +370,15 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
_useState22 = _slicedToArray(_useState21, 2),
cropRect = _useState22[0],
setCropRect = _useState22[1];
useEffect(function () {
var _videoInsRef$current4, _videoInsRef$current5;
showCrop ? videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current4 = videoInsRef.current) === null || _videoInsRef$current4 === void 0 ? void 0 : _videoInsRef$current4.pause() : videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current5 = videoInsRef.current) === null || _videoInsRef$current5 === void 0 ? void 0 : _videoInsRef$current5.play();
}, [showCrop]);
// 监听showCrop, isReady - 是否可编辑、视频播放组件是否挂载
useEffect(function () {
var handlerCropStart;
var handlerCropEnd;
setCropRect(null);
if (!isReady) return;
if (showCrop) {
var _canvas$parentNode;
var _canvas$parentNode, _videoInsRef$current4;
handlerCropStart = addEventListenerWrapper(corpContainerRef.current, EVENT_CROP_START, function () {
setCropRect(null);
});
@ -412,18 +426,41 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
};
}
isFirstFlagRef.current = false;
// 初始化圈选工具
cropInsRef.current = new Cropper(corpContainerRef.current, {
showMask: true,
cropBoxLimited: cropBoxLimited,
editAble: false,
img: imageData,
initialCropBoxData: initialCropBoxData
});
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current4 = videoInsRef.current) === null || _videoInsRef$current4 === void 0 || _videoInsRef$current4.pause();
} else {
var _videoInsRef$current5;
var _element = videoInsRef.current._mediaElement || {};
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current5 = videoInsRef.current) === null || _videoInsRef$current5 === void 0 || _videoInsRef$current5.play();
// 挂载图片选择
cropInsRef.current = new Viewer(corpContainerRef.current, {
scaleAble: false,
selectAble: false,
dragAble: false,
width: _element.clientWidth,
height: _element.clientHeight,
backgroundColor: 'transparent'
});
// 判定是否存在od框
showOD && (odList === null || odList === void 0 ? void 0 : odList.forEach(function (_od) {
var _cropInsRef$current;
cropInsRef === null || cropInsRef === void 0 || (_cropInsRef$current = cropInsRef.current) === null || _cropInsRef$current === void 0 || _cropInsRef$current.addShape(_od);
}));
}
return function () {
var _handlerCropStart, _handlerCropEnd, _cropInsRef$current, _cropInsRef$current$d;
var _handlerCropStart, _handlerCropEnd, _cropInsRef$current2, _cropInsRef$current2$;
(_handlerCropStart = handlerCropStart) === null || _handlerCropStart === void 0 || _handlerCropStart.remove();
(_handlerCropEnd = handlerCropEnd) === null || _handlerCropEnd === void 0 || _handlerCropEnd.remove();
cropInsRef === null || cropInsRef === void 0 || (_cropInsRef$current = cropInsRef.current) === null || _cropInsRef$current === void 0 || (_cropInsRef$current$d = _cropInsRef$current.destroy) === null || _cropInsRef$current$d === void 0 || _cropInsRef$current$d.call(_cropInsRef$current);
cropInsRef === null || cropInsRef === void 0 || (_cropInsRef$current2 = cropInsRef.current) === null || _cropInsRef$current2 === void 0 || (_cropInsRef$current2$ = _cropInsRef$current2.destroy) === null || _cropInsRef$current2$ === void 0 || _cropInsRef$current2$.call(_cropInsRef$current2);
cropInsRef.current = null;
};
}, [showCrop, isReady]);
@ -502,7 +539,7 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
};
}();
//回调
// 监听showCrop、cropRect - 监听是否可编辑、绘制的矩形
useEffect(function () {
//计算归一化crop rect
var normalizationRect = null;
@ -528,41 +565,11 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
}, [showCrop, cropRect]);
// ========================== 截帧 =========================
var downloadVideoframe = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4() {
var _videoInsRef$current6, _videoInsRef$current7, video, canvas, ctx, base64;
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
try {
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current6 = videoInsRef.current) === null || _videoInsRef$current6 === void 0 || (_videoInsRef$current7 = _videoInsRef$current6.pause) === null || _videoInsRef$current7 === void 0 || _videoInsRef$current7.call(_videoInsRef$current6);
video = videoRef.current;
canvas = document.createElement('canvas');
ctx = canvas.getContext('2d');
//当视频处于还未加载出来时,截屏为黑色图片
if (video.readyState === 0) {
ctx === null || ctx === void 0 || ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = video.offsetWidth;
canvas.height = video.offsetHeight;
// @ts-ignore
ctx.fillStyle = 'black';
ctx === null || ctx === void 0 || ctx.fillRect(0, 0, canvas.width, canvas.height);
base64 = canvas.toDataURL();
} else {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx === null || ctx === void 0 || ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
base64 = canvas.toDataURL('image/png');
}
download(base64);
} catch (error) {
console.error(error);
}
case 1:
case "end":
return _context4.stop();
}
}, _callee4);
})), []);
var downloadVideoFrame = useCallback(function (opt) {
var _videoInsRef$current6, _videoInsRef$current7;
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current6 = videoInsRef.current) === null || _videoInsRef$current6 === void 0 || (_videoInsRef$current7 = _videoInsRef$current6.pause) === null || _videoInsRef$current7 === void 0 || _videoInsRef$current7.call(_videoInsRef$current6);
downloadFrame(videoRef.current, opt);
}, []);
// ============================== 暴露出去的方法 ===============================
var latestIsReady = useLatest(isReady);
@ -573,17 +580,31 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
setShowCrop: function setShowCrop(dispatch) {
var isReady = latestIsReady.current;
if (!isReady) return;
_setShowCrop(dispatch);
_setShowCrop === null || _setShowCrop === void 0 || _setShowCrop(dispatch);
},
downloadVideoframe: downloadVideoframe
downloadVideoFrame: downloadVideoFrame,
pause: function pause() {
var _videoInsRef$current8, _videoInsRef$current9;
(_videoInsRef$current8 = videoInsRef.current) === null || _videoInsRef$current8 === void 0 || (_videoInsRef$current9 = _videoInsRef$current8.pause) === null || _videoInsRef$current9 === void 0 || _videoInsRef$current9.call(_videoInsRef$current8);
},
play: function play() {
var _videoInsRef$current10, _videoInsRef$current11;
(_videoInsRef$current10 = videoInsRef.current) === null || _videoInsRef$current10 === void 0 || (_videoInsRef$current11 = _videoInsRef$current10.play) === null || _videoInsRef$current11 === void 0 || _videoInsRef$current11.call(_videoInsRef$current10);
},
reload: _reload
};
});
return /*#__PURE__*/React.createElement("div", {
className: classNames("".concat(componentName)),
ref: containerRef
}, url && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FlvPlayer, {
ref: containerRef,
style: {
width: width,
height: height,
backgroundColor: backgroundColor
}
}, url && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FlvPlayer, _extends({
playId: playSeq,
autoPlay: true,
autoPlay: autoPlay,
className: classNames("".concat(componentName, "-flv")),
type: url.startsWith('http') ? 'mp4' : 'flv',
url: url,
@ -594,12 +615,14 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
hasAudio: false,
hasVideo: true
},
onCreat: initPlayer
}), /*#__PURE__*/React.createElement("div", {
onCreate: initPlayer
}, playerProps)), /*#__PURE__*/React.createElement("div", {
className: classNames("".concat(componentName, "-crop-container")),
ref: corpContainerRef,
style: {
display: isFullscreen ? 'none' : 'block'
display: isFullscreen ? 'none' : 'block',
width: '100%',
height: '100%'
}
}), showCrop && cropRect && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
ref: alginContainerRef,
@ -625,27 +648,26 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
cropType: CROP_TYPE['CUSTOM']
}))), !showCrop && /*#__PURE__*/React.createElement("div", {
className: "".concat(componentName, "-opt")
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Button, {
type: "text",
onClick: function onClick() {
if (!isPlay) {
var _videoInsRef$current8;
//播放中暂停
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current8 = videoInsRef.current) === null || _videoInsRef$current8 === void 0 || _videoInsRef$current8.play();
_setShowCrop(false);
} else {
var _videoInsRef$current9;
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current9 = videoInsRef.current) === null || _videoInsRef$current9 === void 0 || _videoInsRef$current9.pause();
}
}
}, /*#__PURE__*/React.createElement(IconFont, {
styles: {
marginRight: '12px',
color: '#fff',
display: 'flex'
},
onIconClick: function onIconClick() {
if (!isPlay) {
var _videoInsRef$current12;
//播放中暂停
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current12 = videoInsRef.current) === null || _videoInsRef$current12 === void 0 || _videoInsRef$current12.play();
_setShowCrop(false);
} else {
var _videoInsRef$current13;
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current13 = videoInsRef.current) === null || _videoInsRef$current13 === void 0 || _videoInsRef$current13.pause();
}
},
color: "#1890ff",
icon: !isPlay ? 'icon-shipinbofang' : 'icon-shipinzanting'
}))), /*#__PURE__*/React.createElement("div", {
icon: !isPlay ? 'icon-bofang3' : 'icon-zanting1'
}), /*#__PURE__*/React.createElement("div", {
className: "".concat(componentName, "-opt-range"),
onClick: function onClick(e) {
e.stopPropagation();
@ -656,20 +678,19 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
max: showMaxDuration,
showSlider: showSlider,
onChange: seek
}), /*#__PURE__*/React.createElement("div", null, formatDurationTime(playTime), "/", formatDurationTime(showMaxDuration))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Button, {
type: "text",
onClick: function onClick(e) {
}), /*#__PURE__*/React.createElement("div", null, formatDurationTime(playTime), "/", formatDurationTime(showMaxDuration))), /*#__PURE__*/React.createElement(IconFont, {
styles: {
display: 'flex',
marginLeft: '12px'
},
color: "#fff",
size: 18,
onIconClick: function onIconClick(e) {
e.stopPropagation();
toggleFullscreen();
}
}, /*#__PURE__*/React.createElement(IconFont, {
styles: {
color: '#fff',
display: 'flex'
},
size: 18,
icon: isFullscreen ? 'icon-cancle_fullscreen' : 'icon-fullscreen'
})))), !!showStatus && /*#__PURE__*/React.createElement(Loading, {
icon: isFullscreen ? 'icon-suoxiao1' : 'icon-quanping1'
})), !!showStatus && /*#__PURE__*/React.createElement(Loading, {
status: showStatus,
reload: function reload() {
return _reload();

View File

@ -1,4 +1,4 @@
var _excluded = ["className", "autoPlay", "config", "onCreat", "playId"];
var _excluded = ["className", "autoPlay", "config", "onCreate", "playId"];
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@ -45,12 +45,13 @@ var VideoPlayer = /*#__PURE__*/function (_Component) {
autoPlay = _this$props$autoPlay === void 0 ? true : _this$props$autoPlay,
_this$props$config = _this$props.config,
config = _this$props$config === void 0 ? {} : _this$props$config,
onCreat = _this$props.onCreat,
onCreate = _this$props.onCreate,
playId = _this$props.playId,
others = _objectWithoutProperties(_this$props, _excluded);
if ($video) {
if (flvjs.isSupported() && _this.props.url && _this.props.url) {
var reload = function reload() {
var _this$flvPlayer;
if (_this.flvPlayer && _this.flvPlayer.destroy) {
try {
_this.flvPlayer.destroy();
@ -65,7 +66,7 @@ var VideoPlayer = /*#__PURE__*/function (_Component) {
flvPlayer.load();
_this.flvPlayer = flvPlayer;
// @ts-ignore
var controller = _this.flvPlayer._transmuxer._controller;
var controller = (_this$flvPlayer = _this.flvPlayer) === null || _this$flvPlayer === void 0 || (_this$flvPlayer = _this$flvPlayer._transmuxer) === null || _this$flvPlayer === void 0 ? void 0 : _this$flvPlayer._controller;
var wsLoader = controller._ioctl._loader;
var oldWsOnCompleteFunc = wsLoader._onComplete;
wsLoader._onComplete = function () {
@ -82,10 +83,9 @@ var VideoPlayer = /*#__PURE__*/function (_Component) {
oldWsOnCompleteFunc();
};
_this.flvPlayer.reload = reload;
onCreat && onCreat(_this.flvPlayer, $video);
onCreate === null || onCreate === void 0 || onCreate(_this.flvPlayer, $video);
};
reload();
onCreat && onCreat(_this.flvPlayer, $video);
}
}
});
@ -95,9 +95,9 @@ var VideoPlayer = /*#__PURE__*/function (_Component) {
key: "componentWillUnmount",
value: function componentWillUnmount() {
if (this.flvPlayer) {
var _this$flvPlayer, _this$flvPlayer2;
(_this$flvPlayer = this.flvPlayer) === null || _this$flvPlayer === void 0 || _this$flvPlayer.unload();
(_this$flvPlayer2 = this.flvPlayer) === null || _this$flvPlayer2 === void 0 || _this$flvPlayer2.detachMediaElement();
var _this$flvPlayer2, _this$flvPlayer3;
(_this$flvPlayer2 = this.flvPlayer) === null || _this$flvPlayer2 === void 0 || _this$flvPlayer2.unload();
(_this$flvPlayer3 = this.flvPlayer) === null || _this$flvPlayer3 === void 0 || _this$flvPlayer3.detachMediaElement();
}
}
}, {
@ -119,13 +119,11 @@ var VideoPlayer = /*#__PURE__*/function (_Component) {
return /*#__PURE__*/React.createElement("video", {
muted: true,
preload: "metadata",
className: className
// controls={true}
,
style: Object.assign({
className: className,
style: _objectSpread({
width: '100%',
height: '100%'
}, style ? style : {}),
}, style),
ref: this.initFlv
});
}

View File

@ -3,7 +3,7 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import React from 'react';
import classNames from 'classnames';
import { Slider } from 'antd';
import { Slider, ConfigProvider } from 'antd';
import "./index.less";
var componentName = "zhst-image__range";
export var Range = function Range(props) {
@ -15,6 +15,15 @@ export var Range = function Range(props) {
return /*#__PURE__*/React.createElement("div", {
style: style,
className: classNames("".concat(componentName), !showSlider && "".concat(componentName, "--no-slider"), className)
}, /*#__PURE__*/React.createElement(Slider, others));
}, /*#__PURE__*/React.createElement(ConfigProvider, {
theme: {
components: {
Slider: {
railBg: 'rgba(255, 255, 255, 0.6)',
railHoverBg: 'rgba(255, 255, 255, 0.6)'
}
}
}
}, /*#__PURE__*/React.createElement(Slider, others)));
};
export default Range;

View File

@ -1,8 +1,6 @@
.zhst-image__video-view {
position: relative;
overflow: hidden;
width: 100%;
height: 532px;
background-color: #333;
// &-flv {
@ -37,7 +35,7 @@
height: 32px;
box-sizing: border-box;
align-items: center;
padding: 0 12px;
padding: 0 12px 0 24px;
background-color: rgb(0 0 0 / 80%);
line-height: 32px;

View File

@ -1,3 +1,4 @@
import download from "downloadjs";
export function getShowStatus(isLoadingVideo, isEnd, isError) {
var status = null;
if (isLoadingVideo) {
@ -10,4 +11,41 @@ export function getShowStatus(isLoadingVideo, isEnd, isError) {
status = 'END';
}
return status;
}
}
// 下载功能的可配置属性
// 视屏截帧、下载
export var downloadFrame = function downloadFrame(_videoDom, opt) {
var _ref = opt || {},
_ref$downloadAble = _ref.downloadAble,
downloadAble = _ref$downloadAble === void 0 ? true : _ref$downloadAble,
_ref$fileName = _ref.fileName,
fileName = _ref$fileName === void 0 ? 'image' : _ref$fileName,
_ref$fileType = _ref.fileType,
fileType = _ref$fileType === void 0 ? 'image/png' : _ref$fileType;
try {
var video = _videoDom;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var base64;
//当视频处于还未加载出来时,截屏为黑色图片
if (video.readyState === 0) {
ctx === null || ctx === void 0 || ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = video.offsetWidth;
canvas.height = video.offsetHeight;
// @ts-ignore
ctx.fillStyle = 'black';
ctx === null || ctx === void 0 || ctx.fillRect(0, 0, canvas.width, canvas.height);
} else {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx === null || ctx === void 0 || ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
}
base64 = canvas.toDataURL(fileType);
downloadAble && download(base64, fileName);
} catch (error) {
console.error(error);
}
};

Some files were not shown because too many files have changed in this diff Show More