feat: 初始化项目(待优化ts)
This commit is contained in:
parent
8d8aa13483
commit
c4390a34d7
@ -13,6 +13,7 @@ export default defineConfig({
|
||||
'@zhst/func': path.join(__dirname, 'packages/func/src'),
|
||||
'@zhst/biz': path.join(__dirname, 'packages/biz/src'),
|
||||
'@zhst/meta': path.join(__dirname, 'packages/meta/src'),
|
||||
'@zhst/request': path.join(__dirname, 'packages/request/src'),
|
||||
},
|
||||
resolve: {
|
||||
docDirs: ['docs'],
|
||||
@ -21,6 +22,9 @@ export default defineConfig({
|
||||
{ type: 'utils', dir: 'packages/func/src' },
|
||||
{ type: 'biz', dir: 'packages/biz/src' },
|
||||
{ type: 'meta', dir: 'packages/meta/src' },
|
||||
{ type: 'request', dir: 'packages/request/src' },
|
||||
{ type: 'constants', dir: 'packages/constants/src' },
|
||||
{ type: 'types', dir: 'packages/types/src' },
|
||||
],
|
||||
},
|
||||
monorepoRedirect: {
|
||||
|
@ -20,6 +20,79 @@ features:
|
||||
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>
|
||||
</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。
|
||||
|
@ -15,7 +15,7 @@
|
||||
"lib/**/style/*",
|
||||
"*.less"
|
||||
],
|
||||
"main": "lib/index.tsx",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"typings": "es/index.d.ts",
|
||||
"exports": {
|
||||
@ -32,6 +32,9 @@
|
||||
"access": "public",
|
||||
"registry": "http://10.0.0.77:4874"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@zhst/types": "workspace:^"
|
||||
},
|
||||
"dependencies": {
|
||||
"@zhst/func": "workspace:^",
|
||||
"@zhst/hooks": "workspace:^",
|
||||
|
@ -67,256 +67,7 @@
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
}
|
||||
.zhst-image__img-view {
|
||||
position: relative;
|
||||
width: calc(100% - 46px - 46px - 20px - 20px);
|
||||
height: 100%;
|
||||
margin: 0 66px;
|
||||
|
||||
&-opt {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&-crop-opt {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&-align {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-main {
|
||||
// height: 532px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
&--cursor {
|
||||
& canvas {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-screenshot {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&-attach {
|
||||
position: absolute;
|
||||
z-index: 11;
|
||||
bottom: 0;
|
||||
|
||||
// left: 78px;
|
||||
left: 0;
|
||||
min-width: 120px;
|
||||
height: 202px;
|
||||
transition: all 200ms;
|
||||
|
||||
&--fixed {
|
||||
width: 152px !important;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
&--zoomin {
|
||||
height: 532px;
|
||||
|
||||
&--fixed {
|
||||
width: 398px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__tab {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 24px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
opacity: 0.5;
|
||||
transition: all 200ms;
|
||||
|
||||
&--select {
|
||||
width: 48px;
|
||||
height: 34px;
|
||||
background: #09f;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__scale {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgb(0 0 0 / 60%);
|
||||
border-radius: 100%;
|
||||
cursor: pointer;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__img {
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
|
||||
&--fixed {
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.zhst-image__video-view {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 532px;
|
||||
background-color: #333;
|
||||
|
||||
// &-flv {
|
||||
// width: 85%;
|
||||
// }
|
||||
&-screenshot {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&-crop-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&-align {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-opt {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
background-color: rgb(0 0 0 / 80%);
|
||||
line-height: 32px;
|
||||
|
||||
&>div:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
&>div:last-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
&-range {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
|
||||
&>div:first-child {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&>div:last-child {
|
||||
width: 100px;
|
||||
margin-left: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__player-mask {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgb(4 4 4 / 70%);
|
||||
|
||||
&--bg {
|
||||
z-index: 999;
|
||||
background-color: rgb(4 4 4 / 100%);
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&-title {
|
||||
margin-top: 12px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
|
||||
& a {
|
||||
color: #09f;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__icon-wraper {
|
||||
display: flex;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgb(255 255 255 / 10%);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
line-height: 80px;
|
||||
text-align: center;
|
||||
|
||||
// &:hover {
|
||||
// background: #0099ff;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
.zhst-image__tool {
|
||||
display: flex;
|
||||
@ -553,57 +304,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.zhst-image__btn-group {
|
||||
// display: flex;
|
||||
width: 30px;
|
||||
box-shadow: 0 2px 6px 0 rgb(0 0 0 / 40%);
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #000;
|
||||
|
||||
&>button {
|
||||
padding: 0;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
color: #09f;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #09f;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&>span {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
&--active {
|
||||
&>button {
|
||||
color: #09f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--circle {
|
||||
background-color: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&--circle &__item {
|
||||
margin-bottom: 4px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.zhst-image__compater-view {
|
||||
display: flex;
|
||||
@ -962,78 +662,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.zhst-image__score {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 50%;
|
||||
|
||||
&-bg {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
border: 2px solid #fff;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
|
||||
&-inner {
|
||||
width: 100% !important;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-score {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
color: rgb(0 0 0 / 88%);
|
||||
font-size: 28px;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
&-text {
|
||||
width: 54px;
|
||||
height: 24px;
|
||||
color: rgb(0 0 0 / 88%);
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.zhst-image__CornerScore {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 56px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
text-align: right;
|
||||
vertical-align: bottom;
|
||||
padding-right: 1px;
|
||||
background-size: 100%;
|
||||
background-image: url('./images/percent_background.png');
|
||||
z-index: 99;
|
||||
|
||||
&>span {
|
||||
padding-right: 6px;
|
||||
line-height: 22px;
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.zhst-image__attributePanel {
|
||||
margin: 0 66px;
|
||||
background: #f6f6f6;
|
||||
|
@ -2,7 +2,7 @@
|
||||
* 适配老的大屏组件数据格式传入
|
||||
*/
|
||||
import React from 'react';
|
||||
import { AlgorithmVersionStr, HumanProperty, ObjectType, Rect, ViewOption, AlignType, IScreenshotButtonProp, ODRECT } from './interface'
|
||||
import { AlgorithmVersionStr, HumanProperty, ObjectType, Rect, ViewOption, AlignType, IScreenshotButtonProp, ODRECT } from '@zhst/types'
|
||||
|
||||
export type TAB_TYPE = 'COMPATER' | 'NORMAL' | 'TRACK';
|
||||
export type MODEL_TYPE = 'VIDEO' | 'IMAGE';
|
||||
|
@ -1,217 +0,0 @@
|
||||
export type CamerasStatusList = [string[], string[], string[]];
|
||||
|
||||
export type Rect = { x: number; y: number; w: number; h: number };
|
||||
|
||||
export type StatusList = {
|
||||
taskOverview: any;
|
||||
taskIndex: {
|
||||
deviceId: string;
|
||||
solutionId: string;
|
||||
};
|
||||
}[];
|
||||
|
||||
export enum OperationType {
|
||||
OPERATION_TYPE_UNKNOW, // 未知状态,传该值会报错
|
||||
OPERATION_TYPE_START,
|
||||
OPERATION_TYPE_STOP
|
||||
}
|
||||
|
||||
|
||||
export enum AlgorithmVersion {
|
||||
VERSION_MGN_BNN, // MGN+BNN算法
|
||||
VERSION_BNN_PRO, // BNNPRO算法
|
||||
VERSION_BNN_PRO_ATTR, // BNNPROATTR算法
|
||||
VERSION_BNN_PRO_ATTR_SCORE, // BNNPROATTR算法
|
||||
VERSION_FACE, // 人脸算法
|
||||
VERSION_HEAD, // 头肩算法
|
||||
VERSION_NON_MOTOR_VEHICLE, // 非机动车的算法
|
||||
VERSION_REID_HEAD_ATTR, // 形体头肩属性三种特征融合的算法
|
||||
VERSION_MOTOR_VEHICLE, // 机动车的算法
|
||||
}
|
||||
|
||||
export enum AlgorithmVersionStr {
|
||||
VERSION_MGN_BNN = 'VERSION_MGN_BNN', // MGN+BNN算法
|
||||
VERSION_BNN_PRO = 'VERSION_BNN_PRO', // BNNPRO算法
|
||||
VERSION_BNN_PRO_ATTR = 'VERSION_BNN_PRO_ATTR', // BNNPROATTR算法
|
||||
VERSION_BNN_PRO_ATTR_SCORE = 'VERSION_BNN_PRO_ATTR_SCORE', // BNNPROATTR算法
|
||||
VERSION_FACE = 'VERSION_FACE', // 人脸算法
|
||||
VERSION_HEAD = 'VERSION_HEAD', // 头肩算法
|
||||
VERSION_NON_MOTOR_VEHICLE = 'VERSION_NON_MOTOR_VEHICLE', // 非机动车的算法
|
||||
VERSION_REID_HEAD_ATTR = 'VERSION_REID_HEAD_ATTR', // 形体头肩属性三种特征融合的算法
|
||||
VERSION_MOTOR_VEHICLE = 'VERSION_MOTOR_VEHICLE', // 机动车的算法
|
||||
}
|
||||
|
||||
类型枚举
|
||||
export enum ObjectType {
|
||||
OBJECT_TYPE_NULL,
|
||||
OBJECT_TYPE_PEDESTRAIN,
|
||||
OBJECT_TYPE_BICYCLE,
|
||||
OBJECT_TYPE_CAR,
|
||||
OBJECT_TYPE_MOTORBIKE,
|
||||
OBJECT_TYPE_AEROPLANE,
|
||||
OBJECT_TYPE_BUS,
|
||||
OBJECT_TYPE_TRAIN,
|
||||
OBJECT_TYPE_TRUCK,
|
||||
OBJECT_TYPE_MOTOR_RIDER,
|
||||
OBJECT_TYPE_BIKE_RIDER,
|
||||
OBJECT_TYPE_MAX,
|
||||
OBJECT_TYPE_FACE = 101,
|
||||
}
|
||||
|
||||
// 性别
|
||||
export enum Gender {
|
||||
GENDER_NONE = 'GENDER_NONE',
|
||||
GENDER_MAN = 'GENDER_MAN',
|
||||
GENDER_WOMAN = 'GENDER_WOMAN',
|
||||
}
|
||||
|
||||
// 年龄
|
||||
export enum Age {
|
||||
AGE_ALL = 'AGE_ALL',
|
||||
AGE_ZERO = 'AGE_ZERO',
|
||||
AGE_OVERENGHTEEN = 'AGE_OVERENGHTEEN',
|
||||
AGE_OVERSIXTY = 'AGE_OVERSIXTY',
|
||||
}
|
||||
|
||||
// 戴帽子状态
|
||||
export enum Hat {
|
||||
HAT_ALL = 'HAT_ALL',
|
||||
HAT_NONE = 'HAT_NONE',
|
||||
HAT_OWNED = 'HAT_OWNED',
|
||||
}
|
||||
|
||||
// 颜色
|
||||
export enum Color {
|
||||
COLOR_ALL = 'COLOR_ALL',
|
||||
COLOR_BLACK = 'COLOR_BLACK',
|
||||
COLOR_WHITE = 'COLOR_WHITE',
|
||||
COLOR_GRAY = 'COLOR_GRAY',
|
||||
COLOR_BROWN = 'COLOR_BROWN',
|
||||
COLOR_PINK = 'COLOR_PINK',
|
||||
COLOR_REDANDORANGE = 'COLOR_REDANDORANGE',
|
||||
COLOR_YELLOW = 'COLOR_YELLOW',
|
||||
COLOR_GREEN = 'COLOR_GREEN',
|
||||
COLOR_BLUE = 'COLOR_BLUE',
|
||||
COLOR_PURPLE = 'COLOR_PURPLE',
|
||||
}
|
||||
|
||||
// 背包
|
||||
export enum Package {
|
||||
PACKAGE_ALL = 'PACKAGE_ALL',
|
||||
PACKAGE_HANDBAG = 'PACKAGE_HANDBAG',
|
||||
PACKAGE_BACKPACK = 'PACKAGE_BACKPACK',
|
||||
PACKAGE_SHOULDERBAG = 'PACKAGE_SHOULDERBAG',
|
||||
PACKAGE_OTHER = 'PACKAGE_OTHER',
|
||||
PACKAGE_NONE = 'PACKAGE_NONE',
|
||||
}
|
||||
|
||||
// 行走模式
|
||||
export enum WalkPattern {
|
||||
WALKPATTERN_ALL = 'WALKPATTERN_ALL',
|
||||
WALKPATTERN_WALK = 'WALKPATTERN_WALK',
|
||||
WALKPATTERN_RIDING = 'WALKPATTERN_RIDING',
|
||||
}
|
||||
|
||||
// 人类属性
|
||||
export interface HumanProperty {
|
||||
age: Age;
|
||||
downColor: Color;
|
||||
gender: Gender;
|
||||
hat: Hat;
|
||||
package: Package;
|
||||
upColor: Color;
|
||||
walkPattern: WalkPattern;
|
||||
}
|
||||
|
||||
// 人类属性枚举
|
||||
export interface EnumHumanProperty {
|
||||
Gender: typeof Gender;
|
||||
Age: typeof Age;
|
||||
Hat: typeof Hat;
|
||||
Color: typeof Color;
|
||||
Package: typeof Package;
|
||||
WalkPattern: typeof WalkPattern;
|
||||
}
|
||||
|
||||
export interface IScreenshotButtonProp {
|
||||
model: 'VIDEO' | 'IMAGE';
|
||||
getCropInfo: () => Promise<RESP>;
|
||||
setShowCrop: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
cropType: typeof cropType[number];
|
||||
selectAlgorithmVersion: number;
|
||||
}
|
||||
|
||||
export interface AlignType {
|
||||
/**
|
||||
* move point of source node to align with point of target node.
|
||||
* Such as ['tr','cc'], align top right point of source node with center point of target node.
|
||||
* Point can be 't'(top), 'b'(bottom), 'c'(center), 'l'(left), 'r'(right) */
|
||||
points?: AlignPoint[];
|
||||
/**
|
||||
* offset source node by offset[0] in x and offset[1] in y.
|
||||
* If offset contains percentage string value, it is relative to sourceNode region.
|
||||
*/
|
||||
offset?: number[];
|
||||
/**
|
||||
* offset target node by offset[0] in x and offset[1] in y.
|
||||
* If targetOffset contains percentage string value, it is relative to targetNode region.
|
||||
*/
|
||||
targetOffset?: number[];
|
||||
/**
|
||||
* If adjustX field is true, will adjust source node in x direction if source node is invisible.
|
||||
* If adjustY field is true, will adjust source node in y direction if source node is invisible.
|
||||
*/
|
||||
overflow?: {
|
||||
adjustX?: boolean | number;
|
||||
adjustY?: boolean | number;
|
||||
};
|
||||
/**
|
||||
* Whether use css right instead of left to position
|
||||
*/
|
||||
useCssRight?: boolean;
|
||||
/**
|
||||
* Whether use css bottom instead of top to position
|
||||
*/
|
||||
useCssBottom?: boolean;
|
||||
/**
|
||||
* Whether use css transform instead of left/top/right/bottom to position if browser supports.
|
||||
* Defaults to false.
|
||||
*/
|
||||
useCssTransform?: boolean;
|
||||
ignoreShake?: boolean;
|
||||
}
|
||||
|
||||
export type ODRECT = {
|
||||
topleft: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export interface ViewOption {
|
||||
/* 图片url */
|
||||
image?: string | HTMLImageElement;
|
||||
|
||||
/* 缩放灵敏度(0,1],default: 0.1 */
|
||||
wheelZoomRatio?: number;
|
||||
|
||||
/*
|
||||
* 是否允许缩放
|
||||
* @default: true
|
||||
*/
|
||||
scaleAble?: boolean;
|
||||
|
||||
/*
|
||||
* 是否允许拖拽
|
||||
* @default: true
|
||||
*/
|
||||
dragAble?: boolean;
|
||||
|
||||
/*
|
||||
* fit scale 作为 最小缩放
|
||||
* @default: false
|
||||
*/
|
||||
fitScaleAsMinScale?: boolean;
|
||||
}
|
0
packages/constants/lib/user/index.d.ts
vendored
Normal file
0
packages/constants/lib/user/index.d.ts
vendored
Normal file
0
packages/constants/lib/user/index.js
Normal file
0
packages/constants/lib/user/index.js
Normal file
@ -1,6 +1,6 @@
|
||||
---
|
||||
nav:
|
||||
title: 业务组件
|
||||
title: 其它
|
||||
order: 1
|
||||
title: 快速上手
|
||||
---
|
||||
|
0
packages/constants/src/user/index.ts
Normal file
0
packages/constants/src/user/index.ts
Normal file
41
packages/func/es/camera/constants.d.ts
vendored
41
packages/func/es/camera/constants.d.ts
vendored
@ -1,41 +0,0 @@
|
||||
/**
|
||||
* 设备类型枚举
|
||||
*/
|
||||
export declare const DeviceType: {
|
||||
VMS: string;
|
||||
DIR: string;
|
||||
CAMERA: string;
|
||||
};
|
||||
export declare const LOCAL_KEY = "local";
|
||||
export declare const DIRE_CONNECT_KEY = "direconnect";
|
||||
export declare const BOX_LIST_KEY = "boxlist";
|
||||
export declare enum VmsplatformOpt {
|
||||
VMSPLATFORMOPT_ID = 0,
|
||||
VMSPLATFORMOPT_PLATFORMNAME = 1,
|
||||
VMSPLATFORMOPT_PLUGINNAME = 2,
|
||||
VMSPLATFORMOPT_IP = 3,
|
||||
VMSPLATFORMOPT_PORT = 4,
|
||||
VMSPLATFORMOPT_USERNAME = 5,
|
||||
VMSPLATFORMOPT_PASSWORD = 6
|
||||
}
|
||||
export declare enum OPT {
|
||||
OR = 0,
|
||||
AND = 1,
|
||||
ORNOT = 2,
|
||||
ANDNOT = 3
|
||||
}
|
||||
export declare enum DevicemanagerCameraType {
|
||||
DEVICEMANAGER_CAMERA_TYPE_DEFAULT = 0,
|
||||
DEVICEMANAGER_CAMERA_TYPE_NORMAL = 1,
|
||||
DEVICEMANAGER_CAMERA_TYPE_1400 = 97,
|
||||
DEVICEMANAGER_CAMERA_TYPE_DHGRABBER = 98,
|
||||
DEVICEMANAGER_CAMERA_TYPE_HKGRABBER = 99,
|
||||
DEVICEMANAGER_CAMERA_TYPE_LOCAL = 100
|
||||
}
|
||||
export declare const BOX_DIRECONNECT_PLATFORM_FILTER: {
|
||||
filtervmsplatformList: {
|
||||
opt: OPT;
|
||||
vmsplatformOpt: VmsplatformOpt;
|
||||
value: string;
|
||||
}[];
|
||||
};
|
40
packages/func/es/camera/index.d.ts
vendored
40
packages/func/es/camera/index.d.ts
vendored
@ -1,40 +0,0 @@
|
||||
import { DevicemanagerCameraType } from './constants';
|
||||
export declare const isFaceCamera: (type: DevicemanagerCameraType) => boolean;
|
||||
/**
|
||||
*
|
||||
* @param value 传入的数据 可以是 item(camera/vms/dirs)/ deviceID
|
||||
* @param isId
|
||||
*/
|
||||
export declare function getDeviceType(value: {
|
||||
[x: string]: any;
|
||||
id: any;
|
||||
} | string): any;
|
||||
/**
|
||||
* 后端设备id/vmsid/dirid是三张表 合并在一起不保证唯一 前端生成唯一key
|
||||
* @param id 设备id
|
||||
* @param type 设备类型
|
||||
*/
|
||||
export declare function deviceIDToDeviceKey(id: any, type: string, vmsId?: any): string;
|
||||
/**
|
||||
* 后端设备id/vmsid/dirid是三张表 合并在一起不保证唯一 前端生成唯一key
|
||||
* @param item camera/vms/dirs
|
||||
*/
|
||||
export declare function deviceToDeviceKey(item: {
|
||||
[x: string]: any;
|
||||
id: any;
|
||||
}): string;
|
||||
/**
|
||||
* 设备树key 转 后端设备原始id dirid是string/vms&camera 是number 和后端保持一致
|
||||
* @param deviceKey 设备树的id
|
||||
*/
|
||||
export declare function deviceKeyToDeviceId(deviceKey: {
|
||||
split: (arg0: string) => [any, any];
|
||||
}): any;
|
||||
export declare const getVmsIdByDeviceId: (key: string) => string;
|
||||
/**
|
||||
* 通过设备id或设备key在树里面找摄像头
|
||||
* @param ids cameraId
|
||||
* @param deviceTree 树
|
||||
* @param type "id" | "key"
|
||||
*/
|
||||
export declare const findCamerasByInDeviceTree: (ids: never[] | undefined, deviceTree: any, type?: string) => any[];
|
61
packages/func/es/file/index.d.ts
vendored
61
packages/func/es/file/index.d.ts
vendored
@ -1,61 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @param url 需要转为图片的链接
|
||||
* @returns 图片的 dom
|
||||
*/
|
||||
export declare const urlToImg: (url: string) => Promise<HTMLImageElement>;
|
||||
export declare const base64DecodeImageKey: (base64ImgKey: string) => string;
|
||||
/**
|
||||
* 通过url获取图片的base64字符串
|
||||
* @param src 图片链接
|
||||
* @param outputFormat 图片格式
|
||||
* @returns base64 @string
|
||||
*/
|
||||
export declare const getBase64ByUrl: (src: string | URL, outputFormat?: string) => Promise<unknown>;
|
||||
/**
|
||||
* 把文件转base64
|
||||
* @param file @file 文件
|
||||
* @returns @string
|
||||
*/
|
||||
export declare const fileToBase64: (file: any) => Promise<string>;
|
||||
/**
|
||||
*
|
||||
* @param image @file 图片文件
|
||||
* @param width @number 宽度
|
||||
* @param height @number 高度
|
||||
* @returns @string base64
|
||||
*/
|
||||
export declare const getBase64Image: (image: any, width?: any, height?: any) => string;
|
||||
/**
|
||||
* 通过图片获取base64
|
||||
* @param src 图片地址
|
||||
* @returns @string
|
||||
*/
|
||||
export declare const getBase64ByImage: (src: string) => Promise<unknown>;
|
||||
/**
|
||||
* url转base64
|
||||
* @param {String} url - url地址
|
||||
*/
|
||||
export declare const urlToBase64V2: (url: string) => Promise<unknown>;
|
||||
/**
|
||||
* base64转Blob
|
||||
* @param {String} base64 - base64
|
||||
*/
|
||||
export declare function base64toBlob(base64: string): Blob | undefined;
|
||||
/**
|
||||
* 图片集打包压缩下载
|
||||
* 1. url -> base64 -> blob
|
||||
* 2. 将blob加入jsZip文件夹内,用file-saver保存
|
||||
* @param {Array<{url:string,name:string}>} imgDataList
|
||||
* @param {string} zipName
|
||||
*/
|
||||
export declare const downloadPackageImages: (imgDataList: string | any[], zipName: string) => Promise<void>;
|
||||
export declare function getFileSize(size: number): string;
|
||||
export declare const dataURLToBlob: (dataurl: string) => Blob;
|
||||
/**
|
||||
* key 转 http 链接
|
||||
* @param originImgkey 图片的值 ,可以是 base64 也可以是 http链接
|
||||
* @param host 图片的域值
|
||||
* @returns {string}
|
||||
*/
|
||||
export declare const generateImg: (imgKey: string, host?: string) => string;
|
@ -343,4 +343,18 @@ export var generateImg = function generateImg(_imgKey) {
|
||||
imgUrl = '';
|
||||
}
|
||||
return imgUrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取指定字符串后面的部分
|
||||
* @param imageKey v1_开头的字符串
|
||||
* @returns
|
||||
*/
|
||||
export var getImageKey = function getImageKey(imageKey, preFix) {
|
||||
var splitIndex = preFix || 'v1_';
|
||||
if (imageKey.startsWith(splitIndex)) {
|
||||
return window.atob(imageKey.split(splitIndex)[1]).replace('_', '/');
|
||||
} else {
|
||||
return imageKey;
|
||||
}
|
||||
};
|
9
packages/func/es/index.d.ts
vendored
9
packages/func/es/index.d.ts
vendored
@ -1,9 +0,0 @@
|
||||
export * from 'lodash-es';
|
||||
export * from './file';
|
||||
export * from './map';
|
||||
export * from './performance';
|
||||
export * from './string';
|
||||
export * from './number';
|
||||
export * from './time';
|
||||
export * from './utils';
|
||||
export * from './camera';
|
@ -6,4 +6,5 @@ export * from "./string";
|
||||
export * from "./number";
|
||||
export * from "./time";
|
||||
export * from "./utils";
|
||||
export * from "./camera";
|
||||
export * from "./camera";
|
||||
export * from "./math";
|
10
packages/func/es/map/index.d.ts
vendored
10
packages/func/es/map/index.d.ts
vendored
@ -1,10 +0,0 @@
|
||||
/**
|
||||
* 获取经纬信息
|
||||
* @param lngLat 位置信息
|
||||
* @returns @object { long, lat }
|
||||
*/
|
||||
export declare const fixedLngLat: (lngLat?: string) => string;
|
||||
export declare const transformLngLat: (lngLat?: string) => {
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
} | null;
|
413
packages/func/es/math/index.js
Normal file
413
packages/func/es/math/index.js
Normal file
File diff suppressed because one or more lines are too long
8
packages/func/es/number/index.d.ts
vendored
8
packages/func/es/number/index.d.ts
vendored
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* 设置数据的精度
|
||||
* @param originNumber
|
||||
* @param accuracy 精度 以原点为中心向左为正,向右为负,
|
||||
* @param isCeil 是否为向上取整
|
||||
* @returns number
|
||||
*/
|
||||
export declare const setNumberAccuracy: (originNumber: number, accuracy?: number, isCeil?: boolean) => number;
|
1
packages/func/es/performance/index.d.ts
vendored
1
packages/func/es/performance/index.d.ts
vendored
@ -1 +0,0 @@
|
||||
export declare const speedConvert: (bps: number, contertUnit?: number) => string;
|
7
packages/func/es/string/index.d.ts
vendored
7
packages/func/es/string/index.d.ts
vendored
@ -1,7 +0,0 @@
|
||||
export declare const getStrLength: (str: string) => number;
|
||||
/**
|
||||
* js截取字符串,中英文都能用
|
||||
* @param str:需要截取的字符串
|
||||
* @param len: 需要截取的长度
|
||||
*/
|
||||
export declare const cutStr: (str: string, len: number) => string | String | undefined;
|
5
packages/func/es/time/index.d.ts
vendored
5
packages/func/es/time/index.d.ts
vendored
@ -1,5 +0,0 @@
|
||||
export declare const formateDuration: (diff: number) => string;
|
||||
export declare function generateTime(): {
|
||||
startDateTime: number;
|
||||
endDateTime: number;
|
||||
};
|
117
packages/func/es/upload/index.js
Normal file
117
packages/func/es/upload/index.js
Normal file
File diff suppressed because one or more lines are too long
20
packages/func/es/utils/index.d.ts
vendored
20
packages/func/es/utils/index.d.ts
vendored
@ -1,20 +0,0 @@
|
||||
export { default as isChrome } from './isChrome';
|
||||
export declare const matchS3Prefix: (str: string) => boolean;
|
||||
/**
|
||||
* 通过文件名获取文件类型
|
||||
* @param fileName 文件名称
|
||||
* @returns 文件类型
|
||||
*/
|
||||
export declare const getFileSuffix: (fileName: string) => string;
|
||||
/**
|
||||
* 通过类型获取文件名
|
||||
* @param type 类型
|
||||
* @returns
|
||||
*/
|
||||
export declare const getImageSuffixByFileType: (type: string) => string;
|
||||
export declare function getChromeVersion(): number | false;
|
||||
export declare const nextTick: (func: (value: void) => void | PromiseLike<void>) => void;
|
||||
export declare const loop: (items: string | any[], callback: (arg0: any) => any) => void;
|
||||
export declare const addEventListener: (target: any, eventType: string, cb: any, option?: any) => {
|
||||
remove: () => void;
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
import { isNumber } from 'lodash-es';
|
||||
import ReactDOM from 'react-dom';
|
||||
export { default as isChrome } from "./isChrome";
|
||||
export var matchS3Prefix = function matchS3Prefix(str) {
|
||||
@ -103,4 +104,41 @@ export var addEventListener = function addEventListener(target, eventType, cb, o
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get transforms base on the given object.
|
||||
* @param {Object} obj - The target object.
|
||||
* @returns {string} A string contains transform values.
|
||||
*/
|
||||
export function getTransforms(_ref) {
|
||||
var rotate = _ref.rotate,
|
||||
scaleX = _ref.scaleX,
|
||||
scaleY = _ref.scaleY,
|
||||
translateX = _ref.translateX,
|
||||
translateY = _ref.translateY;
|
||||
var values = [];
|
||||
if (isNumber(translateX) && translateX !== 0) {
|
||||
values.push("translateX(".concat(translateX, "px)"));
|
||||
}
|
||||
if (isNumber(translateY) && translateY !== 0) {
|
||||
values.push("translateY(".concat(translateY, "px)"));
|
||||
}
|
||||
|
||||
// Rotate should come first before scale to match orientation transform
|
||||
if (isNumber(rotate) && rotate !== 0) {
|
||||
values.push("rotate(".concat(rotate, "deg)"));
|
||||
}
|
||||
if (isNumber(scaleX) && scaleX !== 1) {
|
||||
values.push("scaleX(".concat(scaleX, ")"));
|
||||
}
|
||||
if (isNumber(scaleY) && scaleY !== 1) {
|
||||
values.push("scaleY(".concat(scaleY, ")"));
|
||||
}
|
||||
var transform = values.length ? values.join(' ') : 'none';
|
||||
return {
|
||||
WebkitTransform: transform,
|
||||
msTransform: transform,
|
||||
transform: transform
|
||||
};
|
||||
}
|
3
packages/func/es/utils/isChrome.d.ts
vendored
3
packages/func/es/utils/isChrome.d.ts
vendored
@ -1,3 +0,0 @@
|
||||
export declare const isBrowser: boolean;
|
||||
declare const isChrome: () => boolean;
|
||||
export default isChrome;
|
41
packages/func/lib/camera/constants.d.ts
vendored
41
packages/func/lib/camera/constants.d.ts
vendored
@ -1,41 +0,0 @@
|
||||
/**
|
||||
* 设备类型枚举
|
||||
*/
|
||||
export declare const DeviceType: {
|
||||
VMS: string;
|
||||
DIR: string;
|
||||
CAMERA: string;
|
||||
};
|
||||
export declare const LOCAL_KEY = "local";
|
||||
export declare const DIRE_CONNECT_KEY = "direconnect";
|
||||
export declare const BOX_LIST_KEY = "boxlist";
|
||||
export declare enum VmsplatformOpt {
|
||||
VMSPLATFORMOPT_ID = 0,
|
||||
VMSPLATFORMOPT_PLATFORMNAME = 1,
|
||||
VMSPLATFORMOPT_PLUGINNAME = 2,
|
||||
VMSPLATFORMOPT_IP = 3,
|
||||
VMSPLATFORMOPT_PORT = 4,
|
||||
VMSPLATFORMOPT_USERNAME = 5,
|
||||
VMSPLATFORMOPT_PASSWORD = 6
|
||||
}
|
||||
export declare enum OPT {
|
||||
OR = 0,
|
||||
AND = 1,
|
||||
ORNOT = 2,
|
||||
ANDNOT = 3
|
||||
}
|
||||
export declare enum DevicemanagerCameraType {
|
||||
DEVICEMANAGER_CAMERA_TYPE_DEFAULT = 0,
|
||||
DEVICEMANAGER_CAMERA_TYPE_NORMAL = 1,
|
||||
DEVICEMANAGER_CAMERA_TYPE_1400 = 97,
|
||||
DEVICEMANAGER_CAMERA_TYPE_DHGRABBER = 98,
|
||||
DEVICEMANAGER_CAMERA_TYPE_HKGRABBER = 99,
|
||||
DEVICEMANAGER_CAMERA_TYPE_LOCAL = 100
|
||||
}
|
||||
export declare const BOX_DIRECONNECT_PLATFORM_FILTER: {
|
||||
filtervmsplatformList: {
|
||||
opt: OPT;
|
||||
vmsplatformOpt: VmsplatformOpt;
|
||||
value: string;
|
||||
}[];
|
||||
};
|
@ -1,90 +0,0 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/camera/constants.ts
|
||||
var constants_exports = {};
|
||||
__export(constants_exports, {
|
||||
BOX_DIRECONNECT_PLATFORM_FILTER: () => BOX_DIRECONNECT_PLATFORM_FILTER,
|
||||
BOX_LIST_KEY: () => BOX_LIST_KEY,
|
||||
DIRE_CONNECT_KEY: () => DIRE_CONNECT_KEY,
|
||||
DeviceType: () => DeviceType,
|
||||
DevicemanagerCameraType: () => DevicemanagerCameraType,
|
||||
LOCAL_KEY: () => LOCAL_KEY,
|
||||
OPT: () => OPT,
|
||||
VmsplatformOpt: () => VmsplatformOpt
|
||||
});
|
||||
module.exports = __toCommonJS(constants_exports);
|
||||
var DeviceType = {
|
||||
VMS: "vms",
|
||||
DIR: "dir",
|
||||
CAMERA: "camera"
|
||||
};
|
||||
var LOCAL_KEY = "local";
|
||||
var DIRE_CONNECT_KEY = "direconnect";
|
||||
var BOX_LIST_KEY = "boxlist";
|
||||
var VmsplatformOpt = /* @__PURE__ */ ((VmsplatformOpt2) => {
|
||||
VmsplatformOpt2[VmsplatformOpt2["VMSPLATFORMOPT_ID"] = 0] = "VMSPLATFORMOPT_ID";
|
||||
VmsplatformOpt2[VmsplatformOpt2["VMSPLATFORMOPT_PLATFORMNAME"] = 1] = "VMSPLATFORMOPT_PLATFORMNAME";
|
||||
VmsplatformOpt2[VmsplatformOpt2["VMSPLATFORMOPT_PLUGINNAME"] = 2] = "VMSPLATFORMOPT_PLUGINNAME";
|
||||
VmsplatformOpt2[VmsplatformOpt2["VMSPLATFORMOPT_IP"] = 3] = "VMSPLATFORMOPT_IP";
|
||||
VmsplatformOpt2[VmsplatformOpt2["VMSPLATFORMOPT_PORT"] = 4] = "VMSPLATFORMOPT_PORT";
|
||||
VmsplatformOpt2[VmsplatformOpt2["VMSPLATFORMOPT_USERNAME"] = 5] = "VMSPLATFORMOPT_USERNAME";
|
||||
VmsplatformOpt2[VmsplatformOpt2["VMSPLATFORMOPT_PASSWORD"] = 6] = "VMSPLATFORMOPT_PASSWORD";
|
||||
return VmsplatformOpt2;
|
||||
})(VmsplatformOpt || {});
|
||||
var OPT = /* @__PURE__ */ ((OPT2) => {
|
||||
OPT2[OPT2["OR"] = 0] = "OR";
|
||||
OPT2[OPT2["AND"] = 1] = "AND";
|
||||
OPT2[OPT2["ORNOT"] = 2] = "ORNOT";
|
||||
OPT2[OPT2["ANDNOT"] = 3] = "ANDNOT";
|
||||
return OPT2;
|
||||
})(OPT || {});
|
||||
var DevicemanagerCameraType = /* @__PURE__ */ ((DevicemanagerCameraType2) => {
|
||||
DevicemanagerCameraType2[DevicemanagerCameraType2["DEVICEMANAGER_CAMERA_TYPE_DEFAULT"] = 0] = "DEVICEMANAGER_CAMERA_TYPE_DEFAULT";
|
||||
DevicemanagerCameraType2[DevicemanagerCameraType2["DEVICEMANAGER_CAMERA_TYPE_NORMAL"] = 1] = "DEVICEMANAGER_CAMERA_TYPE_NORMAL";
|
||||
DevicemanagerCameraType2[DevicemanagerCameraType2["DEVICEMANAGER_CAMERA_TYPE_1400"] = 97] = "DEVICEMANAGER_CAMERA_TYPE_1400";
|
||||
DevicemanagerCameraType2[DevicemanagerCameraType2["DEVICEMANAGER_CAMERA_TYPE_DHGRABBER"] = 98] = "DEVICEMANAGER_CAMERA_TYPE_DHGRABBER";
|
||||
DevicemanagerCameraType2[DevicemanagerCameraType2["DEVICEMANAGER_CAMERA_TYPE_HKGRABBER"] = 99] = "DEVICEMANAGER_CAMERA_TYPE_HKGRABBER";
|
||||
DevicemanagerCameraType2[DevicemanagerCameraType2["DEVICEMANAGER_CAMERA_TYPE_LOCAL"] = 100] = "DEVICEMANAGER_CAMERA_TYPE_LOCAL";
|
||||
return DevicemanagerCameraType2;
|
||||
})(DevicemanagerCameraType || {});
|
||||
var BOX_DIRECONNECT_PLATFORM_FILTER = {
|
||||
filtervmsplatformList: [
|
||||
{
|
||||
opt: 0 /* OR */,
|
||||
vmsplatformOpt: 1 /* VMSPLATFORMOPT_PLATFORMNAME */,
|
||||
value: "direconnect"
|
||||
},
|
||||
{
|
||||
opt: 0 /* OR */,
|
||||
vmsplatformOpt: 1 /* VMSPLATFORMOPT_PLATFORMNAME */,
|
||||
value: "boxlist"
|
||||
}
|
||||
]
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
BOX_DIRECONNECT_PLATFORM_FILTER,
|
||||
BOX_LIST_KEY,
|
||||
DIRE_CONNECT_KEY,
|
||||
DeviceType,
|
||||
DevicemanagerCameraType,
|
||||
LOCAL_KEY,
|
||||
OPT,
|
||||
VmsplatformOpt
|
||||
});
|
40
packages/func/lib/camera/index.d.ts
vendored
40
packages/func/lib/camera/index.d.ts
vendored
@ -1,40 +0,0 @@
|
||||
import { DevicemanagerCameraType } from './constants';
|
||||
export declare const isFaceCamera: (type: DevicemanagerCameraType) => boolean;
|
||||
/**
|
||||
*
|
||||
* @param value 传入的数据 可以是 item(camera/vms/dirs)/ deviceID
|
||||
* @param isId
|
||||
*/
|
||||
export declare function getDeviceType(value: {
|
||||
[x: string]: any;
|
||||
id: any;
|
||||
} | string): any;
|
||||
/**
|
||||
* 后端设备id/vmsid/dirid是三张表 合并在一起不保证唯一 前端生成唯一key
|
||||
* @param id 设备id
|
||||
* @param type 设备类型
|
||||
*/
|
||||
export declare function deviceIDToDeviceKey(id: any, type: string, vmsId?: any): string;
|
||||
/**
|
||||
* 后端设备id/vmsid/dirid是三张表 合并在一起不保证唯一 前端生成唯一key
|
||||
* @param item camera/vms/dirs
|
||||
*/
|
||||
export declare function deviceToDeviceKey(item: {
|
||||
[x: string]: any;
|
||||
id: any;
|
||||
}): string;
|
||||
/**
|
||||
* 设备树key 转 后端设备原始id dirid是string/vms&camera 是number 和后端保持一致
|
||||
* @param deviceKey 设备树的id
|
||||
*/
|
||||
export declare function deviceKeyToDeviceId(deviceKey: {
|
||||
split: (arg0: string) => [any, any];
|
||||
}): any;
|
||||
export declare const getVmsIdByDeviceId: (key: string) => string;
|
||||
/**
|
||||
* 通过设备id或设备key在树里面找摄像头
|
||||
* @param ids cameraId
|
||||
* @param deviceTree 树
|
||||
* @param type "id" | "key"
|
||||
*/
|
||||
export declare const findCamerasByInDeviceTree: (ids: never[] | undefined, deviceTree: any, type?: string) => any[];
|
@ -1,143 +0,0 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/camera/index.ts
|
||||
var camera_exports = {};
|
||||
__export(camera_exports, {
|
||||
deviceIDToDeviceKey: () => deviceIDToDeviceKey,
|
||||
deviceKeyToDeviceId: () => deviceKeyToDeviceId,
|
||||
deviceToDeviceKey: () => deviceToDeviceKey,
|
||||
findCamerasByInDeviceTree: () => findCamerasByInDeviceTree,
|
||||
getDeviceType: () => getDeviceType,
|
||||
getVmsIdByDeviceId: () => getVmsIdByDeviceId,
|
||||
isFaceCamera: () => isFaceCamera
|
||||
});
|
||||
module.exports = __toCommonJS(camera_exports);
|
||||
var import_lodash_es = require("lodash-es");
|
||||
var import_utils = require("../utils");
|
||||
var import_constants = require("./constants");
|
||||
var isFaceCamera = (type) => {
|
||||
return [
|
||||
import_constants.DevicemanagerCameraType.DEVICEMANAGER_CAMERA_TYPE_1400,
|
||||
import_constants.DevicemanagerCameraType.DEVICEMANAGER_CAMERA_TYPE_HKGRABBER,
|
||||
import_constants.DevicemanagerCameraType.DEVICEMANAGER_CAMERA_TYPE_DHGRABBER
|
||||
].includes(type);
|
||||
};
|
||||
function getDeviceType(value) {
|
||||
let type;
|
||||
let isDeviceKey = (0, import_lodash_es.isString)(value);
|
||||
if (isDeviceKey) {
|
||||
type = value.split("_")[0];
|
||||
} else {
|
||||
if ((0, import_lodash_es.has)(value, "longitude")) {
|
||||
type = import_constants.DeviceType["CAMERA"];
|
||||
}
|
||||
if ((0, import_lodash_es.has)(value, "ip")) {
|
||||
type = import_constants.DeviceType["VMS"];
|
||||
}
|
||||
if (!type) {
|
||||
type = import_constants.DeviceType["DIR"];
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
function deviceIDToDeviceKey(id, type, vmsId) {
|
||||
if (type == import_constants.DeviceType["DIR"]) {
|
||||
return `${type}_${id}_${vmsId}`;
|
||||
} else {
|
||||
return `${type}_${id}`;
|
||||
}
|
||||
}
|
||||
function deviceToDeviceKey(item) {
|
||||
let deviceKey = "";
|
||||
let type = getDeviceType(item);
|
||||
if (!type) {
|
||||
console.error("device type is null!");
|
||||
}
|
||||
switch (type) {
|
||||
case import_constants.DeviceType["DIR"]:
|
||||
{
|
||||
let dirId = item["dirid"] || item["dirId"];
|
||||
if (!dirId && dirId !== 0) {
|
||||
console.error("dirId type is null!");
|
||||
}
|
||||
let vmsId = (0, import_lodash_es.get)(item, "extendInfo.vmsPlatformId");
|
||||
if (!vmsId && vmsId !== 0) {
|
||||
console.error("vmsId type is null!");
|
||||
}
|
||||
deviceKey = `${type}_${dirId}_${vmsId}`;
|
||||
}
|
||||
break;
|
||||
case import_constants.DeviceType["VMS"]:
|
||||
deviceKey = `${type}_${item["id"]}`;
|
||||
break;
|
||||
case import_constants.DeviceType["CAMERA"]:
|
||||
{
|
||||
let vmsId = (0, import_lodash_es.get)(item, "extendInfo.vmsPlatformId");
|
||||
if (!vmsId && vmsId !== 0) {
|
||||
console.error("vmsId type is null!");
|
||||
}
|
||||
deviceKey = `${type}_${item.id}`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return deviceKey;
|
||||
}
|
||||
function deviceKeyToDeviceId(deviceKey) {
|
||||
let [type, id] = deviceKey.split("_");
|
||||
return type === import_constants.DeviceType["DIR"] ? id : Number(id);
|
||||
}
|
||||
var getVmsIdByDeviceId = (key) => {
|
||||
const type = getDeviceType(key);
|
||||
let vmsId = "";
|
||||
switch (type) {
|
||||
case import_constants.DeviceType["CAMERA"]:
|
||||
case import_constants.DeviceType["DIR"]:
|
||||
vmsId = key.split("_")[2];
|
||||
break;
|
||||
case import_constants.DeviceType["VMS"]:
|
||||
vmsId = key.split("_")[1];
|
||||
break;
|
||||
}
|
||||
if (!vmsId) {
|
||||
console.error("vmsid is null!");
|
||||
}
|
||||
return vmsId;
|
||||
};
|
||||
var findCamerasByInDeviceTree = (ids = [], deviceTree, type = "id") => {
|
||||
let cameraInfoList = [];
|
||||
let _ids = ids.map((v) => String(v));
|
||||
(0, import_utils.loop)(deviceTree, (item) => {
|
||||
let isCamera = getDeviceType((0, import_lodash_es.get)(item, "key", "")) === import_constants.DeviceType["CAMERA"];
|
||||
let isMatch = type === "key" ? _ids.includes((0, import_lodash_es.get)(item, "key")) : _ids.includes(`${(0, import_lodash_es.get)(item, "origin.id")}`);
|
||||
if (isCamera && isMatch) {
|
||||
cameraInfoList.push(item);
|
||||
}
|
||||
});
|
||||
return cameraInfoList;
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
deviceIDToDeviceKey,
|
||||
deviceKeyToDeviceId,
|
||||
deviceToDeviceKey,
|
||||
findCamerasByInDeviceTree,
|
||||
getDeviceType,
|
||||
getVmsIdByDeviceId,
|
||||
isFaceCamera
|
||||
});
|
61
packages/func/lib/file/index.d.ts
vendored
61
packages/func/lib/file/index.d.ts
vendored
@ -1,61 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @param url 需要转为图片的链接
|
||||
* @returns 图片的 dom
|
||||
*/
|
||||
export declare const urlToImg: (url: string) => Promise<HTMLImageElement>;
|
||||
export declare const base64DecodeImageKey: (base64ImgKey: string) => string;
|
||||
/**
|
||||
* 通过url获取图片的base64字符串
|
||||
* @param src 图片链接
|
||||
* @param outputFormat 图片格式
|
||||
* @returns base64 @string
|
||||
*/
|
||||
export declare const getBase64ByUrl: (src: string | URL, outputFormat?: string) => Promise<unknown>;
|
||||
/**
|
||||
* 把文件转base64
|
||||
* @param file @file 文件
|
||||
* @returns @string
|
||||
*/
|
||||
export declare const fileToBase64: (file: any) => Promise<string>;
|
||||
/**
|
||||
*
|
||||
* @param image @file 图片文件
|
||||
* @param width @number 宽度
|
||||
* @param height @number 高度
|
||||
* @returns @string base64
|
||||
*/
|
||||
export declare const getBase64Image: (image: any, width?: any, height?: any) => string;
|
||||
/**
|
||||
* 通过图片获取base64
|
||||
* @param src 图片地址
|
||||
* @returns @string
|
||||
*/
|
||||
export declare const getBase64ByImage: (src: string) => Promise<unknown>;
|
||||
/**
|
||||
* url转base64
|
||||
* @param {String} url - url地址
|
||||
*/
|
||||
export declare const urlToBase64V2: (url: string) => Promise<unknown>;
|
||||
/**
|
||||
* base64转Blob
|
||||
* @param {String} base64 - base64
|
||||
*/
|
||||
export declare function base64toBlob(base64: string): Blob | undefined;
|
||||
/**
|
||||
* 图片集打包压缩下载
|
||||
* 1. url -> base64 -> blob
|
||||
* 2. 将blob加入jsZip文件夹内,用file-saver保存
|
||||
* @param {Array<{url:string,name:string}>} imgDataList
|
||||
* @param {string} zipName
|
||||
*/
|
||||
export declare const downloadPackageImages: (imgDataList: string | any[], zipName: string) => Promise<void>;
|
||||
export declare function getFileSize(size: number): string;
|
||||
export declare const dataURLToBlob: (dataurl: string) => Blob;
|
||||
/**
|
||||
* key 转 http 链接
|
||||
* @param originImgkey 图片的值 ,可以是 base64 也可以是 http链接
|
||||
* @param host 图片的域值
|
||||
* @returns {string}
|
||||
*/
|
||||
export declare const generateImg: (imgKey: string, host?: string) => string;
|
@ -1,280 +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/file/index.ts
|
||||
var file_exports = {};
|
||||
__export(file_exports, {
|
||||
base64DecodeImageKey: () => base64DecodeImageKey,
|
||||
base64toBlob: () => base64toBlob,
|
||||
dataURLToBlob: () => dataURLToBlob,
|
||||
downloadPackageImages: () => downloadPackageImages,
|
||||
fileToBase64: () => fileToBase64,
|
||||
generateImg: () => generateImg,
|
||||
getBase64ByImage: () => getBase64ByImage,
|
||||
getBase64ByUrl: () => getBase64ByUrl,
|
||||
getBase64Image: () => getBase64Image,
|
||||
getFileSize: () => getFileSize,
|
||||
urlToBase64V2: () => urlToBase64V2,
|
||||
urlToImg: () => urlToImg
|
||||
});
|
||||
module.exports = __toCommonJS(file_exports);
|
||||
var import_base_64 = __toESM(require("base-64"));
|
||||
var import_jszip = __toESM(require("jszip"));
|
||||
var import_file_saver = __toESM(require("file-saver"));
|
||||
var import_utils = require("../utils");
|
||||
var import_lodash_es = require("lodash-es");
|
||||
var urlToImg = (url) => {
|
||||
const resImage = new Promise((resolve) => {
|
||||
const image = new Image();
|
||||
image.crossOrigin = "";
|
||||
image.src = url;
|
||||
image.onload = () => {
|
||||
resolve(image);
|
||||
};
|
||||
});
|
||||
return resImage;
|
||||
};
|
||||
var base64DecodeImageKey = (base64ImgKey) => {
|
||||
let tempStr = base64ImgKey;
|
||||
if ((0, import_utils.matchS3Prefix)(tempStr)) {
|
||||
tempStr = tempStr.replace(/^v[0-9]_/, "");
|
||||
tempStr = import_base_64.default.decode(tempStr);
|
||||
}
|
||||
const [bucket, ...pathArr] = tempStr.split("_");
|
||||
return tempStr = `${bucket}/${pathArr.join("_")}`;
|
||||
};
|
||||
var getBase64ByUrl = function(src, outputFormat = "image/png") {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", src, true);
|
||||
xhr.responseType = "arraybuffer";
|
||||
xhr.onload = function(e) {
|
||||
if (Number(xhr.status) === 200) {
|
||||
const uInt8Array = new Uint8Array(xhr.response);
|
||||
let i = uInt8Array.length;
|
||||
const binaryString = new Array(i);
|
||||
while (i--) {
|
||||
binaryString[i] = String.fromCharCode(uInt8Array[i]);
|
||||
}
|
||||
const data = binaryString.join("");
|
||||
const base64 = window.btoa(data);
|
||||
const dataUrl = "data:" + (outputFormat || "image/png") + ";base64," + base64;
|
||||
resolve(dataUrl);
|
||||
} else {
|
||||
reject(e);
|
||||
}
|
||||
};
|
||||
xhr.onerror = (e) => {
|
||||
reject(e);
|
||||
};
|
||||
xhr.send();
|
||||
});
|
||||
};
|
||||
var fileToBase64 = (file) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = function(e) {
|
||||
resolve(e == null ? void 0 : e.target.result);
|
||||
};
|
||||
reader.onerror = function(e) {
|
||||
reject(e);
|
||||
};
|
||||
});
|
||||
};
|
||||
var getBase64Image = (image, width, height) => {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = width !== void 0 ? width : image.width;
|
||||
canvas.height = height !== void 0 ? height : image.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx == null ? void 0 : ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
const ext = image.src.substring(image.src.lastIndexOf(".") + 1).toLowerCase();
|
||||
const dataURL = canvas.toDataURL("image/" + ext);
|
||||
return dataURL;
|
||||
};
|
||||
var getBase64ByImage = function(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const image = new Image();
|
||||
const timestamp = (/* @__PURE__ */ new Date()).getTime();
|
||||
const imgUrl = src + "?" + timestamp;
|
||||
image.src = imgUrl;
|
||||
image.onload = function() {
|
||||
function getBase64Image2(img) {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx == null ? void 0 : ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||
const ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
|
||||
const dataURL = canvas.toDataURL("image/" + ext);
|
||||
return dataURL;
|
||||
}
|
||||
const base64 = getBase64Image2(image);
|
||||
resolve(base64);
|
||||
};
|
||||
image.onerror = (e) => {
|
||||
reject(e);
|
||||
};
|
||||
});
|
||||
};
|
||||
var urlToBase64V2 = (url) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let image = new Image();
|
||||
image.onload = function() {
|
||||
var _a;
|
||||
let canvas = document.createElement("canvas");
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
(_a = canvas == null ? void 0 : canvas.getContext("2d")) == null ? void 0 : _a.drawImage(image, 0, 0);
|
||||
let result = canvas.toDataURL("image/png");
|
||||
resolve(result);
|
||||
};
|
||||
const imgUrl = url;
|
||||
image.setAttribute("crossOrigin", "Anonymous");
|
||||
image.src = imgUrl;
|
||||
image.onerror = () => {
|
||||
reject(new Error("Images fail to load"));
|
||||
};
|
||||
}).catch((error) => {
|
||||
throw new Error(error);
|
||||
});
|
||||
};
|
||||
function base64toBlob(base64) {
|
||||
if (!base64)
|
||||
return;
|
||||
var arr = base64.split(","), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
|
||||
while (n--) {
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new Blob([u8arr], { type: mime });
|
||||
}
|
||||
var downloadPackageImages = async (imgDataList, zipName) => {
|
||||
let imgDataDownLoadList = [];
|
||||
let imgBlobList = [];
|
||||
let imageSuffix = [];
|
||||
let zip = new import_jszip.default();
|
||||
let img = zip.folder(zipName);
|
||||
try {
|
||||
for (let i2 = 0; i2 < imgDataList.length; i2++) {
|
||||
let src = imgDataList[i2].url;
|
||||
let suffix = src.substring(src.lastIndexOf("."));
|
||||
let base64ByUrl = await urlToBase64V2(imgDataList[i2].url);
|
||||
if (!base64ByUrl)
|
||||
continue;
|
||||
let blob = base64toBlob(base64ByUrl);
|
||||
imgDataDownLoadList.push(imgDataList[i2]);
|
||||
imgBlobList.push(blob);
|
||||
imageSuffix.push(suffix);
|
||||
}
|
||||
if (imgBlobList.length === 0)
|
||||
throw new Error("The number of pictures is zero !");
|
||||
if (imgBlobList.length > 0) {
|
||||
for (var i = 0; i < imgBlobList.length; i++) {
|
||||
img == null ? void 0 : img.file(
|
||||
imgDataDownLoadList[i].name + (0, import_lodash_es.get)(imageSuffix, `.${i}`, imageSuffix[0]),
|
||||
// @ts-ignore
|
||||
imgBlobList[i],
|
||||
{
|
||||
base64: true
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
zip.generateAsync({ type: "blob" }).then(function(content) {
|
||||
import_file_saver.default.saveAs(content, zipName + ".zip");
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
};
|
||||
function getFileSize(size) {
|
||||
if (!size)
|
||||
return "";
|
||||
var num = 1024;
|
||||
if (size < num)
|
||||
return size + "B";
|
||||
if (size < Math.pow(num, 2))
|
||||
return (size / num).toFixed(2) + "K";
|
||||
if (size < Math.pow(num, 3))
|
||||
return (size / Math.pow(num, 2)).toFixed(2) + "M";
|
||||
if (size < Math.pow(num, 4))
|
||||
return (size / Math.pow(num, 3)).toFixed(2) + "G";
|
||||
return (size / Math.pow(num, 4)).toFixed(2) + "T";
|
||||
}
|
||||
var dataURLToBlob = (dataurl) => {
|
||||
const arr = dataurl.split(",");
|
||||
const mime = arr[0].match(/:(.*?);/)[1];
|
||||
const bstr = atob(arr[1]);
|
||||
let n = bstr.length;
|
||||
const u8arr = new Uint8Array(n);
|
||||
while (n--) {
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new Blob([u8arr], { type: mime });
|
||||
};
|
||||
var generateImg = (_imgKey, host = "http://10.0.0.120") => {
|
||||
let imgKey = _imgKey;
|
||||
let imgUrl = "";
|
||||
if (!imgKey)
|
||||
return "";
|
||||
if (/^(http:|https:)/.test(imgKey)) {
|
||||
return imgKey;
|
||||
}
|
||||
try {
|
||||
if ((0, import_utils.matchS3Prefix)(imgKey)) {
|
||||
imgKey = base64DecodeImageKey(imgKey);
|
||||
if (imgKey.endsWith("/")) {
|
||||
const i = imgKey.substring(0, imgKey.length - 1);
|
||||
imgKey = i;
|
||||
}
|
||||
}
|
||||
imgUrl = `${host}/file/${imgKey}`;
|
||||
if (_imgKey.includes("v3")) {
|
||||
imgUrl = `${host}/minio/${imgKey}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
imgUrl = "";
|
||||
}
|
||||
return imgUrl;
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
base64DecodeImageKey,
|
||||
base64toBlob,
|
||||
dataURLToBlob,
|
||||
downloadPackageImages,
|
||||
fileToBase64,
|
||||
generateImg,
|
||||
getBase64ByImage,
|
||||
getBase64ByUrl,
|
||||
getBase64Image,
|
||||
getFileSize,
|
||||
urlToBase64V2,
|
||||
urlToImg
|
||||
});
|
9
packages/func/lib/index.d.ts
vendored
9
packages/func/lib/index.d.ts
vendored
@ -1,9 +0,0 @@
|
||||
export * from 'lodash-es';
|
||||
export * from './file';
|
||||
export * from './map';
|
||||
export * from './performance';
|
||||
export * from './string';
|
||||
export * from './number';
|
||||
export * from './time';
|
||||
export * from './utils';
|
||||
export * from './camera';
|
@ -1,39 +0,0 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
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 __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/index.ts
|
||||
var src_exports = {};
|
||||
module.exports = __toCommonJS(src_exports);
|
||||
__reExport(src_exports, require("lodash-es"), module.exports);
|
||||
__reExport(src_exports, require("./file"), module.exports);
|
||||
__reExport(src_exports, require("./map"), module.exports);
|
||||
__reExport(src_exports, require("./performance"), module.exports);
|
||||
__reExport(src_exports, require("./string"), module.exports);
|
||||
__reExport(src_exports, require("./number"), module.exports);
|
||||
__reExport(src_exports, require("./time"), module.exports);
|
||||
__reExport(src_exports, require("./utils"), module.exports);
|
||||
__reExport(src_exports, require("./camera"), module.exports);
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
...require("lodash-es"),
|
||||
...require("./file"),
|
||||
...require("./map"),
|
||||
...require("./performance"),
|
||||
...require("./string"),
|
||||
...require("./number"),
|
||||
...require("./time"),
|
||||
...require("./utils"),
|
||||
...require("./camera")
|
||||
});
|
10
packages/func/lib/map/index.d.ts
vendored
10
packages/func/lib/map/index.d.ts
vendored
@ -1,10 +0,0 @@
|
||||
/**
|
||||
* 获取经纬信息
|
||||
* @param lngLat 位置信息
|
||||
* @returns @object { long, lat }
|
||||
*/
|
||||
export declare const fixedLngLat: (lngLat?: string) => string;
|
||||
export declare const transformLngLat: (lngLat?: string) => {
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
} | null;
|
@ -1,54 +0,0 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/map/index.ts
|
||||
var map_exports = {};
|
||||
__export(map_exports, {
|
||||
fixedLngLat: () => fixedLngLat,
|
||||
transformLngLat: () => transformLngLat
|
||||
});
|
||||
module.exports = __toCommonJS(map_exports);
|
||||
var fixedLngLat = (lngLat) => {
|
||||
if (!lngLat) {
|
||||
return "";
|
||||
}
|
||||
const lngLatArr = lngLat.split(",");
|
||||
const longitude = Number(lngLatArr[0]).toFixed(6);
|
||||
const latitude = Number(lngLatArr[1]).toFixed(6);
|
||||
if (lngLatArr.length < 2) {
|
||||
return "";
|
||||
}
|
||||
return `${longitude},${latitude}`;
|
||||
};
|
||||
var transformLngLat = (lngLat) => {
|
||||
if (lngLat) {
|
||||
const lngLatArr = lngLat ? lngLat.split(",") : [];
|
||||
const longitude = Number(lngLatArr[0]);
|
||||
const latitude = Number(lngLatArr[1]);
|
||||
if (lngLatArr.length < 2) {
|
||||
return null;
|
||||
}
|
||||
return { longitude, latitude };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
fixedLngLat,
|
||||
transformLngLat
|
||||
});
|
8
packages/func/lib/number/index.d.ts
vendored
8
packages/func/lib/number/index.d.ts
vendored
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* 设置数据的精度
|
||||
* @param originNumber
|
||||
* @param accuracy 精度 以原点为中心向左为正,向右为负,
|
||||
* @param isCeil 是否为向上取整
|
||||
* @returns number
|
||||
*/
|
||||
export declare const setNumberAccuracy: (originNumber: number, accuracy?: number, isCeil?: boolean) => number;
|
@ -1,45 +0,0 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/number/index.ts
|
||||
var number_exports = {};
|
||||
__export(number_exports, {
|
||||
setNumberAccuracy: () => setNumberAccuracy
|
||||
});
|
||||
module.exports = __toCommonJS(number_exports);
|
||||
var setNumberAccuracy = (originNumber, accuracy = 0, isCeil = true) => {
|
||||
if (originNumber === 0) {
|
||||
return 0;
|
||||
}
|
||||
let returnData = 0;
|
||||
if (isCeil) {
|
||||
returnData = Math.ceil(originNumber / Math.pow(10, accuracy)) * Math.pow(10, accuracy);
|
||||
} else {
|
||||
returnData = Math.floor(originNumber / Math.pow(10, accuracy)) * Math.pow(10, accuracy);
|
||||
}
|
||||
if (accuracy < 0) {
|
||||
returnData = Number(returnData.toFixed(-accuracy));
|
||||
} else {
|
||||
returnData = Number(returnData.toFixed(0));
|
||||
}
|
||||
return returnData;
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
setNumberAccuracy
|
||||
});
|
1
packages/func/lib/performance/index.d.ts
vendored
1
packages/func/lib/performance/index.d.ts
vendored
@ -1 +0,0 @@
|
||||
export declare const speedConvert: (bps: number, contertUnit?: number) => string;
|
@ -1,41 +0,0 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/performance/index.ts
|
||||
var performance_exports = {};
|
||||
__export(performance_exports, {
|
||||
speedConvert: () => speedConvert
|
||||
});
|
||||
module.exports = __toCommonJS(performance_exports);
|
||||
var speedConvert = (bps, contertUnit = 8) => {
|
||||
if (bps === void 0)
|
||||
return `0KB/s`;
|
||||
const byte = bps / contertUnit;
|
||||
if (bps > 1024 * 1024 * 1024) {
|
||||
return `${(byte / 1024 / 1024 / 1024).toFixed(2)}GB/s`;
|
||||
} else if (byte > 1024 * 1024) {
|
||||
return `${(byte / 1024 / 1024).toFixed(2)}MB/s`;
|
||||
} else if (byte > 1024) {
|
||||
return `${(byte / 1024).toFixed(2)}KB/s`;
|
||||
}
|
||||
return `${byte}KB/s`;
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
speedConvert
|
||||
});
|
7
packages/func/lib/string/index.d.ts
vendored
7
packages/func/lib/string/index.d.ts
vendored
@ -1,7 +0,0 @@
|
||||
export declare const getStrLength: (str: string) => number;
|
||||
/**
|
||||
* js截取字符串,中英文都能用
|
||||
* @param str:需要截取的字符串
|
||||
* @param len: 需要截取的长度
|
||||
*/
|
||||
export declare const cutStr: (str: string, len: number) => string | String | undefined;
|
@ -1,62 +0,0 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/string/index.ts
|
||||
var string_exports = {};
|
||||
__export(string_exports, {
|
||||
cutStr: () => cutStr,
|
||||
getStrLength: () => getStrLength
|
||||
});
|
||||
module.exports = __toCommonJS(string_exports);
|
||||
var getStrLength = function(str) {
|
||||
var realLength = 0, len = str.length, charCode = -1;
|
||||
for (var i = 0; i < len; i++) {
|
||||
charCode = str.charCodeAt(i);
|
||||
if (charCode >= 0 && charCode <= 128)
|
||||
realLength += 1;
|
||||
else
|
||||
realLength += 2;
|
||||
}
|
||||
return realLength;
|
||||
};
|
||||
var cutStr = function cutstr(str, len) {
|
||||
var str_length = 0;
|
||||
var str_len = 0;
|
||||
let str_cut = new String();
|
||||
str_len = str.length;
|
||||
for (var i = 0; i < str_len; i++) {
|
||||
let a = str.charAt(i);
|
||||
str_length++;
|
||||
if (escape(a).length > 4) {
|
||||
str_length++;
|
||||
}
|
||||
str_cut = str_cut.concat(a);
|
||||
if (str_length >= len) {
|
||||
str_cut = str_cut.concat("...");
|
||||
return str_cut;
|
||||
}
|
||||
}
|
||||
if (str_length < len) {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
cutStr,
|
||||
getStrLength
|
||||
});
|
5
packages/func/lib/time/index.d.ts
vendored
5
packages/func/lib/time/index.d.ts
vendored
@ -1,5 +0,0 @@
|
||||
export declare const formateDuration: (diff: number) => string;
|
||||
export declare function generateTime(): {
|
||||
startDateTime: number;
|
||||
endDateTime: number;
|
||||
};
|
@ -1,66 +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/time/index.ts
|
||||
var time_exports = {};
|
||||
__export(time_exports, {
|
||||
formateDuration: () => formateDuration,
|
||||
generateTime: () => generateTime
|
||||
});
|
||||
module.exports = __toCommonJS(time_exports);
|
||||
var import_dayjs = __toESM(require("dayjs"));
|
||||
var formateDuration = (diff) => {
|
||||
var days = Math.floor(diff / (24 * 3600 * 1e3));
|
||||
var leave1 = diff % (24 * 3600 * 1e3);
|
||||
var hours = Math.floor(leave1 / (3600 * 1e3));
|
||||
var leave2 = leave1 % (3600 * 1e3);
|
||||
var minutes = Math.floor(leave2 / (60 * 1e3));
|
||||
var leave3 = leave2 % (60 * 1e3);
|
||||
var seconds = Math.round(leave3 / 1e3);
|
||||
var returnStr = seconds + "秒";
|
||||
if (minutes > 0) {
|
||||
returnStr = minutes + "分";
|
||||
}
|
||||
if (hours > 0) {
|
||||
returnStr = hours + "小时";
|
||||
}
|
||||
if (days > 0) {
|
||||
returnStr = days + "天";
|
||||
}
|
||||
return returnStr;
|
||||
};
|
||||
function generateTime() {
|
||||
let endDateTime = (0, import_dayjs.default)().endOf("day").unix();
|
||||
let startDateTime = (0, import_dayjs.default)().startOf("day").unix();
|
||||
return { startDateTime, endDateTime };
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
formateDuration,
|
||||
generateTime
|
||||
});
|
20
packages/func/lib/utils/index.d.ts
vendored
20
packages/func/lib/utils/index.d.ts
vendored
@ -1,20 +0,0 @@
|
||||
export { default as isChrome } from './isChrome';
|
||||
export declare const matchS3Prefix: (str: string) => boolean;
|
||||
/**
|
||||
* 通过文件名获取文件类型
|
||||
* @param fileName 文件名称
|
||||
* @returns 文件类型
|
||||
*/
|
||||
export declare const getFileSuffix: (fileName: string) => string;
|
||||
/**
|
||||
* 通过类型获取文件名
|
||||
* @param type 类型
|
||||
* @returns
|
||||
*/
|
||||
export declare const getImageSuffixByFileType: (type: string) => string;
|
||||
export declare function getChromeVersion(): number | false;
|
||||
export declare const nextTick: (func: (value: void) => void | PromiseLike<void>) => void;
|
||||
export declare const loop: (items: string | any[], callback: (arg0: any) => any) => void;
|
||||
export declare const addEventListener: (target: any, eventType: string, cb: any, option?: any) => {
|
||||
remove: () => void;
|
||||
};
|
@ -1,138 +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/utils/index.ts
|
||||
var utils_exports = {};
|
||||
__export(utils_exports, {
|
||||
addEventListener: () => addEventListener,
|
||||
getChromeVersion: () => getChromeVersion,
|
||||
getFileSuffix: () => getFileSuffix,
|
||||
getImageSuffixByFileType: () => getImageSuffixByFileType,
|
||||
isChrome: () => import_isChrome.default,
|
||||
loop: () => loop,
|
||||
matchS3Prefix: () => matchS3Prefix,
|
||||
nextTick: () => nextTick
|
||||
});
|
||||
module.exports = __toCommonJS(utils_exports);
|
||||
var import_react_dom = __toESM(require("react-dom"));
|
||||
var import_isChrome = __toESM(require("./isChrome"));
|
||||
var matchS3Prefix = (str) => {
|
||||
return /^v[0-9]_/.test(str);
|
||||
};
|
||||
var getFileSuffix = (fileName) => {
|
||||
const splitArr = fileName.split(".");
|
||||
return splitArr.length < 2 ? "" : splitArr[splitArr.length - 1];
|
||||
};
|
||||
var getImageSuffixByFileType = (type) => {
|
||||
let imageSuffix = "";
|
||||
switch (type) {
|
||||
case "jpeg": {
|
||||
imageSuffix = ".jpg";
|
||||
break;
|
||||
}
|
||||
case "gif": {
|
||||
imageSuffix = ".gif";
|
||||
break;
|
||||
}
|
||||
case "png": {
|
||||
imageSuffix = ".png";
|
||||
break;
|
||||
}
|
||||
case "vnd.wap.wbmp": {
|
||||
imageSuffix = ".wbmp";
|
||||
break;
|
||||
}
|
||||
case "x-up-wpng": {
|
||||
imageSuffix = ".wpng";
|
||||
break;
|
||||
}
|
||||
case "nbmp": {
|
||||
imageSuffix = ".nbmp";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return imageSuffix;
|
||||
};
|
||||
function getChromeVersion() {
|
||||
const arr = navigator.userAgent.split(" ");
|
||||
let chromeVersion = "";
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (/chrome/i.test(arr[i]))
|
||||
chromeVersion = arr[i];
|
||||
}
|
||||
if (chromeVersion) {
|
||||
return Number(chromeVersion.split("/")[1].split(".")[0]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var nextTick = (func) => {
|
||||
if (queueMicrotask) {
|
||||
queueMicrotask(func);
|
||||
return;
|
||||
}
|
||||
Promise.resolve().then(func);
|
||||
};
|
||||
var loop = (items, callback) => {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const element = items[i];
|
||||
let isBreak = callback(element);
|
||||
if (isBreak) {
|
||||
return;
|
||||
}
|
||||
if (element["children"]) {
|
||||
loop(element["children"], callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
var addEventListener = (target, eventType, cb, option) => {
|
||||
const callback = import_react_dom.default.unstable_batchedUpdates ? function run(e) {
|
||||
import_react_dom.default.unstable_batchedUpdates(cb, e);
|
||||
} : cb;
|
||||
if (target.addEventListener) {
|
||||
target.addEventListener(eventType, callback, option);
|
||||
}
|
||||
return {
|
||||
remove: () => {
|
||||
if (target.removeEventListener) {
|
||||
target.removeEventListener(eventType, callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
addEventListener,
|
||||
getChromeVersion,
|
||||
getFileSuffix,
|
||||
getImageSuffixByFileType,
|
||||
isChrome,
|
||||
loop,
|
||||
matchS3Prefix,
|
||||
nextTick
|
||||
});
|
3
packages/func/lib/utils/isChrome.d.ts
vendored
3
packages/func/lib/utils/isChrome.d.ts
vendored
@ -1,3 +0,0 @@
|
||||
export declare const isBrowser: boolean;
|
||||
declare const isChrome: () => boolean;
|
||||
export default isChrome;
|
@ -1,42 +0,0 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/utils/isChrome.ts
|
||||
var isChrome_exports = {};
|
||||
__export(isChrome_exports, {
|
||||
default: () => isChrome_default,
|
||||
isBrowser: () => isBrowser
|
||||
});
|
||||
module.exports = __toCommonJS(isChrome_exports);
|
||||
var isBrowser = !!(typeof window !== "undefined" && window);
|
||||
var isChrome = () => {
|
||||
const winNav = isBrowser && window.navigator;
|
||||
const vendorName = winNav && winNav.vendor;
|
||||
const userAgent = winNav && winNav.userAgent;
|
||||
const isChromium = isBrowser && typeof chrome !== "undefined";
|
||||
const isOpera = isBrowser && typeof opr !== "undefined";
|
||||
const isIEedge = userAgent && userAgent.indexOf("Edge") > -1;
|
||||
const isIOSChrome = !!(userAgent && userAgent.match("CriOS"));
|
||||
const isDesktopChrome = isChromium && vendorName === "Google Inc." && !isOpera && !isIEedge;
|
||||
return isIOSChrome || isDesktopChrome;
|
||||
};
|
||||
var isChrome_default = isChrome;
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
isBrowser
|
||||
});
|
@ -13,10 +13,11 @@
|
||||
"lib/**/style/*",
|
||||
"*.less"
|
||||
],
|
||||
"main": "lib/index.tsx",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"typings": "es/index.d.ts",
|
||||
"exports": {
|
||||
"./*": "./*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
@ -32,15 +33,19 @@
|
||||
"registry": "http://10.0.0.77:4874"
|
||||
},
|
||||
"dependencies": {
|
||||
"@zhst/request": "workspace:^",
|
||||
"base-64": "^1.0.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"file-saver": "^2.0.5",
|
||||
"jszip": "^3.10.1",
|
||||
"lodash-es": "^4.17.21"
|
||||
"lodash-es": "^4.17.21",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/base-64": "^1.0.2",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/lodash-es": "^4.17.12"
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"@zhst/types": "workspace:^"
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ import Base64 from 'base-64'
|
||||
import JSZip from 'jszip';
|
||||
import FileSaver from 'file-saver';
|
||||
import { matchS3Prefix } from '../utils'
|
||||
import { get } from 'lodash-es';
|
||||
import { get, isString } from 'lodash-es';
|
||||
import { Rect } from '@zhst/types'
|
||||
|
||||
/**
|
||||
*
|
||||
@ -296,3 +297,49 @@ export const generateImg: (imgKey: string, host?: string) => string = (_imgKey,
|
||||
}
|
||||
return imgUrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取指定字符串后面的部分
|
||||
* @param imageKey v1_开头的字符串
|
||||
* @returns
|
||||
*/
|
||||
export const getImageKey = (imageKey: string, preFix?: string) => {
|
||||
const splitIndex = preFix || 'v1_';
|
||||
if (imageKey.startsWith(splitIndex)) {
|
||||
return window.atob(imageKey.split(splitIndex)[1]).replace('_', '/');
|
||||
} else {
|
||||
return imageKey;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取图片
|
||||
* @param img 图片的url链接
|
||||
* @param odRect
|
||||
* @returns file
|
||||
*/
|
||||
export const getFileByRect = async (img: string, odRect: Rect) => {
|
||||
let image;
|
||||
|
||||
if (isString(img)) {
|
||||
const url = generateImg(img);
|
||||
image = await urlToImg(url);
|
||||
} else {
|
||||
image = img;
|
||||
}
|
||||
const commonCanvas = document.createElement('canvas');
|
||||
commonCanvas.width = odRect.w * image.width;
|
||||
commonCanvas.height = odRect.h * image.height;
|
||||
commonCanvas.style.display = 'none';
|
||||
document.body.appendChild(commonCanvas);
|
||||
const commonCtx = commonCanvas.getContext('2d');
|
||||
commonCtx?.translate(-odRect.x * image.width, -odRect.y * image.height);
|
||||
commonCtx?.drawImage(image, 0, 0);
|
||||
const base64 = commonCanvas.toDataURL('image/jpeg');
|
||||
const blobData = dataURLToBlob(base64);
|
||||
commonCanvas.parentNode?.removeChild(commonCanvas);
|
||||
const file = new window.File([blobData], `${new Date().getTime()}`, {
|
||||
type: 'image/jpeg',
|
||||
});
|
||||
return file;
|
||||
};
|
||||
|
@ -7,3 +7,5 @@ export * from './number'
|
||||
export * from './time'
|
||||
export * from './utils'
|
||||
export * from './camera'
|
||||
export * from './math'
|
||||
export * from './upload'
|
||||
|
448
packages/func/src/math/index.ts
Normal file
448
packages/func/src/math/index.ts
Normal file
@ -0,0 +1,448 @@
|
||||
import { cloneDeep, get, isNull, isNumber, isString } from 'lodash-es';
|
||||
import { dataURLToBlob, generateImg, urlToImg } from '../file';
|
||||
import { IOdRectOrigin, Rect } from '@zhst/types';
|
||||
|
||||
const proto = {
|
||||
Common: {
|
||||
AlgorithmVersion: {
|
||||
VERSION_REID_HEAD_ATTR: '形体',
|
||||
VERSION_FACE: '人脸',
|
||||
VERSION_NON_MOTOR_VEHICLE: '非机动车',
|
||||
},
|
||||
},
|
||||
};
|
||||
export const ALGORITHM_VERSION = {
|
||||
['7']: '形体',
|
||||
['4']: '人脸',
|
||||
['6']: '非机动车',
|
||||
};
|
||||
|
||||
export const algorithmVersions = [...Object.keys(ALGORITHM_VERSION)]
|
||||
|
||||
export const getBikeExtendRect = (rect: Rect, maxW: number) => {
|
||||
const newRect = { ...rect };
|
||||
//向上扩大一倍
|
||||
const oldY = cloneDeep(rect.y);
|
||||
newRect.y = newRect.y - newRect.h < 0 ? 0 : newRect.y - newRect.h;
|
||||
newRect.h += oldY - newRect.y;
|
||||
|
||||
let newX = Math.round(newRect.x - newRect.w * 0.15);
|
||||
if (newX < 0) {
|
||||
newX = 0;
|
||||
}
|
||||
let newW = newRect.x - newX + newRect.w + Math.round(newRect.w * 0.15);
|
||||
if (newX + newW > maxW) {
|
||||
newW = maxW - newX;
|
||||
}
|
||||
newRect.x = newX;
|
||||
newRect.w = newW;
|
||||
|
||||
return newRect;
|
||||
};
|
||||
|
||||
export const getOtherExtendRect = (srcRect, maxW, maxH, type) => {
|
||||
const wExtendRadio = 0.25;
|
||||
const upExtendRadio = 0.25;
|
||||
const downExtendRadio = 0.25;
|
||||
const fixPersonExtend = true;
|
||||
|
||||
let nx = 0;
|
||||
let nw = 0;
|
||||
|
||||
nx = srcRect.x - Math.round(srcRect.w * wExtendRadio);
|
||||
if (nx < 0) {
|
||||
nx = 0;
|
||||
}
|
||||
nw = srcRect.x - nx + srcRect.w + Math.round(srcRect.w * wExtendRadio);
|
||||
if (nx + nw > maxW) {
|
||||
nw = maxW - nx;
|
||||
}
|
||||
|
||||
let ny = 0;
|
||||
let nh = 0;
|
||||
ny = srcRect.y - Math.round(upExtendRadio * srcRect.h);
|
||||
if (ny < 0) {
|
||||
ny = 0;
|
||||
}
|
||||
nh = srcRect.y - ny + srcRect.h + Math.round(srcRect.h * downExtendRadio);
|
||||
if (ny + nh > maxH) {
|
||||
nh = maxH - ny;
|
||||
}
|
||||
|
||||
let newRect = {
|
||||
x: nx,
|
||||
y: ny,
|
||||
w: nw,
|
||||
h: nh,
|
||||
};
|
||||
if (
|
||||
(type === proto.Common.AlgorithmVersion.VERSION_REID_HEAD_ATTR ||
|
||||
type === proto.Common.AlgorithmVersion.VERSION_FACE) &&
|
||||
fixPersonExtend
|
||||
) {
|
||||
const fixW = Math.round(nh * 0.75);
|
||||
if (nw < fixW) {
|
||||
// 应该扩展宽度
|
||||
let newX = nx + Math.round(nw / 2.0 - 0.5 * fixW);
|
||||
if (newX < 0) {
|
||||
newX = 0;
|
||||
}
|
||||
let newW = fixW;
|
||||
if (newW + newX > maxW) {
|
||||
newW = maxW - newX;
|
||||
}
|
||||
|
||||
newRect = {
|
||||
x: newX,
|
||||
y: ny,
|
||||
w: newW,
|
||||
h: nh,
|
||||
};
|
||||
} else if (nw > fixW) {
|
||||
// 应该扩展高度
|
||||
const fixH = Math.round(nw * 1.333);
|
||||
let newY = ny + Math.round(nh / 2.0 - 0.5 * fixH);
|
||||
if (newY < 0) {
|
||||
newY = 0;
|
||||
}
|
||||
let newH = fixH;
|
||||
if (newY + newH > maxH) {
|
||||
newH = maxH - newY;
|
||||
}
|
||||
newRect = {
|
||||
x: nx,
|
||||
y: newY,
|
||||
w: nw,
|
||||
h: newH,
|
||||
};
|
||||
}
|
||||
}
|
||||
return newRect;
|
||||
};
|
||||
|
||||
export const getNormalization = (srcRect: Rect, maxW: number, maxH: number) => {
|
||||
const newRect = {
|
||||
...srcRect,
|
||||
};
|
||||
newRect.x = srcRect.x / maxW;
|
||||
newRect.y = srcRect.y / maxH;
|
||||
newRect.w = srcRect.w / maxW;
|
||||
newRect.h = srcRect.h / maxH;
|
||||
if (newRect.x + newRect.w > 1) {
|
||||
newRect.w = 1 - newRect.x;
|
||||
}
|
||||
if (newRect.y + newRect.h > 1) {
|
||||
newRect.h = 1 - newRect.y;
|
||||
}
|
||||
return newRect;
|
||||
};
|
||||
//传入od框 穿出 od扩展框
|
||||
export const getExtendRect = (normalizationRect: Rect, imgW: number, imgH: number, type: string) => {
|
||||
const rect = {
|
||||
x: normalizationRect.x * imgW,
|
||||
y: normalizationRect.y * imgH,
|
||||
w: normalizationRect.w * imgW,
|
||||
h: normalizationRect.h * imgH,
|
||||
};
|
||||
|
||||
let newRect;
|
||||
if (type === proto.Common.AlgorithmVersion.VERSION_NON_MOTOR_VEHICLE) {
|
||||
newRect = getBikeExtendRect(rect, imgW);
|
||||
} else {
|
||||
newRect = getOtherExtendRect(rect, imgW, imgH, type);
|
||||
}
|
||||
newRect = getNormalization(newRect, imgW, imgH);
|
||||
return newRect;
|
||||
};
|
||||
|
||||
export const getTransformRect = (image: { height: number; width: number; }, transform: { translateX: any; translateY: any; scale: any; rotate: any; }, rect: Rect) => {
|
||||
const canvasRect = {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
x2: rect.x + rect.w,
|
||||
y2: rect.h + rect.y,
|
||||
};
|
||||
//1.转成缩放前的坐标
|
||||
const { translateX, translateY, scale, rotate } = transform;
|
||||
const originAxisRect = {
|
||||
x: (canvasRect.x - translateX) / scale,
|
||||
y: (canvasRect.y - translateY) / scale,
|
||||
x2: (canvasRect.x2 - translateX) / scale,
|
||||
y2: (canvasRect.y2 - translateY) / scale,
|
||||
};
|
||||
//2.转成图片坐标
|
||||
//不考虑旋转 图片原点就是坐标原点
|
||||
let imgAxisRect = originAxisRect;
|
||||
//但是旋转90度后图片不在坐标原点 加上这部分diff
|
||||
if (rotate % 180 !== 0) {
|
||||
//90度调整偏移量
|
||||
const offsetX = -(image.height - image.width) / 2;
|
||||
const offsetY = -(image.width - image.height) / 2;
|
||||
imgAxisRect = {
|
||||
x: originAxisRect.x - offsetX,
|
||||
y: originAxisRect.y - offsetY,
|
||||
x2: originAxisRect.x2 - offsetX,
|
||||
y2: originAxisRect.y2 - offsetY,
|
||||
};
|
||||
}
|
||||
//3.限制框不要超出图片
|
||||
let imgW = image.width;
|
||||
let imgH = image.height;
|
||||
if (rotate % 180 !== 0) {
|
||||
[imgW, imgH] = [imgH, imgW];
|
||||
}
|
||||
imgAxisRect.x = Math.min(imgW, Math.max(imgAxisRect.x, 0));
|
||||
imgAxisRect.y = Math.min(imgH, Math.max(imgAxisRect.y, 0));
|
||||
imgAxisRect.x2 = Math.min(imgW, Math.max(imgAxisRect.x2, 0));
|
||||
imgAxisRect.y2 = Math.min(imgH, Math.max(imgAxisRect.y2, 0));
|
||||
//获取归一化坐标
|
||||
const endRect = {
|
||||
x: imgAxisRect.x2 > imgAxisRect.x ? imgAxisRect.x : imgAxisRect.x2,
|
||||
y: imgAxisRect.y2 > imgAxisRect.y ? imgAxisRect.y : imgAxisRect.y2,
|
||||
w: Math.abs(imgAxisRect.x2 - imgAxisRect.x),
|
||||
h: Math.abs(imgAxisRect.y2 - imgAxisRect.y),
|
||||
};
|
||||
|
||||
return getNormalization(endRect, imgW, imgH);
|
||||
};
|
||||
|
||||
//旋转图片后转成file 对象
|
||||
export const getRotateImg = (image: HTMLImageElement, rotate: number) => {
|
||||
let imgW = image.width;
|
||||
let imgH = image.height;
|
||||
if (rotate % 180 !== 0) {
|
||||
[imgW, imgH] = [imgH, imgW];
|
||||
}
|
||||
const commonCanvas = document.createElement('canvas');
|
||||
commonCanvas.width = imgW;
|
||||
commonCanvas.height = imgH;
|
||||
commonCanvas.style.display = 'none';
|
||||
document.body.appendChild(commonCanvas);
|
||||
const commonCtx = commonCanvas.getContext('2d');
|
||||
//移动到图片中心 旋转
|
||||
|
||||
commonCtx?.save();
|
||||
if (rotate % 180 !== 0) {
|
||||
//90度调整偏移量
|
||||
commonCtx?.translate((image.height - image.width) / 2, (image.width - image.height) / 2);
|
||||
}
|
||||
commonCtx?.translate(image.width / 2, image.height / 2);
|
||||
commonCtx?.rotate((rotate / 180) * Math.PI);
|
||||
commonCtx?.translate(-image.width / 2, -image.height / 2);
|
||||
commonCtx?.drawImage(image, 0, 0);
|
||||
commonCtx?.restore();
|
||||
|
||||
const dataUrl = commonCanvas.toDataURL('image/jpeg');
|
||||
const blobData = dataURLToBlob(dataUrl);
|
||||
const file = new window.File([blobData], `${new Date().getTime()}`, {
|
||||
type: 'image/jpeg',
|
||||
});
|
||||
commonCanvas.parentNode?.removeChild(commonCanvas);
|
||||
return file;
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化工具
|
||||
* @param originData
|
||||
* @returns
|
||||
*/
|
||||
export const getOdRect = (originData: IOdRectOrigin) => {
|
||||
let data = get(originData, 'objects', [])
|
||||
.filter((v: any) => !isNull(get(v, 'infoOnSource.bboxInFrame.bboxRatio')))
|
||||
.map((v: any, index: any) => {
|
||||
// objectId==0 特征没有提取到过滤1掉
|
||||
const rect = get(v, 'infoOnSource.bboxInFrame.bboxRatio');
|
||||
const extendBox = get(v, 'infoOnSource.bboxInFrame.extendBoxRatio');
|
||||
const frameTimestamp = get(v, 'timestamp'); //时间戳创建档案的时候需要
|
||||
const qualityScore = get(v, 'qualityScore');
|
||||
const algorithmVersion =
|
||||
get(v, 'objectType') === 'OBJECT_TYPE_PEDESTRAIN'
|
||||
? 'VERSION_REID_HEAD_ATTR'
|
||||
: get(v, 'objectType') === 'OBJECT_TYPE_FACE'
|
||||
? 'VERSION_FACE'
|
||||
: 'VERSION_REID_HEAD_ATTR';
|
||||
const featureData = get(v, 'feature', []).filter(
|
||||
(v: any) => v.type === 'FEATURE_TYPE_BYTE'
|
||||
);
|
||||
const objectRectIndex = algorithmVersion === 'VERSION_FACE' ? 0 : 1;
|
||||
const objectType = get(v, 'objectType');
|
||||
const objectId = get(v, 'objectIndex.objectId');
|
||||
const sourceObjectId = get(v, 'sourceObjectId');
|
||||
return {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
w: rect.w,
|
||||
h: rect.h,
|
||||
// faceCorrectImage: faceCorrectImage,
|
||||
id: index,
|
||||
qualityScore: qualityScore,
|
||||
algorithmVersion: algorithmVersion,
|
||||
featureData: get(featureData, '0.featureByte'),
|
||||
objectRectIndex: objectRectIndex,
|
||||
objectType: objectType,
|
||||
objectId: objectId,
|
||||
frameTimestamp: frameTimestamp,
|
||||
sourceObjectId: sourceObjectId,
|
||||
extendBox: extendBox,
|
||||
};
|
||||
});
|
||||
if (data.length > 0) {
|
||||
data = data.filter((v: { objectId: string; }) => v.objectId !== '0');
|
||||
} else {
|
||||
throw new Error('empty');
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
//档案库od
|
||||
export const getOdRectV2 = (originData: { odv2Result: any[]; }) => {
|
||||
// const fileKey = 'v1_' + window.btoa('public_' + imgKey.split('public/')[1]);
|
||||
const resp = originData.odv2Result[0];
|
||||
const subObjects: { x: any; y: any; w: any; h: any; id: any; qualityScore: any; algorithmVersion: any; featrueData: any; objectRectIndex: number; objectType: any; objectId: any; }[] = []; //形体
|
||||
const data = get(resp, 'objects', [])
|
||||
.filter((v) => !isNull(get(v, 'subObjects[0].infoOnSource.bboxInFrame.bboxRatio')))
|
||||
.map((v, index) => {
|
||||
const rect = get(v, 'infoOnSource.bboxInFrame.bboxRatio');
|
||||
const qualityScore = get(v, 'qualityScore');
|
||||
const algorithmVersion = get(v, 'objectType');
|
||||
const featrueData = get(v, 'feature', []).filter(
|
||||
(v) => v.name === 'feature-body' || v.name === 'feature-face'
|
||||
);
|
||||
const objectRectIndex = algorithmVersion === 'OBJECT_TYPE_FACE' ? 0 : 1;
|
||||
const objectType = get(v, 'objectType');
|
||||
const objectId = get(v, 'objectIndex.objectId');
|
||||
//如果存在subObjects的数组不为null表示形体里面带人脸,人脸的od框也要显示出来
|
||||
if (get(v, 'subObjects', []).length) {
|
||||
get(v, 'subObjects', []).forEach((e) => {
|
||||
const rect = get(e, 'infoOnSource.bboxInFrame.bboxRatio');
|
||||
const qualityScore = get(e, 'qualityScore');
|
||||
const algorithmVersion = get(e, 'objectType');
|
||||
const featrueData = get(e, 'feature', []).filter(
|
||||
(v: { name: string; }) => v.name === 'feature-body' || v.name === 'feature-face'
|
||||
);
|
||||
const objectRectIndex = algorithmVersion === 'OBJECT_TYPE_FACE' ? 0 : 1;
|
||||
const objectType = get(e, 'objectType');
|
||||
const objectId = get(e, 'objectIndex.objectId');
|
||||
subObjects.push({
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
w: rect.w,
|
||||
h: rect.h,
|
||||
id: index,
|
||||
qualityScore: qualityScore,
|
||||
algorithmVersion: algorithmVersion,
|
||||
featrueData: featrueData.length ? featrueData[0].featureByte : '',
|
||||
objectRectIndex: objectRectIndex,
|
||||
objectType: objectType,
|
||||
objectId,
|
||||
});
|
||||
});
|
||||
}
|
||||
return {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
w: rect.w,
|
||||
h: rect.h,
|
||||
id: index,
|
||||
qualityScore: qualityScore,
|
||||
algorithmVersion: algorithmVersion,
|
||||
featrueData: featrueData[0].featureByte,
|
||||
objectRectIndex: objectRectIndex,
|
||||
objectType: objectType,
|
||||
objectId: objectId,
|
||||
};
|
||||
});
|
||||
const brr = data.concat(subObjects).map((v: { id: any; }, vs: any) => {
|
||||
if (String(v.id)) {
|
||||
v.id = vs;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
if (brr.length > 0) {
|
||||
console.log(brr, 'data111');
|
||||
} else {
|
||||
throw new Error('empty');
|
||||
}
|
||||
console.log(brr);
|
||||
return brr;
|
||||
};
|
||||
|
||||
//获取图片
|
||||
|
||||
export const getFileByRect = async (img: any, odRect: Rect) => {
|
||||
let image;
|
||||
if (isString(img)) {
|
||||
const url = generateImg(img);
|
||||
console.log(url, '获取图片');
|
||||
image = await urlToImg(url);
|
||||
console.log(image, '获取的图片');
|
||||
} else {
|
||||
image = img;
|
||||
}
|
||||
const commonCanvas = document.createElement('canvas');
|
||||
commonCanvas.width = odRect.w * image.width;
|
||||
commonCanvas.height = odRect.h * image.height;
|
||||
commonCanvas.style.display = 'none';
|
||||
document.body.appendChild(commonCanvas);
|
||||
const commonCtx = commonCanvas.getContext('2d');
|
||||
commonCtx?.translate(-odRect.x * image.width, -odRect.y * image.height);
|
||||
commonCtx?.drawImage(image, 0, 0);
|
||||
const base64 = commonCanvas.toDataURL('image/jpeg');
|
||||
const blobData = dataURLToBlob(base64);
|
||||
commonCanvas?.parentNode?.removeChild(commonCanvas);
|
||||
const file = new window.File([blobData], `${new Date().getTime()}`, {
|
||||
type: 'image/jpeg',
|
||||
});
|
||||
return file;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get transforms base on the given object.
|
||||
* @param {Object} obj - The target object.
|
||||
* @returns {string} A string contains transform values.
|
||||
*/
|
||||
export function getTransforms({
|
||||
rotate,
|
||||
scaleX,
|
||||
scaleY,
|
||||
translateX,
|
||||
translateY,
|
||||
}: {
|
||||
rotate?: number;
|
||||
scaleX?: number;
|
||||
scaleY?: number;
|
||||
translateX?: number;
|
||||
translateY?: number;
|
||||
}) {
|
||||
const values = [];
|
||||
|
||||
if (isNumber(translateX) && translateX !== 0) {
|
||||
values.push(`translateX(${translateX}px)`);
|
||||
}
|
||||
|
||||
if (isNumber(translateY) && translateY !== 0) {
|
||||
values.push(`translateY(${translateY}px)`);
|
||||
}
|
||||
|
||||
// Rotate should come first before scale to match orientation transform
|
||||
if (isNumber(rotate) && rotate !== 0) {
|
||||
values.push(`rotate(${rotate}deg)`);
|
||||
}
|
||||
|
||||
if (isNumber(scaleX) && scaleX !== 1) {
|
||||
values.push(`scaleX(${scaleX})`);
|
||||
}
|
||||
|
||||
if (isNumber(scaleY) && scaleY !== 1) {
|
||||
values.push(`scaleY(${scaleY})`);
|
||||
}
|
||||
|
||||
const transform = values.length ? values.join(' ') : 'none';
|
||||
|
||||
return {
|
||||
WebkitTransform: transform,
|
||||
msTransform: transform,
|
||||
transform,
|
||||
};
|
||||
}
|
@ -25,3 +25,14 @@ export const setNumberAccuracy = (originNumber: number, accuracy = 0, isCeil = t
|
||||
}
|
||||
return returnData;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取数字
|
||||
* @param number 需要校验的数值
|
||||
* @returns @number
|
||||
*/
|
||||
export const toRealNumber = (number: any) => {
|
||||
if (isNaN(number) || number === Infinity) {
|
||||
return 0;
|
||||
} else return number;
|
||||
};
|
||||
|
@ -47,3 +47,14 @@ export function generateTime() {
|
||||
let startDateTime = dayjs().startOf('day').unix();
|
||||
return { startDateTime, endDateTime };
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间为 00:00
|
||||
* @param seconds 时间,单位秒
|
||||
* @returns
|
||||
*/
|
||||
export function formatDurationTime(seconds: number) {
|
||||
var minutes = Math.floor(seconds / 60) || 0;
|
||||
var remainingSeconds = Math.floor(seconds % 60);
|
||||
return (minutes < 10 ? `0${minutes}` : minutes) + ":" + (remainingSeconds < 10 ? "0" : "") + remainingSeconds;
|
||||
}
|
||||
|
58
packages/func/src/upload/index.ts
Normal file
58
packages/func/src/upload/index.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import dayjs from "dayjs";
|
||||
import { getFileSuffix, getImageSuffixByFileType } from "../utils";
|
||||
import base64 from "base-64";
|
||||
import { v4 as uuidV4 } from 'uuid'
|
||||
import { get } from "lodash-es";
|
||||
import request from '@zhst/request'
|
||||
|
||||
//小文件上传走s3
|
||||
const defaultBucket = 'public';
|
||||
type uploadOption = {
|
||||
bucket?: string;
|
||||
dir?: string;
|
||||
withSuFuffix?: boolean;
|
||||
};
|
||||
|
||||
export const commonUpload = async (file: File, option: uploadOption = {}, type: string) => {
|
||||
const { bucket = defaultBucket, dir = 'file', withSuFuffix = false } = option;
|
||||
const prefix = `${dayjs().format('YYYYMMDD')}`;
|
||||
const fileSuffix = withSuFuffix ? getFileSuffix(get(file, 'name')) : '';
|
||||
const fileType = file['type'].split('/', 2);
|
||||
let imageSuffix = '';
|
||||
if (fileType['0'] === 'image') {
|
||||
imageSuffix = getImageSuffixByFileType(fileType['1']);
|
||||
}
|
||||
|
||||
const key = `${prefix}/${dir ? `${dir}/` : ''}${uuidV4()}${!fileSuffix ? '' : `.${fileSuffix}`}`;
|
||||
let imgKey = `${bucket}_${bucket}_${key}${imageSuffix}`; //后端约定 见https://docs.qq.com/doc/DUklodHNxUGl2U3NM》
|
||||
if (type === 'upload') {
|
||||
imgKey = `v1_${base64.encode(imgKey)}`;
|
||||
return new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = async () => {
|
||||
if (reader.result) {
|
||||
await request({
|
||||
method: 'PUT',
|
||||
url: '/singer.FileServerService/PutObject',
|
||||
data: {
|
||||
version: 1,
|
||||
bucket: defaultBucket,
|
||||
objectName: `${bucket}_${key}${imageSuffix}`,
|
||||
// fileData: reader.result,
|
||||
putObjectOption: {
|
||||
contentType: file.type,
|
||||
},
|
||||
fileDataBase64: reader.result?.split(';base64,')[1],
|
||||
},
|
||||
});
|
||||
resolve(imgKey);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const upload = async (file: File, option: uploadOption = {}) => {
|
||||
return await commonUpload(file, option, 'upload');
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
import { isNumber } from 'lodash-es';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
export { default as isChrome } from './isChrome';
|
||||
@ -90,7 +91,7 @@ export const loop = (items: string | any[], callback: (arg0: any) => any) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const addEventListener = (target: any, eventType: string, cb: any, option?: any) => {
|
||||
export const addEventListenerWrapper = (target: any, eventType: string, cb: any, option?: any) => {
|
||||
/* eslint camelcase: 2 */
|
||||
const callback = ReactDOM.unstable_batchedUpdates
|
||||
? function run(e: any) {
|
||||
@ -109,3 +110,54 @@ export const addEventListener = (target: any, eventType: string, cb: any, option
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get transforms base on the given object.
|
||||
* @param {Object} obj - The target object.
|
||||
* @returns {string} A string contains transform values.
|
||||
*/
|
||||
export function getTransforms({
|
||||
rotate,
|
||||
scaleX,
|
||||
scaleY,
|
||||
translateX,
|
||||
translateY,
|
||||
}: {
|
||||
rotate?: number;
|
||||
scaleX?: number;
|
||||
scaleY?: number;
|
||||
translateX?: number;
|
||||
translateY?: number;
|
||||
}) {
|
||||
const values = [];
|
||||
|
||||
if (isNumber(translateX) && translateX !== 0) {
|
||||
values.push(`translateX(${translateX}px)`);
|
||||
}
|
||||
|
||||
if (isNumber(translateY) && translateY !== 0) {
|
||||
values.push(`translateY(${translateY}px)`);
|
||||
}
|
||||
|
||||
// Rotate should come first before scale to match orientation transform
|
||||
if (isNumber(rotate) && rotate !== 0) {
|
||||
values.push(`rotate(${rotate}deg)`);
|
||||
}
|
||||
|
||||
if (isNumber(scaleX) && scaleX !== 1) {
|
||||
values.push(`scaleX(${scaleX})`);
|
||||
}
|
||||
|
||||
if (isNumber(scaleY) && scaleY !== 1) {
|
||||
values.push(`scaleY(${scaleY})`);
|
||||
}
|
||||
|
||||
const transform = values.length ? values.join(' ') : 'none';
|
||||
|
||||
return {
|
||||
WebkitTransform: transform,
|
||||
msTransform: transform,
|
||||
transform,
|
||||
};
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
"lib/**/style/*",
|
||||
"*.less"
|
||||
],
|
||||
"main": "lib/index.tsx",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"typings": "es/index.d.ts",
|
||||
"exports": {
|
||||
@ -31,6 +31,9 @@
|
||||
"access": "public",
|
||||
"registry": "http://10.0.0.77:4874"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@zhst/types": "workspace:^"
|
||||
},
|
||||
"dependencies": {
|
||||
"@zhst/func": "workspace:^",
|
||||
"ahooks": "^3.7.8",
|
||||
|
@ -16,7 +16,7 @@
|
||||
"lib/**/style/*",
|
||||
"*.less"
|
||||
],
|
||||
"main": "lib/index.tsx",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"typings": "es/index.d.ts",
|
||||
"files": [
|
||||
|
@ -14,7 +14,7 @@
|
||||
"lib/**/style/*",
|
||||
"*.less"
|
||||
],
|
||||
"main": "lib/index.tsx",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"typings": "es/index.d.ts",
|
||||
"exports": {
|
||||
@ -32,13 +32,20 @@
|
||||
"access": "public",
|
||||
"registry": "http://10.0.0.77:4874"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@zhst/types": "workspace:^"
|
||||
},
|
||||
"dependencies": {
|
||||
"@turf/boolean-point-in-polygon": "^6.5.0",
|
||||
"@turf/turf": "^6.5.0",
|
||||
"@types/downloadjs": "^1.4.6",
|
||||
"@zhst/func": "workspace:^",
|
||||
"@zhst/hooks": "workspace:^",
|
||||
"antd": "^5.12.5",
|
||||
"classnames": "^2.5.1",
|
||||
"downloadjs": "^1.4.7",
|
||||
"flv.js": "^1.6.2",
|
||||
"rc-align": "^4.0.15",
|
||||
"rc-util": "^5.38.1",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
|
@ -0,0 +1,51 @@
|
||||
.zhst-image__btn-group {
|
||||
// display: flex;
|
||||
width: 30px;
|
||||
box-shadow: 0 2px 6px 0 rgb(0 0 0 / 40%);
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #000;
|
||||
|
||||
&>button {
|
||||
padding: 0;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
color: #09f;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #09f;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&>span {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
&--active {
|
||||
&>button {
|
||||
color: #09f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--circle {
|
||||
background-color: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&--circle &__item {
|
||||
margin-bottom: 4px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
import React, { MouseEvent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Button, Tooltip, TooltipProps } from 'antd';
|
||||
import Icon from '../../../iconfont';
|
||||
import './index.less';
|
||||
|
||||
const componentName = `zhst-image__btn-group`;
|
||||
|
||||
export interface BtnGroupProps {
|
||||
className: string;
|
||||
dataSource: Array<{ key: string; icon: string; title: string }>;
|
||||
onClick: (v: string, e: MouseEvent<HTMLElement>) => void;
|
||||
selectKey?: string;
|
||||
circle?: boolean;
|
||||
placement?: TooltipProps['placement'];
|
||||
}
|
||||
export const BtnGroup: React.FC<BtnGroupProps> = (props) => {
|
||||
const { dataSource, onClick, className, circle, selectKey = '', placement = 'right' } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
componentName,
|
||||
circle && `${componentName}--circle`,
|
||||
className
|
||||
)}
|
||||
>
|
||||
{dataSource.map(({ key, icon, title }) => {
|
||||
const isSelect = key === selectKey;
|
||||
return (
|
||||
<Tooltip key={key} placement={placement} title={title}>
|
||||
<div
|
||||
key={key}
|
||||
className={classNames(
|
||||
`${componentName}__item`,
|
||||
isSelect && `${componentName}__item--active`
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
onClick={(e) => {
|
||||
onClick(key, e);
|
||||
}}
|
||||
>
|
||||
<Icon size={18} icon={icon} />
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
BtnGroup.displayName = 'BtnGroup';
|
||||
|
||||
export default BtnGroup;
|
@ -0,0 +1,21 @@
|
||||
.bigImageWrapper--v2__screenshot {
|
||||
min-width: 90px;
|
||||
background: rgb(0 0 0 / 50%);
|
||||
border-radius: 0;
|
||||
|
||||
&>button {
|
||||
width: 100%;
|
||||
color: #fff !important;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 12px !important;
|
||||
border-radius: 0;
|
||||
|
||||
&>span {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #09f !important;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
import React from'react'
|
||||
import { Button } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import type { AlgorithmVersion } from '@zhst/types'
|
||||
import { IBigImageOpt } from '@zhst/types/BigImageModal'
|
||||
import './index.less';
|
||||
|
||||
const componentName = 'bigImageWrapper--v2';
|
||||
|
||||
interface IScreenShotButton {
|
||||
getCropInfo: () => void
|
||||
setShowCrop: any
|
||||
cropType: string
|
||||
selectAlgorithmVersion: AlgorithmVersion
|
||||
}
|
||||
|
||||
const getScreenshotButtonRender = (arg: {
|
||||
disableBtn: number[];
|
||||
onBigImageActionClick: (type: number, item: any) => void;
|
||||
}) => {
|
||||
const { disableBtn = [], onBigImageActionClick } = arg;
|
||||
return (param: IScreenShotButton) => {
|
||||
const { getCropInfo, setShowCrop, cropType, selectAlgorithmVersion } = param;
|
||||
let isAuto = cropType === 'AUTO';
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(`${componentName}__screenshot`)}
|
||||
style={{
|
||||
zIndex: 100,
|
||||
position: 'absolute',
|
||||
width: '86px',
|
||||
}}
|
||||
>
|
||||
{!disableBtn.includes(IBigImageOpt['ADD_HISTORY_WITH_CROP']) && isAuto && (
|
||||
<Button
|
||||
type={'text'}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
const image = await getCropInfo();
|
||||
setShowCrop(false);
|
||||
onBigImageActionClick(IBigImageOpt['ADD_HISTORY_WITH_CROP'], image);
|
||||
}}
|
||||
>
|
||||
目标检索
|
||||
</Button>
|
||||
)}
|
||||
{!disableBtn.includes(IBigImageOpt['ADD_HISTORY_WITH_CROP_BODY']) && !isAuto && (
|
||||
<Button
|
||||
type={'text'}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
const image = await getCropInfo();
|
||||
setShowCrop(false);
|
||||
onBigImageActionClick(IBigImageOpt['ADD_HISTORY_WITH_CROP_BODY'], image);
|
||||
}}
|
||||
>
|
||||
搜形体
|
||||
</Button>
|
||||
)}
|
||||
{!disableBtn.includes(IBigImageOpt['ADD_HISTORY_WITH_CROP_VEHICLE']) && !isAuto && (
|
||||
<Button
|
||||
type={'text'}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
const image = await getCropInfo();
|
||||
setShowCrop(false);
|
||||
onBigImageActionClick(IBigImageOpt['ADD_HISTORY_WITH_CROP_VEHICLE'], image);
|
||||
}}
|
||||
>
|
||||
搜非机动车
|
||||
</Button>
|
||||
)}
|
||||
{!disableBtn.includes(IBigImageOpt['ADD_HISTORY_WITH_CROP_ARCHIVE']) &&
|
||||
selectAlgorithmVersion !== 0 && (
|
||||
<Button
|
||||
type={'text'}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
let image = await getCropInfo();
|
||||
setShowCrop(false);
|
||||
if (!image.rectList[0].algorithmVersion) {
|
||||
image.rectList[0].algorithmVersion = 0;
|
||||
image.extendRectList[0].algorithmVersion = 0;
|
||||
}
|
||||
onBigImageActionClick(IBigImageOpt['ADD_HISTORY_WITH_CROP_ARCHIVE'], image);
|
||||
}}
|
||||
>
|
||||
档案检索
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type={'text'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setShowCrop(false);
|
||||
}}
|
||||
>
|
||||
退出框选
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default getScreenshotButtonRender;
|
129
packages/meta/src/BigImagePreview/index.less
Normal file
129
packages/meta/src/BigImagePreview/index.less
Normal file
@ -0,0 +1,129 @@
|
||||
.zhst-image__img-view {
|
||||
position: relative;
|
||||
width: calc(100%);
|
||||
height: 100%;
|
||||
|
||||
&__face-score {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 80px;
|
||||
color: red;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 19px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&-opt {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&-crop-opt {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&-align {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 0;
|
||||
|
||||
&--cursor {
|
||||
& canvas {
|
||||
min-height: 320px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-screenshot {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&-attach {
|
||||
position: absolute;
|
||||
z-index: 11;
|
||||
bottom: 0;
|
||||
|
||||
// left: 78px;
|
||||
left: 0;
|
||||
min-width: 120px;
|
||||
height: 202px;
|
||||
transition: all 200ms;
|
||||
|
||||
&--fixed {
|
||||
}
|
||||
|
||||
&--zoomin {
|
||||
height: 100%;
|
||||
|
||||
&--fixed {
|
||||
}
|
||||
}
|
||||
|
||||
&__tab {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 24px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
opacity: 0.5;
|
||||
transition: all 200ms;
|
||||
|
||||
&--select {
|
||||
width: 48px;
|
||||
height: 34px;
|
||||
background: #09f;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__scale {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgb(0 0 0 / 60%);
|
||||
border-radius: 100%;
|
||||
cursor: pointer;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__img {
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
|
||||
&--fixed {
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
packages/meta/src/BigImagePreview/index.md
Normal file
62
packages/meta/src/BigImagePreview/index.md
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
nav:
|
||||
title: 元组件
|
||||
order: 1
|
||||
group:
|
||||
title: 通用
|
||||
order: 3
|
||||
---
|
||||
|
||||
# BigImagePreview 大图预览组件
|
||||
|
||||
```jsx
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Button, Space } from 'antd'
|
||||
import { BigImagePreview } from '@zhst/meta'
|
||||
|
||||
|
||||
const props = {
|
||||
imageKey:"http://10.0.0.120:30003/file/singer-20240110/1/5/1744894622934503424.jpg",
|
||||
odRect:{
|
||||
"x":0.5445312,
|
||||
"y":0.19166666,
|
||||
"w":0.08671875,
|
||||
"h":0.40138888
|
||||
},
|
||||
heigth: '500px',
|
||||
attachImg: [
|
||||
{
|
||||
"url": "http://10.0.0.120:30003/file/singer-20240110/1/5/1744894622695428096.jpg","label": "形体"
|
||||
},{
|
||||
"url": "http://10.0.0.120:30003/file/singer-20240110/1/5/1744894588427964418.jpg",
|
||||
"label": "人脸"
|
||||
}
|
||||
],
|
||||
score: 0.891417,
|
||||
objects: [{"objectIndex":{"objectId":"1745329264976201728","solutionId":"0","deviceId":"0","fragmentId":"0"},"objectType":"OBJECT_TYPE_PEDESTRAIN","sourceObjectId":"0","level":0,"confidence":0.8910453,"pathInfo":null,"frameInfo":{"frameId":"0","frameTimestamp":"1704953898021","width":0,"height":0,"originWidth":0,"originHeight":0,"offsetTime":"0","skipNumber":"0"},"deviceInfo":null,"infoOnSource":{"bboxInSource":null,"bboxInFrame":{"bbox":null,"bboxRatio":{"x":0.69192713,"y":0.53585213,"w":0.031508446,"h":0.12733544},"extendBbox":null,"extendBoxRatio":null},"countInSource":0,"indexInSource":0},"qualityScore":0,"frameImage":null,"objectImage":null,"objectExtImage":null,"feature":[{"name":"feature-body","type":"FEATURE_TYPE_BYTE","featureId":"0","featureByte":"","featureBool":[],"featureUint8":[],"featureUint16":[],"featureUint32":[],"featureUint64":[],"featureInt8":[],"featureInt16":[],"featureInt32":[],"featureInt64":[],"featureFloat32":[],"featureString":[]},{"name":"property-body","type":"FEATURE_TYPE_FLOAT32","featureId":"0","featureByte":"","featureBool":[],"featureUint8":[],"featureUint16":[],"featureUint32":[],"featureUint64":[],"featureInt8":[],"featureInt16":[],"featureInt32":[],"featureInt64":[],"featureFloat32":[0.8251953,0.40527344,0.4567871,0.20178223,0.26220703,0.5083008,0.36499023,0.045196533,0.92822266,0.08300781,0.9663086,0.71533203,0.112976074,0.3935547,0.25048828,0.11694336,0.5317383,0.26293945,0.13134766,0.11657715,0.4868164,0.3647461,0.06903076,0.234375,0.84472656,0.07684326,0.08227539,0.035827637,0.11315918,0.0637207,0.1850586,0.057495117,0.9013672,0.15673828,0.14099121,0.040618896,0.08270264,0.076538086,0.828125,0.055419922,0.038391113,0.03137207,0.07574463,0.08105469,0.05340576,0.023330688,0.25561523,0.05090332,0.17480469,0.73779297,0.49169922,0.63500977,0.033691406],"featureString":[]}],"subObjects":[]},{"objectIndex":{"objectId":"1745329264976201729","solutionId":"0","deviceId":"0","fragmentId":"0"},"objectType":"OBJECT_TYPE_PEDESTRAIN","sourceObjectId":"0","level":0,"confidence":0.74931294,"pathInfo":null,"frameInfo":{"frameId":"0","frameTimestamp":"1704953898021","width":0,"height":0,"originWidth":0,"originHeight":0,"offsetTime":"0","skipNumber":"0"},"deviceInfo":null,"infoOnSource":{"bboxInSource":null,"bboxInFrame":{"bbox":null,"bboxRatio":{"x":0.73869747,"y":0.5493781,"w":0.025924563,"h":0.11845186},"extendBbox":null,"extendBoxRatio":null},"countInSource":0,"indexInSource":0},"qualityScore":0,"frameImage":null,"objectImage":null,"objectExtImage":null,"feature":[{"name":"feature-body","type":"FEATURE_TYPE_BYTE","featureId":"0","featureByte":"","featureBool":[],"featureUint8":[],"featureUint16":[],"featureUint32":[],"featureUint64":[],"featureInt8":[],"featureInt16":[],"featureInt32":[],"featureInt64":[],"featureFloat32":[],"featureString":[]},{"name":"property-body","type":"FEATURE_TYPE_FLOAT32","featureId":"0","featureByte":"","featureBool":[],"featureUint8":[],"featureUint16":[],"featureUint32":[],"featureUint64":[],"featureInt8":[],"featureInt16":[],"featureInt32":[],"featureInt64":[],"featureFloat32":[0.41308594,0.33789062,0.5917969,0.17590332,0.7392578,0.3046875,0.5053711,0.017181396,0.94677734,0.19189453,0.8696289,0.21728516,0.14611816,0.27514648,0.5161133,0.49414062,0.77978516,0.25952148,0.26586914,0.07751465,0.15344238,0.2680664,0.82958984,0.13134766,0.41992188,0.118774414,0.04046631,0.13342285,0.16113281,0.16186523,0.07922363,0.044525146,0.9760742,0.19250488,0.22131348,0.044921875,0.046539307,0.040161133,0.9453125,0.04751587,0.1361084,0.013069153,0.049316406,0.0385437,0.06903076,0.056762695,0.047790527,0.09057617,0.58691406,0.2607422,0.6953125,0.4946289,0.1303711],"featureString":[]}],"subObjects":[]}]
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const imgRef = useRef(null)
|
||||
|
||||
return (
|
||||
<Space size={[8, 16]} direction="vertical">
|
||||
<BigImagePreview {...props} ref={imgRef} />
|
||||
<Space>
|
||||
<Button type="primary" onClick={() => imgRef.current?.setShowCrop(true)}>编辑</Button>
|
||||
<Button onClick={() => imgRef.current?.setShowCrop(false)}>取消</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
| 参数名 | 参数类型 | 参数说明 |
|
||||
| ------ | -------- | ---- |
|
||||
| imageKey | string(必填) | 当前大图链接 |
|
||||
| odRect | { x: number; y: number; w: number; h: number } | 圈选矩形 |
|
||||
|height|string(选填)|高度|
|
||||
|width|string(选填)|宽度|
|
||||
|score|string(选填)|相似度|
|
||||
|attachImg|{ url: string; label: string; }[](选填)|缩略图|
|
||||
|objects | IOdRectOrigin[] | 编辑状态参数 |
|
540
packages/meta/src/BigImagePreview/index.tsx
Normal file
540
packages/meta/src/BigImagePreview/index.tsx
Normal file
@ -0,0 +1,540 @@
|
||||
import React, { useEffect, ReactElement, useState, useCallback, useRef, useImperativeHandle } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useLatest } from '@zhst/hooks';
|
||||
import {
|
||||
get,
|
||||
pick,
|
||||
isNull,
|
||||
generateImg,
|
||||
dataURLToBlob,
|
||||
getOdRect,
|
||||
getExtendRect,
|
||||
getTransformRect,
|
||||
getRotateImg,
|
||||
getTransforms,
|
||||
addEventListenerWrapper,
|
||||
upload,
|
||||
getFileByRect
|
||||
} from '@zhst/func';
|
||||
import Align from 'rc-align';
|
||||
import { Button } from 'antd';
|
||||
import type { Rect, IScreenshotButtonProp, ODRECT, AlignType, ViewOption, IOdRectOrigin } from '@zhst/types'
|
||||
import Icon from '../iconfont';
|
||||
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'
|
||||
import getScreenshotButtonRender from './components/ScreenhotButtons';
|
||||
|
||||
const componentName = `zhst-image__img-view`;
|
||||
|
||||
export const CROP_TYPE = {
|
||||
CUSTOM: 'CSUTOM',
|
||||
AUTO: 'AUTO',
|
||||
};
|
||||
|
||||
export const defaultAlignOption = {
|
||||
points: ['bl', 'br'],
|
||||
offset: [6, 0],
|
||||
overflow: {
|
||||
adjustX: true,
|
||||
adjustY: true,
|
||||
},
|
||||
};
|
||||
|
||||
export interface ImgViewProps extends React.HTMLAttributes<HTMLElement> {
|
||||
imageKey: string; //不在监听url变化 更新走销毁
|
||||
odRect: ODRECT;
|
||||
attachImg?: Array<{ label: string; url: string }>; // 缩略图列表
|
||||
showAttachImgLabel: boolean; // 是否显示缩略图
|
||||
showOpt: boolean; // 是否显示操作面板
|
||||
width?: string | number;
|
||||
height?: string | number;
|
||||
/* 截图渲染 */
|
||||
screenshotButtonAlign: AlignType;
|
||||
screenshotButtonRender: (screenshotButtonProp: IScreenshotButtonProp) => ReactElement;
|
||||
hideLeftTopBtn?: boolean;
|
||||
score?: number;
|
||||
viewOption?: ViewOption;
|
||||
objects: IOdRectOrigin[]
|
||||
}
|
||||
export interface ImgViewRef {
|
||||
/* 图片实例 */
|
||||
imgInsRef: React.MutableRefObject<any>;
|
||||
/* 切换编辑模式 */
|
||||
setShowCrop: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
const cropBtnDataSource = [
|
||||
{
|
||||
key: 'close',
|
||||
icon: 'icon-danchuangguanbi',
|
||||
title: '退出',
|
||||
},
|
||||
{
|
||||
key: 'autoCrop',
|
||||
icon: 'icon-zidong',
|
||||
title: '智能框选',
|
||||
},
|
||||
{
|
||||
key: 'customCrop',
|
||||
icon: 'icon-shoudong',
|
||||
title: '手动框选',
|
||||
},
|
||||
];
|
||||
|
||||
const operateBtnDataSource = [
|
||||
{
|
||||
key: 'zoomOut',
|
||||
icon: 'icon-fangda',
|
||||
title: '放大',
|
||||
},
|
||||
{
|
||||
key: 'zoomIn',
|
||||
icon: 'icon-suoxiao',
|
||||
title: '缩小',
|
||||
},
|
||||
{
|
||||
key: 'reset',
|
||||
icon: 'icon-zhongzhi3',
|
||||
title: '重置图片',
|
||||
},
|
||||
];
|
||||
|
||||
export const ImgView = React.forwardRef<ImgViewRef, ImgViewProps>((props, ref) => {
|
||||
const {
|
||||
imageKey,
|
||||
width,
|
||||
height,
|
||||
odRect,
|
||||
score = 0,
|
||||
attachImg = [],
|
||||
objects = [],
|
||||
showOpt = false,
|
||||
showAttachImgLabel = true,
|
||||
screenshotButtonAlign = defaultAlignOption,
|
||||
screenshotButtonRender = getScreenshotButtonRender({
|
||||
onBigImageActionClick: () => {},
|
||||
disableBtn: [],
|
||||
}),
|
||||
hideLeftTopBtn = true,
|
||||
viewOption = {}
|
||||
} = props;
|
||||
const imgContainerRef = React.useRef(null);
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
|
||||
console.log('props', props)
|
||||
|
||||
const init = useCallback(($container: null) => {
|
||||
imgContainerRef.current = $container;
|
||||
setIsReady(true);
|
||||
}, []);
|
||||
// ============================= viewer =========================
|
||||
const imgInsRef = useRef<any>(null);
|
||||
const [isImgReady, setIsImgReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isReady) return;
|
||||
const handleReady = addEventListenerWrapper(imgContainerRef.current, EVENT_VIEWER_READY, () => {
|
||||
setIsImgReady(true);
|
||||
});
|
||||
const handleTransformChange = addEventListenerWrapper(
|
||||
imgContainerRef.current,
|
||||
EVENT_VIEWER_TRANSFORM_CHANGE,
|
||||
() => {
|
||||
}
|
||||
);
|
||||
imgInsRef.current = new Viewer(imgContainerRef.current, {
|
||||
...viewOption,
|
||||
fitScaleAsMinScale: true,
|
||||
image: generateImg(imageKey),
|
||||
});
|
||||
return () => {
|
||||
handleReady?.remove();
|
||||
handleTransformChange?.remove();
|
||||
imgInsRef?.current?.destroy?.();
|
||||
imgInsRef.current = null;
|
||||
};
|
||||
}, [isReady, imageKey]);
|
||||
|
||||
// ============================= viewer操作按钮 =========================
|
||||
const handleOptClick = (v: string) => {
|
||||
switch (v) {
|
||||
case 'zoomOut':
|
||||
imgInsRef?.current?.scaleTo?.(0.1);
|
||||
break;
|
||||
case 'zoomIn':
|
||||
imgInsRef?.current?.scaleTo?.(-0.1);
|
||||
break;
|
||||
case 'reset':
|
||||
imgInsRef?.current?.reset?.(-0.1);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// ============================= cropper =========================
|
||||
// 手动截图相关参数
|
||||
const cropInsRef = useRef(null);
|
||||
const [showCrop, setShowCrop] = useState(showOpt);
|
||||
const [cropType, setCropType] = useState(CROP_TYPE['AUTO']);
|
||||
|
||||
// 自动截图相关参数
|
||||
const [odList, setOdList] = useState([]);
|
||||
const [extendOdList, setExtendOdList] = useState([]);
|
||||
const [selectRectId, setSelectRectId] = useState(null);
|
||||
|
||||
// 定位按钮相关参数
|
||||
const aliginContainerRef = useRef(null);
|
||||
const alignRef = useRef(null);
|
||||
const [cropRect, setCropRect] = useState<Rect | null>(null);
|
||||
|
||||
// 选中的版本号
|
||||
const [selectAlgorithmVersion, setSelectAlgorithmVersion] = useState(null);
|
||||
const handlerCropStartRef = useRef(null);
|
||||
const handlerCropEndRef = useRef(null);
|
||||
const handleShapeSelectRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
initData(objects)
|
||||
return () => {
|
||||
imgInsRef.current?.clearShape?.();
|
||||
handlerCropStartRef.current?.remove();
|
||||
handlerCropEndRef.current?.remove();
|
||||
handleShapeSelectRef.current?.remove();
|
||||
cropInsRef?.current?.destroy?.();
|
||||
cropInsRef.current = null;
|
||||
};
|
||||
}, [isImgReady, showCrop, cropType]);
|
||||
|
||||
const initData = (_objects: IOdRectOrigin[]) => {
|
||||
const imgIns = imgInsRef.current;
|
||||
//清理crop
|
||||
setCropRect(null);
|
||||
if (!isImgReady) return;
|
||||
if (!showCrop) {
|
||||
imgIns?.addShape?.({
|
||||
x: get(odRect, 'x', 0),
|
||||
y: get(odRect, 'y', 0),
|
||||
w: get(odRect, 'w', 0),
|
||||
h: get(odRect, 'h', 0),
|
||||
selectAble: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (cropType === CROP_TYPE['AUTO']) {
|
||||
const handleGetOD = (odList: any) => {
|
||||
const imgSize = imgIns.getImgSize();
|
||||
const shapeList = odList.map((rect: { [x: string]: any; algorithmVersion: any; }) => ({
|
||||
...rect,
|
||||
selectAble: true,
|
||||
id: rect['id'],
|
||||
algorithmVersion: rect.algorithmVersion,
|
||||
}));
|
||||
imgIns.replaceShape(shapeList);
|
||||
//顺便吧扩展框拿到
|
||||
const extendRect = shapeList.map((rect: { algorithmVersion: string; }) => {
|
||||
const extendRect = getExtendRect(rect, imgSize.w, imgSize.h, rect.algorithmVersion);
|
||||
return { ...rect, ...extendRect };
|
||||
});
|
||||
setExtendOdList(extendRect);
|
||||
imgIns.replaceShape(shapeList);
|
||||
handleShapeSelectRef.current = addEventListenerWrapper(imgContainerRef.current, 'shape-select', (e) => {
|
||||
const id = e.detail;
|
||||
setSelectRectId(id);
|
||||
const selectShape = shapeList.find((v) => v['id'] === id);
|
||||
if (selectShape) {
|
||||
setSelectAlgorithmVersion(selectShape['algorithmVersion']);
|
||||
//换算成屏幕坐标
|
||||
const axisRect = imgIns.imgRectAxisToCanvasAxisRect(selectShape);
|
||||
const rect = {
|
||||
x: axisRect.x2 > axisRect.x ? axisRect.x : axisRect.x2,
|
||||
y: axisRect.y2 > axisRect.y ? axisRect.y : axisRect.y2,
|
||||
w: Math.abs(axisRect.x2 - axisRect.x),
|
||||
h: Math.abs(axisRect.y2 - axisRect.y),
|
||||
};
|
||||
setCropRect(rect);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
setCropRect(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
const rect = getOdRect({ objects })
|
||||
setOdList(rect);
|
||||
handleGetOD(rect);
|
||||
}
|
||||
|
||||
if (cropType === CROP_TYPE['CUSTOM']) {
|
||||
// 手动框选状态预先清除imgIns
|
||||
imgIns?.clearShape?.();
|
||||
handlerCropStartRef.current = addEventListenerWrapper(imgContainerRef.current, EVENT_CROP_START, (event) => {
|
||||
setSelectAlgorithmVersion(null);
|
||||
setCropRect(null);
|
||||
});
|
||||
handlerCropEndRef.current = addEventListenerWrapper(imgContainerRef.current, EVENT_CROP_END, (event) => {
|
||||
const data = event.detail;
|
||||
setSelectAlgorithmVersion(null);
|
||||
setCropRect({
|
||||
x: data.left,
|
||||
y: data.top,
|
||||
w: data.width,
|
||||
h: data.height,
|
||||
});
|
||||
alignRef?.current?.forceAlign?.();
|
||||
});
|
||||
cropInsRef.current = new Cropper(imgContainerRef.current, {
|
||||
showMask: true,
|
||||
viewer: imgIns,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取框选的截图框信息
|
||||
const latestCropType = useLatest(cropType);
|
||||
const latestImgKey = useLatest(imageKey);
|
||||
const latestCropRect = useLatest(cropRect);
|
||||
const getCropInfo = async () => {
|
||||
const cropType = latestCropType.current;
|
||||
const cropRect = latestCropRect.current;
|
||||
const imgIns = imgInsRef.current;
|
||||
const transform = imgIns.targetTransform;
|
||||
let newImgKey = latestImgKey.current;
|
||||
let rectList = [];
|
||||
let extendRectList = [];
|
||||
let selectIndex = 0;
|
||||
switch (cropType) {
|
||||
case CROP_TYPE['AUTO']:
|
||||
const shapes = imgIns.getSelectShape();
|
||||
const shapeIds = shapes.map((v) => v['id']);
|
||||
rectList = odList
|
||||
.filter((v) => shapeIds.includes(v['id']))
|
||||
.map((item: any) => {
|
||||
if (
|
||||
item.algorithmVersion === 'OBJECT_TYPE_FACE' ||
|
||||
item.objectType === 'OBJECT_TYPE_FACE'
|
||||
) {
|
||||
if (!isNull(item.extendBox)) {
|
||||
return {
|
||||
...item,
|
||||
w: get(item, 'extendBox.w'),
|
||||
h: get(item, 'extendBox.h'),
|
||||
x: get(item, 'extendBox.x'),
|
||||
y: get(item, 'extendBox.y'),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
});
|
||||
extendRectList = extendOdList
|
||||
.filter((v) => shapeIds.includes(v['id']))
|
||||
.map((v) => pick(v, ['x', 'y', 'w', 'h', 'algorithmVersion', 'id']));
|
||||
selectIndex = rectList.findIndex((v) => v['id'] === selectRectId);
|
||||
break;
|
||||
default:
|
||||
//获取旋转过的坐标
|
||||
const newRect = getTransformRect(imgIns.image, transform, cropRect);
|
||||
//判断是不是旋转过
|
||||
if (get(transform, 'rotate', 0) % 360 != 0) {
|
||||
const data = getRotateImg(imgIns.image, get(transform, 'rotate', 0));
|
||||
//在画布上画旋转后的图片
|
||||
newImgKey = await upload(data);
|
||||
}
|
||||
rectList.push(newRect);
|
||||
extendRectList.push(newRect);
|
||||
break;
|
||||
}
|
||||
//扩展框获取imgkey
|
||||
await Promise.all(
|
||||
extendRectList.map(async (rect, index) => {
|
||||
const file = await getFileByRect(imgIns.image, rect);
|
||||
const imgKey = await upload(file);
|
||||
extendRectList[index] = { ...rect, imgKey };
|
||||
})
|
||||
);
|
||||
//人脸图获取矫正图
|
||||
await Promise.all(
|
||||
rectList.map(async (rect, index) => {
|
||||
const faceCorrectImage = rect['faceCorrectImage'];
|
||||
let faceCorrectImageKey;
|
||||
if (faceCorrectImage) {
|
||||
const base64 = `data:image/jpg;base64,${faceCorrectImage}`;
|
||||
const blobData = dataURLToBlob(base64);
|
||||
const file = new window.File([blobData], `${new Date().getTime()}`);
|
||||
faceCorrectImageKey = await upload(file);
|
||||
}
|
||||
const newRect = {
|
||||
...rect,
|
||||
...(faceCorrectImageKey ? { faceCorrectImageKey } : {}),
|
||||
};
|
||||
delete newRect['faceCorrectImage'];
|
||||
rectList[index] = newRect;
|
||||
})
|
||||
);
|
||||
return { rectList, extendRectList, selectIndex, imgKey: newImgKey };
|
||||
};
|
||||
|
||||
// 操作界面判断
|
||||
const handleCropBtnClick = (v: string) => {
|
||||
switch (v) {
|
||||
case 'close':
|
||||
setShowCrop(false);
|
||||
break;
|
||||
case 'autoCrop':
|
||||
setCropType(CROP_TYPE['AUTO']);
|
||||
break;
|
||||
case 'customCrop':
|
||||
setCropType(CROP_TYPE['CUSTOM']);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// ============================= attact img =========================
|
||||
const showAttachImg = attachImg.length !== 0;
|
||||
const [selectAttachImgIndex, setSelectAttachImgIndex] = useState(0);
|
||||
const [isZoomin, setIsZoomin] = useState(false);
|
||||
|
||||
// ============================== Ref ===============================
|
||||
useImperativeHandle(ref, () => ({
|
||||
imgInsRef,
|
||||
setShowCrop,
|
||||
initData,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className={classNames(`${componentName}`)} style={{ height, width }}>
|
||||
{/*场景图大图 */}
|
||||
<div
|
||||
className={classNames(
|
||||
`${componentName}-main`,
|
||||
cropType === CROP_TYPE['AUTO'] && `${componentName}-main--cursor`
|
||||
)}
|
||||
ref={init}
|
||||
/>
|
||||
{/* 图片操作 */}
|
||||
{!hideLeftTopBtn && (
|
||||
<BtnGroup
|
||||
className={classNames(`${componentName}-opt`)}
|
||||
dataSource={operateBtnDataSource}
|
||||
onClick={handleOptClick}
|
||||
placement="left"
|
||||
/>
|
||||
)}
|
||||
{showCrop && (
|
||||
<BtnGroup
|
||||
circle
|
||||
className={classNames(`${componentName}-crop-opt`)}
|
||||
dataSource={cropBtnDataSource}
|
||||
onClick={handleCropBtnClick}
|
||||
selectKey={cropType === CROP_TYPE['AUTO'] ? 'autoCrop' : 'customCrop'}
|
||||
/>
|
||||
)}
|
||||
{showCrop && cropRect && screenshotButtonRender && (
|
||||
<>
|
||||
<div
|
||||
ref={aliginContainerRef}
|
||||
className={classNames(`${componentName}-align`)}
|
||||
style={Object.assign(
|
||||
{
|
||||
width: cropRect.w,
|
||||
height: cropRect.h,
|
||||
},
|
||||
getTransforms({
|
||||
translateX: cropRect.x,
|
||||
translateY: cropRect.y,
|
||||
})
|
||||
)}
|
||||
></div>
|
||||
<Align
|
||||
ref={alignRef}
|
||||
monitorWindowResize
|
||||
align={screenshotButtonAlign}
|
||||
target={function () {
|
||||
return aliginContainerRef.current;
|
||||
}}
|
||||
>
|
||||
{screenshotButtonRender({
|
||||
model: 'IMAGE',
|
||||
getCropInfo,
|
||||
setShowCrop,
|
||||
cropType,
|
||||
selectAlgorithmVersion,
|
||||
})}
|
||||
</Align>
|
||||
</>
|
||||
)}
|
||||
{/* 场景图小图 */}
|
||||
{showAttachImg && !showCrop && (
|
||||
<div
|
||||
className={classNames(
|
||||
`${componentName}-attach`,
|
||||
isZoomin && `${componentName}-attach--zoomin`,
|
||||
`${componentName}-attach--fixed`,
|
||||
isZoomin && `${componentName}-attach--zoomin--fixed`
|
||||
)}
|
||||
>
|
||||
<div className={classNames(`${componentName}-attach__tab`)}>
|
||||
{showAttachImgLabel
|
||||
? attachImg.map(({ label, url }, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={classNames(
|
||||
`${componentName}-attach__tab-item`,
|
||||
selectAttachImgIndex === index && `${componentName}-attach__tab-item--select`
|
||||
)}
|
||||
onMouseEnter={() => {
|
||||
setSelectAttachImgIndex(index);
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
<div className={classNames(`${componentName}-attach__scale`)}>
|
||||
<Button
|
||||
type="text"
|
||||
//绝对定位下onClick事件失效采用onMouseDown
|
||||
onMouseDown={(e) => {
|
||||
//如果是左键执行
|
||||
if (e.button == 0) {
|
||||
setIsZoomin((pre) => !pre);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
color: '#fff',
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
styles={{ display: 'flex' }}
|
||||
icon={isZoomin ? 'icon-cancle_fullscreen' : 'icon-fullscreen'}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
<img
|
||||
draggable="false"
|
||||
className={classNames(
|
||||
`${componentName}-attach__img`,
|
||||
`${componentName}-attach__img--fixed`
|
||||
)}
|
||||
src={get(attachImg, `${selectAttachImgIndex}.url`, '')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{ bottom: 20 }}
|
||||
className={classNames(`${componentName}__face-score`)}
|
||||
>{`人脸质量分:${(score as number).toFixed(2)}`}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
ImgView.displayName = 'ImgView';
|
||||
|
||||
export default ImgView;
|
@ -1,6 +1,6 @@
|
||||
//@ts-nocheck
|
||||
import { addClass, removeClass } from 'rc-util/lib/Dom/class';
|
||||
import { addEventlistener, isNumber, get } from '@zhst/func';
|
||||
import { addEventListenerWrapper, isNumber, get } from '@zhst/func';
|
||||
import warn from 'rc-util/lib/warn';
|
||||
import { getData, getPointer, getOffset, dispatchEvent } from '../utils';
|
||||
import {
|
||||
@ -36,13 +36,13 @@ export default {
|
||||
bind() {
|
||||
const { container, element, eventHandleList = [], option } = this;
|
||||
|
||||
const handleCropStart = addEventlistener(
|
||||
const handleCropStart = addEventListenerWrapper(
|
||||
container,
|
||||
EVENT_POINTER_DOWN,
|
||||
this.onCropStart.bind(this)
|
||||
);
|
||||
eventHandleList.push(handleCropStart);
|
||||
const handleCropMove = addEventlistener(
|
||||
const handleCropMove = addEventListenerWrapper(
|
||||
element.ownerDocument,
|
||||
EVENT_POINTER_MOVE,
|
||||
this.onCropMove.bind(this)
|
||||
@ -51,7 +51,7 @@ export default {
|
||||
EVENT_POINTER_UP.trim()
|
||||
.split(REGEXP_SPACES)
|
||||
.forEach((eventName) => {
|
||||
const handleCropEnd = addEventlistener(
|
||||
const handleCropEnd = addEventListenerWrapper(
|
||||
element.ownerDocument,
|
||||
eventName,
|
||||
this.onCropEnd.bind(this)
|
||||
|
@ -1,5 +1,5 @@
|
||||
//@ts-nocheck
|
||||
import {addEventlistener} from '@zhst/func';
|
||||
import {addEventListenerWrapper} from '@zhst/func';
|
||||
import { EVENT_WHEEL } from './constants';
|
||||
import { EVENT_VIEWER_TRANSFORM_CHANGE } from '../viewer/constants';
|
||||
|
||||
@ -10,12 +10,12 @@ export default {
|
||||
if (this.options.viewer) {
|
||||
const viewer = this.options.viewer;
|
||||
//添加缩放事件
|
||||
const handleWhele = addEventlistener(container, EVENT_WHEEL, this.onWheel.bind(this));
|
||||
const handleWhele = addEventListenerWrapper(container, EVENT_WHEEL, this.onWheel.bind(this));
|
||||
eventHandleList.push(handleWhele);
|
||||
//添加事件监听 获取limit crop box & 渲染canvas
|
||||
this.onTransformChange(viewer);
|
||||
this.limited = true;
|
||||
const handleTransformChange = addEventlistener(
|
||||
const handleTransformChange = addEventListenerWrapper(
|
||||
viewer.element,
|
||||
EVENT_VIEWER_TRANSFORM_CHANGE,
|
||||
(event) => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
//@ts-nocheck
|
||||
import { isNumber, get, addEventListener } from '@zhst/func';
|
||||
import { isNumber, get, addEventListenerWrapper } from '@zhst/func';
|
||||
import { addClass, removeClass } from 'rc-util/lib/Dom/class.js';
|
||||
import guid from 'rc-util/lib/guid';
|
||||
import warn from 'rc-util/lib/warn';
|
||||
@ -33,19 +33,19 @@ export default {
|
||||
//图片事件
|
||||
const scaleAble = get(options, 'scaleAble', true);
|
||||
if (scaleAble) {
|
||||
const handleWhele = addEventListener(canvas, EVENT_WHEEL, this.onWheel.bind(this));
|
||||
const handleWhele = addEventListenerWrapper(canvas, EVENT_WHEEL, this.onWheel.bind(this));
|
||||
eventHandleList.push(handleWhele);
|
||||
}
|
||||
|
||||
const dragAble = get(options, 'dragAble', true);
|
||||
if (dragAble) {
|
||||
const handleDragStart = addEventListener(
|
||||
const handleDragStart = addEventListenerWrapper(
|
||||
canvas,
|
||||
EVENT_POINTER_DOWN,
|
||||
this.onDragStart.bind(this)
|
||||
);
|
||||
eventHandleList.push(addEventListener);
|
||||
const handleDragMove = addEventListener(
|
||||
eventHandleList.push(addEventListenerWrapper);
|
||||
const handleDragMove = addEventListenerWrapper(
|
||||
element.ownerDocument,
|
||||
EVENT_POINTER_MOVE,
|
||||
this.onDragMove.bind(this)
|
||||
@ -54,7 +54,7 @@ export default {
|
||||
EVENT_POINTER_UP.trim()
|
||||
.split(REGEXP_SPACES)
|
||||
.forEach((eventName) => {
|
||||
const handleDragEnd = addEventListener(
|
||||
const handleDragEnd = addEventListenerWrapper(
|
||||
element.ownerDocument,
|
||||
eventName,
|
||||
this.onDragEnd.bind(this)
|
||||
@ -64,11 +64,11 @@ export default {
|
||||
}
|
||||
|
||||
//rect事件
|
||||
const handleClick = addEventListener(canvas, EVENT_CLICK, this.onClick.bind(this));
|
||||
const handleClick = addEventListenerWrapper(canvas, EVENT_CLICK, this.onClick.bind(this));
|
||||
eventHandleList.push(handleClick);
|
||||
// const handleLeveal = addEventListener(canvas, EVENT_LEAVEL, this.onLeavel.bind(this));
|
||||
// const handleLeveal = addEventListenerWrapper(canvas, EVENT_LEAVEL, this.onLeavel.bind(this));
|
||||
// eventHandleList.push(handleLeveal);
|
||||
// const handleEnter = addEventListener(canvas, EVENT_ENTER, this.onEnter.bind(this));
|
||||
// const handleEnter = addEventListenerWrapper(canvas, EVENT_ENTER, this.onEnter.bind(this));
|
||||
// eventHandleList.push(handleEnter);
|
||||
},
|
||||
unbind() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useRef, useState, FC, useEffect, useCallback } from 'react'
|
||||
import { generateImg, get, addEventListener } from '@zhst/func';
|
||||
import { generateImg, get, addEventListenerWrapper } from '@zhst/func';
|
||||
import { useUpdateEffect } from '@zhst/hooks';
|
||||
import { Button } from 'antd';
|
||||
import classNames from 'classnames'
|
||||
@ -43,7 +43,7 @@ const CompaterImage: FC<CompaterImageProps> = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
setShowUrl(url);
|
||||
const handleTransformChange = addEventListener(
|
||||
const handleTransformChange = addEventListenerWrapper(
|
||||
imgContainerRef.current,
|
||||
'viewer-transform-change',
|
||||
(event: any) => {
|
||||
|
116
packages/meta/src/VideoPlayer/components/FlvPlayer/index.tsx
Normal file
116
packages/meta/src/VideoPlayer/components/FlvPlayer/index.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
import React, { Component, CSSProperties } from 'react';
|
||||
import flvjs from 'flv.js';
|
||||
import { isEqual } from '@zhst/func';
|
||||
|
||||
export const FLV_EVENT = flvjs.Events;
|
||||
|
||||
export interface VideoPlayerProps {
|
||||
className: string;
|
||||
style?: CSSProperties;
|
||||
type: string;
|
||||
isLive?: boolean;
|
||||
cors?: boolean;
|
||||
withCredentials?: boolean;
|
||||
playId?: number;
|
||||
|
||||
hasAudio?: boolean;
|
||||
hasVideo?: boolean;
|
||||
|
||||
duration?: number;
|
||||
filesize?: number;
|
||||
url?: string;
|
||||
autoPlay?: boolean;
|
||||
onCreat?: any;
|
||||
|
||||
/**
|
||||
* @see https://github.com/Bilibili/flv.js/blob/master/docs/api.md#config
|
||||
*/
|
||||
config: object;
|
||||
}
|
||||
|
||||
export default class VideoPlayer extends Component<VideoPlayerProps, any> {
|
||||
state = {
|
||||
curPlayUrl: '',
|
||||
shouldReinit: false,
|
||||
};
|
||||
flvPlayer = null;
|
||||
videoElement = null;
|
||||
|
||||
static getDerivedStateFromProps = (nextProps: { url?: any; playId?: any; }, prevState: { curPlayUrl?: any; playId?: any; }) => {
|
||||
const { playId = 0 } = nextProps;
|
||||
const { playId: prePlayId = 0 } = prevState;
|
||||
|
||||
if (nextProps.url !== undefined) {
|
||||
if (!isEqual(nextProps.url, prevState.curPlayUrl) || !isEqual(playId, prePlayId)) {
|
||||
return {
|
||||
playId: playId,
|
||||
curPlayUrl: nextProps.url,
|
||||
shouldReinit: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
// 否则,对于state不进行任何操作
|
||||
return null;
|
||||
};
|
||||
|
||||
initFlv = ($video: null) => {
|
||||
this.videoElement = $video;
|
||||
const { className, autoPlay = true, config = {}, onCreat, playId, ...others } = this.props;
|
||||
|
||||
if ($video) {
|
||||
|
||||
if (flvjs.isSupported() && this.props.url && this.props.url !== '') {
|
||||
const reload = () => {
|
||||
|
||||
if (this.flvPlayer && this.flvPlayer.destroy) {
|
||||
try {
|
||||
this.flvPlayer?.destroy();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
let flvPlayer = flvjs.createPlayer({ ...others }, config);
|
||||
flvPlayer.attachMediaElement($video);
|
||||
flvPlayer.load();
|
||||
this.flvPlayer = flvPlayer;
|
||||
this.flvPlayer.reload = reload;
|
||||
onCreat && onCreat(this.flvPlayer, $video);
|
||||
};
|
||||
reload();
|
||||
onCreat && onCreat(this.flvPlayer, $video);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.flvPlayer) {
|
||||
this.flvPlayer?.unload();
|
||||
this.flvPlayer?.detachMediaElement();
|
||||
}
|
||||
}
|
||||
componentDidUpdate() {
|
||||
if (this.state.shouldReinit) {
|
||||
this.setState({ shouldReinit: false });
|
||||
this.initFlv(this.videoElement);
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { className, style } = this.props;
|
||||
return (
|
||||
<video
|
||||
muted
|
||||
preload="metadata"
|
||||
className={className}
|
||||
// controls={true}
|
||||
style={Object.assign(
|
||||
{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
style ? style : {}
|
||||
)}
|
||||
ref={this.initFlv}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
53
packages/meta/src/VideoPlayer/components/Loading/index.less
Normal file
53
packages/meta/src/VideoPlayer/components/Loading/index.less
Normal file
@ -0,0 +1,53 @@
|
||||
.zhst-image__video-view__player-mask {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgb(4 4 4 / 70%);
|
||||
|
||||
&--bg {
|
||||
z-index: 999;
|
||||
background-color: rgb(4 4 4 / 100%);
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&-title {
|
||||
margin-top: 12px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
|
||||
& a {
|
||||
color: #09f;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.zhst-image__video-view__icon-wraper {
|
||||
display: flex;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgb(255 255 255 / 10%);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
line-height: 80px;
|
||||
text-align: center;
|
||||
|
||||
// &:hover {
|
||||
// background: #0099ff;
|
||||
// }
|
||||
}
|
72
packages/meta/src/VideoPlayer/components/Loading/index.tsx
Normal file
72
packages/meta/src/VideoPlayer/components/Loading/index.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import React, { FC } from 'react'
|
||||
import { Spin } from 'antd'
|
||||
import classNames from 'classnames'
|
||||
import Icon from '../../../iconfont'
|
||||
import './index.less'
|
||||
|
||||
const componentName = `zhst-image__video-view`;
|
||||
|
||||
export interface ILoading {
|
||||
status: 'LOADING' | 'COMPLETED' | 'END' | 'ERROR'
|
||||
reload: () => void
|
||||
}
|
||||
|
||||
const Loading: FC<ILoading> = (props) => {
|
||||
const { status, reload } = props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(`${componentName}__player-mask`)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{/* todo:图标颜色 */}
|
||||
{status === 'LOADING' && (
|
||||
<Spin tip="加载中..." spinning={status === 'LOADING'} />
|
||||
)}
|
||||
{status === 'END' && (
|
||||
<>
|
||||
<div
|
||||
onClick={() => {
|
||||
reload();
|
||||
}}
|
||||
className={classNames(`${componentName}__icon-wraper`)}
|
||||
>
|
||||
<Icon
|
||||
styles={{
|
||||
fill: '#ffffff',
|
||||
color: '#ffffff',
|
||||
}}
|
||||
size={54}
|
||||
icon={'icon-shuaxin'}
|
||||
/>
|
||||
</div>
|
||||
<div className={classNames(`${componentName}__player-mask-title`)}>
|
||||
{'点击刷新后,将重新播放'}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{status === 'ERROR' && (
|
||||
<>
|
||||
<div className={classNames(`${componentName}__icon-wraper`)}>
|
||||
<Icon
|
||||
styles={{
|
||||
color: '#ffffff',
|
||||
}}
|
||||
size={54}
|
||||
icon={'icon-jiazaishibai'}
|
||||
/>
|
||||
</div>
|
||||
<div className={classNames(`${componentName}__player-mask-title`)}>
|
||||
{'视频加载失败,'}
|
||||
<a onClick={reload}> {'刷新'}</a>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Loading
|
49
packages/meta/src/VideoPlayer/components/Progress/index.less
Normal file
49
packages/meta/src/VideoPlayer/components/Progress/index.less
Normal file
@ -0,0 +1,49 @@
|
||||
.zhst-image__range {
|
||||
position: relative;
|
||||
|
||||
&--no-slider {
|
||||
.next-range-slider {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
& .next-range .next-range-track {
|
||||
height: 8px;
|
||||
margin-top: -4px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
& .next-range .next-range-selected {
|
||||
height: 8px;
|
||||
margin-top: -4px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
& .next-range .next-range-slider-inner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-color: #fff;
|
||||
margin-top: -7px;
|
||||
margin-left: -7px;
|
||||
background-color: #0098ff;
|
||||
}
|
||||
|
||||
& .next-range .next-range-slider {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-top: -7px;
|
||||
margin-left: -7px;
|
||||
}
|
||||
|
||||
& .next-range.simulation-click>.next-range-slider-inner {
|
||||
border: 2px solid #fff !important;
|
||||
}
|
||||
|
||||
& .next-range .next-range-frag.next-range-active .next-range-slider .next-range-slider-inner {
|
||||
border: 2px solid #fff !important;
|
||||
}
|
||||
|
||||
& .next-range .next-range-slider.next-range-slider-moving .next-range-slider-inner {
|
||||
border: 2px solid #fff !important;
|
||||
}
|
||||
}
|
29
packages/meta/src/VideoPlayer/components/Progress/index.tsx
Normal file
29
packages/meta/src/VideoPlayer/components/Progress/index.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Slider } from 'antd';
|
||||
import './index.less';
|
||||
|
||||
const componentName = `zhst-image__range`;
|
||||
|
||||
export interface RangeWrapperProps extends React.HTMLAttributes<HTMLElement> {
|
||||
showSlider: boolean;
|
||||
}
|
||||
|
||||
export const Range: React.FC<RangeWrapperProps> = (props) => {
|
||||
const { className, style, showSlider = true, ...others } = props;
|
||||
return (
|
||||
<div
|
||||
style={style}
|
||||
className={classNames(
|
||||
`${componentName}`,
|
||||
!showSlider && `${componentName}--no-slider`,
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* @ts-ignore */}
|
||||
<Slider {...others}></Slider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Range;
|
75
packages/meta/src/VideoPlayer/index.less
Normal file
75
packages/meta/src/VideoPlayer/index.less
Normal file
@ -0,0 +1,75 @@
|
||||
.zhst-image__video-view {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 532px;
|
||||
background-color: #333;
|
||||
|
||||
// &-flv {
|
||||
// width: 85%;
|
||||
// }
|
||||
&-screenshot {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&-crop-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&-align {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-opt {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
background-color: rgb(0 0 0 / 80%);
|
||||
line-height: 32px;
|
||||
|
||||
&>div:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
&>div:last-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
&-range {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
|
||||
&>div:first-child {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&>div:last-child {
|
||||
width: 100px;
|
||||
margin-left: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
packages/meta/src/VideoPlayer/index.md
Normal file
27
packages/meta/src/VideoPlayer/index.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
nav:
|
||||
title: 元组件
|
||||
order: 1
|
||||
group:
|
||||
title: 通用
|
||||
order: 3
|
||||
---
|
||||
|
||||
# VideoPlayer 视频播放
|
||||
|
||||
```jsx
|
||||
import React from 'react';
|
||||
import { VideoPlayer } from '@zhst/meta'
|
||||
|
||||
const props = {
|
||||
"flvUrl":"ws://10.0.0.120:9033/flv/HaikangNvr/45.flv?ip=10.0.2.103&stime=1705051970&etime=1705051990",
|
||||
"maxDuration":20,
|
||||
"showCrop$":false
|
||||
}
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<VideoPlayer {...props} />
|
||||
)
|
||||
}
|
||||
```
|
696
packages/meta/src/VideoPlayer/index.tsx
Normal file
696
packages/meta/src/VideoPlayer/index.tsx
Normal file
@ -0,0 +1,696 @@
|
||||
import React, { Dispatch, ReactElement, SetStateAction, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
noop,
|
||||
get,
|
||||
addEventListenerWrapper,
|
||||
dataURLToBlob,
|
||||
nextTick,
|
||||
toRealNumber,
|
||||
getTransforms,
|
||||
formatDurationTime
|
||||
} from '@zhst/func';
|
||||
import Align from 'rc-align';
|
||||
import { Rect, IScreenshotButtonProp, AlignType } from '@zhst/types'
|
||||
import { useLatest, useUpdateEffect, useFullscreen, useUnmount } from '@zhst/hooks';
|
||||
import classNames from 'classnames';
|
||||
import download from 'downloadjs';
|
||||
import { Button, message } from 'antd';
|
||||
import Icon from '../iconfont';
|
||||
import {
|
||||
Cropper,
|
||||
EVENT_CROP_START,
|
||||
EVENT_CROP_END,
|
||||
} from '../ImageEditor';
|
||||
import FlvPlayer, { FLV_EVENT } from './components/FlvPlayer';
|
||||
import Range from './components/Progress';
|
||||
import Loading, { ILoading } from './components/Loading';
|
||||
import getScreenshotButtonRender from '../BigImagePreview/components/ScreenhotButtons';
|
||||
import './index.less'
|
||||
|
||||
const componentName = `zhst-image__video-view`;
|
||||
|
||||
export const CROP_TYPE = {
|
||||
CUSTOM: 'CSUTOM',
|
||||
AUTO: 'AUTO',
|
||||
};
|
||||
|
||||
export function getShowStatus(isLoadingVideo: boolean, isEnd: boolean, isError: boolean): ILoading['status'] {
|
||||
let status = null;
|
||||
if (isLoadingVideo) {
|
||||
status = 'LOADING';
|
||||
}
|
||||
if (isError) {
|
||||
status = 'ERROR';
|
||||
}
|
||||
if (isEnd) {
|
||||
status = 'END';
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
export interface VideoViewProps {
|
||||
/* 播放地址 */
|
||||
flvUrl: string;
|
||||
/* 播放总时间 */
|
||||
maxDuration?: number;
|
||||
/* 截图渲染 */
|
||||
screenshotButtonAlign?: AlignType;
|
||||
screenshotButtonRender?: (screenshotButtonProp: IScreenshotButtonProp) => ReactElement;
|
||||
/* 默认截图框 */
|
||||
defautlNormalizationRect?: Rect;
|
||||
/* 截图回调 */
|
||||
onCropChange?: (showCrop: boolean, normalizationRect: null | Rect) => void;
|
||||
}
|
||||
|
||||
export interface VideoViewRef {
|
||||
/* 当前图片模式 */
|
||||
cropAble: boolean;
|
||||
setShowCrop: Dispatch<SetStateAction<boolean>>;
|
||||
downloadVideoframe: () => void;
|
||||
}
|
||||
|
||||
const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
|
||||
const {
|
||||
flvUrl,
|
||||
maxDuration,
|
||||
screenshotButtonAlign = {
|
||||
points: ['bl', 'br'],
|
||||
offset: [6, 0],
|
||||
overflow: {
|
||||
adjustX: true,
|
||||
adjustY: true,
|
||||
},
|
||||
},
|
||||
screenshotButtonRender = getScreenshotButtonRender({
|
||||
onBigImageActionClick: () => {},
|
||||
disableBtn: [],
|
||||
}),
|
||||
onCropChange,
|
||||
defautlNormalizationRect: defaultNormalizationRect,
|
||||
} = props;
|
||||
const videoType = useMemo(() => (flvUrl && flvUrl.startsWith('http') ? 'mp4' : 'flv'), [flvUrl]);
|
||||
// ========================== 播放 =========================
|
||||
//实例参数
|
||||
const containerRef = useRef(null); //容器ref
|
||||
const videoRef = useRef(null); //video 标签dom
|
||||
const videoInsRef: any = useRef(null); //flv 实例
|
||||
const [playSeq, setPlaySeq] = useState(0); // 通过重置playid使FLV组件重新渲染
|
||||
const videoRemoveListener = useRef(noop); //移除dom监听的中间函数
|
||||
const loadingTimeRef = useRef<number | null>(0); //最后一次加载时间
|
||||
const delayLoadingTimer = useRef(null); //算loading的定时器
|
||||
//状态参数
|
||||
const [isReady, setIsReady] = useState(false); //
|
||||
const [isPlay, setIsPlay] = useState(false); //当前是否播放
|
||||
|
||||
const [isEnd, setIsEnd] = useState(false); //是否播放结束
|
||||
const [isError, setIsError] = useState(false); //播放出错
|
||||
const [isVideoLoadFinished, setIsVideoLoadFinish] = useState(false); //是否缓存加载完成
|
||||
const [playTime, setPlayTime] = useState(0); //当前播放时间
|
||||
const [isLoadingVideo, setIsLoadingVideo] = useState(true); //是否加载中
|
||||
const [isDelayLoading, setIsDelayLoading] = useState(false); //显示的转圈loading 延迟0.2s显示
|
||||
//设置延迟转圈圈
|
||||
const latestIsLoadingVideo = useLatest(isLoadingVideo);
|
||||
const setIsLoadingVideoWrapper = (isLoading: boolean) => {
|
||||
setIsLoadingVideo((preLoading) => {
|
||||
if (!preLoading && isLoading) {
|
||||
loadingTimeRef.current = new Date().getTime();
|
||||
}
|
||||
if (!isLoading) {
|
||||
loadingTimeRef.current = null;
|
||||
}
|
||||
//延迟0。2s相关
|
||||
if (!isLoading) {
|
||||
setIsDelayLoading(false);
|
||||
}
|
||||
if (!delayLoadingTimer.current && preLoading) {
|
||||
delayLoadingTimer.current = setTimeout(() => {
|
||||
if (latestIsLoadingVideo.current) {
|
||||
//0.2s后才显示
|
||||
setIsDelayLoading(true);
|
||||
}
|
||||
delayLoadingTimer.current = null;
|
||||
}, 200);
|
||||
}
|
||||
return isLoading;
|
||||
});
|
||||
};
|
||||
// 初始化loading 30s 直接显示错误
|
||||
// TODO :逻辑忘记了 不应该是每次init player吗?
|
||||
useEffect(() => {
|
||||
let timer = setInterval(() => {
|
||||
if (loadingTimeRef.current) {
|
||||
if (new Date().getTime() - loadingTimeRef.current > 1000 * 30) {
|
||||
checkIsErr()
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
};
|
||||
}, []);
|
||||
|
||||
//结束的时候暂停 保证不播了
|
||||
useUpdateEffect(() => {
|
||||
if (isEnd) {
|
||||
videoInsRef?.current?.pause?.();
|
||||
}
|
||||
}, [isEnd]);
|
||||
|
||||
// 捕捉视频播放报错
|
||||
const checkIsErr = () => {
|
||||
setIsError(true)
|
||||
try {
|
||||
videoInsRef?.current?.destroy?.();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
const latestMaxDuration = useLatest(maxDuration);
|
||||
const initPlayer = useCallback((ins: any, dom: any) => {
|
||||
videoRef.current = dom;
|
||||
videoInsRef.current = ins;
|
||||
const maxDuration = latestMaxDuration.current || 0;
|
||||
//监听播放事件
|
||||
let video = dom;
|
||||
let errorLister = (e: any) => {
|
||||
checkIsErr();
|
||||
console.error('视频出错了', e, video.currentTime);
|
||||
};
|
||||
let waitingListener = (e: any) => {
|
||||
setIsLoadingVideoWrapper(true);
|
||||
console.debug('视频加载等待', e, video.currentTime);
|
||||
};
|
||||
let playingListener = (e: any) => {
|
||||
setIsLoadingVideoWrapper(false);
|
||||
setIsError(false)
|
||||
console.debug('视频从等待中播放', e, video.currentTime);
|
||||
};
|
||||
let playLister = (e: any) => {
|
||||
setIsPlay(true);
|
||||
setIsError(false)
|
||||
console.debug('提示该视频正在播放中', e, video.currentTime);
|
||||
};
|
||||
let pauseListener = (e: any) => {
|
||||
setIsPlay(false);
|
||||
console.debug('暂停播放', e, video.currentTime);
|
||||
};
|
||||
let endedListner = (e: any) => {
|
||||
setIsEnd(true);
|
||||
setIsVideoLoadFinish(true);
|
||||
console.debug('视频播放完了', e, video.currentTime);
|
||||
};
|
||||
let timeupdateListner = (e: any) => {
|
||||
console.debug(
|
||||
'视频播放时间更新',
|
||||
e,
|
||||
video.currentTime,
|
||||
videoRef.current?.duration,
|
||||
maxDuration
|
||||
);
|
||||
let nowTime = video.currentTime;
|
||||
if (nowTime >= maxDuration) {
|
||||
setIsEnd(true);
|
||||
setIsVideoLoadFinish(true);
|
||||
}
|
||||
setPlayTime(nowTime);
|
||||
};
|
||||
// see https://github.com/bilibili/flv.js/issues/337
|
||||
let windowErrorHandle = (errorEvent: { [x: string]: string; }) => {
|
||||
try {
|
||||
if (
|
||||
errorEvent['message'] ==
|
||||
"Uncaught TypeError: Cannot read property 'flushStashedSamples' of null"
|
||||
) {
|
||||
checkIsErr();
|
||||
console.error('视频出错了 window监听', errorEvent);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
video.addEventListener('error', errorLister);
|
||||
video.addEventListener('waiting', waitingListener);
|
||||
video.addEventListener('playing', playingListener);
|
||||
video.addEventListener('play', playLister);
|
||||
video.addEventListener('pause', pauseListener);
|
||||
video.addEventListener('ended', endedListner);
|
||||
video.addEventListener('timeupdate', timeupdateListner);
|
||||
window.addEventListener('error', windowErrorHandle);
|
||||
|
||||
videoRemoveListener.current = () => {
|
||||
video.removeEventListener('error', errorLister);
|
||||
video.removeEventListener('waiting', waitingListener);
|
||||
video.removeEventListener('playing', playingListener);
|
||||
video.removeEventListener('play', playLister);
|
||||
video.removeEventListener('pause', pauseListener);
|
||||
video.removeEventListener('ended', endedListner);
|
||||
video.removeEventListener('timeupdate', timeupdateListner);
|
||||
window.removeEventListener('error', windowErrorHandle);
|
||||
};
|
||||
|
||||
videoInsRef?.current.on(FLV_EVENT.ERROR, (type: any, errDetail: any, info: any) => {
|
||||
checkIsErr();
|
||||
console.error('videoInsRef 错误', type, errDetail, info, video.currentTime);
|
||||
});
|
||||
let playPromise = videoInsRef?.current.play();
|
||||
//先ready 遮挡会导致播放失败
|
||||
setIsReady(true);
|
||||
playPromise
|
||||
.then(() => {
|
||||
setIsReady(true);
|
||||
})
|
||||
.catch((...arg: any) => {
|
||||
try {
|
||||
} catch (error) {}
|
||||
// setIsError(true);
|
||||
console.error('playPromise视频出错了', arg);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useUnmount(() => {
|
||||
try {
|
||||
videoRemoveListener.current();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
const reload = async () => {
|
||||
if (videoInsRef.current) {
|
||||
let oldTime = videoInsRef.current.currentTime;
|
||||
videoInsRef.current.currentTime = 0;
|
||||
|
||||
//如果修改时间不成功,则走重新加载的逻辑
|
||||
if (oldTime === videoInsRef.current.currentTime) {
|
||||
//重置状态
|
||||
setIsReady(false);
|
||||
setIsPlay(false);
|
||||
setIsLoadingVideoWrapper(false);
|
||||
setIsReady(false);
|
||||
setIsEnd(false);
|
||||
setIsVideoLoadFinish(false);
|
||||
setPlayTime(0);
|
||||
|
||||
//清楚dom事件监听
|
||||
try {
|
||||
videoRemoveListener.current();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
setPlaySeq((pre) => pre + 1);
|
||||
return;
|
||||
}
|
||||
videoInsRef.current.play();
|
||||
}
|
||||
|
||||
setPlayTime(0);
|
||||
setIsEnd(false);
|
||||
};
|
||||
const seek = (v: string) => {
|
||||
if (videoInsRef.current && isVideoLoadFinished) {
|
||||
setPlayTime(parseFloat(v));
|
||||
videoInsRef.current.currentTime = parseFloat(v);
|
||||
} else {
|
||||
message.warning('待视频加载完,才可操作进度条')
|
||||
}
|
||||
};
|
||||
// ========================== 视频opt bar =========================
|
||||
const [isFullscreen, { toggleFullscreen }] = useFullscreen(containerRef, {
|
||||
pageFullscreen: true,
|
||||
});
|
||||
const showMaxDuration = !!maxDuration
|
||||
? maxDuration
|
||||
: toRealNumber(get(videoRef, 'current.duration', 0));
|
||||
const showSlider = videoInsRef.current && isVideoLoadFinished;
|
||||
const showStatus = getShowStatus(isDelayLoading, isEnd, isError);
|
||||
|
||||
// ========================== 截图 =========================
|
||||
const corpContainerRef = useRef();
|
||||
const cropInsRef = useRef(null);
|
||||
const [showCrop, setShowCrop] = useState<boolean>(false);
|
||||
|
||||
//回显默认框选
|
||||
const isFirstFlagRef = useRef<boolean>(true);
|
||||
useEffect(() => {
|
||||
const isFirst = isFirstFlagRef.current;
|
||||
if (!isLoadingVideo && isReady && isFirst && defaultNormalizationRect && !showStatus) {
|
||||
nextTick(() => {
|
||||
setShowCrop(true);
|
||||
});
|
||||
}
|
||||
}, [isLoadingVideo, showStatus]);
|
||||
|
||||
//定位按钮相关参数
|
||||
const alginContainerRef = useRef(null);
|
||||
const alignRef = useRef(null);
|
||||
const [cropRect, setCropRect] = useState<Rect| null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
showCrop ? videoInsRef?.current?.pause() : videoInsRef?.current?.play();
|
||||
}, [showCrop]);
|
||||
|
||||
useEffect(() => {
|
||||
let handlerCropStart: { remove: () => void; };
|
||||
let handlerCropEnd: { remove: () => void; };
|
||||
setCropRect(null);
|
||||
if (!isReady) return;
|
||||
if (showCrop) {
|
||||
handlerCropStart = addEventListenerWrapper(corpContainerRef.current, EVENT_CROP_START, (event) => {
|
||||
setCropRect(null);
|
||||
});
|
||||
handlerCropEnd = addEventListenerWrapper(corpContainerRef.current, EVENT_CROP_END, (event) => {
|
||||
const data = event.detail;
|
||||
setCropRect({
|
||||
x: data.left,
|
||||
y: data.top,
|
||||
w: data.width,
|
||||
h: data.height,
|
||||
});
|
||||
alignRef?.current?.forceAlign?.();
|
||||
});
|
||||
let video: any = videoRef.current;
|
||||
//计算limitcroppbox
|
||||
let scale = Math.min(
|
||||
video.offsetWidth / video.videoWidth,
|
||||
video.offsetHeight / video.videoHeight
|
||||
);
|
||||
let finalVideoWidth = video.videoWidth * scale;
|
||||
let finalVideoHeight = video.videoHeight * scale;
|
||||
let cropBoxLimited = {
|
||||
width: finalVideoWidth,
|
||||
height: finalVideoHeight,
|
||||
top: (video.offsetHeight - finalVideoHeight) / 2,
|
||||
left: (video.offsetWidth - finalVideoWidth) / 2,
|
||||
};
|
||||
//获取视频图片
|
||||
let canvas = document.createElement('canvas');
|
||||
canvas.width = video.offsetWidth;
|
||||
canvas.height = video.offsetHeight;
|
||||
canvas.style.display = 'none';
|
||||
document.body.appendChild(canvas);
|
||||
let ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(
|
||||
video,
|
||||
(video.offsetWidth - finalVideoWidth) / 2,
|
||||
(video.offsetHeight - finalVideoHeight) / 2,
|
||||
finalVideoWidth,
|
||||
finalVideoHeight
|
||||
);
|
||||
let imageData = canvas.toDataURL('image/png');
|
||||
canvas.parentNode?.removeChild(canvas);
|
||||
//回显编辑框
|
||||
const isFirst = isFirstFlagRef.current;
|
||||
let initialCropBoxData = null;
|
||||
if (isFirst && defaultNormalizationRect) {
|
||||
initialCropBoxData = {
|
||||
left: defaultNormalizationRect.x * finalVideoWidth + cropBoxLimited.left,
|
||||
top: defaultNormalizationRect.y * finalVideoHeight + cropBoxLimited.top,
|
||||
width: defaultNormalizationRect.w * finalVideoWidth,
|
||||
height: defaultNormalizationRect.h * finalVideoHeight,
|
||||
};
|
||||
}
|
||||
isFirstFlagRef.current = false;
|
||||
|
||||
cropInsRef.current = new Cropper(corpContainerRef.current, {
|
||||
showMask: true,
|
||||
cropBoxLimited,
|
||||
img: imageData,
|
||||
initialCropBoxData,
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
handlerCropStart?.remove();
|
||||
handlerCropEnd?.remove();
|
||||
cropInsRef?.current?.destroy?.();
|
||||
cropInsRef.current = null;
|
||||
};
|
||||
}, [showCrop, isReady]);
|
||||
|
||||
const latestCropRect = useLatest(cropRect);
|
||||
|
||||
const getCropInfo = async () => {
|
||||
const cropRect = latestCropRect.current as any;
|
||||
let video: any = videoRef.current;
|
||||
if (!video) return
|
||||
let rectList = [];
|
||||
let extendRectList = [];
|
||||
let selectIndex = 0;
|
||||
//获取视频图片的url
|
||||
let scale = Math.min(
|
||||
video.offsetWidth / video.videoWidth,
|
||||
video.offsetHeight / video.videoHeight
|
||||
);
|
||||
let finalVideoWidth = video.videoWidth * scale;
|
||||
let finalVideoHeight = video.videoHeight * scale;
|
||||
let canvas = document.createElement('canvas');
|
||||
canvas.width = finalVideoWidth;
|
||||
canvas.height = finalVideoHeight;
|
||||
canvas.style.display = 'none';
|
||||
document.body.appendChild(canvas);
|
||||
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
ctx.drawImage(
|
||||
video,
|
||||
0,
|
||||
0,
|
||||
finalVideoWidth,
|
||||
finalVideoHeight
|
||||
);
|
||||
let base64 = canvas.toDataURL('image/jpeg');
|
||||
const blobData = dataURLToBlob(base64);
|
||||
canvas.parentNode?.removeChild(canvas);
|
||||
const file = new window.File([blobData], `${new Date().getTime()}`);
|
||||
let newRect = {
|
||||
w: cropRect.w / finalVideoWidth,
|
||||
h: cropRect.h / finalVideoHeight,
|
||||
x: (cropRect.x - (video.offsetWidth - finalVideoWidth) / 2) / finalVideoWidth,
|
||||
y: (cropRect.y - (video.offsetHeight - finalVideoHeight) / 2) / finalVideoHeight
|
||||
};
|
||||
rectList.push(newRect);
|
||||
extendRectList.push(newRect);
|
||||
//扩展框获取imgkey
|
||||
extendRectList.forEach(async (rect, index) => {
|
||||
extendRectList[index] = {
|
||||
...rect,
|
||||
};
|
||||
})
|
||||
return {
|
||||
rectList,
|
||||
extendRectList,
|
||||
selectIndex,
|
||||
file
|
||||
};
|
||||
};
|
||||
|
||||
//回调
|
||||
useEffect(() => {
|
||||
//计算归一化crop rect
|
||||
let normalizationRect = null;
|
||||
if (showCrop && cropRect) {
|
||||
let video: any = videoRef.current;
|
||||
let scale = Math.min(
|
||||
video.offsetWidth / video.videoWidth,
|
||||
video.offsetHeight / video.videoHeight
|
||||
);
|
||||
let finalVideoWidth = video.videoWidth * scale;
|
||||
let finalVideoHeight = video.videoHeight * scale;
|
||||
let cropBoxLimited = {
|
||||
width: finalVideoWidth,
|
||||
height: finalVideoHeight,
|
||||
top: (video.offsetHeight - finalVideoHeight) / 2,
|
||||
left: (video.offsetWidth - finalVideoWidth) / 2,
|
||||
};
|
||||
normalizationRect = {
|
||||
x: (cropRect.x - cropBoxLimited.left) / cropBoxLimited.width,
|
||||
y: (cropRect.y - cropBoxLimited.top) / cropBoxLimited.height,
|
||||
w: cropRect.w / cropBoxLimited.width,
|
||||
h: cropRect.h / cropBoxLimited.height,
|
||||
};
|
||||
}
|
||||
|
||||
onCropChange?.(showCrop, normalizationRect);
|
||||
}, [showCrop, cropRect]);
|
||||
|
||||
// ========================== 截帧 =========================
|
||||
const downloadVideoframe = useCallback(async () => {
|
||||
try {
|
||||
videoInsRef?.current?.pause?.();
|
||||
let video: any = videoRef.current;
|
||||
var canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d')
|
||||
let base64;
|
||||
//当视频处于还未加载出来时,截屏为黑色图片
|
||||
if (video.readyState === 0) {
|
||||
ctx?.clearRect(0, 0, canvas.width, canvas.height);
|
||||
canvas.width = video.offsetWidth;
|
||||
canvas.height = video.offsetHeight;
|
||||
// @ts-ignore
|
||||
ctx.fillStyle = 'black';
|
||||
ctx?.fillRect(0, 0, canvas.width, canvas.height);
|
||||
base64 = canvas.toDataURL();
|
||||
} else {
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
base64 = canvas.toDataURL('image/png');
|
||||
}
|
||||
download(base64);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// ============================== 暴露出去的方法 ===============================
|
||||
const latestIsReady = useLatest(isReady);
|
||||
const cropAble = !showStatus && isReady;
|
||||
useImperativeHandle(ref, () => ({
|
||||
cropAble,
|
||||
setShowCrop: (dispatch) => {
|
||||
const isReady = latestIsReady.current;
|
||||
if (!isReady) return;
|
||||
setShowCrop(dispatch);
|
||||
},
|
||||
downloadVideoframe,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className={classNames(`${componentName}`)} ref={containerRef}>
|
||||
{flvUrl && (
|
||||
<>
|
||||
<FlvPlayer
|
||||
playId={playSeq}
|
||||
autoPlay={true}
|
||||
className={classNames(`${componentName}-flv`)}
|
||||
type={videoType}
|
||||
url={flvUrl}
|
||||
config={{
|
||||
enableStashBuffer: true,
|
||||
stashInitialSize: 1024 * 700,
|
||||
isLive: true,
|
||||
hasAudio: false,
|
||||
hasVideo: true,
|
||||
}}
|
||||
onCreat={initPlayer}
|
||||
/>
|
||||
{/* //截图 */}
|
||||
<div
|
||||
className={classNames(`${componentName}-crop-container`)}
|
||||
ref={corpContainerRef}
|
||||
style={{
|
||||
display: isFullscreen ? 'none' : 'block',
|
||||
}}
|
||||
>
|
||||
{/* <div ref={corpRef}></div> */}
|
||||
</div>
|
||||
{showCrop && cropRect && screenshotButtonRender && (
|
||||
<>
|
||||
<div
|
||||
ref={alginContainerRef}
|
||||
className={classNames(`${componentName}-align`)}
|
||||
style={Object.assign(
|
||||
{
|
||||
width: cropRect.w,
|
||||
height: cropRect.h,
|
||||
},
|
||||
getTransforms({
|
||||
translateX: cropRect.x,
|
||||
translateY: cropRect.y,
|
||||
})
|
||||
)}
|
||||
></div>
|
||||
<Align
|
||||
ref={alignRef}
|
||||
monitorWindowResize
|
||||
align={screenshotButtonAlign}
|
||||
target={function () {
|
||||
return alginContainerRef.current;
|
||||
}}
|
||||
>
|
||||
{screenshotButtonRender({
|
||||
model: 'IMAGE',
|
||||
getCropInfo,
|
||||
setShowCrop,
|
||||
cropType: CROP_TYPE['CUSTOM'],
|
||||
})}
|
||||
</Align>
|
||||
</>
|
||||
)}
|
||||
{/* 视频进度条 */}
|
||||
{!showCrop && (
|
||||
<div className={`${componentName}-opt`}>
|
||||
<div>
|
||||
<Button
|
||||
type="text"
|
||||
onClick={(e) => {
|
||||
if (!isPlay) {
|
||||
//播放中暂停
|
||||
videoInsRef?.current?.play();
|
||||
setShowCrop(false);
|
||||
} else {
|
||||
videoInsRef?.current?.pause();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
styles={{
|
||||
color: '#fff',
|
||||
display: 'flex',
|
||||
}}
|
||||
color="#1890ff"
|
||||
size={18}
|
||||
icon={!isPlay ? 'icon-shipinbofang' : 'icon-shipinzanting'}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
className={`${componentName}-opt-range`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Range
|
||||
value={playTime}
|
||||
min={0}
|
||||
max={showMaxDuration}
|
||||
hasTip={false}
|
||||
showSlider={showSlider}
|
||||
onChange={seek}
|
||||
/>
|
||||
<div>
|
||||
{/* TODO: 删除扩展方法format */}
|
||||
{formatDurationTime(playTime)}/{formatDurationTime(showMaxDuration)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
type="text"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
toggleFullscreen();
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
styles={{
|
||||
color: '#fff',
|
||||
display: 'flex',
|
||||
}}
|
||||
size={18}
|
||||
icon={isFullscreen ? 'icon-cancle_fullscreen' : 'icon-fullscreen'}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* mask */}
|
||||
{!!showStatus && (
|
||||
<Loading status={showStatus} reload={() => reload()} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default VideoPlayer;
|
1679
packages/meta/src/iconfont/iconfont.css
Normal file
1679
packages/meta/src/iconfont/iconfont.css
Normal file
File diff suppressed because it is too large
Load Diff
1
packages/meta/src/iconfont/iconfont.js
Normal file
1
packages/meta/src/iconfont/iconfont.js
Normal file
File diff suppressed because one or more lines are too long
BIN
packages/meta/src/iconfont/iconfont.ttf
Normal file
BIN
packages/meta/src/iconfont/iconfont.ttf
Normal file
Binary file not shown.
BIN
packages/meta/src/iconfont/iconfont.woff
Normal file
BIN
packages/meta/src/iconfont/iconfont.woff
Normal file
Binary file not shown.
BIN
packages/meta/src/iconfont/iconfont.woff2
Normal file
BIN
packages/meta/src/iconfont/iconfont.woff2
Normal file
Binary file not shown.
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import './iconfont.css'
|
||||
|
||||
interface IconFontProps {
|
||||
styles?: React.CSSProperties;
|
||||
|
@ -1,3 +1,5 @@
|
||||
export { default as doubleClick } from './doubleClick';
|
||||
export { default as Icon } from './iconfont';
|
||||
export { default as ImagePreview } from './ImagePreview'
|
||||
export { default as BigImagePreview } from './BigImagePreview'
|
||||
export { default as VideoPlayer } from './VideoPlayer'
|
||||
|
7
packages/request/.fatherrc.ts
Normal file
7
packages/request/.fatherrc.ts
Normal file
@ -0,0 +1,7 @@
|
||||
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' },
|
||||
});
|
19
packages/request/CHANGELOG.md
Normal file
19
packages/request/CHANGELOG.md
Normal file
@ -0,0 +1,19 @@
|
||||
# @zhst/request
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- feat: 新增 meta 包
|
||||
- Updated dependencies
|
||||
- @zhst/func@0.2.4
|
||||
- @zhst/hooks@0.2.4
|
||||
- @zhst/meta@0.2.4
|
||||
|
||||
## 0.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- fix: 调试包 link
|
||||
- Updated dependencies
|
||||
- @zhst/hooks@0.2.3
|
14
packages/request/README.md
Normal file
14
packages/request/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
## 介绍
|
||||
|
||||
zhst 请求库
|
||||
|
||||
## 安装
|
||||
|
||||
> pnpm install @zhst/request
|
||||
|
||||
## 使用
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import request from '@zhst/request'
|
||||
```
|
185
packages/request/es/index.js
Normal file
185
packages/request/es/index.js
Normal file
File diff suppressed because one or more lines are too long
45
packages/request/package.json
Normal file
45
packages/request/package.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@zhst/request",
|
||||
"version": "0.2.4",
|
||||
"description": "请求库",
|
||||
"keywords": [
|
||||
"request",
|
||||
"umi-request"
|
||||
],
|
||||
"license": "ISC",
|
||||
"author": "dev",
|
||||
"sideEffects": [
|
||||
"dist/*",
|
||||
"es/**/style/*",
|
||||
"lib/**/style/*",
|
||||
"*.less"
|
||||
],
|
||||
"main": "lib/index.ts",
|
||||
"module": "es/index.js",
|
||||
"typings": "es/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"es",
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "father build"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "http://10.0.0.77:4874"
|
||||
},
|
||||
"dependencies": {
|
||||
"antd": "^5.12.5",
|
||||
"base-64": "^1.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"umi-request": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/base-64": "^1.0.2",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@zhst/types": "workspace:^"
|
||||
}
|
||||
}
|
8
packages/request/src/index.md
Normal file
8
packages/request/src/index.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
nav:
|
||||
title: 请求库
|
||||
order: 1
|
||||
title: 快速上手
|
||||
---
|
||||
|
||||
<embed src="../README.md" ></embed>
|
149
packages/request/src/index.ts
Normal file
149
packages/request/src/index.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import { extend } from 'umi-request';
|
||||
import type { RequestOptionsInit } from 'umi-request';
|
||||
import { omit, get } from 'lodash-es';
|
||||
import { message } from 'antd';
|
||||
import { User } from '@zhst/types/user';
|
||||
import base64 from 'base-64';
|
||||
|
||||
export class ResponseError<D> extends Error {
|
||||
name;
|
||||
data;
|
||||
response;
|
||||
request;
|
||||
type;
|
||||
constructor(
|
||||
response: Response,
|
||||
text: string,
|
||||
data: D,
|
||||
request: {
|
||||
url: string;
|
||||
options: RequestOptionsInit;
|
||||
},
|
||||
type = 'ResponseError'
|
||||
) {
|
||||
super(text || response.statusText);
|
||||
this.name = 'ResponseError';
|
||||
this.data = data;
|
||||
this.response = response;
|
||||
this.request = request;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export const req = extend({
|
||||
getResponse: true,
|
||||
// timeout: 1000,
|
||||
parseResponse: false,
|
||||
});
|
||||
|
||||
//错误处理中间件
|
||||
req.use(async (ctx, next) => {
|
||||
const { req } = ctx;
|
||||
const { toast = true } = req?.options || {};
|
||||
try {
|
||||
await next();
|
||||
const { res } = ctx;
|
||||
const d = await res.text();
|
||||
if (res.status === 401) {
|
||||
localStorage.removeItem(User.TOKEN_KEY);
|
||||
localStorage.removeItem(User.USER_KEY);
|
||||
message.warning('登录过期,请重新登录!');
|
||||
return;
|
||||
}
|
||||
const isEmptyRes = d === ''; //有些后端接口成功会返回空 做下兼容
|
||||
if (!res) return
|
||||
const body = !isEmptyRes ? JSON.parse(d) : d;
|
||||
if (res.status >= 200 && res.status < 300) {
|
||||
ctx.res = body;
|
||||
} else {
|
||||
// 先判断Grpc-Metadata-Errorx-Message
|
||||
let errMsg = res.headers.get('Grpc-Metadata-Errorx-Message');
|
||||
if (errMsg) {
|
||||
errMsg = window?.utf8?.decode(base64.decode(errMsg));
|
||||
// 后判断 body中的message
|
||||
} else if (!errMsg && get(body, 'message')) {
|
||||
errMsg = `${get(body, 'message')}`;
|
||||
} else {
|
||||
// 最后看状态码
|
||||
errMsg = '您的网络发生异常,无法连接服务器';
|
||||
}
|
||||
toast && message.error(errMsg);
|
||||
throw new ResponseError(res, errMsg, d, req, 'CustomError');
|
||||
}
|
||||
} catch (error) {
|
||||
if (get(error, 'type') !== 'CustomError') {
|
||||
toast && message.error('您的网络发生异常,无法连接服务器');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
export interface OPTION extends RequestOptionsInit {
|
||||
toast?: boolean;
|
||||
}
|
||||
|
||||
interface CGI {
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
||||
url: string;
|
||||
baseUrl?: string;
|
||||
data?: { [key: string]: any };
|
||||
useBaseUrl?: boolean;
|
||||
originUrl?: boolean;
|
||||
refererSuffix?: string;
|
||||
}
|
||||
export const doRequest = <T>(cgi: CGI, option?: OPTION): Promise<T> => {
|
||||
const {
|
||||
method,
|
||||
url,
|
||||
baseUrl,
|
||||
data = {},
|
||||
useBaseUrl = true,
|
||||
originUrl = false,
|
||||
refererSuffix = '',
|
||||
} = cgi;
|
||||
const token = localStorage.getItem(User.TOKEN_KEY);
|
||||
const userInfo = localStorage.getItem(User.USER_KEY)
|
||||
? JSON.parse(localStorage.getItem(User.USER_KEY)!)
|
||||
: null;
|
||||
let newUrl = '';
|
||||
if (useBaseUrl) {
|
||||
newUrl = `${baseUrl}${url}`;
|
||||
} else {
|
||||
// 本地Mock -- http://127.0.0.1:4523/m1/2822485-0-default
|
||||
// 7环境gateway -- http://10.0.0.7:32223
|
||||
newUrl = `http://10.0.0.7:32223${url}`; // 7环境进行调试
|
||||
}
|
||||
if (originUrl) {
|
||||
newUrl = url;
|
||||
}
|
||||
// 对于 /:id 类的 url 进行参数填充
|
||||
const regex = /\/:(\w+)/g; // 替换 url 参数
|
||||
const params = [];
|
||||
let matches;
|
||||
while ((matches = regex.exec(newUrl)) != null) {
|
||||
if (matches[1]) {
|
||||
params.push(matches[1]);
|
||||
}
|
||||
}
|
||||
params.forEach(function (name) {
|
||||
let d = data?.[name];
|
||||
if (d == null) {
|
||||
d = '';
|
||||
}
|
||||
newUrl = newUrl.replace(`:${name}`, d);
|
||||
});
|
||||
//通过method 判断解析成data / params
|
||||
const newData = omit(data, params);
|
||||
const paramObj = method.toLowerCase() === 'get' ? { params: newData } : { data: newData };
|
||||
return req(newUrl, {
|
||||
method: method,
|
||||
...paramObj,
|
||||
...option,
|
||||
headers: {
|
||||
authorization: token!,
|
||||
...(refererSuffix ? { zhst_referer: `${baseUrl}${refererSuffix}` } : {}),
|
||||
},
|
||||
}) as Promise<T>;
|
||||
};
|
||||
|
||||
export default doRequest;
|
28
packages/types/BigImageModal.d.ts
vendored
Normal file
28
packages/types/BigImageModal.d.ts
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
export enum IBigImageOpt {
|
||||
ADD_CONDITION, //添加目标
|
||||
ADD_TRACK_POINT, //添加到轨迹
|
||||
ADD_HISTORY, //全息检索
|
||||
PLAY_VIDEO, //视频播放按钮
|
||||
ADD_CONDITION_WITH_CROP,
|
||||
ADD_HISTORY_WITH_CROP,
|
||||
SWITCH_DIALOG_MODE,
|
||||
NO_IMAGE_DOWNLOAD_BTN,
|
||||
NO_VIDEO_DOWNLOAD_BTN,
|
||||
ADD_HISTORY_WITH_CROP_BODY, //以人搜人,搜形体
|
||||
ADD_HISTORY_WITH_CROP_VEHICLE, //搜非机动车
|
||||
ADD_STORAGE_RACK, //加入暂存架
|
||||
ADD_HISTORY_WITH_CROP_ARCHIVE, //添加到档案检索
|
||||
ADD_HISTORY_ARCHIVE, //添加到档案检索
|
||||
ADD_ARCHIVE, //添加到档案库
|
||||
CREATE_MONITOR, //创建布控
|
||||
ADD_HISTORY_WITH_CROP_FACE,
|
||||
ADD_HISTORY_WITH_CROP_CAR,
|
||||
BOX_SELECTION,
|
||||
DELETE_TRACK, //删除轨迹
|
||||
ADD_SMART_TRACK_IMAGE, //跳去智能追踪
|
||||
TECHNICAL_WARFARE_APPLICATION, //技战应用
|
||||
PEER_ANALYSIS, //同行人分析
|
||||
FLAG_BY_BACK_SEARCH, //背影搜脸
|
||||
ADD_ARCHIVE_WITH_HK, //添加到档案库
|
||||
ADD_CURRENT_SRARCH, //添加到当前检索(目标检索)
|
||||
}
|
@ -4,12 +4,12 @@
|
||||
|
||||
## 安装
|
||||
|
||||
> pnpm install @zhst/constants
|
||||
> pnpm install @zhst/types
|
||||
|
||||
## 使用
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import { TYPE } from '@zhst/constants'
|
||||
import type { User } from '@zhst/types'
|
||||
|
||||
```
|
||||
|
278
packages/types/index.d.ts
vendored
278
packages/types/index.d.ts
vendored
@ -0,0 +1,278 @@
|
||||
/// <reference path="BigImageModal.d.ts" />
|
||||
export * from './BigImageModal'
|
||||
|
||||
export type CamerasStatusList = [string[], string[], string[]];
|
||||
|
||||
export type Rect = { x: number; y: number; w: number; h: number };
|
||||
|
||||
export type StatusList = {
|
||||
taskOverview: any;
|
||||
taskIndex: {
|
||||
deviceId: string;
|
||||
solutionId: string;
|
||||
};
|
||||
}[];
|
||||
|
||||
export enum OperationType {
|
||||
OPERATION_TYPE_UNKNOW, // 未知状态,传该值会报错
|
||||
OPERATION_TYPE_START,
|
||||
OPERATION_TYPE_STOP
|
||||
}
|
||||
|
||||
|
||||
export enum AlgorithmVersion {
|
||||
VERSION_MGN_BNN, // MGN+BNN算法
|
||||
VERSION_BNN_PRO, // BNNPRO算法
|
||||
VERSION_BNN_PRO_ATTR, // BNNPROATTR算法
|
||||
VERSION_BNN_PRO_ATTR_SCORE, // BNNPROATTR算法
|
||||
VERSION_FACE, // 人脸算法
|
||||
VERSION_HEAD, // 头肩算法
|
||||
VERSION_NON_MOTOR_VEHICLE, // 非机动车的算法
|
||||
VERSION_REID_HEAD_ATTR, // 形体头肩属性三种特征融合的算法
|
||||
VERSION_MOTOR_VEHICLE, // 机动车的算法
|
||||
}
|
||||
|
||||
export enum AlgorithmVersionStr {
|
||||
VERSION_MGN_BNN = 'VERSION_MGN_BNN', // MGN+BNN算法
|
||||
VERSION_BNN_PRO = 'VERSION_BNN_PRO', // BNNPRO算法
|
||||
VERSION_BNN_PRO_ATTR = 'VERSION_BNN_PRO_ATTR', // BNNPROATTR算法
|
||||
VERSION_BNN_PRO_ATTR_SCORE = 'VERSION_BNN_PRO_ATTR_SCORE', // BNNPROATTR算法
|
||||
VERSION_FACE = 'VERSION_FACE', // 人脸算法
|
||||
VERSION_HEAD = 'VERSION_HEAD', // 头肩算法
|
||||
VERSION_NON_MOTOR_VEHICLE = 'VERSION_NON_MOTOR_VEHICLE', // 非机动车的算法
|
||||
VERSION_REID_HEAD_ATTR = 'VERSION_REID_HEAD_ATTR', // 形体头肩属性三种特征融合的算法
|
||||
VERSION_MOTOR_VEHICLE = 'VERSION_MOTOR_VEHICLE', // 机动车的算法
|
||||
}
|
||||
|
||||
类型枚举
|
||||
export enum ObjectType {
|
||||
OBJECT_TYPE_NULL,
|
||||
OBJECT_TYPE_PEDESTRAIN,
|
||||
OBJECT_TYPE_BICYCLE,
|
||||
OBJECT_TYPE_CAR,
|
||||
OBJECT_TYPE_MOTORBIKE,
|
||||
OBJECT_TYPE_AEROPLANE,
|
||||
OBJECT_TYPE_BUS,
|
||||
OBJECT_TYPE_TRAIN,
|
||||
OBJECT_TYPE_TRUCK,
|
||||
OBJECT_TYPE_MOTOR_RIDER,
|
||||
OBJECT_TYPE_BIKE_RIDER,
|
||||
OBJECT_TYPE_MAX,
|
||||
OBJECT_TYPE_FACE = 101,
|
||||
}
|
||||
|
||||
// 性别
|
||||
export enum Gender {
|
||||
GENDER_NONE = 'GENDER_NONE',
|
||||
GENDER_MAN = 'GENDER_MAN',
|
||||
GENDER_WOMAN = 'GENDER_WOMAN',
|
||||
}
|
||||
|
||||
// 年龄
|
||||
export enum Age {
|
||||
AGE_ALL = 'AGE_ALL',
|
||||
AGE_ZERO = 'AGE_ZERO',
|
||||
AGE_OVERENGHTEEN = 'AGE_OVERENGHTEEN',
|
||||
AGE_OVERSIXTY = 'AGE_OVERSIXTY',
|
||||
}
|
||||
|
||||
// 戴帽子状态
|
||||
export enum Hat {
|
||||
HAT_ALL = 'HAT_ALL',
|
||||
HAT_NONE = 'HAT_NONE',
|
||||
HAT_OWNED = 'HAT_OWNED',
|
||||
}
|
||||
|
||||
// 颜色
|
||||
export enum Color {
|
||||
COLOR_ALL = 'COLOR_ALL',
|
||||
COLOR_BLACK = 'COLOR_BLACK',
|
||||
COLOR_WHITE = 'COLOR_WHITE',
|
||||
COLOR_GRAY = 'COLOR_GRAY',
|
||||
COLOR_BROWN = 'COLOR_BROWN',
|
||||
COLOR_PINK = 'COLOR_PINK',
|
||||
COLOR_REDANDORANGE = 'COLOR_REDANDORANGE',
|
||||
COLOR_YELLOW = 'COLOR_YELLOW',
|
||||
COLOR_GREEN = 'COLOR_GREEN',
|
||||
COLOR_BLUE = 'COLOR_BLUE',
|
||||
COLOR_PURPLE = 'COLOR_PURPLE',
|
||||
}
|
||||
|
||||
// 背包
|
||||
export enum Package {
|
||||
PACKAGE_ALL = 'PACKAGE_ALL',
|
||||
PACKAGE_HANDBAG = 'PACKAGE_HANDBAG',
|
||||
PACKAGE_BACKPACK = 'PACKAGE_BACKPACK',
|
||||
PACKAGE_SHOULDERBAG = 'PACKAGE_SHOULDERBAG',
|
||||
PACKAGE_OTHER = 'PACKAGE_OTHER',
|
||||
PACKAGE_NONE = 'PACKAGE_NONE',
|
||||
}
|
||||
|
||||
// 行走模式
|
||||
export enum WalkPattern {
|
||||
WALKPATTERN_ALL = 'WALKPATTERN_ALL',
|
||||
WALKPATTERN_WALK = 'WALKPATTERN_WALK',
|
||||
WALKPATTERN_RIDING = 'WALKPATTERN_RIDING',
|
||||
}
|
||||
|
||||
// 人类属性
|
||||
export interface HumanProperty {
|
||||
age: Age;
|
||||
downColor: Color;
|
||||
gender: Gender;
|
||||
hat: Hat;
|
||||
package: Package;
|
||||
upColor: Color;
|
||||
walkPattern: WalkPattern;
|
||||
}
|
||||
|
||||
// 人类属性枚举
|
||||
export interface EnumHumanProperty {
|
||||
Gender: typeof Gender;
|
||||
Age: typeof Age;
|
||||
Hat: typeof Hat;
|
||||
Color: typeof Color;
|
||||
Package: typeof Package;
|
||||
WalkPattern: typeof WalkPattern;
|
||||
}
|
||||
|
||||
export interface IScreenshotButtonProp {
|
||||
model: 'VIDEO' | 'IMAGE';
|
||||
getCropInfo: () => Promise<RESP>;
|
||||
setShowCrop: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
cropType: typeof cropType[number];
|
||||
selectAlgorithmVersion: number | null;
|
||||
}
|
||||
|
||||
export interface AlignType {
|
||||
/**
|
||||
* move point of source node to align with point of target node.
|
||||
* Such as ['tr','cc'], align top right point of source node with center point of target node.
|
||||
* Point can be 't'(top), 'b'(bottom), 'c'(center), 'l'(left), 'r'(right) */
|
||||
points?: AlignPoint[];
|
||||
/**
|
||||
* offset source node by offset[0] in x and offset[1] in y.
|
||||
* If offset contains percentage string value, it is relative to sourceNode region.
|
||||
*/
|
||||
offset?: number[];
|
||||
/**
|
||||
* offset target node by offset[0] in x and offset[1] in y.
|
||||
* If targetOffset contains percentage string value, it is relative to targetNode region.
|
||||
*/
|
||||
targetOffset?: number[];
|
||||
/**
|
||||
* If adjustX field is true, will adjust source node in x direction if source node is invisible.
|
||||
* If adjustY field is true, will adjust source node in y direction if source node is invisible.
|
||||
*/
|
||||
overflow?: {
|
||||
adjustX?: boolean | number;
|
||||
adjustY?: boolean | number;
|
||||
};
|
||||
/**
|
||||
* Whether use css right instead of left to position
|
||||
*/
|
||||
useCssRight?: boolean;
|
||||
/**
|
||||
* Whether use css bottom instead of top to position
|
||||
*/
|
||||
useCssBottom?: boolean;
|
||||
/**
|
||||
* Whether use css transform instead of left/top/right/bottom to position if browser supports.
|
||||
* Defaults to false.
|
||||
*/
|
||||
useCssTransform?: boolean;
|
||||
ignoreShake?: boolean;
|
||||
}
|
||||
|
||||
export type ODRECT = {
|
||||
topleft: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export interface ViewOption {
|
||||
/* 图片url */
|
||||
image?: string | HTMLImageElement;
|
||||
|
||||
/* 缩放灵敏度(0,1],default: 0.1 */
|
||||
wheelZoomRatio?: number;
|
||||
|
||||
/*
|
||||
* 是否允许缩放
|
||||
* @default: true
|
||||
*/
|
||||
scaleAble?: boolean;
|
||||
|
||||
/*
|
||||
* 是否允许拖拽
|
||||
* @default: true
|
||||
*/
|
||||
dragAble?: boolean;
|
||||
|
||||
/*
|
||||
* fit scale 作为 最小缩放
|
||||
* @default: false
|
||||
*/
|
||||
fitScaleAsMinScale?: boolean;
|
||||
}
|
||||
|
||||
export type IOdRectOrigin {
|
||||
objectIndex: {
|
||||
objectId: string
|
||||
solutionId: string
|
||||
deviceId: string
|
||||
fragmentId: string
|
||||
}
|
||||
objectType: ObjectType,
|
||||
sourceObjectId: string,
|
||||
level: string
|
||||
confidence: string | number,
|
||||
pathInfo: any,
|
||||
frameInfo: {
|
||||
frameId: string
|
||||
frameTimestamp: string | number
|
||||
width: number
|
||||
height: number
|
||||
originWidth: number
|
||||
originHeight: number
|
||||
offsetTime: number
|
||||
skipNumber: number
|
||||
},
|
||||
deviceInfo: any
|
||||
infoOnSource:{
|
||||
bboxInSource:any
|
||||
bboxInFrame: {
|
||||
bbox: any,
|
||||
bboxRatio: Rect
|
||||
extendBbox: any
|
||||
extendBoxRatio: number
|
||||
},
|
||||
countInSource: any
|
||||
indexInSource: any
|
||||
},
|
||||
qualityScore: number
|
||||
frameImage: any
|
||||
objectImage: any
|
||||
objectExtImage: any
|
||||
feature: {
|
||||
name: string
|
||||
type: ObjectType,
|
||||
featureId: number
|
||||
featureByte: string
|
||||
featureBool:any[]
|
||||
featureUint8: any[]
|
||||
featureUint16: any[]
|
||||
featureUint32: any[]
|
||||
featureUint64: any[]
|
||||
featureInt8: any[]
|
||||
featureInt16: any[]
|
||||
featureInt32: any[]
|
||||
featureInt64: any[]
|
||||
featureFloat32: any[]
|
||||
featureString: any[]
|
||||
}
|
||||
subObjects: any[]
|
||||
}
|
@ -11,13 +11,15 @@
|
||||
"license": "ISC",
|
||||
"author": "dev",
|
||||
"main": "",
|
||||
"typings": "es/index.d.ts",
|
||||
"typings": "index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": {
|
||||
"default": "./index.d.ts"
|
||||
}
|
||||
},
|
||||
"./user": "./user.d.ts",
|
||||
"./BigImageModal": "./BigImageModal.d.ts",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
nav:
|
||||
title: types
|
||||
order: 1
|
||||
title: 类型定义
|
||||
order: 99
|
||||
title: 快速上手
|
||||
---
|
||||
|
||||
|
4
packages/types/user.d.ts
vendored
Normal file
4
packages/types/user.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
export enum User {
|
||||
TOKEN_KEY = 'USER-TOKEN',
|
||||
USER_KEY = 'USER'
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user