Compare commits
122 Commits
feat/creat
...
master
Author | SHA1 | Date | |
---|---|---|---|
a8b9db109e | |||
|
d643a4f495 | ||
|
ecf46d7ffc | ||
|
bdd3ed09e0 | ||
|
8eb94b1ed8 | ||
b198e9b1fe | |||
7bde75ea15 | |||
765f485556 | |||
320e17ea93 | |||
490cc9ae4a | |||
9e6a87881d | |||
|
c1c0b865dc | ||
|
e8e36098e4 | ||
e5f69c915c | |||
42d8f26af5 | |||
c5fd96fbf0 | |||
b5a74de4d9 | |||
|
21e43c2d35 | ||
|
53ab45c536 | ||
|
17767ac3c0 | ||
|
f47c8533a9 | ||
|
70ffe63766 | ||
|
a30e8c646f | ||
|
46f35cb918 | ||
|
afd7ae13c9 | ||
7f64ffe221 | |||
657af1c40d | |||
c9174fd9b7 | |||
28822b45d8 | |||
012aaacb66 | |||
ec3938a2cf | |||
e44fa64760 | |||
91ba49d94c | |||
bfc2dd7930 | |||
2a7ea08e02 | |||
90b18d2624 | |||
|
ba96f96f58 | ||
5e370b0af3 | |||
ce6a719bc2 | |||
|
a501656907 | ||
ed4918c6a2 | |||
|
121ebb5007 | ||
27958367ff | |||
|
79a2b74a16 | ||
12e5046f53 | |||
782a0f021d | |||
03433e3108 | |||
|
08ea724555 | ||
dc98cd8ad4 | |||
d7a29f51b7 | |||
5f15108949 | |||
|
b75aaaf22e | ||
89ca980f9e | |||
|
5c5115471d | ||
066428c8b6 | |||
|
b0a2dc1bd2 | ||
1ff779c1de | |||
|
5a2c358fe8 | ||
074766b7fa | |||
|
bb5db6d967 | ||
fcf7f08fe4 | |||
3af900f998 | |||
|
2798253fb9 | ||
e56a19e4fa | |||
357a4c677a | |||
a129d39168 | |||
|
e5b176c148 | ||
15536f32c8 | |||
|
05064d9473 | ||
4fb101b7e3 | |||
|
17fa6f6990 | ||
98439dc5e8 | |||
|
006dc0ceeb | ||
c4489fb0a8 | |||
|
a2afc51ad8 | ||
3d8959891e | |||
|
5e483436b0 | ||
f5ff6e760f | |||
|
079df27f4b | ||
|
fe789b5479 | ||
|
08a14b3ae0 | ||
|
8acf8242ea | ||
|
92bcaace2b | ||
|
b104d383e8 | ||
|
5bab7caa8c | ||
085b790121 | |||
|
28ba34894d | ||
da8a0be612 | |||
|
aa1930e1b7 | ||
638ccf5d8d | |||
|
e1d7ab2621 | ||
5a1e41a7a3 | |||
|
b64abd7164 | ||
d0607af936 | |||
|
efb5acaf0a | ||
8d45ba6481 | |||
|
2ada801a32 | ||
|
c3573d9efb | ||
46e6d0b4d0 | |||
|
8e6b1fc8f2 | ||
54a680c0ff | |||
|
6a75d0412a | ||
0718d8c4d1 | |||
|
ed22f8bcf9 | ||
6f4797694f | |||
|
375d5983f4 | ||
c65756bbb2 | |||
3666fc4ed1 | |||
|
05f99f7359 | ||
3bd50deeb3 | |||
|
60ef9e88ee | ||
b930efa40d | |||
|
0cfdaa4851 | ||
f9c5dccb73 | |||
f778ec1b72 | |||
dd6ff1d2be | |||
|
e8ddd60daa | ||
da3c1714c5 | |||
|
e90dcce641 | ||
d02a2b2015 | |||
|
d95fe37e43 | ||
570f183382 |
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#root .dumi-default-sidebar {
|
#root .dumi-default-sidebar {
|
||||||
width: 188px;
|
width: 188px;
|
||||||
overflow-y: hidden;
|
overflow-y: scroll;
|
||||||
/* stylelint-disable-next-line rule-empty-line-before */
|
/* stylelint-disable-next-line rule-empty-line-before */
|
||||||
&:hover {
|
&:hover {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
|
||||||
<defs>
|
|
||||||
<path id="path" d="M50 15A15 35 0 0 1 50 85A15 35 0 0 1 50 15" fill="none"></path>
|
|
||||||
<path id="patha" d="M0 0A15 35 0 0 1 0 70A15 35 0 0 1 0 0" fill="none"></path>
|
|
||||||
</defs><g transform="rotate(0 50 50)">
|
|
||||||
<use xlink:href="#path" stroke="#dfdfdf" stroke-width="3"></use>
|
|
||||||
</g><g transform="rotate(60 50 50)">
|
|
||||||
<use xlink:href="#path" stroke="#dfdfdf" stroke-width="3"></use>
|
|
||||||
</g><g transform="rotate(120 50 50)">
|
|
||||||
<use xlink:href="#path" stroke="#dfdfdf" stroke-width="3"></use>
|
|
||||||
</g><g transform="rotate(0 50 50)">
|
|
||||||
<circle cx="50" cy="15" r="9" fill="#e15b64">
|
|
||||||
<animateMotion dur="1s" repeatCount="indefinite" begin="0s">
|
|
||||||
<mpath xlink:href="#patha"></mpath>
|
|
||||||
</animateMotion>
|
|
||||||
</circle>
|
|
||||||
</g><g transform="rotate(60 50 50)">
|
|
||||||
<circle cx="50" cy="15" r="9" fill="#f8b26a">
|
|
||||||
<animateMotion dur="1s" repeatCount="indefinite" begin="-0.16666666666666666s">
|
|
||||||
<mpath xlink:href="#patha"></mpath>
|
|
||||||
</animateMotion>
|
|
||||||
</circle>
|
|
||||||
</g><g transform="rotate(120 50 50)">
|
|
||||||
<circle cx="50" cy="15" r="9" fill="#abbd81">
|
|
||||||
<animateMotion dur="1s" repeatCount="indefinite" begin="-0.3333333333333333s">
|
|
||||||
<mpath xlink:href="#patha"></mpath>
|
|
||||||
</animateMotion>
|
|
||||||
</circle>
|
|
||||||
</g></svg>
|
|
Before Width: | Height: | Size: 1.4 KiB |
@ -1,11 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import loading from './loading.svg'
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
return (
|
|
||||||
<div style={{ textAlign: 'center' }}>
|
|
||||||
<image src={loading} />
|
|
||||||
<p>客官,您慢点儿...</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -19,6 +19,7 @@ export default defineConfig({
|
|||||||
'@zhst/slave': path.join(__dirname, 'packages/slave/src'),
|
'@zhst/slave': path.join(__dirname, 'packages/slave/src'),
|
||||||
'@zhst/material': path.join(__dirname, 'packages/material/src'),
|
'@zhst/material': path.join(__dirname, 'packages/material/src'),
|
||||||
'@zhst/icon': path.join(__dirname, 'packages/icon/src'),
|
'@zhst/icon': path.join(__dirname, 'packages/icon/src'),
|
||||||
|
'@zhst/icon-v2': path.join(__dirname, 'packages/icon-v2/src'),
|
||||||
'@zhst/map': path.join(__dirname, 'packages/map/src'),
|
'@zhst/map': path.join(__dirname, 'packages/map/src'),
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
@ -34,6 +35,7 @@ export default defineConfig({
|
|||||||
{ type: 'slave', dir: 'packages/slave/src' },
|
{ type: 'slave', dir: 'packages/slave/src' },
|
||||||
{ type: 'material', dir: 'packages/material/src' },
|
{ type: 'material', dir: 'packages/material/src' },
|
||||||
{ type: 'icon', dir: 'packages/icon/src' },
|
{ type: 'icon', dir: 'packages/icon/src' },
|
||||||
|
{ type: 'icon-v2', dir: 'packages/icon-v2/src' },
|
||||||
{ type: 'map', dir: 'packages/map/src' },
|
{ type: 'map', dir: 'packages/map/src' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
12
.fatherrc.ts
12
.fatherrc.ts
@ -1,5 +1,15 @@
|
|||||||
import { defineConfig } from 'father';
|
import { defineConfig } from 'father-plugin-less';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
// more father config: https://github.com/umijs/father/blob/master/docs/config.md
|
// more father config: https://github.com/umijs/father/blob/master/docs/config.md
|
||||||
|
esm: {
|
||||||
|
output: 'es',
|
||||||
|
ignores: ['**/demo/*', 'src/**/demo/*'],
|
||||||
|
transformer: 'babel',
|
||||||
|
},
|
||||||
|
cjs: {
|
||||||
|
output: 'lib',
|
||||||
|
ignores: ['**/demo/*', 'src/**/demo/*'],
|
||||||
|
transformer: 'babel',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -9,6 +9,10 @@ vueuse
|
|||||||
/temp
|
/temp
|
||||||
packages/**/es
|
packages/**/es
|
||||||
packages/**/lib
|
packages/**/lib
|
||||||
|
**/es
|
||||||
|
**/lib
|
||||||
|
**/**/es
|
||||||
|
**/**/lib
|
||||||
/es
|
/es
|
||||||
/lib
|
/lib
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -10,6 +10,9 @@
|
|||||||
"flvjs",
|
"flvjs",
|
||||||
"indicatorsize",
|
"indicatorsize",
|
||||||
"lambo",
|
"lambo",
|
||||||
|
"mapbox",
|
||||||
|
"maxzoom",
|
||||||
|
"minzoom",
|
||||||
"remuxer",
|
"remuxer",
|
||||||
"stylelint",
|
"stylelint",
|
||||||
"transmuxer",
|
"transmuxer",
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
|
pnpm --version
|
||||||
|
pnpm -v
|
||||||
|
node -v
|
||||||
pnpm install --force
|
pnpm install --force
|
||||||
|
|
||||||
pnpm run pkg:build
|
pnpm run pkg:build
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
pnpm run pub
|
pnpm run pub
|
||||||
|
|
||||||
# 打tag失败
|
# 打tag失败
|
||||||
|
3
global.d.ts
vendored
3
global.d.ts
vendored
@ -1 +1,4 @@
|
|||||||
declare module '*.less';
|
declare module '*.less';
|
||||||
|
declare module '*.png';
|
||||||
|
declare module '*.jpg';
|
||||||
|
declare module '*.jpeg';
|
||||||
|
10
package.json
10
package.json
@ -61,6 +61,7 @@
|
|||||||
"dumi": "^2.2.13",
|
"dumi": "^2.2.13",
|
||||||
"eslint": "^8.23.0",
|
"eslint": "^8.23.0",
|
||||||
"father": "^4.1.0",
|
"father": "^4.1.0",
|
||||||
|
"father-plugin-less": "^0.0.2",
|
||||||
"husky": "^8.0.1",
|
"husky": "^8.0.1",
|
||||||
"lerna": "^8.0.0",
|
"lerna": "^8.0.0",
|
||||||
"lint-staged": "^13.0.3",
|
"lint-staged": "^13.0.3",
|
||||||
@ -82,5 +83,12 @@
|
|||||||
},
|
},
|
||||||
"authors": [
|
"authors": [
|
||||||
"dev<710328466@qq.com>"
|
"dev<710328466@qq.com>"
|
||||||
]
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@ant-design/happy-work-theme": "^1.0.0",
|
||||||
|
"@zhst/meta": "workspace:^",
|
||||||
|
"rc-rate": "~2.12.0",
|
||||||
|
"react-fast-marquee": "^1.6.4",
|
||||||
|
"react-infinite-scroll-component": "^6.1.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
import { defineConfig } from 'father';
|
import { defineConfig } from 'father-plugin-less';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
// more father config: https://github.com/umijs/father/blob/master/docs/config.md
|
// more father config: https://github.com/umijs/father/blob/master/docs/config.md
|
||||||
esm: {
|
esm: {
|
||||||
output: 'es',
|
output: 'es',
|
||||||
ignores: ['**/demo/*', 'src/**/demo/*']
|
ignores: ['**/demo/*', 'src/**/demo/*'],
|
||||||
|
transformer: 'babel',
|
||||||
},
|
},
|
||||||
cjs: {
|
cjs: {
|
||||||
output: 'lib',
|
output: 'lib',
|
||||||
ignores: ['**/demo/*', 'src/**/demo/*']
|
ignores: ['**/demo/*', 'src/**/demo/*'],
|
||||||
|
transformer: 'babel',
|
||||||
},
|
},
|
||||||
|
lessInBabel: {
|
||||||
|
modifyVars: {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: ['father-plugin-less'],
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,153 @@
|
|||||||
# @zhst/biz
|
# @zhst/biz
|
||||||
|
|
||||||
|
## 0.34.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- feat(material,biz,meta): 修改算法配置物料,树组件业务传参,修复大图组件 od 变化不重新渲染
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/meta@0.30.0
|
||||||
|
|
||||||
|
## 0.33.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 修改穿梭框不能修改宽度
|
||||||
|
|
||||||
|
## 0.33.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 修改穿梭框不能修改宽度 bug
|
||||||
|
|
||||||
|
## 0.33.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- feat(biz): 修改穿梭框的样式和注释
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/meta@0.29.0
|
||||||
|
|
||||||
|
## 0.32.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/meta@0.28.0
|
||||||
|
|
||||||
|
## 0.32.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/icon@0.8.0
|
||||||
|
- @zhst/meta@0.27.1
|
||||||
|
|
||||||
|
## 0.32.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- zhst/biz 废弃 tree 组件
|
||||||
|
|
||||||
|
## 0.31.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 修改 less to css 配置
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/func@0.17.0
|
||||||
|
- @zhst/hooks@0.15.0
|
||||||
|
- @zhst/icon@0.7.0
|
||||||
|
- @zhst/meta@0.27.0
|
||||||
|
|
||||||
|
## 0.30.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 新增 zhst/biz 的大图组件 v2,修改其它包打包样式文件为 css
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/hooks@0.14.0
|
||||||
|
- @zhst/icon@0.6.0
|
||||||
|
- @zhst/meta@0.26.0
|
||||||
|
|
||||||
|
## 0.29.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- zhst/biz: 添加 treePanel 的一些例子,修复一些 ts 报错
|
||||||
|
|
||||||
|
## 0.28.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- zhst/biz: 新增树面板
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/meta@0.25.0
|
||||||
|
|
||||||
|
## 0.27.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- zhst/meta 全量迁移 antd-5.17.4
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/meta@0.24.0
|
||||||
|
|
||||||
|
## 0.26.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 穿梭框完成,新增 meta 组件
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/meta@0.23.0
|
||||||
|
- @zhst/hooks@0.13.2
|
||||||
|
- @zhst/func@0.16.1
|
||||||
|
- @zhst/icon@0.5.1
|
||||||
|
|
||||||
|
## 0.25.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- feat(zhst/map,zhst/biz): map 完善文档,biz 修改 boxSelectTree 的文档格式
|
||||||
|
|
||||||
|
## 0.25.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- zhst/map:拓展地图 api,加入 tools 组件、cluster 组价、marker 组件、draw 组件、popup 组件;zhst/mata:添加 floatButton 组件
|
||||||
|
|
||||||
|
## 0.24.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 树组件支持 tag 面板、优化 filter 传参、优化 option 传参、废弃之前的定制化方案
|
||||||
|
|
||||||
|
## 0.23.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- feat(biz 无限滚动组件): 添加无限滚动组件,屏幕自适应撑开
|
||||||
|
|
||||||
## 0.22.2
|
## 0.22.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
@ -1,256 +0,0 @@
|
|||||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
||||||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
||||||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
||||||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
||||||
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
||||||
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
||||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
||||||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
||||||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
||||||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
||||||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
||||||
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
||||||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
||||||
// @ts-nocheck
|
|
||||||
import React, { forwardRef, useImperativeHandle, useRef, useState, useEffect } from 'react';
|
|
||||||
import { ConfigProvider, Descriptions, Modal, Tabs, CropperImage, AttachImage, VideoPlayer, BtnGroup, RelatedImage } from '@zhst/meta';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { get, isEmpty, pick } from '@zhst/func';
|
|
||||||
import Navigation from "./components/navigation";
|
|
||||||
import CombineImage from "./components/CombineImage";
|
|
||||||
import "./index.less";
|
|
||||||
var DescriptionsItem = Descriptions.Item;
|
|
||||||
export var componentPrefix = 'zhst-image';
|
|
||||||
|
|
||||||
// 对比图模式、场景图模式、视频模式
|
|
||||||
|
|
||||||
var initialStyle = {
|
|
||||||
fontSize: '12px'
|
|
||||||
};
|
|
||||||
var BigImageModal = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
||||||
var _props$title = props.title,
|
|
||||||
title = _props$title === void 0 ? '-' : _props$title,
|
|
||||||
width = props.width,
|
|
||||||
open = props.open,
|
|
||||||
children = props.children,
|
|
||||||
onCancel = props.onCancel,
|
|
||||||
activeTab = props.activeTab,
|
|
||||||
_props$attributeList = props.attributeList,
|
|
||||||
attributeList = _props$attributeList === void 0 ? [] : _props$attributeList,
|
|
||||||
_props$isRelated = props.isRelated,
|
|
||||||
isRelated = _props$isRelated === void 0 ? false : _props$isRelated,
|
|
||||||
_props$tabs = props.tabs,
|
|
||||||
tabs = _props$tabs === void 0 ? {} : _props$tabs,
|
|
||||||
_props$footer = props.footer,
|
|
||||||
footer = _props$footer === void 0 ? null : _props$footer,
|
|
||||||
_props$dataSource = props.dataSource,
|
|
||||||
dataSource = _props$dataSource === void 0 ? {} : _props$dataSource,
|
|
||||||
onTabChange = props.onTabChange,
|
|
||||||
compareImageProps = props.compareImageProps,
|
|
||||||
showNavigation = props.showNavigation,
|
|
||||||
modalProps = props.modalProps,
|
|
||||||
cropperImageProps = props.cropperImageProps,
|
|
||||||
prevButtonProps = props.prevButtonProps,
|
|
||||||
onPrevButtonClick = props.onPrevButtonClick,
|
|
||||||
onNextButtonClick = props.onNextButtonClick,
|
|
||||||
nextButtonProps = props.nextButtonProps,
|
|
||||||
tabsProps = props.tabsProps,
|
|
||||||
btnGroupProps = props.btnGroupProps,
|
|
||||||
descriptionsProps = props.descriptionsProps,
|
|
||||||
relatedImageProps = props.relatedImageProps,
|
|
||||||
theme = props.theme;
|
|
||||||
var combineImageRef = useRef(null);
|
|
||||||
var videoPlayerRef = useRef(null);
|
|
||||||
var modalRef = useRef(null);
|
|
||||||
|
|
||||||
// ========================== 头切换 =========================
|
|
||||||
var _useState = useState(activeTab || get(tabsProps, 'data[0].key')),
|
|
||||||
_useState2 = _slicedToArray(_useState, 2),
|
|
||||||
tab = _useState2[0],
|
|
||||||
setTab = _useState2[1];
|
|
||||||
var _useState3 = useState('related'),
|
|
||||||
_useState4 = _slicedToArray(_useState3, 2),
|
|
||||||
activeKey = _useState4[0],
|
|
||||||
setActiveKey = _useState4[1];
|
|
||||||
|
|
||||||
// ========================= 预览切换下标 =========================
|
|
||||||
var _useState5 = useState('auto'),
|
|
||||||
_useState6 = _slicedToArray(_useState5, 2),
|
|
||||||
cropType = _useState6[0],
|
|
||||||
setCropType = _useState6[1];
|
|
||||||
var cropBtnDataSource = [{
|
|
||||||
key: 'close',
|
|
||||||
icon: 'icon-danchuangguanbi',
|
|
||||||
title: '退出'
|
|
||||||
}, {
|
|
||||||
key: 'auto',
|
|
||||||
icon: 'icon-zidong',
|
|
||||||
title: '智能框选'
|
|
||||||
}, {
|
|
||||||
key: 'custom',
|
|
||||||
icon: 'icon-shoudong',
|
|
||||||
title: '手动框选'
|
|
||||||
}];
|
|
||||||
var handleCropBtnClick = function handleCropBtnClick(v) {
|
|
||||||
setCropType(v);
|
|
||||||
};
|
|
||||||
var defaultTabsProps = !isEmpty(tabs) ? tabs : {
|
|
||||||
items: [{
|
|
||||||
label: '对比图模式',
|
|
||||||
key: 'COMPATER',
|
|
||||||
children: /*#__PURE__*/React.createElement(CombineImage, _extends({
|
|
||||||
ref: combineImageRef,
|
|
||||||
data: pick(dataSource, 'compaterImage', 'imgSummary', 'imageKey', 'score')
|
|
||||||
}, compareImageProps))
|
|
||||||
}, {
|
|
||||||
label: '场景图模式',
|
|
||||||
key: 'NORMAL',
|
|
||||||
children: /*#__PURE__*/React.createElement("div", {
|
|
||||||
style: {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
width: '100%'
|
|
||||||
}
|
|
||||||
}, /*#__PURE__*/React.createElement("div", {
|
|
||||||
style: {
|
|
||||||
width: '85%',
|
|
||||||
height: '500px'
|
|
||||||
}
|
|
||||||
}, /*#__PURE__*/React.createElement(CropperImage, _extends({
|
|
||||||
type: "rect",
|
|
||||||
odList: get(dataSource, 'odRect', [])
|
|
||||||
}, cropperImageProps), !(cropperImageProps !== null && cropperImageProps !== void 0 && cropperImageProps.editAble) && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AttachImage, {
|
|
||||||
data: [{
|
|
||||||
label: '测试',
|
|
||||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
|
|
||||||
}]
|
|
||||||
}), /*#__PURE__*/React.createElement("div", {
|
|
||||||
style: {
|
|
||||||
position: 'absolute',
|
|
||||||
right: '24px',
|
|
||||||
bottom: '24px',
|
|
||||||
fontSize: '18px',
|
|
||||||
color: 'red',
|
|
||||||
cursor: 'default'
|
|
||||||
}
|
|
||||||
}, "\u4EBA\u8138\u8D28\u91CF\u5206\uFF1A".concat(Number(cropperImageProps === null || cropperImageProps === void 0 ? void 0 : cropperImageProps.score).toFixed(2)))), (cropperImageProps === null || cropperImageProps === void 0 ? void 0 : cropperImageProps.showEditTools) && /*#__PURE__*/React.createElement(BtnGroup, _extends({
|
|
||||||
circle: true,
|
|
||||||
style: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
right: 0
|
|
||||||
},
|
|
||||||
dataSource: cropBtnDataSource,
|
|
||||||
onClick: handleCropBtnClick,
|
|
||||||
selectKey: cropType
|
|
||||||
}, btnGroupProps)))))
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: 页面初始化
|
|
||||||
useEffect(function () {}, [dataSource]);
|
|
||||||
|
|
||||||
// 暴露 ref 实例
|
|
||||||
useImperativeHandle(ref, function () {
|
|
||||||
return {
|
|
||||||
ref: ref,
|
|
||||||
tab: tab,
|
|
||||||
setTab: setTab,
|
|
||||||
modalRef: modalRef,
|
|
||||||
activeKey: activeKey,
|
|
||||||
setActiveKey: setActiveKey,
|
|
||||||
videoPlayerRef: videoPlayerRef,
|
|
||||||
combineImageRef: combineImageRef
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return /*#__PURE__*/React.createElement(Modal, _extends({
|
|
||||||
destroyOnClose: true,
|
|
||||||
width: width,
|
|
||||||
open: open,
|
|
||||||
ref: modalRef,
|
|
||||||
footer: footer,
|
|
||||||
className: componentPrefix,
|
|
||||||
title: title,
|
|
||||||
onCancel: onCancel
|
|
||||||
}, modalProps), /*#__PURE__*/React.createElement("div", {
|
|
||||||
style: {
|
|
||||||
marginTop: '16px'
|
|
||||||
}
|
|
||||||
}, /*#__PURE__*/React.createElement(ConfigProvider, {
|
|
||||||
theme: _objectSpread({
|
|
||||||
token: {
|
|
||||||
colorTextSecondary: 'rgba(0,0,0,0.45)'
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Descriptions: {
|
|
||||||
titleMarginBottom: '20px',
|
|
||||||
viewBg: '#f6f6f6',
|
|
||||||
titleColor: 'rgba(0,0,0,0.45)',
|
|
||||||
colorTextLabel: 'rgba(0,0,0,0.45)',
|
|
||||||
contentColor: 'rgba(0,0,0,0.88)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, theme)
|
|
||||||
}, attributeList.map(function (descriptions) {
|
|
||||||
var _descriptions$childre;
|
|
||||||
return /*#__PURE__*/React.createElement(Descriptions, _extends({
|
|
||||||
key: descriptions.title,
|
|
||||||
title: /*#__PURE__*/React.createElement("p", {
|
|
||||||
style: {
|
|
||||||
margin: '12px 0 0',
|
|
||||||
fontSize: initialStyle.fontSize
|
|
||||||
}
|
|
||||||
}, descriptions.title),
|
|
||||||
column: 8,
|
|
||||||
style: {
|
|
||||||
padding: '0 64px'
|
|
||||||
}
|
|
||||||
}, descriptionsProps), descriptions === null || descriptions === void 0 || (_descriptions$childre = descriptions.children) === null || _descriptions$childre === void 0 ? void 0 : _descriptions$childre.map(function (item) {
|
|
||||||
return /*#__PURE__*/React.createElement(DescriptionsItem, {
|
|
||||||
key: item.key,
|
|
||||||
label: item.label,
|
|
||||||
span: 1,
|
|
||||||
contentStyle: {
|
|
||||||
fontSize: initialStyle.fontSize
|
|
||||||
},
|
|
||||||
labelStyle: {
|
|
||||||
fontSize: initialStyle.fontSize
|
|
||||||
}
|
|
||||||
}, item.children);
|
|
||||||
}));
|
|
||||||
})), /*#__PURE__*/React.createElement("div", {
|
|
||||||
className: classNames("".concat(componentPrefix, "-view-container"))
|
|
||||||
}, /*#__PURE__*/React.createElement(Tabs, _extends({
|
|
||||||
activeKey: tab,
|
|
||||||
centered: true,
|
|
||||||
destroyInactiveTabPane: true,
|
|
||||||
onChange: function onChange(v) {
|
|
||||||
setTab(function (pre) {
|
|
||||||
onTabChange === null || onTabChange === void 0 || onTabChange(v, pre);
|
|
||||||
return v;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
tabBarStyle: {
|
|
||||||
fontSize: '18px',
|
|
||||||
fontWeight: 'bold'
|
|
||||||
}
|
|
||||||
}, defaultTabsProps, tabsProps)), tab === 'VIDEO' && /*#__PURE__*/React.createElement(VideoPlayer, {
|
|
||||||
ref: videoPlayerRef,
|
|
||||||
url: dataSource === null || dataSource === void 0 ? void 0 : dataSource.flvUrl
|
|
||||||
}), /*#__PURE__*/React.createElement(Navigation, _extends({
|
|
||||||
className: classNames("".concat(componentPrefix, "-view-container__nav"), (prevButtonProps === null || prevButtonProps === void 0 ? void 0 : prevButtonProps.disabled) && "".concat(componentPrefix, "-view-container__nav--disabled"), "".concat(componentPrefix, "-view-container__nav--left")),
|
|
||||||
prev: true,
|
|
||||||
show: showNavigation,
|
|
||||||
onClick: onPrevButtonClick
|
|
||||||
}, prevButtonProps)), /*#__PURE__*/React.createElement(Navigation, _extends({
|
|
||||||
className: classNames("".concat(componentPrefix, "-view-container__nav"), (nextButtonProps === null || nextButtonProps === void 0 ? void 0 : nextButtonProps.disabled) && "".concat(componentPrefix, "-view-container__nav--disabled"), "".concat(componentPrefix, "-view-container__nav--right")),
|
|
||||||
next: true,
|
|
||||||
show: showNavigation,
|
|
||||||
onClick: onNextButtonClick
|
|
||||||
}, nextButtonProps)), isRelated && /*#__PURE__*/React.createElement("div", {
|
|
||||||
style: {
|
|
||||||
margin: '24px 0'
|
|
||||||
}
|
|
||||||
}, /*#__PURE__*/React.createElement(RelatedImage, relatedImageProps)))), children);
|
|
||||||
});
|
|
||||||
export default BigImageModal;
|
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@zhst/biz",
|
"name": "@zhst/biz",
|
||||||
"version": "0.22.2",
|
"version": "0.34.0",
|
||||||
"description": "业务库",
|
"description": "业务库",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"business",
|
"business",
|
||||||
@ -35,6 +35,7 @@
|
|||||||
"registry": "http://10.0.0.77:4874"
|
"registry": "http://10.0.0.77:4874"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@swc/core": "^1.3.9",
|
||||||
"@zhst/types": "workspace:^"
|
"@zhst/types": "workspace:^"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
304
packages/biz/src/BigImage/BigImage.tsx
Normal file
304
packages/biz/src/BigImage/BigImage.tsx
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
import React, { forwardRef, useImperativeHandle, useRef } from 'react'
|
||||||
|
import {
|
||||||
|
ConfigProvider,
|
||||||
|
Descriptions,
|
||||||
|
CropperImage,
|
||||||
|
AttachImage,
|
||||||
|
VideoPlayer,
|
||||||
|
RelatedImage,
|
||||||
|
Radio,
|
||||||
|
Tooltip,
|
||||||
|
Button,
|
||||||
|
Tabs
|
||||||
|
} from '@zhst/meta';
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import { IconFont } from '@zhst/icon'
|
||||||
|
import { BigImageProps, BigImageRef } from './interface'
|
||||||
|
import CombineImage from './components/CombineImage'
|
||||||
|
import './index.less'
|
||||||
|
|
||||||
|
const DescriptionsItem = Descriptions.Item
|
||||||
|
|
||||||
|
export const componentPrefix = 'zhst-big-image'
|
||||||
|
|
||||||
|
const initialStyle ={
|
||||||
|
fontSize: '12px'
|
||||||
|
}
|
||||||
|
|
||||||
|
const BigImage = forwardRef<BigImageRef, BigImageProps>((props, ref) => {
|
||||||
|
const {
|
||||||
|
// ------------ 通用配置 -------------------
|
||||||
|
type,
|
||||||
|
viewHeight,
|
||||||
|
width = '100%',
|
||||||
|
children,
|
||||||
|
// ------------ 顶部按钮 -----------------
|
||||||
|
topButtonRender,
|
||||||
|
// ------------ 描述 -----------------
|
||||||
|
descriptionList = [],
|
||||||
|
showDescription,
|
||||||
|
// ------------- tab 导航 ----------------
|
||||||
|
customTabBarExtraContent,
|
||||||
|
tabProps,
|
||||||
|
onTabChange,
|
||||||
|
// ------------- 场景图 -------------
|
||||||
|
cropperImageProps = {
|
||||||
|
cropButtonList: []
|
||||||
|
},
|
||||||
|
// ----------------- 对比图 ---------------------
|
||||||
|
combineImageProps,
|
||||||
|
// ------------ 视频模式 -----------------
|
||||||
|
videoProps,
|
||||||
|
// ------------ view 操作按钮 -----------------
|
||||||
|
showModeChange,
|
||||||
|
modeButtonGroupProps,
|
||||||
|
viewTopModeButtonList = [
|
||||||
|
{
|
||||||
|
key: 'image',
|
||||||
|
label: '图片'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'video',
|
||||||
|
label: '视频'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// ------------------ 翻页 ----------------------
|
||||||
|
showNavigation,
|
||||||
|
prevButtonProps,
|
||||||
|
onPrevButtonClick,
|
||||||
|
onNextButtonClick,
|
||||||
|
nextButtonProps,
|
||||||
|
// ------------------ 人脸碰撞模型 -----------------
|
||||||
|
relatedImageProps,
|
||||||
|
isRelated = false,
|
||||||
|
theme,
|
||||||
|
} = props
|
||||||
|
const combineImageRef = useRef<any>(null)
|
||||||
|
const videoPlayerRef = useRef<any>(null)
|
||||||
|
const cropperImageRef = useRef<any>(null)
|
||||||
|
|
||||||
|
cropperImageProps.cropButtonList = cropperImageProps?.cropButtonList || [
|
||||||
|
{
|
||||||
|
key: 'auto',
|
||||||
|
icon: <IconFont icon='icon-zidong' />,
|
||||||
|
label: '智能框选',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'custom',
|
||||||
|
icon: <IconFont icon='icon-shoudong' />,
|
||||||
|
label: '手动框选',
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// ------------------- 初始化模式 ----------------------
|
||||||
|
const initMode = (type: BigImageProps['type'] = 'normal') => {
|
||||||
|
switch (type) {
|
||||||
|
case 'compater':
|
||||||
|
return (
|
||||||
|
<CombineImage
|
||||||
|
// @ts-ignore
|
||||||
|
ref={combineImageRef}
|
||||||
|
height={viewHeight}
|
||||||
|
{...combineImageProps}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
case 'normal':
|
||||||
|
return (
|
||||||
|
<div style={{ height: viewHeight, width: '100%' }}>
|
||||||
|
<CropperImage
|
||||||
|
ref={cropperImageRef}
|
||||||
|
type='rect'
|
||||||
|
// @ts-ignore
|
||||||
|
height={viewHeight}
|
||||||
|
{...cropperImageProps}
|
||||||
|
>
|
||||||
|
{/* // ------------ 显示条件:当不为编辑状态,并且有图片值 ----------------- */}
|
||||||
|
{!cropperImageProps?.editAble && cropperImageProps?.attachImageData && (
|
||||||
|
<>
|
||||||
|
<AttachImage
|
||||||
|
data={cropperImageProps?.attachImageData}
|
||||||
|
{...cropperImageProps?.attachImageProps}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
right: '4px',
|
||||||
|
bottom: '4px',
|
||||||
|
fontSize: '18px',
|
||||||
|
color: 'red',
|
||||||
|
cursor: 'default'
|
||||||
|
}}
|
||||||
|
>{`人脸质量分:${(Number(cropperImageProps?.score) as number).toFixed(2)}`}</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{/* // ------------ 场景图绘制工具栏 ----------------- */}
|
||||||
|
{cropperImageProps?.showEditTools && (
|
||||||
|
<div className={`${componentPrefix}-view-cropper-btn`}>
|
||||||
|
<Radio.Group
|
||||||
|
className={`${componentPrefix}-view-cropper-btn-group`}
|
||||||
|
size="small"
|
||||||
|
buttonStyle='solid'
|
||||||
|
// @ts-ignore
|
||||||
|
onChange={cropperImageProps?.onCropperTypeChange}
|
||||||
|
{...cropperImageProps.btnGroupProps}
|
||||||
|
>
|
||||||
|
{cropperImageProps?.cropButtonList?.map(btn => (
|
||||||
|
<Tooltip key={btn.key} title={btn.label}>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<Radio.Button value={btn.key}{...btn.props}><div className={`${componentPrefix}-view-cropper-btn-group-radio`}>{btn.icon} {btn.label}</div></Radio.Button>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</Radio.Group>
|
||||||
|
<Button onClick={cropperImageProps?.onExitEdit} className={`${componentPrefix}-view-cropper-btn_close`} type="primary" size="small" icon={<IconFont icon="icon-danchuangguanbi" />} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CropperImage>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
case 'video':
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<VideoPlayer ref={videoPlayerRef} height={viewHeight} {...videoProps} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露 ref 实例
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
// @ts-ignore
|
||||||
|
cropperImageRef: cropperImageRef?.current,
|
||||||
|
videoPlayerRef: videoPlayerRef?.current,
|
||||||
|
combineImageRef: combineImageRef?.current,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(`${componentPrefix}-view`)}
|
||||||
|
style={{ width: `${parseInt(width as string)}px` }}
|
||||||
|
>
|
||||||
|
<div className={classNames(`${componentPrefix}-view-btns`)}>
|
||||||
|
{topButtonRender}
|
||||||
|
</div>
|
||||||
|
{showDescription && (
|
||||||
|
<div
|
||||||
|
className={classNames(`${componentPrefix}-view-desc`)}
|
||||||
|
>
|
||||||
|
<ConfigProvider
|
||||||
|
theme={{
|
||||||
|
components: {
|
||||||
|
Descriptions: {
|
||||||
|
viewBg: '#f6f6f6',
|
||||||
|
colorBgLayout:'#ccc',
|
||||||
|
titleColor: 'rgba(0,0,0,0.45)',
|
||||||
|
colorTextLabel: 'rgba(0,0,0,0.45)',
|
||||||
|
contentColor: 'rgba(0,0,0,0.88)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...theme
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{descriptionList.map(descriptions => (
|
||||||
|
<Descriptions
|
||||||
|
key={descriptions.title}
|
||||||
|
title={descriptions.showTitle && descriptions.title}
|
||||||
|
column={8}
|
||||||
|
{...descriptions.props}
|
||||||
|
>
|
||||||
|
{descriptions?.children?.map(item => (
|
||||||
|
<DescriptionsItem
|
||||||
|
key={item.key}
|
||||||
|
label={item.label}
|
||||||
|
span={1}
|
||||||
|
contentStyle={{ fontSize: initialStyle.fontSize }}
|
||||||
|
labelStyle={{ fontSize: initialStyle.fontSize }}
|
||||||
|
>{item.children}</DescriptionsItem>
|
||||||
|
))}
|
||||||
|
</Descriptions>
|
||||||
|
))}
|
||||||
|
</ConfigProvider>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Tabs
|
||||||
|
tabBarExtraContent={customTabBarExtraContent}
|
||||||
|
onChange={onTabChange}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
label: `场景图`,
|
||||||
|
key: '1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `对比图`,
|
||||||
|
key: '2',
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
{...tabProps}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={classNames(`${componentPrefix}-view-container`)}
|
||||||
|
>
|
||||||
|
{/* ---------------- view 视图左上角导航按钮 ------------------ */}
|
||||||
|
{showModeChange && (
|
||||||
|
<div
|
||||||
|
className={classNames(`${componentPrefix}-view-container-modeNav`)}
|
||||||
|
>
|
||||||
|
<Radio.Group
|
||||||
|
buttonStyle='solid'
|
||||||
|
size='small'
|
||||||
|
{...modeButtonGroupProps}
|
||||||
|
>
|
||||||
|
{viewTopModeButtonList?.map(btn => (
|
||||||
|
<Tooltip key={btn.key} title={btn.tooltipTxt}>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<Radio.Button value={btn.key} {...btn.props}>{btn.icon} {btn.label}</Radio.Button>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</Radio.Group>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* --------------------------------- 视频播放模式 --------------------------------- */}
|
||||||
|
{initMode(type)}
|
||||||
|
{/* 切换按钮组件 */}
|
||||||
|
{/* ----------------------------------- 上一张按钮 ---------------------------------- */}
|
||||||
|
{showNavigation && (
|
||||||
|
<Button
|
||||||
|
className={classNames(
|
||||||
|
`${componentPrefix}-view-container-nav`,
|
||||||
|
`${componentPrefix}-view-container-nav_left`
|
||||||
|
)}
|
||||||
|
icon={<IconFont icon="icon-qiehuanzuo" />}
|
||||||
|
type="primary"
|
||||||
|
// style={{ backgroundColor: 'rgba(255,255,255,0.8)' }}
|
||||||
|
shape='circle'
|
||||||
|
onClick={onPrevButtonClick}
|
||||||
|
{...prevButtonProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{/* ----------------------------------- 下一张按钮 ---------------------------------- */}
|
||||||
|
{showNavigation && (
|
||||||
|
<Button
|
||||||
|
className={classNames(
|
||||||
|
`${componentPrefix}-view-container-nav`,
|
||||||
|
`${componentPrefix}-view-container-nav_right`
|
||||||
|
)}
|
||||||
|
icon={<IconFont icon="icon-qiehuanyou" />}
|
||||||
|
type="primary"
|
||||||
|
// style={{ backgroundColor: 'rgba(255,255,255,0.8)' }}
|
||||||
|
shape='circle'
|
||||||
|
onClick={onNextButtonClick}
|
||||||
|
{...nextButtonProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{/* ----------------------------------- 人脸碰撞组件 ---------------------------------- */}
|
||||||
|
{isRelated && (
|
||||||
|
// @ts-ignore
|
||||||
|
<RelatedImage
|
||||||
|
{...relatedImageProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default BigImage
|
@ -0,0 +1,3 @@
|
|||||||
|
.zhst-big-image-combime-image {
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
}
|
82
packages/biz/src/BigImage/components/CombineImage/index.tsx
Normal file
82
packages/biz/src/BigImage/components/CombineImage/index.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
|
||||||
|
import React, { FC, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||||
|
import { CompareImage, Flex, Progress, ProgressProps, CompareImageProps } from '@zhst/meta'
|
||||||
|
import './index.less'
|
||||||
|
|
||||||
|
export interface ComBineImageProps {
|
||||||
|
height?: string | number
|
||||||
|
data: {
|
||||||
|
imgSummary: string;
|
||||||
|
compaterImage: string;
|
||||||
|
imageKey: string;
|
||||||
|
score: number;
|
||||||
|
}
|
||||||
|
prevDisable?: boolean;
|
||||||
|
nextDisable?: boolean;
|
||||||
|
onPre?: () => void;
|
||||||
|
onNext?: () => void;
|
||||||
|
openRoll?: boolean
|
||||||
|
targetImageProps?: CompareImageProps
|
||||||
|
sourceImageProps?: CompareImageProps
|
||||||
|
}
|
||||||
|
|
||||||
|
const conicColors: ProgressProps['strokeColor'] = {
|
||||||
|
'0%': '#42E2AC',
|
||||||
|
'50%': '#DFAF2E',
|
||||||
|
'100%': '#F95C55',
|
||||||
|
};
|
||||||
|
|
||||||
|
const componentName = 'zhst-big-image-combime-image'
|
||||||
|
|
||||||
|
const ComBineImage: FC<ComBineImageProps> = forwardRef((props, ref) => {
|
||||||
|
const {
|
||||||
|
height,
|
||||||
|
data,
|
||||||
|
prevDisable,
|
||||||
|
nextDisable,
|
||||||
|
onNext,
|
||||||
|
onPre,
|
||||||
|
openRoll
|
||||||
|
} = props
|
||||||
|
const { imgSummary, compaterImage, score } = data
|
||||||
|
const targetImageRef = useRef(null)
|
||||||
|
const compareImageRef = useRef(null)
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
compareImageRef,
|
||||||
|
targetImageRef
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex className={componentName} justify='space-between' align='center' >
|
||||||
|
<CompareImage
|
||||||
|
ref={targetImageRef}
|
||||||
|
height={height as string}
|
||||||
|
width="400px"
|
||||||
|
preDisable={prevDisable}
|
||||||
|
nextDisable={nextDisable}
|
||||||
|
onNext={onNext}
|
||||||
|
onPre={onPre}
|
||||||
|
showScore={false}
|
||||||
|
openRoll={openRoll}
|
||||||
|
url={imgSummary}
|
||||||
|
label="目标图"
|
||||||
|
{...props.targetImageProps}
|
||||||
|
/>
|
||||||
|
<Progress type="circle" size={72} strokeWidth={6} percent={Number(score || 0)} strokeColor={conicColors} />
|
||||||
|
<CompareImage
|
||||||
|
ref={compareImageRef}
|
||||||
|
width="400px"
|
||||||
|
height={height as string}
|
||||||
|
url={compaterImage}
|
||||||
|
openRoll={false}
|
||||||
|
score={score}
|
||||||
|
label="预警图"
|
||||||
|
labelColor='#FA5852'
|
||||||
|
{...props.sourceImageProps}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default ComBineImage
|
44
packages/biz/src/BigImage/components/navigation/index.less
Normal file
44
packages/biz/src/BigImage/components/navigation/index.less
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
.zhst-image__nav {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
width: 48px;
|
||||||
|
height: 100%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0;
|
||||||
|
|
||||||
|
&>button {
|
||||||
|
& span {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:global {
|
||||||
|
i:hover {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--disable {
|
||||||
|
:global {
|
||||||
|
i {
|
||||||
|
color: #f0f0f0;
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-prev {
|
||||||
|
left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-next {
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
}
|
40
packages/biz/src/BigImage/components/navigation/index.tsx
Normal file
40
packages/biz/src/BigImage/components/navigation/index.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// !! 已废弃
|
||||||
|
import * as React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { Button } from '@zhst/meta';
|
||||||
|
import { IconFont } from '@zhst/icon'
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
|
const componentName = `zhst-image__nav`;
|
||||||
|
|
||||||
|
const Navigation: React.FC<{
|
||||||
|
show?: boolean;
|
||||||
|
onClick?: React.MouseEventHandler<HTMLElement>;
|
||||||
|
prev?: boolean;
|
||||||
|
next?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
className?: string;
|
||||||
|
color?: string;
|
||||||
|
hoverColor?: string;
|
||||||
|
}> = (props) => {
|
||||||
|
const { show, prev, next, disabled, onClick, className, color } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
`${componentName}`,
|
||||||
|
prev && `${componentName}-prev`,
|
||||||
|
next && `${componentName}-next`,
|
||||||
|
disabled && `${componentName}--disable`,
|
||||||
|
!show && `${componentName}--hide`,
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Button type="text" disabled={disabled} shape='circle' onClick={onClick}>
|
||||||
|
<IconFont size={28} color={color} icon={prev ? 'icon-qiehuanzuo' : 'icon-qiehuanyou'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navigation;
|
Binary file not shown.
After Width: | Height: | Size: 143 KiB |
71
packages/biz/src/BigImage/demo/index.tsx
Normal file
71
packages/biz/src/BigImage/demo/index.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { BigImage } from '@zhst/biz'
|
||||||
|
import { Space, Switch } from '@zhst/meta'
|
||||||
|
import { pick, get } from '@zhst/func'
|
||||||
|
import { BIG_IMAGE_DATA, attributeList } from '../mock'
|
||||||
|
import Img from './imgs/photo-1503185912284-5271ff81b9a8.webp'
|
||||||
|
|
||||||
|
const BigModal = (props: any) => {
|
||||||
|
const {
|
||||||
|
} = props
|
||||||
|
const [dataSource, setDataSource] = useState(BIG_IMAGE_DATA)
|
||||||
|
const [showDesc, setShowDesc] = useState(true)
|
||||||
|
const [type, setType] = useState('normal')
|
||||||
|
const modalRef = useRef(null)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '32px', border: '1px solid #09f' }}>
|
||||||
|
<Space>
|
||||||
|
<span>显示属性:<Switch value={showDesc} onChange={e => setShowDesc(pre => !pre)} /></span>
|
||||||
|
</Space>
|
||||||
|
<BigImage
|
||||||
|
type={type}
|
||||||
|
ref={modalRef}
|
||||||
|
width={896}
|
||||||
|
viewHeight={'440px'}
|
||||||
|
showModeChange
|
||||||
|
onTabChange={(newVal) => setType(newVal === '1'? 'normal': 'compater')}
|
||||||
|
// ------------ 属性列表 -----------------
|
||||||
|
showDescription={showDesc}
|
||||||
|
descriptionList={attributeList}
|
||||||
|
// ------------ 图片/视频切换 -----------------
|
||||||
|
viewTopModeButtonList={[
|
||||||
|
{
|
||||||
|
key: 'image',
|
||||||
|
label: '图片'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'video',
|
||||||
|
label: '视频'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
modeButtonGroupProps={{
|
||||||
|
defaultValue: 'image',
|
||||||
|
onChange: e => console.log('模式切换', e)
|
||||||
|
}}
|
||||||
|
// ------------ 导航功能 -----------------
|
||||||
|
showNavigation
|
||||||
|
onPrevButtonClick={val => console.log('pre', val)}
|
||||||
|
onNextButtonClick={val => console.log('next', val)}
|
||||||
|
// ------------ 场景图功能 -----------------
|
||||||
|
cropperImageProps={{
|
||||||
|
url: Img,
|
||||||
|
score: 50,
|
||||||
|
odList: get(dataSource, 'odRect', []),
|
||||||
|
attachImageData: get(dataSource, 'attachImg', []),
|
||||||
|
}}
|
||||||
|
// ------------ 对比图模式 -----------------
|
||||||
|
combineImageProps={{
|
||||||
|
data: pick(dataSource, 'compaterImage', 'imgSummary', 'imageKey', 'score'),
|
||||||
|
}}
|
||||||
|
// ------------ 视频模块 -----------------
|
||||||
|
videoProps={{
|
||||||
|
url: get(dataSource, 'flvUrl', '')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</BigImage>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BigModal
|
128
packages/biz/src/BigImage/demo/oldData.tsx
Normal file
128
packages/biz/src/BigImage/demo/oldData.tsx
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { BigImageModal } from '@zhst/biz'
|
||||||
|
import { Button, DescriptionsProps } from '@zhst/meta'
|
||||||
|
import { IMAGE_DATA, BIG_IMAGE_DATA } from '../mock'
|
||||||
|
import bigImageModalAdapter from '../util/bigImageModalAdapter'
|
||||||
|
import { get } from '@zhst/func';
|
||||||
|
|
||||||
|
const descriptionList: DescriptionsProps['items'] = [
|
||||||
|
{
|
||||||
|
title: '人员属性',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: '性别',
|
||||||
|
children: '男',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: '年龄',
|
||||||
|
children: '成年',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
label: '帽子',
|
||||||
|
children: '无',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '4',
|
||||||
|
label: '上身颜色',
|
||||||
|
children: '灰',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '5',
|
||||||
|
label: '下身颜色',
|
||||||
|
children: '蓝色',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '6',
|
||||||
|
label: '附着物',
|
||||||
|
children: '无',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '7',
|
||||||
|
label: '骑行',
|
||||||
|
children: '否',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 适配器,适配老接口
|
||||||
|
const NewImageModal = bigImageModalAdapter(BigImageModal, {
|
||||||
|
oldMode: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const BigModal = (props) => {
|
||||||
|
const {
|
||||||
|
onClose,
|
||||||
|
isArchiveDetail = false,
|
||||||
|
specialTitle = '对比图2',
|
||||||
|
transformPropFunc,
|
||||||
|
screenshotButtonRender,
|
||||||
|
showLowFaceTip = false
|
||||||
|
} = props
|
||||||
|
const [visible, setVisible] = useState(false)
|
||||||
|
const [selectIndex, setSelectIndex] = useState(0)
|
||||||
|
const [dataSource, setDataSource] = useState(IMAGE_DATA.dataSource)
|
||||||
|
const [dataSources, setDataSources] = useState(IMAGE_DATA.dataSource)
|
||||||
|
const [selectItem, setSelectItem] = useState({})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button onClick={() => setVisible(true)} >查看大图</Button>
|
||||||
|
<NewImageModal
|
||||||
|
title="查看大图"
|
||||||
|
dataSource={dataSource}
|
||||||
|
imageData={dataSource}
|
||||||
|
width={1098}
|
||||||
|
onClose={() => onClose}
|
||||||
|
descriptionConfig={{ data: descriptionList }}
|
||||||
|
visible={visible}
|
||||||
|
isArchiveDetail={isArchiveDetail}
|
||||||
|
ToolProps={{
|
||||||
|
// renderLeft: leftOperateBar({ disableBtn, onActionClick: onBigImageActionClick }),
|
||||||
|
// renderRight: rightOperateBar({
|
||||||
|
// disableBtn,
|
||||||
|
// onActionClick: onBigImageActionClick,
|
||||||
|
// isArchiveDetail,
|
||||||
|
// }),
|
||||||
|
// renderVideoBtn: !disableBtn.includes(OPT['PLAY_VIDEO']),
|
||||||
|
// disableVideo: disableVideo,
|
||||||
|
}}
|
||||||
|
selectIndex={selectIndex}
|
||||||
|
onSelectIndexChange={(index: number) => {
|
||||||
|
index > 0 && index < dataSources.length && setSelectIndex(index);
|
||||||
|
}}
|
||||||
|
// tabsFilter={tabsFilter}
|
||||||
|
specialTitle={specialTitle}
|
||||||
|
transformPropFunc={async (item: any) => {
|
||||||
|
let bigImageInfo = !!transformPropFunc && (await transformPropFunc(item));
|
||||||
|
setSelectItem({ ...bigImageInfo });
|
||||||
|
return { ...bigImageInfo };
|
||||||
|
}}
|
||||||
|
screenshotButtonRender={screenshotButtonRender}
|
||||||
|
//@ts-ignore
|
||||||
|
transformVideoPropFunc={async (item) => {
|
||||||
|
const { maxDuration: duration = 20 } = item || {};
|
||||||
|
const time = get(item, 'timestamp');
|
||||||
|
const cameraId = get(item, 'cameraId');
|
||||||
|
const { url: flvUrl } = {
|
||||||
|
url: 'url',
|
||||||
|
downloadUrl: 'url',
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
flvUrl,
|
||||||
|
maxDuration: duration,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
nullDialogProp={{
|
||||||
|
emptyText: showLowFaceTip ? '目标图人脸质量较低,暂无聚类数据' : '暂无数据',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BigModal
|
89
packages/biz/src/BigImage/demo/withEdit.tsx
Normal file
89
packages/biz/src/BigImage/demo/withEdit.tsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { BigImage } from '@zhst/biz'
|
||||||
|
import { Space, Switch, Button } from '@zhst/meta'
|
||||||
|
import { pick, get } from '@zhst/func'
|
||||||
|
import { BIG_IMAGE_DATA, attributeList } from '../mock'
|
||||||
|
import Img from './imgs/photo-1503185912284-5271ff81b9a8.webp'
|
||||||
|
|
||||||
|
const testOd = [
|
||||||
|
{
|
||||||
|
"id": "456",
|
||||||
|
"x": 0.58543766,
|
||||||
|
"y": 0.3203356,
|
||||||
|
"w": 0.052037954,
|
||||||
|
"h": 0.2664015
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const BigModal = (props: any) => {
|
||||||
|
const {
|
||||||
|
} = props
|
||||||
|
const [dataSource, setDataSource] = useState(BIG_IMAGE_DATA)
|
||||||
|
const [showDesc, setShowDesc] = useState(true)
|
||||||
|
const [editAble, setEditAble] = useState(false)
|
||||||
|
const [odList, setOdList] = useState(get(dataSource, 'odRect', []))
|
||||||
|
const [type, setType] = useState('normal')
|
||||||
|
const bigImageRef = useRef(null)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '32px', border: '1px solid #09f' }}>
|
||||||
|
<Space>
|
||||||
|
<span>显示属性:<Switch value={showDesc} onChange={e => setShowDesc(pre => !pre)} /></span>
|
||||||
|
<span>打开编辑模式:<Switch value={editAble} onChange={e => setEditAble(pre => !pre)} /></span>
|
||||||
|
<Button onClick={() => setOdList(testOd)}>修改Od</Button>
|
||||||
|
</Space>
|
||||||
|
<BigImage
|
||||||
|
type={type}
|
||||||
|
ref={bigImageRef}
|
||||||
|
width={896}
|
||||||
|
viewHeight={'440px'}
|
||||||
|
showModeChange
|
||||||
|
onTabChange={(newVal) => setType(newVal === '1'? 'normal': 'compater')}
|
||||||
|
// ------------ 属性列表 -----------------
|
||||||
|
showDescription={showDesc}
|
||||||
|
descriptionList={attributeList}
|
||||||
|
// ------------ 图片/视频切换 -----------------
|
||||||
|
viewTopModeButtonList={[
|
||||||
|
{
|
||||||
|
key: 'image',
|
||||||
|
label: '图片'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'video',
|
||||||
|
label: '视频'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
modeButtonGroupProps={{
|
||||||
|
defaultValue: 'image',
|
||||||
|
onChange: e => console.log('模式切换', e)
|
||||||
|
}}
|
||||||
|
// ------------ 导航功能 -----------------
|
||||||
|
showNavigation
|
||||||
|
onPrevButtonClick={val => console.log('pre', val)}
|
||||||
|
onNextButtonClick={val => console.log('next', val)}
|
||||||
|
// ------------ 场景图功能 -----------------
|
||||||
|
cropperImageProps={{
|
||||||
|
editAble,
|
||||||
|
url: Img,
|
||||||
|
score: 50,
|
||||||
|
odList: odList,
|
||||||
|
showEditTools: editAble,
|
||||||
|
attachImageData: get(dataSource, 'attachImg', []),
|
||||||
|
onCropperTypeChange: v => console.log('框选模式', v),
|
||||||
|
onExitEdit: () => setEditAble(pre => !pre)
|
||||||
|
}}
|
||||||
|
// ------------ 对比图模式 -----------------
|
||||||
|
combineImageProps={{
|
||||||
|
data: pick(dataSource, 'compaterImage', 'imgSummary', 'imageKey', 'score'),
|
||||||
|
}}
|
||||||
|
// ------------ 视频模块 -----------------
|
||||||
|
videoProps={{
|
||||||
|
url: get(dataSource, 'flvUrl', '')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</BigImage>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BigModal
|
78
packages/biz/src/BigImage/demo/withRelatedImage.tsx
Normal file
78
packages/biz/src/BigImage/demo/withRelatedImage.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { BigImage } from '@zhst/biz'
|
||||||
|
import { Button, Space, Switch } from '@zhst/meta'
|
||||||
|
import { pick, get } from '@zhst/func'
|
||||||
|
import { BIG_IMAGE_DATA, attributeList, RELATED_IMAGES } from '../mock'
|
||||||
|
|
||||||
|
const BigModal = (props: any) => {
|
||||||
|
const {
|
||||||
|
} = props
|
||||||
|
const [visible, setVisible] = useState(true)
|
||||||
|
const [dataSource, setDataSource] = useState(BIG_IMAGE_DATA)
|
||||||
|
const [selectedItemKey, setSelectedItemKey] = useState()
|
||||||
|
const [showFaceModel, setShowFaceModel] = useState(true)
|
||||||
|
const [type, setType] = useState('normal')
|
||||||
|
const modalRef = useRef(null)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '32px', border: '1px solid #09f' }}>
|
||||||
|
<Space>
|
||||||
|
<span>显示人脸碰撞模型:<Switch value={showFaceModel} onChange={e => setShowFaceModel(pre => !pre)} /></span>
|
||||||
|
</Space>
|
||||||
|
<BigImage
|
||||||
|
type={type}
|
||||||
|
open={visible}
|
||||||
|
ref={modalRef}
|
||||||
|
viewHeight={'440px'}
|
||||||
|
width={896}
|
||||||
|
onCancel={() => setVisible(false)}
|
||||||
|
showModeChange
|
||||||
|
onTabChange={(newVal, oldVal) => setType(newVal === '1'? 'normal': 'compater')}
|
||||||
|
// ------------ 图片/视频切换 -----------------
|
||||||
|
viewTopModeButtonList={[
|
||||||
|
{
|
||||||
|
key: 'image',
|
||||||
|
label: '图片'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'video',
|
||||||
|
label: '视频'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
modeButtonGroupProps={{
|
||||||
|
defaultValue: 'image',
|
||||||
|
onChange: e => console.log('模式切换', e)
|
||||||
|
}}
|
||||||
|
// ------------ 场景图功能 -----------------
|
||||||
|
cropperImageProps={{
|
||||||
|
url: "https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp",
|
||||||
|
score: 50,
|
||||||
|
odList: get(dataSource, 'odRect', []),
|
||||||
|
attachImageData: get(dataSource, 'attachImg', []),
|
||||||
|
}}
|
||||||
|
// ------------ 对比图模式 -----------------
|
||||||
|
combineImageProps={{
|
||||||
|
data: pick(dataSource, 'compaterImage', 'imgSummary', 'imageKey', 'score'),
|
||||||
|
}}
|
||||||
|
// ------------ 视频模块 -----------------
|
||||||
|
videoProps={{
|
||||||
|
url: get(dataSource, 'flvUrl', '')
|
||||||
|
}}
|
||||||
|
// ------------ 人脸碰撞模型 -----------------
|
||||||
|
isRelated={showFaceModel}
|
||||||
|
relatedImageProps={{
|
||||||
|
activeTab: 'related',
|
||||||
|
selectedItemKey,
|
||||||
|
data: RELATED_IMAGES,
|
||||||
|
onCancel: () => console.log('取消关联'),
|
||||||
|
onConnect: () => console.log('关联'),
|
||||||
|
onTabChange: (newVal, oldVal) => console.log('tab切换', newVal, oldVal),
|
||||||
|
onItemSelected: (item) => setSelectedItemKey(item.key)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</BigImage>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BigModal
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 514 B After Width: | Height: | Size: 514 B |
132
packages/biz/src/BigImage/index.less
Normal file
132
packages/biz/src/BigImage/index.less
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
.zhst-big-image {
|
||||||
|
.zhst-dialog-content {
|
||||||
|
box-shadow: 0 4px 12px rgb(0 0 0 / 20%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.zhst-tabs .zhst-tabs-nav-wrap {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-view {
|
||||||
|
&-btns {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
&-desc {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 16px 16px 0 16px;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
.zhst-descriptions-header {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.zhst-descriptions-title {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ------------ tab -----------------
|
||||||
|
.zhst-tabs-nav {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
.zhst-tabs-tab {
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
|
padding: 0;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-container {
|
||||||
|
position: relative;
|
||||||
|
min-height: 440px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-size: 0;
|
||||||
|
&-modeNav {
|
||||||
|
padding: 2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
left: 4px;
|
||||||
|
z-index: 10;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
&__nav {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 99;
|
||||||
|
top: 50%;
|
||||||
|
width: 40px !important;
|
||||||
|
height: 40px !important;
|
||||||
|
background: #d9d9d9;
|
||||||
|
border-radius: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
|
||||||
|
&>button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
|
||||||
|
&>button {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__nav:hover {
|
||||||
|
background: #09f;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
// ------------ 导航 -----------------
|
||||||
|
&-nav {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8)!important;
|
||||||
|
}
|
||||||
|
&_left {
|
||||||
|
left: 8px;
|
||||||
|
}
|
||||||
|
&_right {
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ------------ 场景图 -----------------
|
||||||
|
&-cropper-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
&_close {
|
||||||
|
margin-left: 4px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
&-group {
|
||||||
|
.zhst-radio-button-wrapper {
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #191919;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8)
|
||||||
|
}
|
||||||
|
&-radio {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
.anticon {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
96
packages/biz/src/BigImage/index.md
Normal file
96
packages/biz/src/BigImage/index.md
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
group: 进阶组件
|
||||||
|
category: Components
|
||||||
|
subtitle: 大图组件
|
||||||
|
toc: content
|
||||||
|
title: BigImage 大图组件
|
||||||
|
---
|
||||||
|
|
||||||
|
## 大图弹框
|
||||||
|
|
||||||
|
<code src="./demo/index.tsx">基本</code>
|
||||||
|
<code src="./demo/withRelatedImage.tsx">人脸碰撞模型</code>
|
||||||
|
<code src="./demo/withEdit.tsx">编辑模式</code>
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| type | 当前模式 | 'compater'、'normal'、'video' | - | |
|
||||||
|
| viewHeight | 视图高度 | string、number | - | |
|
||||||
|
| width | 宽度 | string、number | - | |
|
||||||
|
| showDescription | 描述列表显示\隐藏 | boolean | false | |
|
||||||
|
| descriptionsProps | 描述列表透传 | | antd - DescriptionsProps | |
|
||||||
|
| descriptionList | 描述列表数据(见下文) | | IDescriptionList | |
|
||||||
|
| topButtonRender | 顶部按钮 | | ReactNode、string | |
|
||||||
|
| customTabBarExtraContent | 自定义tab尾部导航插槽 | | ReactNode、 string | |
|
||||||
|
| onTabChange | tab事件监听 | | antd - TabsProps['onChange'] | |
|
||||||
|
| tabProps | tab透传 | | antd - TabsProps | |
|
||||||
|
| showModeChange | 是否显示模式切换按钮 | | boolean | |
|
||||||
|
| viewTopModeButtonList | 模式切换按钮列表 | | TypeRadio[] | |
|
||||||
|
| modeButtonGroupProps | 模式切换按钮组透传 | RadioGroupProps | - | |
|
||||||
|
| isRelated | 人脸碰撞模型显示\隐藏 | boolean | | |
|
||||||
|
| relatedImageProps | 人脸碰撞模型透传 | zhst/meta - RelatedImageProps | | |
|
||||||
|
| cropperImageProps | 场景图模式透传 | ICropperImageProps | | |
|
||||||
|
| combineImageProps | 对比图模式透传 | ComBineImageProps | | |
|
||||||
|
| videoProps | 视频模式透传 | videoProps | | |
|
||||||
|
| showNavigation | 是否展示导航 | boolean | | |
|
||||||
|
| prevButtonProps | 前翻箭头透传 | prevButtonProps | | |
|
||||||
|
| onPrevButtonClick | 前翻箭头点击事件 | () => void; | | |
|
||||||
|
| nextButtonProps | 下翻箭头透传 | () => void; | | |
|
||||||
|
| onNextButtonClick | 下翻箭头点击事件 | () => void; | | |
|
||||||
|
| children | | ReactNode | | |
|
||||||
|
|
||||||
|
### IDescriptionList
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface IDescriptionList {
|
||||||
|
title: string;
|
||||||
|
showTitle?: boolean;
|
||||||
|
props?: DescriptionsProps
|
||||||
|
children: DescriptionsProps['items']
|
||||||
|
}[]
|
||||||
|
```
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface ICropperImageProps extends CropperImageProps {
|
||||||
|
showEditTools?: boolean // 是否展示编辑按钮
|
||||||
|
editAble?: boolean // 是否开启编辑模式
|
||||||
|
score?: string | number // 分数
|
||||||
|
btnGroupProps?: BtnGroupProps; // crop 场景图模式下的按钮拓展
|
||||||
|
cropButtonList?: TypeRadio[] // 编辑按钮列表
|
||||||
|
onCropperTypeChange?: (type: RadioProps['onChange']) => void
|
||||||
|
onExitEdit?: ButtonProps['onClick'] // 退出编辑模式
|
||||||
|
attachImageData?: AttachImageProps['data'] // 左下角预览图
|
||||||
|
attachImageProps?: AttachImageProps // 左下角预览图透传
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 设计方案
|
||||||
|
|
||||||
|
结合当下的业务场景,目前大图组件有三种模式
|
||||||
|
|
||||||
|
1. 场景图模式
|
||||||
|
-
|
||||||
|
2. 对比图模式
|
||||||
|
3. 视频模式
|
||||||
|
|
||||||
|
场景图和视频模式,支持用户编辑圈选
|
||||||
|
|
||||||
|
三种模式状态下,都会有外层模块的嵌套,目前有以下几种:
|
||||||
|
|
||||||
|
1. 描述模块
|
||||||
|
2. 顶部拓展
|
||||||
|
- 目前仅支持自定义
|
||||||
|
- 默认下边间距 16px
|
||||||
|
3. tabs 切换
|
||||||
|
- 默认下间距:16px
|
||||||
|
- 支持自定义文案
|
||||||
|
- 支持自定义数量
|
||||||
|
- 支持后方插槽
|
||||||
|
4. 人脸碰撞模型
|
||||||
|
- 支持用户自定义传入数据
|
||||||
|
|
||||||
|
本来想通过插件的形式按需加载
|
5
packages/biz/src/BigImage/index.tsx
Normal file
5
packages/biz/src/BigImage/index.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import BigImage from "./BigImage";
|
||||||
|
|
||||||
|
export type { BigImageProps, BigImageRef } from './interface'
|
||||||
|
|
||||||
|
export default BigImage
|
84
packages/biz/src/BigImage/interface.ts
Normal file
84
packages/biz/src/BigImage/interface.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { ReactNode } from 'react'
|
||||||
|
import {
|
||||||
|
ButtonProps,
|
||||||
|
RadioProps,
|
||||||
|
RadioGroupProps,
|
||||||
|
VideoViewProps,
|
||||||
|
DescriptionsProps,
|
||||||
|
TabsProps,
|
||||||
|
RelatedImageProps,
|
||||||
|
BtnGroupProps,
|
||||||
|
CropperImageProps,
|
||||||
|
VideoViewRef,
|
||||||
|
AttachImageProps
|
||||||
|
} from '@zhst/meta'
|
||||||
|
import { ComBineImageProps } from './components/CombineImage'
|
||||||
|
|
||||||
|
export type TypeRadio = {
|
||||||
|
label?: string;
|
||||||
|
key: string;
|
||||||
|
tooltipTxt?: string;
|
||||||
|
onClick?: ButtonProps['onClick']
|
||||||
|
icon?: ReactNode | string;
|
||||||
|
props?: ButtonProps
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypePlugin = 'compater' | 'normal' | 'video' | 'faceMode'
|
||||||
|
|
||||||
|
export interface BigImageProps {
|
||||||
|
type?: TypePlugin // 当前 tab
|
||||||
|
viewHeight?: string | number;
|
||||||
|
width?: string | number
|
||||||
|
typePlugins?: TypePlugin[] // TODO: 开启插件类型
|
||||||
|
// ------------ 描述列表 -----------------
|
||||||
|
showDescription?: boolean;
|
||||||
|
descriptionsProps?: DescriptionsProps
|
||||||
|
descriptionList?: {
|
||||||
|
title: string;
|
||||||
|
showTitle?: boolean;
|
||||||
|
props?: DescriptionsProps
|
||||||
|
children: DescriptionsProps['items']
|
||||||
|
}[]
|
||||||
|
// ------------------ 顶部按钮列表
|
||||||
|
topButtonRender?: ReactNode | string
|
||||||
|
// ---------------- Tabs 导航 ------------------
|
||||||
|
customTabBarExtraContent?: string | ReactNode
|
||||||
|
onTabChange?: TabsProps['onChange']
|
||||||
|
tabProps?: TabsProps
|
||||||
|
// ------------ 图片 | 视频切换导航 -----------------
|
||||||
|
showModeChange?: boolean
|
||||||
|
viewTopModeButtonList?: TypeRadio[]
|
||||||
|
modeButtonGroupProps?: RadioGroupProps
|
||||||
|
// ----------------- 人脸碰转 -----------------
|
||||||
|
isRelated?: boolean // 人脸碰撞功能打开
|
||||||
|
relatedImageProps?: RelatedImageProps
|
||||||
|
// -------------------------- crop 场景图模式 -----------------------
|
||||||
|
cropperImageProps?: CropperImageProps & {
|
||||||
|
showEditTools?: boolean
|
||||||
|
editAble?: boolean
|
||||||
|
score?: string | number
|
||||||
|
btnGroupProps?: BtnGroupProps; // crop 场景图模式下的按钮拓展
|
||||||
|
cropButtonList?: TypeRadio[]
|
||||||
|
onCropperTypeChange?: (type: RadioProps['onChange']) => void
|
||||||
|
onExitEdit?: ButtonProps['onClick']
|
||||||
|
attachImageData?: AttachImageProps['data']
|
||||||
|
attachImageProps?: AttachImageProps
|
||||||
|
}
|
||||||
|
// -------------------- 对比图模式 -------------------------
|
||||||
|
combineImageProps?: ComBineImageProps
|
||||||
|
// ------------ 导航 -----------------
|
||||||
|
showNavigation?: boolean // 是否展示导航箭头
|
||||||
|
prevButtonProps?: any;
|
||||||
|
onPrevButtonClick?: () => void;
|
||||||
|
nextButtonProps?: any
|
||||||
|
onNextButtonClick?: () => void;
|
||||||
|
theme?: any
|
||||||
|
children?: React.ReactNode
|
||||||
|
// ------------ 视频模式 -----------------
|
||||||
|
videoProps?: VideoViewProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BigImageRef {
|
||||||
|
videoPlayerRef: VideoViewRef
|
||||||
|
combineImageRef: any
|
||||||
|
}
|
293
packages/biz/src/BigImage/mock.ts
Normal file
293
packages/biz/src/BigImage/mock.ts
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import imageUrl from './demo/imgs/photo-1503185912284-5271ff81b9a8.webp'
|
||||||
|
|
||||||
|
export const IMAGE_DATA = {
|
||||||
|
"enAbleDeleteFeature": true,
|
||||||
|
"tabsFilter": [
|
||||||
|
"COMPATER",
|
||||||
|
"NORMAL"
|
||||||
|
],
|
||||||
|
"selectIndex": 4,
|
||||||
|
"disableBtn": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"dataSource": {
|
||||||
|
"objectId": "1742110565582518272",
|
||||||
|
"condition": {
|
||||||
|
"featureInfo": null,
|
||||||
|
"featureData": "AAAAAAAAAAAAAAAAAAAAAAA==",
|
||||||
|
"imageData": "",
|
||||||
|
"alg": "VERSION_REID_HEAD_ATTR",
|
||||||
|
"rect": {
|
||||||
|
"x": 0.271875,
|
||||||
|
"y": 0.32222223,
|
||||||
|
"w": 0.2859375,
|
||||||
|
"h": 0.67777777
|
||||||
|
},
|
||||||
|
"objectImageUrl": "singer-20240102/1/129529/1742047651878156288.jpg",
|
||||||
|
"srcImageUrl": "singer-20240102/1/129529/1742047652511496192.jpg"
|
||||||
|
},
|
||||||
|
"score": 0.7163062,
|
||||||
|
"timestamp": 1704186491979,
|
||||||
|
"deviceId": "129533",
|
||||||
|
"id": "129533",
|
||||||
|
"name": "4楼门口过道人脸",
|
||||||
|
"dirid": "0",
|
||||||
|
"status": "1",
|
||||||
|
"longitude": 120.125,
|
||||||
|
"latitude": 30.280500411987305,
|
||||||
|
"caseId": "0",
|
||||||
|
"caseGroup": "",
|
||||||
|
"isDeleted": "DEVICEMANAGER_BOOL_DEFAULT",
|
||||||
|
"objectIndex": {
|
||||||
|
"objectId": "1742110565582518272",
|
||||||
|
"solutionId": "1",
|
||||||
|
"deviceId": "129533",
|
||||||
|
"fragmentId": "0"
|
||||||
|
},
|
||||||
|
"objectType": "OBJECT_TYPE_PEDESTRAIN",
|
||||||
|
"isObjectTrack": true,
|
||||||
|
"pathId": "1742110532019697664",
|
||||||
|
"frameInfo": {
|
||||||
|
"frameId": "0",
|
||||||
|
"frameTimestamp": "1704186491979",
|
||||||
|
"width": 0,
|
||||||
|
"height": 0,
|
||||||
|
"originWidth": 1920,
|
||||||
|
"originHeight": 1080,
|
||||||
|
"offsetTime": "24714687",
|
||||||
|
"skipNumber": "0"
|
||||||
|
},
|
||||||
|
"level": 1,
|
||||||
|
"bboxInFrame": {
|
||||||
|
"x": 0.603125,
|
||||||
|
"y": 0.3314815,
|
||||||
|
"w": 0.0578125,
|
||||||
|
"h": 0.2712963
|
||||||
|
},
|
||||||
|
"bboxExtInFrame": {
|
||||||
|
"x": 0.546875,
|
||||||
|
"y": 0.2638889,
|
||||||
|
"w": 0.17135416,
|
||||||
|
"h": 0.40648147
|
||||||
|
},
|
||||||
|
"objectImageKey": "",
|
||||||
|
"objectExtImageKey": "http://10.0.0.7:30003/file/singer-20240102/1/129533/1742110565582518272.jpg",
|
||||||
|
"frameImageKey": "http://10.0.0.7:30003/file/singer-20240102/1/129533/1742110565603489792.jpg",
|
||||||
|
"confidence": 0.817271,
|
||||||
|
"sourceObjectId": "1742110565603489792",
|
||||||
|
"storeTimestamp": "0",
|
||||||
|
"gbNumber": "",
|
||||||
|
"qualityScore": 0,
|
||||||
|
"subObjectCount": 1,
|
||||||
|
"subObjectType": [
|
||||||
|
"OBJECT_TYPE_FACE"
|
||||||
|
],
|
||||||
|
"subObjectIds": [
|
||||||
|
"1742110532015503360"
|
||||||
|
],
|
||||||
|
"solutionId": "1",
|
||||||
|
"fragmentId": "0",
|
||||||
|
"contrastKey": "singer-20240102/1/129533/1742110565582518272.jpg",
|
||||||
|
"compaterImages": [
|
||||||
|
"http://10.0.0.7:30003/file/singer-20240102/1/129529/1742047651878156288.jpg"
|
||||||
|
],
|
||||||
|
"imgSummary": "singer-20240102/1/129533/1742110565582518272.jpg",
|
||||||
|
"imageKey": "http://10.0.0.7:30003/file/singer-20240102/1/129533/1742110565582518272.jpg",
|
||||||
|
"srcImageUrl": "http://10.0.0.7:30003/file/singer-20240102/1/129533/1742110565603489792.jpg",
|
||||||
|
"algorithmVersion": "VERSION_REID_HEAD_ATTR",
|
||||||
|
"cameraId": "129533",
|
||||||
|
"cameraName": "4楼门口过道人脸"
|
||||||
|
},
|
||||||
|
"isArchiveDetail": false,
|
||||||
|
"ToolProps": {
|
||||||
|
"renderVideoBtn": true,
|
||||||
|
"disableVideo": false
|
||||||
|
},
|
||||||
|
"specialTitle": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RELATED_IMAGES = [
|
||||||
|
{ key: '123', url: imageUrl },
|
||||||
|
{ key: '22', url: imageUrl },
|
||||||
|
{ key: '22122333', url: imageUrl },
|
||||||
|
{ key: '2212243', url: imageUrl },
|
||||||
|
{ key: '224523433', url: imageUrl },
|
||||||
|
{ key: '224235453', url: imageUrl },
|
||||||
|
{ key: '245423', url: imageUrl },
|
||||||
|
{ key: '224233543', url: imageUrl },
|
||||||
|
{ key: '22452343', url: imageUrl },
|
||||||
|
{ key: '22323243', url: imageUrl },
|
||||||
|
{ key: '2236456', url: imageUrl },
|
||||||
|
{ key: '224563', url: imageUrl },
|
||||||
|
{ key: '24565423', url: imageUrl },
|
||||||
|
{ key: '245645623', url: imageUrl },
|
||||||
|
{ key: '2456435623', url: imageUrl },
|
||||||
|
{ key: '2323', url: imageUrl }
|
||||||
|
]
|
||||||
|
|
||||||
|
export const BIG_IMAGE_DATA = {
|
||||||
|
imageKey: imageUrl,
|
||||||
|
imgSummary: imageUrl,
|
||||||
|
flvUrl: 'ws://10.0.0.120:9033/flv/HaikangNvr/45.flv?ip=10.0.2.103&stime=1712539148&etime=1712539168',
|
||||||
|
compaterImage: imageUrl,
|
||||||
|
odRect: [
|
||||||
|
{
|
||||||
|
id: '123',
|
||||||
|
"x":0.5445312,
|
||||||
|
"y":0.19166666,
|
||||||
|
"w":0.08671875,
|
||||||
|
"h":0.40138888
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "123",
|
||||||
|
"x": 0.5519352,
|
||||||
|
"y": 0.2965385,
|
||||||
|
"w": 0.05185461,
|
||||||
|
"h": 0.24698898,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "456",
|
||||||
|
"x": 0.58543766,
|
||||||
|
"y": 0.3203356,
|
||||||
|
"w": 0.052037954,
|
||||||
|
"h": 0.2664015
|
||||||
|
}
|
||||||
|
],
|
||||||
|
attachImg: [
|
||||||
|
{
|
||||||
|
"url": imageUrl,
|
||||||
|
"label": "形体"
|
||||||
|
},{
|
||||||
|
"url": imageUrl,
|
||||||
|
"label": "人脸"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
score: 60, // 人脸质量分
|
||||||
|
showScore: true, // 人脸质量分
|
||||||
|
cameraPosition: 'string', // 摄像头位置
|
||||||
|
time: '2022-01-01', // 摄像头拍摄时间
|
||||||
|
objects: [
|
||||||
|
{
|
||||||
|
"objectIndex": {
|
||||||
|
"objectId": "1746832189053474816",
|
||||||
|
"solutionId": "0",
|
||||||
|
"deviceId": "0",
|
||||||
|
"fragmentId": "0"
|
||||||
|
},
|
||||||
|
"objectType": "OBJECT_TYPE_PEDESTRAIN",
|
||||||
|
"sourceObjectId": "0",
|
||||||
|
"level": 0,
|
||||||
|
"confidence": 0.881164,
|
||||||
|
"frameInfo": {
|
||||||
|
"frameId": "0",
|
||||||
|
"frameTimestamp": "1705312223057",
|
||||||
|
"width": 0,
|
||||||
|
"height": 0,
|
||||||
|
"originWidth": 0,
|
||||||
|
"originHeight": 0,
|
||||||
|
"offsetTime": "0",
|
||||||
|
"skipNumber": "0"
|
||||||
|
},
|
||||||
|
"infoOnSource": {
|
||||||
|
"bboxInFrame": {
|
||||||
|
"bboxRatio": {
|
||||||
|
"x": 0.61418945,
|
||||||
|
"y": 0.34309354,
|
||||||
|
"w": 0.067661405,
|
||||||
|
"h": 0.34659258
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"countInSource": 0,
|
||||||
|
"indexInSource": 0
|
||||||
|
},
|
||||||
|
"qualityScore": 0,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const attributeList = [
|
||||||
|
{
|
||||||
|
title: '人员属性',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: '性别',
|
||||||
|
children: '男',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: '年龄',
|
||||||
|
children: '成年',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
label: '帽子',
|
||||||
|
children: '无',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '4',
|
||||||
|
label: '上身颜色',
|
||||||
|
children: '灰',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '5',
|
||||||
|
label: '下身颜色',
|
||||||
|
children: '蓝色',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '6',
|
||||||
|
label: '附着物',
|
||||||
|
children: '无',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '7',
|
||||||
|
label: '骑行',
|
||||||
|
children: '否',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '社区规范',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: '性别',
|
||||||
|
children: '男',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: '年龄',
|
||||||
|
children: '成年',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
label: '帽子',
|
||||||
|
children: '无',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '4',
|
||||||
|
label: '上身颜色',
|
||||||
|
children: '灰',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '5',
|
||||||
|
label: '下身颜色',
|
||||||
|
children: '蓝色',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '6',
|
||||||
|
label: '附着物',
|
||||||
|
children: '无',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '7',
|
||||||
|
label: '骑行',
|
||||||
|
children: '否',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
195
packages/biz/src/BigImage/util/bigImageModalAdapter.tsx
Normal file
195
packages/biz/src/BigImage/util/bigImageModalAdapter.tsx
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
/**
|
||||||
|
* 适配老的大屏组件数据格式传入
|
||||||
|
*/
|
||||||
|
import React, { } from 'react';
|
||||||
|
import { AlgorithmVersionStr, HumanProperty, ObjectType, Rect, IScreenshotButtonProp } from '@zhst/types'
|
||||||
|
import { VideoViewProps, ImgViewProps, VideoViewRef, ImgViewRef } from '@zhst/meta'
|
||||||
|
|
||||||
|
export type TAB_TYPE = 'COMPATER' | 'NORMAL' | 'TRACK';
|
||||||
|
export type MODEL_TYPE = 'VIDEO' | 'IMAGE';
|
||||||
|
|
||||||
|
export interface CarouselProps {
|
||||||
|
hasPre?: boolean;
|
||||||
|
hasNext?: boolean;
|
||||||
|
selectIndex: number;
|
||||||
|
setSelectIndex: React.Dispatch<React.SetStateAction<number>>;
|
||||||
|
dataSource: Array<{
|
||||||
|
key: string;
|
||||||
|
url: string;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ISelectItem = Partial<Omit<ImgViewProps, 'screenshotButtonRender'>> &
|
||||||
|
Partial<Omit<VideoViewProps, 'screenshotButtonRender'>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述列表 description
|
||||||
|
*/
|
||||||
|
export interface HeaderProps {
|
||||||
|
value: TAB_TYPE;
|
||||||
|
onChange: (type: TAB_TYPE) => void;
|
||||||
|
tabsFilter: TAB_TYPE[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ParamProps {
|
||||||
|
tab: string;
|
||||||
|
selectItem: ISelectItem;
|
||||||
|
imgViewRef: React.MutableRefObject<ImgViewRef>;
|
||||||
|
VideoViewRef: React.MutableRefObject<VideoViewRef>;
|
||||||
|
model: MODEL_TYPE;
|
||||||
|
setModel: React.Dispatch<React.SetStateAction<MODEL_TYPE>>;
|
||||||
|
/* 可观测值 */
|
||||||
|
scale$: number;
|
||||||
|
showCrop$: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具栏
|
||||||
|
*/
|
||||||
|
export interface ToolProps {
|
||||||
|
renderRight?: (props: ParamProps) => React.ReactNode;
|
||||||
|
renderLeft?: (props: ParamProps) => React.ReactNode;
|
||||||
|
renderVideoBtn?: boolean;
|
||||||
|
param: ParamProps;
|
||||||
|
disableVideo: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface BigImageData {
|
||||||
|
//imageKey 小图
|
||||||
|
extendRectList: (Rect & { algorithmVersion: AlgorithmVersionStr; imageKey: string })[];
|
||||||
|
rectList: (Rect & { algorithmVersion: AlgorithmVersionStr; imageKey: string })[];
|
||||||
|
attachImg: { url: string; label: '形体' | '人脸' }[];
|
||||||
|
odRect: Rect;
|
||||||
|
compaterImages: string[] // 目标图列表
|
||||||
|
constractKey: string; // 当前比较中的目标图
|
||||||
|
frameImageKey: string; // 场景图
|
||||||
|
imageKey?: string; // 大图
|
||||||
|
imgSummary: string; // 摘要图
|
||||||
|
objectExtImageKey: string; //比对到的目标图扩展图 === imgSummary
|
||||||
|
|
||||||
|
attributeList: { label: string; list: any[] }[];
|
||||||
|
archiveImages?: any;
|
||||||
|
spaceName: string;
|
||||||
|
|
||||||
|
objectIndex?: {
|
||||||
|
deviceId: string;
|
||||||
|
fragmentId: string;
|
||||||
|
objectId: string;
|
||||||
|
solutionId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
objectType: ObjectType;
|
||||||
|
objectId: string; //这张摘要本身的Id
|
||||||
|
bodyObjectId?: string;
|
||||||
|
faceObjectId?: string; //这张摘要下的人脸Id(如果有)
|
||||||
|
sourceObjectId?: string; //这张摘要上游的形体Id(如果有)
|
||||||
|
cameraId: string;
|
||||||
|
cameraName: string;
|
||||||
|
selectIndex: number;
|
||||||
|
humanProperty: HumanProperty;
|
||||||
|
qualityScore?: number; //人脸质量分
|
||||||
|
score: number; // 相似度
|
||||||
|
timestamp: string;
|
||||||
|
bodyImageUrl: string;
|
||||||
|
faceImageUrl: string;
|
||||||
|
algorithmVersion: AlgorithmVersionStr;
|
||||||
|
|
||||||
|
bodySpaceName: string;
|
||||||
|
faceSpaceName: string;
|
||||||
|
position: {
|
||||||
|
lat: number
|
||||||
|
lng: number
|
||||||
|
}
|
||||||
|
|
||||||
|
solutionId?: string;
|
||||||
|
[index: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface IOldImageData {
|
||||||
|
visible?: boolean; // 显示隐藏弹框
|
||||||
|
defaultModel?: MODEL_TYPE; // 视频模式 | 图片模式
|
||||||
|
onClose?: () => void; // 关闭弹框
|
||||||
|
isLoading?: boolean; // 是否加载中
|
||||||
|
hasPre?: boolean; // 向前翻页
|
||||||
|
hasNext?: boolean; // 向后翻页
|
||||||
|
selectIndex?: number; // 选中的数据(dataSource为数组情况下)
|
||||||
|
onSelectIndexChange?: (i: number) => void; // 修改当前下标
|
||||||
|
dataSource: BigImageData[]; // 数据1
|
||||||
|
dataSources: BigImageData[]; // 数据2
|
||||||
|
relatedData?: BigImageData[]; // 数据3
|
||||||
|
transformPropFunc: (item: any) => ISelectItem; // 格式化数据方法
|
||||||
|
transformVideoPropFunc: (
|
||||||
|
item: ISelectItem
|
||||||
|
) => Promise<Omit<VideoViewProps, 'screenshotButtonRender'>>; // 视频模式格式化数据方法
|
||||||
|
screenshotButtonRender?: (screenshotButtonProp: IScreenshotButtonProp) => React.ReactElement;
|
||||||
|
showTool?: boolean; // 是否显示底部菜单
|
||||||
|
showCarousel?: boolean; //
|
||||||
|
imgViewProp?: Partial<ImgViewProps>; // 图片模式的配置
|
||||||
|
videoViewProp?: Partial<VideoViewProps>; // 视频模式的配置
|
||||||
|
ToolProps?: Partial<ToolProps>; // 底部菜单的配置
|
||||||
|
nullDialogProp?: {
|
||||||
|
emptyText?: string;
|
||||||
|
}; // 暂无数据的配置
|
||||||
|
showHeader?: boolean; // 是否显示 description
|
||||||
|
tabsFilter?: TAB_TYPE[]; // tabs 过滤
|
||||||
|
useVirtual?: boolean; // 是否显示虚拟
|
||||||
|
loadNext?: (i: number) => Promise<void>; // 下一个
|
||||||
|
loadPre?: (i: number) => Promise<void>; // 前一个
|
||||||
|
children: React.ReactNode; // 子元素
|
||||||
|
title?: string; // 标题
|
||||||
|
specialTitle?: string; // 对比图模式下标题
|
||||||
|
isRelated?: boolean;
|
||||||
|
carouselProp?: Partial<CarouselProps>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface ImageModalDataProps {
|
||||||
|
targetData: BigImageData[]
|
||||||
|
compactData: BigImageData[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModalAdapterConfigProps {
|
||||||
|
oldMode?: boolean; // 是否是老模式
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 兼容老数据格式
|
||||||
|
* @param _data 老数据格式
|
||||||
|
* @returns newData
|
||||||
|
*/
|
||||||
|
const translateOldImageData = (_data: IOldImageData) => {
|
||||||
|
return {
|
||||||
|
..._data,
|
||||||
|
open: _data.visible,
|
||||||
|
onCancel: _data.onClose
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 大图组件适配器,兼容老接口
|
||||||
|
* @param Cmp 大图组件
|
||||||
|
* @param config 额外配置
|
||||||
|
* @returns 大图组件
|
||||||
|
*/
|
||||||
|
const adapter = (Cmp: any, config: ModalAdapterConfigProps): any => {
|
||||||
|
const { oldMode = false } = config
|
||||||
|
|
||||||
|
return (props: IOldImageData) => {
|
||||||
|
const newProps = oldMode ? translateOldImageData(props) : props
|
||||||
|
console.log('adapter----适配数据', props, newProps)
|
||||||
|
|
||||||
|
// 该属性已经废弃
|
||||||
|
delete newProps.visible
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Cmp
|
||||||
|
{...newProps}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default adapter
|
36
packages/biz/src/BigImage/util/interface.ts
Normal file
36
packages/biz/src/BigImage/util/interface.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export interface IBigImageModalData {
|
||||||
|
imageKey?: string // 目标图
|
||||||
|
imgSummary?: string // 大图
|
||||||
|
flvUrl?: string // 视频链接
|
||||||
|
compaterImages?: string[] // 对比图
|
||||||
|
odRect?: { // od 框数据
|
||||||
|
"x": number
|
||||||
|
"y": number
|
||||||
|
"w": number
|
||||||
|
"h": number
|
||||||
|
[key: string]: string | number; // 拓展参数
|
||||||
|
}[],
|
||||||
|
attachImg?: { // 小图,只有在场景图模式生效(人脸、形体)
|
||||||
|
"url": string
|
||||||
|
"label": string
|
||||||
|
[key: string]: string
|
||||||
|
}[],
|
||||||
|
score?: number | string // 人脸质量分
|
||||||
|
showScore?: boolean // 人脸质量分
|
||||||
|
cameraPosition?: string // 摄像头位置
|
||||||
|
time?: string // 摄像头拍摄时间
|
||||||
|
objects: { // 拓展参数、可以自由支配
|
||||||
|
objectIndex: {
|
||||||
|
[key: string]: any
|
||||||
|
},
|
||||||
|
objectType: string
|
||||||
|
sourceObjectId: string
|
||||||
|
level: number
|
||||||
|
confidence: number
|
||||||
|
infoOnSource: {
|
||||||
|
[key: string]: any
|
||||||
|
},
|
||||||
|
qualityScore: number
|
||||||
|
[key: string]: any
|
||||||
|
}[]
|
||||||
|
}
|
@ -2,14 +2,14 @@
|
|||||||
* Created by jiangzhixiong on 2024/04/28
|
* Created by jiangzhixiong on 2024/04/28
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { forwardRef, useContext, useImperativeHandle } from 'react'
|
import React, { forwardRef, ReactNode, useContext, useImperativeHandle } from 'react'
|
||||||
import { ConfigProvider, EMPTY_BASE64 } from '@zhst/meta'
|
import { ConfigProvider, EMPTY_BASE64 } from '@zhst/meta'
|
||||||
import { Flex, Image } from 'antd';
|
import { Image } from 'antd';
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
|
||||||
const { ConfigContext } = ConfigProvider
|
const { ConfigContext } = ConfigProvider
|
||||||
|
|
||||||
export interface Idata {
|
export interface IData {
|
||||||
id?: string | number;
|
id?: string | number;
|
||||||
url?: string;
|
url?: string;
|
||||||
sort?: number;
|
sort?: number;
|
||||||
@ -17,24 +17,25 @@ export interface Idata {
|
|||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchCardProps extends Idata {
|
export interface CommonCardProps extends IData {
|
||||||
prefixCls?: string;
|
prefixCls?: string;
|
||||||
data?: Idata
|
data?: IData
|
||||||
width?: string;
|
width?: string;
|
||||||
height?: string;
|
height?: string;
|
||||||
onCreateTxt?: string;
|
onCreateTxt?: string;
|
||||||
onCreate?: (data: any) => void;
|
onCreate?: () => void;
|
||||||
onAddTxt?: string;
|
onAddTxt?: string;
|
||||||
onAdd?: (data: any) => void;
|
onAdd?: (data: any) => void;
|
||||||
onRemoveTxt?: string;
|
onRemoveTxt?: string;
|
||||||
onRemove?: (data: any) => void;
|
onRemove?: (data: any) => void;
|
||||||
customOptionRender?: React.ReactNode
|
actions?: ReactNode[]
|
||||||
|
onItemClick?: (data: any, e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchCardRefProps {
|
export interface CommonCardRefProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchCard = forwardRef<SearchCardRefProps, SearchCardProps>((props, ref) => {
|
const CommonCard = forwardRef<CommonCardRefProps, CommonCardProps>((props, ref) => {
|
||||||
const {
|
const {
|
||||||
prefixCls: customizePrefixCls,
|
prefixCls: customizePrefixCls,
|
||||||
url,
|
url,
|
||||||
@ -43,26 +44,29 @@ const SearchCard = forwardRef<SearchCardRefProps, SearchCardProps>((props, ref)
|
|||||||
subtitle,
|
subtitle,
|
||||||
sort,
|
sort,
|
||||||
data,
|
data,
|
||||||
onCreate,
|
actions = [],
|
||||||
onCreateTxt = '创建检索',
|
|
||||||
onAddTxt = '添加目标',
|
|
||||||
onRemoveTxt = '移除轨迹',
|
|
||||||
onAdd,
|
|
||||||
onRemove,
|
|
||||||
customOptionRender,
|
|
||||||
width = '184px',
|
width = '184px',
|
||||||
height = '100%'
|
height = '100%',
|
||||||
|
onItemClick
|
||||||
} = props
|
} = props
|
||||||
const { getPrefixCls } = useContext(ConfigContext)
|
const { getPrefixCls } = useContext(ConfigContext)
|
||||||
const componentName = getPrefixCls('biz-search-card', customizePrefixCls);
|
const componentName = getPrefixCls('biz-search-card', customizePrefixCls);
|
||||||
|
|
||||||
const stopBumble = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, fn?: ((data: Idata) => void), data?: Idata) => {
|
const optionListRender = (_actions: ReactNode[]) => {
|
||||||
e.stopPropagation()
|
return _actions.map((action, i) => (
|
||||||
fn?.(data!)
|
// eslint-disable-next-line react/no-array-index-key
|
||||||
|
<li key={`${componentName}-main-opt-action-${i}`}>
|
||||||
|
{action}
|
||||||
|
{i !== _actions.length - 1 && <em className={`${componentName}-main-opt-action-split`} />}
|
||||||
|
</li>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleItemClick = (e: React.MouseEvent<HTMLDivElement, MouseEvent>, _data: IData | undefined) => {
|
||||||
|
onItemClick?.(data, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -72,9 +76,10 @@ const SearchCard = forwardRef<SearchCardRefProps, SearchCardProps>((props, ref)
|
|||||||
width,
|
width,
|
||||||
height
|
height
|
||||||
}}
|
}}
|
||||||
|
onClick={e => handleItemClick(e, data)}
|
||||||
>
|
>
|
||||||
<div className={`${componentName}-main`}>
|
<div className={`${componentName}-main`}>
|
||||||
<i className={`${componentName}-main-num`}>{id || sort}</i>
|
<i className={`${componentName}-main-num`}>{sort || id}</i>
|
||||||
<Image
|
<Image
|
||||||
className={`${componentName}-main-img`}
|
className={`${componentName}-main-img`}
|
||||||
src={url || data?.url}
|
src={url || data?.url}
|
||||||
@ -82,17 +87,9 @@ const SearchCard = forwardRef<SearchCardRefProps, SearchCardProps>((props, ref)
|
|||||||
preview={false}
|
preview={false}
|
||||||
fallback={EMPTY_BASE64}
|
fallback={EMPTY_BASE64}
|
||||||
/>
|
/>
|
||||||
<Flex align='center' justify='space-between' className={`${componentName}-main-opt`}>
|
<ul className={`${componentName}-main-opt`}>
|
||||||
{customOptionRender || (
|
{optionListRender(actions)}
|
||||||
<>
|
</ul>
|
||||||
<a onClick={(e) => stopBumble(e, onCreate, data)}>{onCreateTxt}</a>
|
|
||||||
|
|
|
||||||
<a onClick={(e) => stopBumble(e, onAdd, data)}>{onAddTxt}</a>
|
|
||||||
|
|
|
||||||
<a onClick={(e) => stopBumble(e, onRemove, data)}>{onRemoveTxt}</a>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={`${componentName}-footer`}>
|
<div className={`${componentName}-footer`}>
|
||||||
<p className={`${componentName}-footer-tit`}>{title || data?.title}</p>
|
<p className={`${componentName}-footer-tit`}>{title || data?.title}</p>
|
||||||
@ -102,4 +99,4 @@ const SearchCard = forwardRef<SearchCardRefProps, SearchCardProps>((props, ref)
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export default SearchCard
|
export default CommonCard
|
@ -1,18 +1,23 @@
|
|||||||
.zhst-biz-search-card {
|
.zhst-biz-search-card {
|
||||||
|
display: inline-block;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
transition: .1s ease-in all;
|
transition: .1s ease-in all;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
transition: .5s ease all;
|
||||||
border: 2px solid #09f;
|
border: 2px solid #09f;
|
||||||
|
|
||||||
.zhst-biz-search-card-main-opt {
|
.zhst-biz-search-card-main-opt {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-main {
|
&-main {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&-num {
|
&-num {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -32,11 +37,17 @@
|
|||||||
|
|
||||||
&-img {
|
&-img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 240px;
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transition: .5s ease-in all;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-opt {
|
&-opt {
|
||||||
display: none;
|
display: none;
|
||||||
|
margin: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding: 6px 3px;
|
padding: 6px 3px;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -48,11 +59,28 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
transition: .2s ease-in all;
|
transition: .2s ease-in all;
|
||||||
|
|
||||||
|
li {
|
||||||
|
position: relative;
|
||||||
|
list-style: none;
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-action-split {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 0;
|
||||||
|
width: 1px;
|
||||||
|
height: 80%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 0.9;
|
opacity: 0.88;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import CommonCard from './CommonCard'
|
||||||
|
|
||||||
|
export type { CommonCardProps, CommonCardRefProps } from './CommonCard'
|
||||||
|
|
||||||
|
export default CommonCard
|
42
packages/biz/src/CustomCard/demo/commonCard.tsx
Normal file
42
packages/biz/src/CustomCard/demo/commonCard.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { CommonCard } from '@zhst/biz'
|
||||||
|
import { uniqueId } from '@zhst/func'
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const [data, setData] = useState([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo')
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((body) => {
|
||||||
|
let res = body.results.map((o, index) => {
|
||||||
|
return {
|
||||||
|
id: uniqueId(),
|
||||||
|
sort: index + 1,
|
||||||
|
title: o.name.first,
|
||||||
|
subtitle: o.name.last,
|
||||||
|
url: o.picture.large
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setData(res);
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{data?.map(item => (
|
||||||
|
<CommonCard
|
||||||
|
key={item.id}
|
||||||
|
sort={item.sort}
|
||||||
|
data={item}
|
||||||
|
width="184px"
|
||||||
|
actions={[
|
||||||
|
<a>创建检索</a>,
|
||||||
|
<a>创建布控</a>,
|
||||||
|
<a>删除点位</a>
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
31
packages/biz/src/CustomCard/index.md
Normal file
31
packages/biz/src/CustomCard/index.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
category: Components
|
||||||
|
title: CustomCard 定制化卡片
|
||||||
|
toc: content
|
||||||
|
group:
|
||||||
|
title: 数据展示
|
||||||
|
---
|
||||||
|
|
||||||
|
定制化卡片
|
||||||
|
|
||||||
|
## 代码演示
|
||||||
|
|
||||||
|
<code src="./demo/commonCard.tsx">基本卡片</code>
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### CommonCardProps
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| data | 数据源 | IData[] | [] | - |
|
||||||
|
| prefixCls | 数据源 | string | [] | - |
|
||||||
|
| width | 宽度 | string | [] | - |
|
||||||
|
| height | 高度 | string | [] | - |
|
||||||
|
| onCreateTxt | 创建方法文字 | string | [] | - |
|
||||||
|
| onCreate | 创建方法 | () => void | [] | - |
|
||||||
|
| onAddTxt | 数据源 | string | [] | - |
|
||||||
|
| onAdd | 数据源 | () => void | [] | - |
|
||||||
|
| onRemoveTxt | 数据源 | string | [] | - |
|
||||||
|
| onRemove | 数据源 | () => void | [] | - |
|
||||||
|
| customOptionRender | 数据源 | React.ReactNode | - | - |
|
6
packages/biz/src/CustomCard/index.tsx
Normal file
6
packages/biz/src/CustomCard/index.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Created by jiangzhixiong on 2024/04/28
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { default as CommonCard } from './components/commonCard'
|
||||||
|
export type { CommonCardProps, CommonCardRefProps } from './components/commonCard'
|
@ -1,12 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import { Button } from '@zhst/meta'
|
|
||||||
import { useThrottleFn } from '@zhst/hooks'
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
|
|
||||||
const { run } = useThrottleFn(() => console.log('123'))
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button onClick={() => run()} >测试</Button>
|
|
||||||
)
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ import { IRecord } from '../../../WarningRecordCard';
|
|||||||
import { ViewLargerImageModalRef } from '../../../ViewLargerImageModal';
|
import { ViewLargerImageModalRef } from '../../../ViewLargerImageModal';
|
||||||
import WarningRecordCard from '../../../WarningRecordCard';
|
import WarningRecordCard from '../../../WarningRecordCard';
|
||||||
import ViewLargerImageModal from '../../../ViewLargerImageModal';
|
import ViewLargerImageModal from '../../../ViewLargerImageModal';
|
||||||
import { Empty, Space, Spin } from 'antd';
|
import { Empty, Space, Spin } from '@zhst/meta';
|
||||||
import { pxToRem } from '@zhst/func'
|
import { pxToRem } from '@zhst/func'
|
||||||
import { LoadingOutlined } from '@ant-design/icons';
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import VideoPlayerCard from '../../../VideoPlayerCard';
|
import VideoPlayerCard from '../../../VideoPlayerCard';
|
||||||
import { VideoPlayerCardProps } from '../../../VideoPlayerCard';
|
import { VideoPlayerCardProps } from '../../../VideoPlayerCard';
|
||||||
import { Row, Col, Segmented, theme } from 'antd';
|
import { Row, Col, Segmented, theme } from '@zhst/meta';
|
||||||
import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
|
import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
|
||||||
import { pxToRem } from '@zhst/func'
|
import { pxToRem } from '@zhst/func'
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useImperativeHandle, useRef, useState, forwardRef, useContext } from 'react';
|
import React, { useImperativeHandle, useRef, useState, forwardRef, useContext } from 'react';
|
||||||
import { Modal, ModalProps, Space, SpaceProps, theme } from 'antd';
|
import { Modal, ModalProps, Space, SpaceProps, theme } from '@zhst/meta';
|
||||||
import { DownloadOutlined } from '@ant-design/icons';
|
import { DownloadOutlined } from '@ant-design/icons';
|
||||||
import { ConfigProvider, CropperImage } from '@zhst/meta';
|
import { ConfigProvider, CropperImage } from '@zhst/meta';
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Card, Space, Divider, CardProps, theme } from 'antd';
|
import { Card, Space, Divider, CardProps, theme } from '@zhst/meta';
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { ConfigProvider, CropperImage } from '@zhst/meta';
|
import { ConfigProvider, CropperImage } from '@zhst/meta';
|
||||||
|
@ -1,39 +1,26 @@
|
|||||||
import React, { FC, useContext } from 'react';
|
import React, { FC, ReactNode, useContext } from 'react';
|
||||||
import { Tabs, TabsProps } from 'antd'
|
import { ConfigProvider, Tabs, TabsProps } from '@zhst/meta';
|
||||||
import { ConfigProvider } from '@zhst/meta';
|
|
||||||
import BoxPanel from './components/boxPanel';
|
import BoxPanel from './components/boxPanel';
|
||||||
import type { BoxPanelProps } from './components/boxPanel';
|
import type { BoxPanelProps } from './components/boxPanel';
|
||||||
import './index.less'
|
import './index.less'
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
export interface BoxSelectTreeProps extends BoxPanelProps {
|
export interface BoxSelectTreeProps extends BoxPanelProps {
|
||||||
onTabChange?: (e: any) => void
|
onTabChange?: (e: any) => void
|
||||||
tabsProps?: TabsProps
|
tabsProps?: TabsProps
|
||||||
prefixCls?: string;
|
prefixCls?: string;
|
||||||
|
footer?: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
const { ConfigContext } = ConfigProvider
|
const { ConfigContext } = ConfigProvider
|
||||||
|
|
||||||
const BoxSelectTree: FC<BoxSelectTreeProps> = (props) => {
|
const BoxSelectTree: FC<BoxSelectTreeProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
data,
|
prefixCls: customizePrefixCls,
|
||||||
boxDataSource = [],
|
|
||||||
onTabChange,
|
|
||||||
onSearch,
|
|
||||||
onItemCheck,
|
|
||||||
onItemSelect,
|
|
||||||
onBoxBatchDelete,
|
|
||||||
onBoxDelete,
|
|
||||||
onCreateSubmit,
|
|
||||||
onClockClick,
|
|
||||||
onImport,
|
|
||||||
onCreate,
|
|
||||||
tabsProps,
|
tabsProps,
|
||||||
searchInputProps,
|
onTabChange,
|
||||||
treeProps,
|
footer,
|
||||||
customImport,
|
...rest
|
||||||
showOptions,
|
|
||||||
extraBtns,
|
|
||||||
prefixCls: customizePrefixCls
|
|
||||||
} = props
|
} = props
|
||||||
const { getPrefixCls } = useContext(ConfigContext);
|
const { getPrefixCls } = useContext(ConfigContext);
|
||||||
const componentName = getPrefixCls('biz-box-select-tree', customizePrefixCls);
|
const componentName = getPrefixCls('biz-box-select-tree', customizePrefixCls);
|
||||||
@ -57,27 +44,14 @@ const BoxSelectTree: FC<BoxSelectTreeProps> = (props) => {
|
|||||||
items={items}
|
items={items}
|
||||||
onChange={onTabChange}
|
onChange={onTabChange}
|
||||||
tabBarGutter={0}
|
tabBarGutter={0}
|
||||||
|
// @ts-ignore
|
||||||
indicator={{ size: (origin) => origin, align: 'center' }}
|
indicator={{ size: (origin) => origin, align: 'center' }}
|
||||||
{...tabsProps}
|
{...tabsProps}
|
||||||
/>
|
/>
|
||||||
<BoxPanel
|
<BoxPanel
|
||||||
searchInputProps={searchInputProps}
|
{...rest}
|
||||||
boxDataSource={boxDataSource}
|
|
||||||
treeProps={treeProps}
|
|
||||||
data={data}
|
|
||||||
onCreate={onCreate}
|
|
||||||
onCreateSubmit={onCreateSubmit}
|
|
||||||
onBoxBatchDelete={onBoxBatchDelete}
|
|
||||||
onBoxDelete={onBoxDelete}
|
|
||||||
onSearch={onSearch}
|
|
||||||
onItemCheck={onItemCheck}
|
|
||||||
onItemSelect={onItemSelect}
|
|
||||||
showOptions={showOptions}
|
|
||||||
customImport={customImport}
|
|
||||||
extraBtns={extraBtns}
|
|
||||||
onClockClick={onClockClick}
|
|
||||||
onImport={onImport}
|
|
||||||
/>
|
/>
|
||||||
|
{footer}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
.zhst-biz-box-select-tree-panel {
|
.zhst-biz-box-select-tree-panel {
|
||||||
&-search {
|
&-search {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
|
|
||||||
&-input {
|
&-input {
|
||||||
|
flex: 1;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,6 +20,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
&-common {
|
&-common {
|
||||||
|
flex: 1;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +33,60 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-tags {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 0;
|
||||||
|
border-top: 1px solid #09f;
|
||||||
|
border-bottom: 1px solid #09f;
|
||||||
|
|
||||||
|
&-tag {
|
||||||
|
margin-right: 6px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
transition:.3s ease all;
|
||||||
|
|
||||||
|
&_common{
|
||||||
|
margin-bottom: 6px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 6px;
|
||||||
|
padding: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
transition: .3s ease all;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-sizing: content-box;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #09f;
|
||||||
|
background-color: rgba(0, 0, 0, 6%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_checked {
|
||||||
|
color: #09f;
|
||||||
|
background-color: #edf8ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_fz12 {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_option {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_absolute {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-tree {
|
&-tree {
|
||||||
padding: 6px 0;
|
padding: 6px 0;
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,165 @@
|
|||||||
import React, { FC, useState, useRef, useContext } from 'react';
|
import React, { FC, useState, useContext, ReactNode } from 'react';
|
||||||
import{ Button, Divider, Input, Space, TreeDataNode } from 'antd'
|
import { ModalFormProps } from '@ant-design/pro-components'
|
||||||
import { ModalForm, ModalFormProps, ProFormInstance, ProFormText } from '@ant-design/pro-components'
|
import {
|
||||||
import { ConfigProvider } from '@zhst/meta';
|
ButtonProps,
|
||||||
|
ConfigProvider,
|
||||||
|
Input,
|
||||||
|
Dropdown,
|
||||||
|
Tooltip,
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
DataNode as TreeDataNode,
|
||||||
|
Tree as BoxTree,
|
||||||
|
TreeProps as BoxTreeProps,
|
||||||
|
TreeProps,
|
||||||
|
InputProps,
|
||||||
|
DropDownProps
|
||||||
|
} from '@zhst/meta';
|
||||||
|
import { IconFont } from '@zhst/icon';
|
||||||
import { ClockCircleOutlined, CloseCircleOutlined, DiffOutlined, FolderAddOutlined, ImportOutlined, SwitcherOutlined } from '@ant-design/icons'
|
import { ClockCircleOutlined, CloseCircleOutlined, DiffOutlined, FolderAddOutlined, ImportOutlined, SwitcherOutlined } from '@ant-design/icons'
|
||||||
import type { TreeProps, InputProps } from 'antd';
|
|
||||||
import type { BoxTreeProps } from '../../../tree';
|
|
||||||
import TreeTransferModal from '../../../treeTransferModal'
|
|
||||||
import BoxTree from '../../../tree';
|
|
||||||
import './index.less'
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import './index.less'
|
||||||
|
|
||||||
|
interface IOption {
|
||||||
|
label: string
|
||||||
|
key: string
|
||||||
|
icon?: string | ReactNode
|
||||||
|
type?: ButtonProps['type'] | 'dropdown'
|
||||||
|
disabled?: boolean;
|
||||||
|
showTooltip?: boolean;
|
||||||
|
props?: ButtonProps
|
||||||
|
onClick?: () => void
|
||||||
|
dropdownConfig?: DropDownProps
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ITag {
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
icon?: ReactNode
|
||||||
|
parentNode?: string
|
||||||
|
children?: ITag[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface BoxPanelProps {
|
export interface BoxPanelProps {
|
||||||
searchInputProps?: InputProps
|
searchInputProps?: InputProps
|
||||||
showOptions?: boolean
|
showOptions?: boolean
|
||||||
treeProps?: Partial<BoxTreeProps>
|
treeProps?: Partial<BoxTreeProps>
|
||||||
data: TreeDataNode[]
|
data: TreeDataNode[]
|
||||||
boxDataSource: TreeDataNode[]
|
|
||||||
handleImport?: () => void
|
|
||||||
onSearch?: (e: any) => void
|
onSearch?: (e: any) => void
|
||||||
onItemCheck?: TreeProps['onCheck']
|
onItemCheck?: TreeProps['onCheck']
|
||||||
onItemSelect?: TreeProps['onSelect']
|
onItemSelect?: TreeProps['onSelect']
|
||||||
|
/**
|
||||||
|
* @deprecated 将于下个版本 0.23.0 以后弃用
|
||||||
|
*/
|
||||||
onBoxBatchDelete?: (data?: any) => void
|
onBoxBatchDelete?: (data?: any) => void
|
||||||
onBoxDelete?: (data?: any) => void
|
/**
|
||||||
|
* @deprecated 将于下个版本 0.23.0 以后弃用
|
||||||
|
*/
|
||||||
onCreateSubmit?: ModalFormProps['onFinish']
|
onCreateSubmit?: ModalFormProps['onFinish']
|
||||||
onClockClick?: () => void
|
/**
|
||||||
|
* @deprecated 将于下个版本 0.23.0 以后弃用
|
||||||
|
*/
|
||||||
|
onClockClick?: () => void //
|
||||||
|
/**
|
||||||
|
* @deprecated 将于下个版本 0.23.0 以后弃用
|
||||||
|
*/
|
||||||
onImport?: () => void
|
onImport?: () => void
|
||||||
|
/**
|
||||||
|
* @deprecated 将于下个版本 0.23.0 以后弃用
|
||||||
|
*/
|
||||||
onBatch?: () => void
|
onBatch?: () => void
|
||||||
|
/**
|
||||||
|
* @deprecated 将于下个版本 0.23.0 以后弃用
|
||||||
|
*/
|
||||||
onCreate?: () => void
|
onCreate?: () => void
|
||||||
customImport?: any
|
customImport?: ReactNode | string // 自定义搜索栏边上的过滤图标
|
||||||
extraBtns?: any
|
extraBtns?: ReactNode | string // 搜索栏下面的插槽
|
||||||
prefixCls?: string;
|
prefixCls?: string
|
||||||
|
showSearchBar?: boolean // 是否显示搜索栏
|
||||||
|
noFilter?: boolean // 是否显示搜索拓展 icon
|
||||||
|
filterList?: IOption[]
|
||||||
|
optionList?: IOption[]
|
||||||
|
showTagPanel?: boolean // 标签插槽
|
||||||
|
tagList?: ITag[] // 标签列表
|
||||||
|
tagExpandAll?: boolean // 展开所有
|
||||||
|
onTagCheck?: (value: string, tag: ITag) => void;
|
||||||
|
checkedTags?: string[]
|
||||||
|
onResetTags?: () => void
|
||||||
|
onTagExpand?: (e: any) => void
|
||||||
|
tagFootRender?: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
const { ConfigContext } = ConfigProvider
|
const { ConfigContext } = ConfigProvider
|
||||||
|
|
||||||
const BoxPanel: FC<BoxPanelProps> = (props) => {
|
const BoxPanel: FC<BoxPanelProps> = (props) => {
|
||||||
|
const [isTreeCheckable, setIsTreeCheckable] = useState(false)
|
||||||
const {
|
const {
|
||||||
searchInputProps,
|
searchInputProps,
|
||||||
showOptions = true,
|
showOptions = true,
|
||||||
extraBtns,
|
extraBtns,
|
||||||
|
noFilter,
|
||||||
data = [],
|
data = [],
|
||||||
onSearch,
|
onSearch,
|
||||||
treeProps,
|
treeProps,
|
||||||
onItemCheck,
|
onItemCheck,
|
||||||
onItemSelect,
|
onItemSelect,
|
||||||
onCreateSubmit,
|
|
||||||
onBoxBatchDelete,
|
onBoxBatchDelete,
|
||||||
onBoxDelete,
|
|
||||||
onClockClick,
|
onClockClick,
|
||||||
onImport,
|
onImport,
|
||||||
onBatch,
|
onBatch,
|
||||||
onCreate,
|
onCreate,
|
||||||
boxDataSource,
|
showSearchBar = true,
|
||||||
|
optionList = [
|
||||||
|
{
|
||||||
|
label: '导入盒子',
|
||||||
|
key: 'import',
|
||||||
|
icon: <ImportOutlined />,
|
||||||
|
onClick: () => onImport?.()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '新建组',
|
||||||
|
key: 'add',
|
||||||
|
icon: <FolderAddOutlined />,
|
||||||
|
onClick: () => onCreate?.()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
key: 'del',
|
||||||
|
icon: <CloseCircleOutlined />,
|
||||||
|
onClick: () => onBoxBatchDelete?.(),
|
||||||
|
props: {
|
||||||
|
danger: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
filterList = [
|
||||||
|
{
|
||||||
|
label: '多选',
|
||||||
|
key: 'multi',
|
||||||
|
icon: isTreeCheckable ? <SwitcherOutlined /> : <DiffOutlined />,
|
||||||
|
onClick: () => onBatch?.() || handleCheckable()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '时钟',
|
||||||
|
key: 'clock',
|
||||||
|
icon: <ClockCircleOutlined />,
|
||||||
|
onClick: () => onClockClick?.()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
showTagPanel,
|
||||||
|
tagList,
|
||||||
|
tagExpandAll,
|
||||||
|
onTagExpand,
|
||||||
|
checkedTags = [],
|
||||||
|
onTagCheck,
|
||||||
|
onResetTags,
|
||||||
|
tagFootRender,
|
||||||
prefixCls: customizePrefixCls,
|
prefixCls: customizePrefixCls,
|
||||||
customImport
|
customImport: customFilter
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const { getPrefixCls } = useContext(ConfigContext);
|
const { getPrefixCls } = useContext(ConfigContext);
|
||||||
const componentName = getPrefixCls('biz-box-select-tree-panel', customizePrefixCls);
|
const componentName = getPrefixCls('biz-box-select-tree-panel', customizePrefixCls);
|
||||||
const [isTreeCheckable, setIsTreeCheckable] = useState(false)
|
|
||||||
const [targetItems, setTargetItems] = useState<TreeDataNode[]>([]);
|
|
||||||
const [boxChoiceOpen, setBoxChoiceOpen] = useState(false)
|
|
||||||
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
|
|
||||||
const createFormRef = useRef<
|
|
||||||
ProFormInstance<{
|
|
||||||
name: string;
|
|
||||||
boxList?: any[];
|
|
||||||
}>
|
|
||||||
>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改选择状态
|
* 修改选择状态
|
||||||
@ -77,158 +169,161 @@ const BoxPanel: FC<BoxPanelProps> = (props) => {
|
|||||||
setIsTreeCheckable(pre => !pre)
|
setIsTreeCheckable(pre => !pre)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onTreeCheck: TreeProps['onCheck'] = (keys: any, info) => {
|
/**
|
||||||
let _targetItems: TreeDataNode[] = []
|
* 初始化拓展 filter
|
||||||
setCheckedKeys(keys)
|
* @param _list
|
||||||
info.checkedNodes.forEach(o => {
|
* @returns
|
||||||
o.isLeaf && _targetItems.push(o)
|
*/
|
||||||
})
|
const initFilter = (_list?: BoxPanelProps['filterList']) => {
|
||||||
setTargetItems(_targetItems)
|
const WithDropdown = (dom: ReactNode, _config?: DropDownProps) => {
|
||||||
|
return (
|
||||||
|
<Dropdown placement="bottomLeft" arrow {..._config}>
|
||||||
|
{dom}
|
||||||
|
</Dropdown>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return _list?.map(item => (
|
||||||
|
<Tooltip
|
||||||
|
title={item.label}
|
||||||
|
open={item.showTooltip}
|
||||||
|
>
|
||||||
|
{item.type === 'dropdown' ? (
|
||||||
|
WithDropdown(
|
||||||
|
<Button className={classNames(componentName + '-search-btns-btn')} type="text" onClick={item.onClick} icon={item.icon} />,
|
||||||
|
item.dropdownConfig
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Button className={classNames(componentName + '-search-btns-btn')} type="text" onClick={item.onClick} icon={item.icon} />
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除
|
* 初始化拓展 filter
|
||||||
* @param key
|
* @param _list
|
||||||
* @param param1
|
* @returns
|
||||||
*/
|
*/
|
||||||
const onItemDelete = (key: any, { keys }: any) => {
|
const initOptions = (_list?: BoxPanelProps['optionList']) => {
|
||||||
setCheckedKeys(pre => {
|
return _list?.map((item, idx) => (
|
||||||
const newKeys = pre.filter(_key => !keys.includes(_key))
|
<>
|
||||||
return newKeys
|
<Tooltip
|
||||||
|
title={item.label}
|
||||||
|
>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<Button className={classNames(componentName + '-btns-common')} type={item.type || 'text'} onClick={item.onClick} icon={item.icon} {...item?.props}>{item.label}</Button>
|
||||||
|
</Tooltip>
|
||||||
|
{idx !== _list.length - 1 && <Divider className={classNames(componentName + '-btns-divider')} type="vertical" />}
|
||||||
|
</>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化标签面板
|
||||||
|
* @param _tagList
|
||||||
|
* @param sort 是否分类
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const initTagPanel = (_tagList: BoxPanelProps['tagList'], sort?: boolean) => {
|
||||||
|
// 正常标签渲染
|
||||||
|
const commonTag = (_tagProps: ITag) => (
|
||||||
|
<span
|
||||||
|
className={classNames(
|
||||||
|
componentName + '-tags-tag_common',
|
||||||
|
{[componentName + '-tags-tag_checked']: checkedTags.includes(_tagProps.value)}
|
||||||
|
)}
|
||||||
|
key={_tagProps.value}
|
||||||
|
onClick={() => onTagCheck?.(_tagProps.value, _tagProps)}
|
||||||
|
>{_tagProps.label}</span>
|
||||||
|
)
|
||||||
|
// 包装父级标签
|
||||||
|
const _withFather = (tag: ITag) => (
|
||||||
|
<div key={tag.value}>
|
||||||
|
<span className={classNames(componentName + '-tags-tag')}>{tag.label}</span>
|
||||||
|
{tag.children?.map?.(_tag => commonTag(_tag))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return _tagList?.map(tag => {
|
||||||
|
if (tag.children?.length && sort) {
|
||||||
|
return _withFather(tag)
|
||||||
|
} else {
|
||||||
|
return commonTag(tag)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
setTargetItems(pre => pre.filter(o => o.key !== key))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 盒子点击确定
|
|
||||||
const onBoxChoiceOk = async (data: any) => {
|
|
||||||
createFormRef.current?.setFieldValue('boxList', data)
|
|
||||||
createFormRef.current?.setFieldValue('boxName', 123)
|
|
||||||
console.log(createFormRef.current?.getFieldValue('boxList'))
|
|
||||||
setBoxChoiceOpen(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 盒子选择重置
|
|
||||||
const onBoxChoiceReset = () => {
|
|
||||||
setCheckedKeys([])
|
|
||||||
setTargetItems([])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={componentName}>
|
<div className={componentName}>
|
||||||
{/* 盒子选择弹框 */}
|
{/* 搜索栏 */}
|
||||||
<TreeTransferModal
|
{showSearchBar && (
|
||||||
open={boxChoiceOpen}
|
<div className={classNames(componentName + '-search')}>
|
||||||
onCancel={() => setBoxChoiceOpen(false)}
|
<Input
|
||||||
onRadioChange={(e) => console.log('radio', e.target.value)} // 顶部 radio 事件
|
className={classNames(componentName + '-search-input')}
|
||||||
dataSource={boxDataSource} // 数据源
|
size='middle'
|
||||||
targetItems={targetItems} // 右侧选中项
|
onChange={(e) => onSearch?.(e)}
|
||||||
checkedKeys={checkedKeys} // 左侧选中
|
placeholder='请输入盒子名称'
|
||||||
onReset={onBoxChoiceReset} // 重置按钮事件
|
{...searchInputProps}
|
||||||
onOk={onBoxChoiceOk} // 确定按钮事件
|
/>
|
||||||
onTreeCheck={onTreeCheck} // 树check选中事件
|
{customFilter || (!noFilter && (
|
||||||
onItemDelete={onItemDelete} // 右侧点击删除事件
|
<div
|
||||||
/>
|
className={classNames(componentName + '-search-btns')}
|
||||||
<div className={classNames(componentName + '-search')}>
|
>
|
||||||
<Input
|
{/* @ts-ignore */}
|
||||||
className={classNames(componentName + '-search-input')}
|
{initFilter(filterList)}
|
||||||
size='middle'
|
</div>
|
||||||
onChange={(e) => onSearch?.(e)}
|
))}
|
||||||
placeholder='请输入盒子名称'
|
</div>
|
||||||
{...searchInputProps}
|
)}
|
||||||
/>
|
{/* 默认操作按钮 */}
|
||||||
{customImport || (
|
|
||||||
<div
|
|
||||||
className={classNames(componentName + '-search-btns')}
|
|
||||||
>
|
|
||||||
<Button type="text" onClick={() => onBatch?.() || handleCheckable()} icon={isTreeCheckable ? <SwitcherOutlined /> : <DiffOutlined />} />
|
|
||||||
<Button type="text" onClick={() => onClockClick?.()} icon={<ClockCircleOutlined />} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{/* 是否显示操作按钮 */}
|
|
||||||
{showOptions && (
|
{showOptions && (
|
||||||
<>
|
<>
|
||||||
<div className={classNames(componentName + '-btns')}>
|
<div className={classNames(componentName + '-btns')}>
|
||||||
<Button className={classNames(componentName + '-btns-common')} type='text' onClick={() => onImport?.()} icon={<ImportOutlined />} >导入盒子</Button>
|
{initOptions(optionList)}
|
||||||
<Divider className={classNames(componentName + '-btns-divider')} type="vertical" />
|
|
||||||
{onCreate ?
|
|
||||||
(
|
|
||||||
<Button className={classNames(componentName + '-btns-common')} onClick={onCreate} type='text' icon={<FolderAddOutlined />} >新建组</Button>
|
|
||||||
) : (
|
|
||||||
<ModalForm<{
|
|
||||||
name: string
|
|
||||||
boxList?: any[]
|
|
||||||
}>
|
|
||||||
className={classNames(componentName + '-create-modal')}
|
|
||||||
open={onCreate ? false : undefined}
|
|
||||||
formRef={createFormRef}
|
|
||||||
title="新建组"
|
|
||||||
modalProps={{ destroyOnClose: true }}
|
|
||||||
layout='horizontal'
|
|
||||||
labelCol={{ span: 6 }}
|
|
||||||
wrapperCol={{ span: 18 }}
|
|
||||||
trigger={<Button type='text' className={classNames(componentName + '-btns-common')} icon={<FolderAddOutlined />} >新建组</Button>}
|
|
||||||
submitter={{
|
|
||||||
searchConfig: {
|
|
||||||
submitText: '确定',
|
|
||||||
resetText: '取消',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onFinish={onCreateSubmit}
|
|
||||||
>
|
|
||||||
<ProFormText
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
max: 20,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: /^[^\s]*$/g,
|
|
||||||
message: '禁止输入空格'
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
fieldProps={{ showCount: true }}
|
|
||||||
width="md"
|
|
||||||
name="name"
|
|
||||||
label="盒子组名称"
|
|
||||||
placeholder="请输入盒子名称"
|
|
||||||
/>
|
|
||||||
<ProFormText
|
|
||||||
width="md"
|
|
||||||
name="boxList"
|
|
||||||
label="盒子选择"
|
|
||||||
fieldProps={{
|
|
||||||
readOnly: true,
|
|
||||||
value: `已选择${createFormRef.current?.getFieldValue('boxList')?.length || 0}个盒子`,
|
|
||||||
suffix: (
|
|
||||||
<Space>
|
|
||||||
<a onClick={() => {
|
|
||||||
createFormRef.current?.setFieldValue('boxList', null)
|
|
||||||
onBoxChoiceReset()
|
|
||||||
}} >恢复默认</a>
|
|
||||||
<a onClick={() => setBoxChoiceOpen(true)}>范围选择</a>
|
|
||||||
</Space>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ModalForm>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<Divider className={classNames(componentName + '-btns-divider')} type="vertical" />
|
|
||||||
{/* @ts-ignore */}
|
|
||||||
<Button className={classNames(componentName + '-btns-common')} danger type='text' icon={<CloseCircleOutlined />} disabled={treeProps?.checkedKeys?.length <= 0} onClick={onBoxBatchDelete} >删除</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<Divider style={{ margin: 0 }} />
|
<Divider style={{ margin: 0 }} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{extraBtns}
|
{extraBtns}
|
||||||
|
{showTagPanel && (
|
||||||
|
<div className={classNames(componentName + '-tags')}>
|
||||||
|
<span
|
||||||
|
className={classNames(componentName + '-tags-tag')}
|
||||||
|
style={tagExpandAll ? {
|
||||||
|
marginBottom: '12px',
|
||||||
|
display: tagExpandAll ? 'block' : 'inline-block'
|
||||||
|
} : {}
|
||||||
|
}
|
||||||
|
>标签:</span>
|
||||||
|
{initTagPanel(tagList, tagExpandAll)}
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
componentName + '-tags-tag_option',
|
||||||
|
{ [componentName + '-tags-tag_absolute']: tagExpandAll },
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{tagExpandAll && (
|
||||||
|
<span
|
||||||
|
className={classNames(componentName + '-tags-tag_common', componentName + '-tags-tag_fz12')}
|
||||||
|
onClick={onResetTags}
|
||||||
|
>重置</span>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className={classNames(componentName + '-tags-tag_common', componentName + '-tags-tag_fz12')}
|
||||||
|
onClick={onTagExpand}
|
||||||
|
>{tagExpandAll ? '收起' : '更多'}<IconFont icon={tagExpandAll ? 'icon-shangjiantou' : 'icon-xiajiantou'} /></span>
|
||||||
|
</div>
|
||||||
|
{tagFootRender}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<BoxTree
|
<BoxTree
|
||||||
className={classNames(componentName + '-tree')}
|
className={classNames(componentName + '-tree')}
|
||||||
treeCheckable={isTreeCheckable}
|
checkable={isTreeCheckable}
|
||||||
data={data}
|
treeData={data}
|
||||||
onItemSelect={onItemSelect}
|
blockNode
|
||||||
onItemCheck={onItemCheck}
|
onSelect={onItemSelect}
|
||||||
onItemDelete={onBoxDelete}
|
onCheck={onItemCheck}
|
||||||
{...treeProps}
|
{...treeProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { BoxSelectTree } from '@zhst/biz';
|
import { BoxSelectTree } from '@zhst/biz';
|
||||||
import { treeData, boxDataSource } from './mock'
|
import { treeData, boxDataSource } from './mock'
|
||||||
import { Select, TreeProps, Modal, Checkbox } from 'antd';
|
import { Select, TreeProps, Modal, Checkbox, Button } from '@zhst/meta';
|
||||||
|
|
||||||
const { Option } = Select
|
const { Option } = Select
|
||||||
|
|
||||||
@ -17,7 +17,6 @@ const demo = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onBoxBatchDelete = () => {
|
const onBoxBatchDelete = () => {
|
||||||
console.log('盒子批量删除', checkedKeys)
|
|
||||||
modal.warning({
|
modal.warning({
|
||||||
content: (
|
content: (
|
||||||
<div>
|
<div>
|
||||||
@ -81,6 +80,7 @@ const demo = () => {
|
|||||||
onItemRenameFinish: async (val, data) => console.log('盒子重命名提交(返回boolean,控制弹框显示\隐藏)', val, data),
|
onItemRenameFinish: async (val, data) => console.log('盒子重命名提交(返回boolean,控制弹框显示\隐藏)', val, data),
|
||||||
checkedKeys,
|
checkedKeys,
|
||||||
}}
|
}}
|
||||||
|
footer={<Button block >加载更多</Button>}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
68
packages/biz/src/boxSelectTree/demo/customFilter.tsx
Normal file
68
packages/biz/src/boxSelectTree/demo/customFilter.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { BoxSelectTree } from '@zhst/biz';
|
||||||
|
import { treeData, boxDataSource } from './mock'
|
||||||
|
import { FilterOutlined } from '@ant-design/icons';
|
||||||
|
import { Badge } from 'antd';
|
||||||
|
|
||||||
|
const demo = () => {
|
||||||
|
const [activeKey] = useState('1')
|
||||||
|
const [checkedKeys] = useState<string[]>([]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
|
||||||
|
<BoxSelectTree
|
||||||
|
data={activeKey === '1' ? treeData : boxDataSource}
|
||||||
|
boxDataSource={boxDataSource}
|
||||||
|
showOptions={false}
|
||||||
|
tabsProps={{
|
||||||
|
activeKey,
|
||||||
|
}}
|
||||||
|
treeProps={{
|
||||||
|
checkedKeys
|
||||||
|
}}
|
||||||
|
filterList={[
|
||||||
|
{
|
||||||
|
label: '过滤',
|
||||||
|
key: 'multi',
|
||||||
|
icon: <FilterOutlined />,
|
||||||
|
type: 'dropdown',
|
||||||
|
showTooltip: false,
|
||||||
|
dropdownConfig: {
|
||||||
|
menu: {
|
||||||
|
// 自定义返回项
|
||||||
|
_internalRenderMenuItem: (originNode, menuItemProps, stateProps) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{originNode}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
selectable: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: <p style={{ margin: '0', textAlign: 'center' }} >全部</p>,
|
||||||
|
key: 'all',
|
||||||
|
onClick: () => console.log('多选1')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <Badge status="success" />,
|
||||||
|
label: '多选1',
|
||||||
|
key: 'multi1',
|
||||||
|
onClick: () => console.log('多选1')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '多选2',
|
||||||
|
icon: <Badge status='error' />,
|
||||||
|
key: 'multi2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default demo;
|
33
packages/biz/src/boxSelectTree/demo/customOptions.tsx
Normal file
33
packages/biz/src/boxSelectTree/demo/customOptions.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { BoxSelectTree } from '@zhst/biz';
|
||||||
|
import { treeData, boxDataSource } from './mock'
|
||||||
|
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
const demo = () => {
|
||||||
|
const [activeKey, setActiveKey] = useState('1')
|
||||||
|
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
|
||||||
|
<BoxSelectTree
|
||||||
|
data={activeKey === '1' ? treeData : boxDataSource}
|
||||||
|
boxDataSource={boxDataSource}
|
||||||
|
tabsProps={{
|
||||||
|
activeKey,
|
||||||
|
}}
|
||||||
|
treeProps={{
|
||||||
|
checkedKeys
|
||||||
|
}}
|
||||||
|
optionList={[
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
key: 'del',
|
||||||
|
icon: <CloseCircleOutlined />,
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default demo;
|
@ -2,10 +2,12 @@ import React, { useState } from 'react';
|
|||||||
import { BoxSelectTree } from '@zhst/biz';
|
import { BoxSelectTree } from '@zhst/biz';
|
||||||
import { treeData, boxDataSource } from './mock'
|
import { treeData, boxDataSource } from './mock'
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
|
import { ClockCircleOutlined, DiffOutlined, SwitcherOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
const demo = () => {
|
const demo = () => {
|
||||||
const [activeKey, setActiveKey] = useState('1')
|
const [activeKey, setActiveKey] = useState('1')
|
||||||
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
|
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
|
||||||
|
const [isTreeCheckable, setIsTreeCheckable] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
|
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
|
||||||
@ -13,10 +15,23 @@ const demo = () => {
|
|||||||
data={activeKey === '1' ? treeData : boxDataSource}
|
data={activeKey === '1' ? treeData : boxDataSource}
|
||||||
boxDataSource={boxDataSource}
|
boxDataSource={boxDataSource}
|
||||||
showOptions={false}
|
showOptions={false}
|
||||||
extraBtns={<Button type="dashed" style={{ color: 'green' }}>自定义按钮</Button>}
|
extraBtns={<div><Button type="dashed" style={{ color: 'red' }}>自定义区域</Button> Hello Lambo</div>}
|
||||||
tabsProps={{
|
tabsProps={{
|
||||||
activeKey,
|
activeKey,
|
||||||
}}
|
}}
|
||||||
|
filterList={[
|
||||||
|
{
|
||||||
|
label: '多选',
|
||||||
|
key: 'multi',
|
||||||
|
icon: isTreeCheckable ? <SwitcherOutlined /> : <DiffOutlined />,
|
||||||
|
onClick: () => setIsTreeCheckable(pre => !pre)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '时钟',
|
||||||
|
key: 'clock',
|
||||||
|
icon: <ClockCircleOutlined />,
|
||||||
|
}
|
||||||
|
]}
|
||||||
treeProps={{
|
treeProps={{
|
||||||
checkedKeys
|
checkedKeys
|
||||||
}}
|
}}
|
||||||
|
27
packages/biz/src/boxSelectTree/demo/noFilter.tsx
Normal file
27
packages/biz/src/boxSelectTree/demo/noFilter.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { BoxSelectTree } from '@zhst/biz';
|
||||||
|
import { treeData, boxDataSource } from './mock'
|
||||||
|
|
||||||
|
const demo = () => {
|
||||||
|
const [activeKey, setActiveKey] = useState('1')
|
||||||
|
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
|
||||||
|
<BoxSelectTree
|
||||||
|
data={activeKey === '1' ? treeData : boxDataSource}
|
||||||
|
boxDataSource={boxDataSource}
|
||||||
|
showOptions={false}
|
||||||
|
tabsProps={{
|
||||||
|
activeKey,
|
||||||
|
}}
|
||||||
|
treeProps={{
|
||||||
|
checkedKeys
|
||||||
|
}}
|
||||||
|
noFilter
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default demo;
|
@ -30,7 +30,7 @@ const demo = () => {
|
|||||||
tabsProps={{
|
tabsProps={{
|
||||||
activeKey,
|
activeKey,
|
||||||
}}
|
}}
|
||||||
customImport={<Button type="text" icon={<FilterOutlined />} />}
|
customImport={<div style={{ color: 'red' }}>自定义区域</div>}
|
||||||
searchInputProps={{
|
searchInputProps={{
|
||||||
addonBefore: (
|
addonBefore: (
|
||||||
<Select
|
<Select
|
||||||
@ -43,7 +43,6 @@ const demo = () => {
|
|||||||
>
|
>
|
||||||
{[
|
{[
|
||||||
{ value: '1', label: '盒子' },
|
{ value: '1', label: '盒子' },
|
||||||
{ value: '2', label: '盒子组' }
|
|
||||||
].map(item => (
|
].map(item => (
|
||||||
<Option value={item.value}>{item.label}</Option>
|
<Option value={item.value}>{item.label}</Option>
|
||||||
))}
|
))}
|
||||||
|
84
packages/biz/src/boxSelectTree/demo/withTagPanel.tsx
Normal file
84
packages/biz/src/boxSelectTree/demo/withTagPanel.tsx
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { BoxSelectTree } from '@zhst/biz';
|
||||||
|
import { treeData, boxDataSource } from './mock'
|
||||||
|
import { Button, Switch } from 'antd';
|
||||||
|
|
||||||
|
const demo = () => {
|
||||||
|
const [activeKey] = useState('1')
|
||||||
|
const [checkedKeys] = useState<string[]>([]);
|
||||||
|
const [checkedTags, setCheckedTags] = useState<string[]>([]);
|
||||||
|
const [tagExpandAll, setTagExpandAll] = useState(false);
|
||||||
|
const [showTagPanel, setShowTagPanel] = useState(true);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div style={{ marginBottom: '12px' }}>
|
||||||
|
显示标签面板:
|
||||||
|
<Switch value={showTagPanel} onChange={status => setShowTagPanel(status)} />
|
||||||
|
</div>
|
||||||
|
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
|
||||||
|
<BoxSelectTree
|
||||||
|
data={activeKey === '1' ? treeData : boxDataSource}
|
||||||
|
boxDataSource={boxDataSource}
|
||||||
|
showOptions={false}
|
||||||
|
tabsProps={{
|
||||||
|
activeKey,
|
||||||
|
}}
|
||||||
|
treeProps={{
|
||||||
|
checkedKeys
|
||||||
|
}}
|
||||||
|
showTagPanel={showTagPanel}
|
||||||
|
tagExpandAll={tagExpandAll}
|
||||||
|
onTagExpand={() => {
|
||||||
|
setTagExpandAll(pre => !pre)
|
||||||
|
setCheckedTags([])
|
||||||
|
}}
|
||||||
|
onTagCheck={(value) => setCheckedTags(pre => {
|
||||||
|
if (pre.includes(value)) {
|
||||||
|
return pre.filter(item => item !== value)
|
||||||
|
} else {
|
||||||
|
return [...pre, value]
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
onResetTags={() => setCheckedTags([])}
|
||||||
|
checkedTags={checkedTags}
|
||||||
|
tagFootRender={<Button danger>自定义dom</Button>}
|
||||||
|
tagList={[
|
||||||
|
{
|
||||||
|
label: '标签组1',
|
||||||
|
value: '1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '标签1-1',
|
||||||
|
value: '1-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '标签1-2',
|
||||||
|
value: '1-2',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '标签组2',
|
||||||
|
value: '2',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '标签2-1',
|
||||||
|
value: '2-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '标签2-2',
|
||||||
|
value: '2-2',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default demo;
|
@ -15,6 +15,10 @@ group:
|
|||||||
<code src="./demo/basic.tsx">基本用法</code>
|
<code src="./demo/basic.tsx">基本用法</code>
|
||||||
<code src="./demo/extraBtns.tsx">自定义其它按钮</code>
|
<code src="./demo/extraBtns.tsx">自定义其它按钮</code>
|
||||||
<code src="./demo/noOptions.tsx">不显示其它按钮</code>
|
<code src="./demo/noOptions.tsx">不显示其它按钮</code>
|
||||||
|
<code src="./demo/customOptions.tsx">自定义配置按钮</code>
|
||||||
|
<code src="./demo/noFilter.tsx">不显示过滤按钮</code>
|
||||||
|
<code src="./demo/customFilter.tsx">自定义过滤按钮</code>
|
||||||
|
<code src="./demo/withTagPanel.tsx">标签看板</code>
|
||||||
<code src="./demo/async.tsx">异步加载数据</code>
|
<code src="./demo/async.tsx">异步加载数据</code>
|
||||||
|
|
||||||
## API
|
## API
|
||||||
@ -22,17 +26,34 @@ group:
|
|||||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| data | 数据源 | Array[] | [] | - |
|
| data | 数据源 | Array[] | [] | - |
|
||||||
|
| tabsProps | Tabs组件的Props | 参考antd的Tabs组件 | - | - |
|
||||||
|
| searchInputProps | 搜索框的 Props | 参考antd的Input组件 | - | - |
|
||||||
|
| showOptions | 展示其它功能按钮 | boolean | true | - |
|
||||||
|
| filterList | 搜索框边上的插槽列表 | boolean | true | - |
|
||||||
|
| customImport | 自定义搜索栏边上的过滤图标 | ReactNode、string | - | - |
|
||||||
|
| extraBtns | 搜索栏下面的插槽 | ReactNode、string | - | - |
|
||||||
|
| prefixCls | class前缀 | string | - | - |
|
||||||
|
| showSearchBar | 是否显示搜索框 | boolean | true | - |
|
||||||
|
| noFilter | 是否显示搜索拓展 icon | boolean | false | - |
|
||||||
|
| filterList | 拓展列表 | IOption[] | [] | - |
|
||||||
|
| optionList | 操作按钮列表 | IOption[] | [] | - |
|
||||||
|
| showTagPanel | 是否显示标签看板 | boolean | false | - |
|
||||||
|
| tagList | 标签列表 | ITag[] | [] | - |
|
||||||
|
| tagExpandAll | 标签是否展开 | boolean | false | - |
|
||||||
|
| checkedTags | 选中的标签值 | string[] | [] | - |
|
||||||
|
| footer | 盒子树底部渲染(需要内容撑开) | ReactNode、string | - | - |
|
||||||
|
| tagFootRender | 标签看板底部自定义 | ReactNode、string | - | - |
|
||||||
|
| onResetTags | 标签重置 | () => void | [] | - |
|
||||||
|
| onTagExpand | 标签展开 | (e: any) => void | [] | - |
|
||||||
|
| onTabChange | tab切换监听 | function: (e) => void | - | - |
|
||||||
|
| onTagCheck | 标签选中事件 | (value: string, tag: ITag) => void | false | - |
|
||||||
| onSearch | 搜索监听 | function: (e) => void | - | - |
|
| onSearch | 搜索监听 | function: (e) => void | - | - |
|
||||||
| onItemSelect | 树当前选中(单选) | function: (e) => void | - | - |
|
| onItemSelect | 树当前选中(单选) | function: (e) => void | - | - |
|
||||||
| onItemCheck | 树选择(支持多选) | function: (e) => void | - | - |
|
| onItemCheck | 树选择(支持多选) | function: (e) => void | - | - |
|
||||||
| tabsProps | Tabs组件的Props | antd的Tabs组件 | - | - |
|
| onBoxDelete | 盒子删除事件 | function: (e) => void | - | 0.23.0 以后弃用 |
|
||||||
| searchInputProps | 搜索框的Props | antd的Input组件 | - | - |
|
| onBatch | 多选 | function: (e) => void | - | 0.23.0 以后弃用 |
|
||||||
| onTabChange | tab切换监听 | function: (e) => void | - | - |
|
| onBoxBatchDelete | 盒子批量删除事件 | function: (e) => void | - | 0.23.0 以后弃用 |
|
||||||
| onBoxDelete | 盒子删除事件 | function: (e) => void | - | - |
|
| onCreateSubmit | 新建提交事件 | function: (e) => void | - | 0.23.0 以后弃用 |
|
||||||
| onBoxBatchDelete | 盒子批量删除事件 | function: (e) => void | - | - |
|
| onImport | 监听导入盒子点击事件 | function: () => void | - | 0.23.0 以后弃用 |
|
||||||
| onCreateSubmit | 新建提交事件 | function: (e) => void | - | - |
|
| onClockClick | 监听时钟点击事件 | function: () => void | - | 0.23.0 以后弃用 |
|
||||||
| onImport | 监听导入盒子点击事件 | function: () => void | - | - |
|
| onCreate | 监听创建点击事件 | function: () => void | 如果不传默认用自带的创建事件 | 0.23.0 以后弃用 |
|
||||||
| onClockClick | 监听时钟点击事件 | function: () => void | - | - |
|
|
||||||
| onCreate | 监听创建点击事件 | function: () => void | 如果不传默认用自带的创建事件 | - |
|
|
||||||
| showOptions | 展示其它功能按钮 | boolean | true | - |
|
|
||||||
|
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
export { default as BigImageModal } from './BigImageModal'
|
export { default as BigImageModal } from './BigImageModal'
|
||||||
export type { BigImageModalProps } from './BigImageModal'
|
export type { BigImageModalProps } from './BigImageModal'
|
||||||
|
export { default as BigImage } from './BigImage'
|
||||||
|
export type { BigImageProps, BigImageRef } from './BigImage'
|
||||||
export { default as BoxSelectTree } from './boxSelectTree'
|
export { default as BoxSelectTree } from './boxSelectTree'
|
||||||
export type { BoxSelectTreeProps } from './boxSelectTree'
|
export type { BoxSelectTreeProps } from './boxSelectTree'
|
||||||
export { default as Tree } from './tree'
|
export { default as Tree } from './tree'
|
||||||
export type { BoxTreeProps, TreeData } from './tree'
|
export type { BoxTreeProps, TreeData } from './tree'
|
||||||
|
export { default as TreePanel } from './treePanel'
|
||||||
|
export type { TreePanelProps } from './treePanel'
|
||||||
export { default as TreeTransfer } from './treeTransfer'
|
export { default as TreeTransfer } from './treeTransfer'
|
||||||
export type { TreeTransferProps } from './treeTransfer'
|
export type { TreeTransferProps } from './treeTransfer'
|
||||||
export { default as TreeTransferModal } from './treeTransferModal'
|
export { default as TreeTransferModal } from './treeTransferModal'
|
||||||
export type { TreeTransferModalProps } from './treeTransferModal'
|
export type { TreeTransferModalProps } from './treeTransferModal'
|
||||||
export { default as WarningRecordCard } from './WarningRecordCard'
|
export { default as WarningRecordCard } from './WarningRecordCard'
|
||||||
|
export { CommonCard } from './CustomCard'
|
||||||
|
export type {
|
||||||
|
CommonCardProps,
|
||||||
|
CommonCardRefProps
|
||||||
|
} from './CustomCard'
|
||||||
export type { IRecord, WarningRecordCardProps } from './WarningRecordCard'
|
export type { IRecord, WarningRecordCardProps } from './WarningRecordCard'
|
||||||
export { default as OdModal } from './odModal'
|
export { default as OdModal } from './odModal'
|
||||||
export type { ODModalProps } from './odModal'
|
export type { ODModalProps } from './odModal'
|
||||||
|
@ -2,24 +2,29 @@
|
|||||||
* Created by jiangzhixiong
|
* Created by jiangzhixiong
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { forwardRef, ReactNode, useContext, useImperativeHandle, useRef } from 'react'
|
import React, { forwardRef, ReactNode, useContext, useEffect, useImperativeHandle, useRef } from 'react'
|
||||||
import { ConfigProvider } from '@zhst/meta';
|
import { ConfigProvider, Spin, SpinProps } from '@zhst/meta';
|
||||||
import { Divider, Flex } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import InfiniteScroll from 'react-infinite-scroll-component';
|
import InfiniteScroll from 'react-infinite-scroll-component';
|
||||||
import { SearchCard, SearchCardProps } from './components';
|
import { useSize } from '@zhst/hooks';
|
||||||
import './index.less'
|
import './index.less'
|
||||||
import { Idata } from './components/SearchCard';
|
|
||||||
|
|
||||||
const { ConfigContext } = ConfigProvider
|
const { ConfigContext } = ConfigProvider
|
||||||
|
|
||||||
export interface InfiniteListProps {
|
export interface IData {
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
index?: number;
|
||||||
|
[k: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InfiniteListProps extends React.HtmlHTMLAttributes<HTMLDivElement> {
|
||||||
type?: 'custom' | 'auto'
|
type?: 'custom' | 'auto'
|
||||||
prefixCls?: string;
|
prefixCls?: string;
|
||||||
height?: number;
|
height?: number;
|
||||||
itemRender?: (data?: any) => React.ReactNode
|
itemRender?: (data?: IData, index?: number) => React.ReactNode
|
||||||
loading?: boolean; //
|
loading?: boolean; //
|
||||||
data: Idata[];
|
data: IData[];
|
||||||
targetId?: string; // 滚动列表 ID
|
targetId?: string; // 滚动列表 ID
|
||||||
loadMore?: (data?: any) => any;
|
loadMore?: (data?: any) => any;
|
||||||
params?: {
|
params?: {
|
||||||
@ -28,101 +33,79 @@ export interface InfiniteListProps {
|
|||||||
hasMore: boolean;
|
hasMore: boolean;
|
||||||
endMessage?: ReactNode
|
endMessage?: ReactNode
|
||||||
loadingMessage?: ReactNode
|
loadingMessage?: ReactNode
|
||||||
onItemClick?: (data: any) => void;
|
loadingProps?: SpinProps
|
||||||
searchCardProps?: SearchCardProps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InfiniteListRefProps {
|
export interface InfiniteListRefProps {
|
||||||
|
scrollViewSize?: { width: number; height: number }
|
||||||
|
listSize?: { width: number; height: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
const InfiniteList = forwardRef<InfiniteListRefProps, InfiniteListProps>((props, ref) => {
|
const InfiniteList = forwardRef<InfiniteListRefProps, InfiniteListProps>((props, ref) => {
|
||||||
const {
|
const {
|
||||||
prefixCls: customizePrefixCls,
|
prefixCls: customizePrefixCls,
|
||||||
height,
|
height = 600,
|
||||||
|
loading,
|
||||||
type = 'auto',
|
type = 'auto',
|
||||||
loadingMessage = <p style={{ textAlign: 'center' }}>加载中...</p>,
|
loadingMessage = <p style={{ textAlign: 'center' }}>加载中...</p>,
|
||||||
targetId = 'scrollableDiv',
|
targetId = 'scrollableDiv',
|
||||||
itemRender,
|
itemRender = (data) => <div>{data?.title}</div>,
|
||||||
hasMore,
|
hasMore,
|
||||||
onItemClick,
|
|
||||||
loadMore,
|
loadMore,
|
||||||
data = [],
|
data = [],
|
||||||
endMessage = <Divider plain>没有更多数据了...🤐</Divider>,
|
endMessage = <div style={{ textAlign: 'center' }} >没有更多数据了...🤐</div>,
|
||||||
searchCardProps
|
style,
|
||||||
|
loadingProps,
|
||||||
|
className
|
||||||
} = props
|
} = props
|
||||||
const { getPrefixCls } = useContext(ConfigContext);
|
const { getPrefixCls } = useContext(ConfigContext);
|
||||||
const componentName = getPrefixCls('biz-infinite-list', customizePrefixCls);
|
const componentName = getPrefixCls('biz-infinite-list', customizePrefixCls);
|
||||||
const listRef = useRef<HTMLDivElement>(null);
|
const listRef = useRef<HTMLDivElement>(null);
|
||||||
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
|
const scrollViewSize = useSize(listRef.current) || { width: 0, height: 0 }; // 无限滚动视窗大小
|
||||||
|
// @ts-ignore
|
||||||
|
const listSize = useSize(scrollRef.current?._infScroll) || { width: 0, height: 0 } // 无限滚动列表大小
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 当数据不够一屏时继续加载
|
||||||
|
if (listSize.height < scrollViewSize.height) {
|
||||||
|
loadMore?.()
|
||||||
|
}
|
||||||
|
}, [listSize.height])
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
|
scrollViewSize,
|
||||||
|
listSize
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Spin spinning={loading} {...loadingProps}>
|
||||||
id={targetId}
|
<div
|
||||||
className={classNames(componentName)}
|
id={targetId}
|
||||||
ref={listRef}
|
className={classNames(componentName, className)}
|
||||||
style={{
|
ref={listRef}
|
||||||
height,
|
style={{
|
||||||
overflow: 'auto',
|
height,
|
||||||
padding: 12
|
overflow: 'auto',
|
||||||
}}
|
...style
|
||||||
>
|
}}
|
||||||
{/* {loading ? (
|
|
||||||
<p>加载中...</p>
|
|
||||||
) : (
|
|
||||||
<Flex wrap='wrap' gap="small" className={classNames(componentName + 'items')}>
|
|
||||||
{data?.list?.map((item) => (
|
|
||||||
itemRender?.(item) || (
|
|
||||||
<div className={classNames(componentName + 'items-item')}>
|
|
||||||
<SearchCard data={item} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
)} */}
|
|
||||||
<InfiniteScroll
|
|
||||||
dataLength={data.length}
|
|
||||||
next={type === 'auto' ? loadMore! : () => {}}
|
|
||||||
hasMore={hasMore}
|
|
||||||
loader={loadingMessage}
|
|
||||||
endMessage={endMessage}
|
|
||||||
scrollableTarget={targetId}
|
|
||||||
>
|
>
|
||||||
<Flex wrap='wrap' gap="small" className={classNames(componentName + 'items')}>
|
<InfiniteScroll
|
||||||
|
// @ts-ignore
|
||||||
|
ref={scrollRef}
|
||||||
|
dataLength={data.length}
|
||||||
|
next={type === 'auto' ? loadMore! : () => {}}
|
||||||
|
hasMore={hasMore}
|
||||||
|
loader={loadingMessage}
|
||||||
|
endMessage={endMessage}
|
||||||
|
scrollableTarget={targetId}
|
||||||
|
>
|
||||||
{data?.map((item, idx) => (
|
{data?.map((item, idx) => (
|
||||||
itemRender?.(item) || (
|
itemRender?.({ ...item, index: idx})
|
||||||
<div
|
|
||||||
key={idx}
|
|
||||||
className={classNames(componentName + 'items-item')}
|
|
||||||
onClick={() => {
|
|
||||||
onItemClick?.(item)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SearchCard
|
|
||||||
id={idx + 1}
|
|
||||||
data={item}
|
|
||||||
width="184px"
|
|
||||||
{...searchCardProps}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</InfiniteScroll>
|
||||||
</InfiniteScroll>
|
</div>
|
||||||
{/* <div style={{ marginTop: 8 }}>
|
</Spin>
|
||||||
{!noMore && (
|
|
||||||
<Button onClick={loadMore} disabled={loadingMore}>
|
|
||||||
{loadingMore ? '加载中...' : '点击加载更多'}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{noMore && <span>没有更多数据了</span>}
|
|
||||||
</div> */}
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
/**
|
|
||||||
* Created by jiangzhixiong on 2024/04/28
|
|
||||||
*/
|
|
||||||
|
|
||||||
export { default as SearchCard } from './SearchCard'
|
|
||||||
export type { SearchCardProps, SearchCardRefProps } from './SearchCard'
|
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { InfiniteList } from '@zhst/biz'
|
import { InfiniteList, CommonCard } from '@zhst/biz'
|
||||||
|
import { uniqueId } from '@zhst/func'
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const [data, setData] = useState([])
|
const [data, setData] = useState([])
|
||||||
@ -13,8 +14,10 @@ export default () => {
|
|||||||
fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo')
|
fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo')
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((body) => {
|
.then((body) => {
|
||||||
let res = body.results.map(o => {
|
let res = body.results.map((o, index) => {
|
||||||
return {
|
return {
|
||||||
|
id: uniqueId(),
|
||||||
|
sort: index + 1,
|
||||||
title: o.name.first,
|
title: o.name.first,
|
||||||
subtitle: o.name.last,
|
subtitle: o.name.last,
|
||||||
url: o.picture.large
|
url: o.picture.large
|
||||||
@ -36,14 +39,23 @@ export default () => {
|
|||||||
<InfiniteList
|
<InfiniteList
|
||||||
loading={loading}
|
loading={loading}
|
||||||
loadMore={loadMoreData}
|
loadMore={loadMoreData}
|
||||||
height={300}
|
height={1200}
|
||||||
hasMore={data.length < 100}
|
hasMore={data.length < 60}
|
||||||
data={data}
|
data={data}
|
||||||
onItemClick={_data => console.log('item点击:', _data)}
|
itemRender={(item) => {
|
||||||
searchCardProps={{
|
return (
|
||||||
onAdd: (_data) => console.log('新增', _data),
|
<CommonCard
|
||||||
onCreate: (_data) => console.log('创建', _data),
|
key={item.id}
|
||||||
onRemove: (_data) => console.log('删除', _data),
|
sort={item.sort}
|
||||||
|
data={item}
|
||||||
|
width="184px"
|
||||||
|
actions={[
|
||||||
|
<a>创建检索</a>,
|
||||||
|
<a>创建布控</a>,
|
||||||
|
<a>删除点位</a>
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -3,11 +3,11 @@ import { InfiniteList } from '@zhst/biz'
|
|||||||
import { Button, Input, Space } from 'antd'
|
import { Button, Input, Space } from 'antd'
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const [data, setData] = useState([])
|
const [data, setData] = useState<any>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [params, setParams] = useState({})
|
const [params, setParams] = useState({})
|
||||||
|
|
||||||
const loadMoreData = (params?: { name: string; age?: number; sex: string; tel: number }) => {
|
const loadMoreData = (params?: any) => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -15,7 +15,7 @@ export default () => {
|
|||||||
fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo')
|
fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo')
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((body) => {
|
.then((body) => {
|
||||||
let res = body.results.map(o => {
|
let res = body.results.map((o: { name: { first: any; last: any }; picture: { large: any } }) => {
|
||||||
return {
|
return {
|
||||||
title: o.name.first,
|
title: o.name.first,
|
||||||
subtitle: o.name.last,
|
subtitle: o.name.last,
|
||||||
@ -35,7 +35,7 @@ export default () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction='vertical'>
|
<Space direction='vertical' size={10} style={{ padding: '12px', border: '1px solid #ccc' }}>
|
||||||
<Space>
|
<Space>
|
||||||
<Input placeholder='名称' onChange={(e) => setParams(pre => ({ ...pre, name: e.target.value }))} style={{ width: '120px' }} />
|
<Input placeholder='名称' onChange={(e) => setParams(pre => ({ ...pre, name: e.target.value }))} style={{ width: '120px' }} />
|
||||||
<Input placeholder='年龄' onChange={(e) => setParams(pre => ({ ...pre, age: e.target.value }))} style={{ width: '120px' }} />
|
<Input placeholder='年龄' onChange={(e) => setParams(pre => ({ ...pre, age: e.target.value }))} style={{ width: '120px' }} />
|
||||||
@ -49,9 +49,7 @@ export default () => {
|
|||||||
height={300}
|
height={300}
|
||||||
hasMore={data.length < 100}
|
hasMore={data.length < 100}
|
||||||
data={data}
|
data={data}
|
||||||
type="custom"
|
|
||||||
loadingMessage={<Button onClick={() => loadMoreData(params)}>加载更多</Button>}
|
loadingMessage={<Button onClick={() => loadMoreData(params)}>加载更多</Button>}
|
||||||
onItemClick={data => console.log('item点击:', data)}
|
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
)
|
)
|
||||||
|
@ -18,21 +18,20 @@ group:
|
|||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| data | 数据源 | Idata[] | [] | - |
|
| data | 数据源 | IData[] | [] | - |
|
||||||
|
| height | 无限滚动列表可视区高度 | number | 600 | - |
|
||||||
| loading | 数据源 | Array[] | [] | - |
|
| loading | 数据源 | Array[] | [] | - |
|
||||||
| data | 数据源 | Array[] | [] | - |
|
| dataLength | 数据数量 | number | [] | - |
|
||||||
| data | 数据源 | Array[] | [] | - |
|
| next | 下一页方法 | function | () => {} | - |
|
||||||
| data | 数据源 | Array[] | [] | - |
|
| hasMore | 是否还有更多 | boolean | false | - |
|
||||||
| data | 数据源 | Array[] | [] | - |
|
| loadingProps | 参考 antd-spin | spinProps | [] | - |
|
||||||
| data | 数据源 | Array[] | [] | - |
|
| itemRender | 自定义渲染项 | (IData) => ReactNode | - | - |
|
||||||
|
|
||||||
## Idata
|
## 设计思路
|
||||||
|
|
||||||
```js
|
无限滚动,同时支持:
|
||||||
interface Idata {
|
|
||||||
id?: string | number;
|
1. 自动、主动加载更多
|
||||||
url?: string; // 链接
|
2. 一屏没加载完,继续加载,直到填满屏幕:
|
||||||
title?: string; // 标题
|
- 需要第二次加载的内容是否为空,为空则停止加载
|
||||||
subtitle?: string; // 副标题
|
- 通过整体的page-height 和 浏览器可视区域
|
||||||
}
|
|
||||||
```
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { forwardRef, useContext, useImperativeHandle, useRef } from 'react';
|
import React, { forwardRef, useContext, useImperativeHandle, useRef } from 'react';
|
||||||
import { Button, Modal, ModalProps, Select, SelectProps, Space, theme } from 'antd';
|
import { ConfigProvider, CropperImage, Scanner, CropperImageProps, CropperImageRefProps, Button, Modal, ModalProps, Select, SelectProps, Space, theme } from '@zhst/meta'
|
||||||
import { ConfigProvider, CropperImage, Scanner, CropperImageProps, CropperImageRefProps } from '@zhst/meta'
|
|
||||||
import { IconFont } from '@zhst/icon'
|
import { IconFont } from '@zhst/icon'
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
// !! 已经弃用,改为使用meta/tree
|
||||||
import React, { FC, useState } from 'react';
|
import React, { FC, useState } from 'react';
|
||||||
import { Tree, Badge, TreeDataNode, Space, TreeProps, theme } from 'antd';
|
import { Tree, Badge, DataNode as TreeDataNode, Space, TreeProps, theme } from '@zhst/meta'
|
||||||
import { CloseOutlined, EditOutlined, SettingOutlined } from '@ant-design/icons'
|
import { CloseOutlined, EditOutlined, SettingOutlined } from '@ant-design/icons'
|
||||||
import { ModalForm, ProFormText } from '@ant-design/pro-components';
|
import classNames from 'classnames';
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
|
||||||
const componentName = 'zhst-biz-tree'
|
const componentName = 'zhst-biz-tree'
|
||||||
@ -30,7 +31,7 @@ const boxTree: FC<BoxTreeProps> = (props) => {
|
|||||||
showItemOption = true,
|
showItemOption = true,
|
||||||
treeCheckable = false,
|
treeCheckable = false,
|
||||||
onItemRename,
|
onItemRename,
|
||||||
onItemRenameFinish,
|
className: customClassName,
|
||||||
customOptions
|
customOptions
|
||||||
} = props
|
} = props
|
||||||
const { token } = useToken()
|
const { token } = useToken()
|
||||||
@ -45,6 +46,7 @@ const boxTree: FC<BoxTreeProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Tree
|
<Tree
|
||||||
|
className={classNames(componentName, customClassName)}
|
||||||
checkable={treeCheckable}
|
checkable={treeCheckable}
|
||||||
blockNode
|
blockNode
|
||||||
onSelect={(selectedKeys, info) => {
|
onSelect={(selectedKeys, info) => {
|
||||||
@ -70,38 +72,11 @@ const boxTree: FC<BoxTreeProps> = (props) => {
|
|||||||
<Space className={`${componentName}-item-render_right`} style={{ float:'right' }} >
|
<Space className={`${componentName}-item-render_right`} style={{ float:'right' }} >
|
||||||
{customOptions || (
|
{customOptions || (
|
||||||
<>
|
<>
|
||||||
<ModalForm
|
<EditOutlined onClick={(e) => {
|
||||||
title="重命名"
|
e.preventDefault();
|
||||||
width={600}
|
e.stopPropagation();
|
||||||
modalProps={{ destroyOnClose: true }}
|
onItemRename?.(_nodeData)
|
||||||
layout='horizontal'
|
}} />
|
||||||
labelCol={{ span: 6 }}
|
|
||||||
wrapperCol={{ span: 18 }}
|
|
||||||
trigger={<EditOutlined onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
onItemRename?.(_nodeData)
|
|
||||||
}} />}
|
|
||||||
submitter={{
|
|
||||||
searchConfig: {
|
|
||||||
submitText: '确定',
|
|
||||||
resetText: '取消',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onFinish={async (value) => onItemRenameFinish?.(value, _nodeData)}
|
|
||||||
>
|
|
||||||
<ProFormText
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
width="md"
|
|
||||||
name="name"
|
|
||||||
label="盒子名称"
|
|
||||||
placeholder="请输入盒子名称"
|
|
||||||
/>
|
|
||||||
</ModalForm>
|
|
||||||
<SettingOutlined onClick={(e) => {
|
<SettingOutlined onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
.zhst-biz-tree-item-render {
|
.zhst-biz-tree {
|
||||||
&_right {
|
&-item-render {
|
||||||
display: none;
|
&_right {
|
||||||
}
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover &_right {
|
&:hover &_right {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
category: Components
|
category: Components
|
||||||
title: Tree 树
|
title: Tree 树 [废弃]
|
||||||
toc: content
|
toc: content
|
||||||
demo:
|
demo:
|
||||||
cols: 2
|
cols: 2
|
||||||
@ -10,6 +10,11 @@ group:
|
|||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
:::warning{title=0.25.1之后版本已废弃}
|
||||||
|
组件迁移到 @zhst/meta
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
## 代码演示
|
## 代码演示
|
||||||
|
|
||||||
<code src="./demo/basic.tsx">基本用法</code>
|
<code src="./demo/basic.tsx">基本用法</code>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { TreeDataNode } from 'antd';
|
// !! 该组件已被废弃
|
||||||
|
import { DataNode } from '@zhst/meta';
|
||||||
import BoxTree from './boxTree';
|
import BoxTree from './boxTree';
|
||||||
|
|
||||||
export interface TreeData extends TreeDataNode {
|
export interface TreeData extends DataNode {
|
||||||
children?: TreeDataNode['children'] & {
|
children?: DataNode['children'] & {
|
||||||
isCamera?: boolean
|
isCamera?: boolean
|
||||||
/**
|
/**
|
||||||
* 0-失败 1-成功 2-进行中 3-未知
|
* 0-失败 1-成功 2-进行中 3-未知
|
||||||
|
269
packages/biz/src/treePanel/TreePanel.tsx
Normal file
269
packages/biz/src/treePanel/TreePanel.tsx
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
import React, { FC, useContext, ReactNode } from 'react';
|
||||||
|
import {
|
||||||
|
ConfigProvider,
|
||||||
|
Input,
|
||||||
|
Dropdown,
|
||||||
|
Tooltip,
|
||||||
|
Button,
|
||||||
|
DataNode as TreeDataNode,
|
||||||
|
Tree as BoxTree,
|
||||||
|
TreeProps as BoxTreeProps,
|
||||||
|
TreeProps,
|
||||||
|
InputProps,
|
||||||
|
DropDownProps,
|
||||||
|
SelectProps,
|
||||||
|
Select
|
||||||
|
} from '@zhst/meta';
|
||||||
|
import { IconFont } from '@zhst/icon';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import './index.less'
|
||||||
|
import { ButtonProps } from 'antd';
|
||||||
|
|
||||||
|
export interface IOption {
|
||||||
|
type?: ButtonProps['type'] & 'dropdown';
|
||||||
|
label: string
|
||||||
|
key: string
|
||||||
|
icon?: string | ReactNode
|
||||||
|
disabled?: boolean;
|
||||||
|
showTooltip?: boolean;
|
||||||
|
onClick?: () => void
|
||||||
|
className?: string;
|
||||||
|
dropdownConfig?: DropDownProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITag {
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
icon?: ReactNode
|
||||||
|
parentNode?: string
|
||||||
|
children?: ITag[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TreePanelProps {
|
||||||
|
treeType?: 'directory' | 'normal'
|
||||||
|
searchInputProps?: InputProps
|
||||||
|
showOptions?: boolean
|
||||||
|
showSelectBar?: boolean // 显示搜索框
|
||||||
|
filterSelectProps?: SelectProps
|
||||||
|
onSelect?: SelectProps['onChange']
|
||||||
|
treeProps?: Partial<BoxTreeProps>
|
||||||
|
data: TreeDataNode[]
|
||||||
|
onSearch?: (e: any) => void
|
||||||
|
onItemCheck?: TreeProps['onCheck']
|
||||||
|
onItemSelect?: TreeProps['onSelect']
|
||||||
|
customImport?: ReactNode | string // 自定义搜索栏边上的过滤图标
|
||||||
|
extra?: ReactNode | string // 搜索栏下面的插槽
|
||||||
|
prefixCls?: string
|
||||||
|
showSearchBar?: boolean // 是否显示搜索栏
|
||||||
|
noFilter?: boolean // 是否显示搜索拓展 icon
|
||||||
|
filterList?: IOption[]
|
||||||
|
optionList?: IOption[]
|
||||||
|
showTagPanel?: boolean // 标签插槽
|
||||||
|
tagList?: ITag[] // 标签列表
|
||||||
|
tagExpandAll?: boolean // 展开所有
|
||||||
|
onTagCheck?: (value: string, tag: ITag) => void;
|
||||||
|
checkedTags?: string[]
|
||||||
|
onResetTags?: () => void
|
||||||
|
onTagExpand?: (e: any) => void
|
||||||
|
tagFootRender?: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const { ConfigContext } = ConfigProvider
|
||||||
|
const { DirectoryTree } = BoxTree
|
||||||
|
|
||||||
|
const TreePanel: FC<TreePanelProps> = (props) => {
|
||||||
|
const {
|
||||||
|
treeType = 'directory',
|
||||||
|
searchInputProps,
|
||||||
|
showOptions = true,
|
||||||
|
showSelectBar,
|
||||||
|
filterSelectProps,
|
||||||
|
extra,
|
||||||
|
noFilter,
|
||||||
|
data = [],
|
||||||
|
treeProps,
|
||||||
|
onSelect,
|
||||||
|
onSearch,
|
||||||
|
onItemCheck,
|
||||||
|
onItemSelect,
|
||||||
|
showSearchBar = true,
|
||||||
|
optionList = [],
|
||||||
|
filterList = [],
|
||||||
|
showTagPanel,
|
||||||
|
tagList,
|
||||||
|
tagExpandAll,
|
||||||
|
onTagExpand,
|
||||||
|
checkedTags = [],
|
||||||
|
onTagCheck,
|
||||||
|
onResetTags,
|
||||||
|
tagFootRender,
|
||||||
|
prefixCls: customizePrefixCls,
|
||||||
|
customImport: customFilter
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const { getPrefixCls } = useContext(ConfigContext);
|
||||||
|
const componentName = getPrefixCls('biz-tree-panel', customizePrefixCls);
|
||||||
|
const CurrentTree = treeType === 'directory' ? DirectoryTree : BoxTree
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化拓展 filter
|
||||||
|
* @param _list
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const initFilter = (_list?: TreePanelProps['filterList']) => {
|
||||||
|
const WithDropdown = (dom: ReactNode, isShow?: boolean, _config?: DropDownProps) => {
|
||||||
|
if (!isShow) {
|
||||||
|
return dom
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown placement="bottomLeft" arrow {..._config}>
|
||||||
|
{dom}
|
||||||
|
</Dropdown>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return _list?.map(item => (
|
||||||
|
<Tooltip
|
||||||
|
title={item.label}
|
||||||
|
open={item.showTooltip}
|
||||||
|
>
|
||||||
|
{WithDropdown(
|
||||||
|
<Button className={classNames(componentName + '-search-btns-btn')} type={item.type} onClick={item.onClick} icon={item.icon} {...item} />,
|
||||||
|
item.type === 'dropdown',
|
||||||
|
item.dropdownConfig
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化拓展 filter
|
||||||
|
* @param _list
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const initOptions = (_list?: TreePanelProps['optionList']) => {
|
||||||
|
return _list?.map((item, idx) => (
|
||||||
|
<>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<div key={idx} className={classNames(componentName + '-btns-btn')}>
|
||||||
|
{item.icon}
|
||||||
|
<span onClick={item.onClick} className={classNames(componentName + '-btns-btn-label', item.className)}>{item.label}</span>
|
||||||
|
</div>
|
||||||
|
{idx % 2 !== 0 && (<br/>)}
|
||||||
|
</>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化标签面板
|
||||||
|
* @param _tagList
|
||||||
|
* @param sort 是否分类
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const initTagPanel = (_tagList: TreePanelProps['tagList'], sort?: boolean) => {
|
||||||
|
// 正常标签渲染
|
||||||
|
const commonTag = (_tagProps: ITag) => (
|
||||||
|
<span
|
||||||
|
className={classNames(
|
||||||
|
componentName + '-tags-tag_common',
|
||||||
|
{[componentName + '-tags-tag_checked']: checkedTags.includes(_tagProps.value)}
|
||||||
|
)}
|
||||||
|
key={_tagProps.value}
|
||||||
|
onClick={() => onTagCheck?.(_tagProps.value, _tagProps)}
|
||||||
|
>{_tagProps.label}</span>
|
||||||
|
)
|
||||||
|
// 包装父级标签
|
||||||
|
const _withFather = (tag: ITag) => (
|
||||||
|
<div key={tag.value}>
|
||||||
|
<span className={classNames(componentName + '-tags-tag')}>{tag.label}</span>
|
||||||
|
{tag.children?.map?.(_tag => commonTag(_tag))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return _tagList?.map(tag => {
|
||||||
|
if (tag.children?.length && sort) {
|
||||||
|
return _withFather(tag)
|
||||||
|
} else {
|
||||||
|
return commonTag(tag)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={componentName}>
|
||||||
|
{/* 搜索栏 */}
|
||||||
|
{showSearchBar && (
|
||||||
|
<div className={classNames(componentName + '-search')}>
|
||||||
|
<Input
|
||||||
|
className={classNames(componentName + '-search-input')}
|
||||||
|
onChange={(e) => onSearch?.(e)}
|
||||||
|
placeholder='请输入盒子名称'
|
||||||
|
{...searchInputProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 搜索栏 */}
|
||||||
|
{showSelectBar && (
|
||||||
|
<div className={classNames(componentName + '-search')}>
|
||||||
|
<Select
|
||||||
|
className={classNames(componentName + '-search-input')}
|
||||||
|
onChange={onSelect}
|
||||||
|
{...filterSelectProps}
|
||||||
|
/>
|
||||||
|
{customFilter || (!noFilter && (
|
||||||
|
<div
|
||||||
|
className={classNames(componentName + '-search-btns')}
|
||||||
|
>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
{initFilter(filterList)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{showTagPanel && (
|
||||||
|
<div className={classNames(componentName + '-tags')}>
|
||||||
|
<div className={classNames(componentName + '-tags-title')}>
|
||||||
|
标签
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
componentName + '-tags-tag_option',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{tagExpandAll && (
|
||||||
|
<span
|
||||||
|
className={classNames(componentName + '-tags-tag_option-btn')}
|
||||||
|
onClick={onResetTags}
|
||||||
|
>重置</span>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className={classNames(componentName + '-tags-tag_option-btn')}
|
||||||
|
onClick={onTagExpand}
|
||||||
|
>{tagExpandAll ? '收起' : '展开'}<IconFont icon={tagExpandAll ? 'icon-shangjiantou' : 'icon-xiajiantou'} /></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{initTagPanel(tagList, tagExpandAll)}
|
||||||
|
{tagFootRender}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 默认操作按钮 */}
|
||||||
|
{showOptions && (
|
||||||
|
<div className={classNames(componentName + '-btns')}>
|
||||||
|
{initOptions(optionList)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{extra}
|
||||||
|
<CurrentTree
|
||||||
|
className={classNames(componentName + '-tree')}
|
||||||
|
treeData={data}
|
||||||
|
showIcon={false}
|
||||||
|
blockNode
|
||||||
|
onSelect={onItemSelect}
|
||||||
|
onCheck={onItemCheck}
|
||||||
|
{...treeProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TreePanel
|
90
packages/biz/src/treePanel/demo/basic.tsx
Normal file
90
packages/biz/src/treePanel/demo/basic.tsx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import React, {} from 'react';
|
||||||
|
import { TreePanel } from '@zhst/biz';
|
||||||
|
import { Badge, Checkbox } from '@zhst/meta'
|
||||||
|
import { boxDataSource } from './mock'
|
||||||
|
import { ImportOutlined, FolderAddOutlined, CloseCircleOutlined, FilterOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
const demo = () => {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '12px', width: '240px', border: '1px solid #09f' }}>
|
||||||
|
<TreePanel
|
||||||
|
data={boxDataSource}
|
||||||
|
showSelectBar
|
||||||
|
filterList={[
|
||||||
|
{
|
||||||
|
label: '过滤',
|
||||||
|
key: 'multi',
|
||||||
|
icon: <FilterOutlined />,
|
||||||
|
type: 'dropdown',
|
||||||
|
showTooltip: false,
|
||||||
|
dropdownConfig: {
|
||||||
|
menu: {
|
||||||
|
// 自定义返回项
|
||||||
|
_internalRenderMenuItem: (originNode, menuItemProps, stateProps) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{originNode}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
selectable: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: <p style={{ margin: '0', textAlign: 'center' }} >全部</p>,
|
||||||
|
key: 'all',
|
||||||
|
onClick: () => console.log('多选1')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <Badge status="success" />,
|
||||||
|
label: '多选1',
|
||||||
|
key: 'multi1',
|
||||||
|
onClick: () => console.log('多选1')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '多选2',
|
||||||
|
icon: <Badge status='error' />,
|
||||||
|
key: 'multi2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '过滤',
|
||||||
|
key: 'ee',
|
||||||
|
icon: <FilterOutlined />,
|
||||||
|
showTooltip: false,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
extra={(
|
||||||
|
<div style={{ border: '1px solid red' }}>
|
||||||
|
<span><Checkbox>全选</Checkbox></span>
|
||||||
|
<a style={{ float: 'right', color: '#09f' }} >批量操作</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
optionList={[
|
||||||
|
{
|
||||||
|
label: '导入盒子',
|
||||||
|
key: 'import',
|
||||||
|
icon: <ImportOutlined />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '新建组',
|
||||||
|
key: 'add',
|
||||||
|
icon: <FolderAddOutlined />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
key: 'del',
|
||||||
|
icon: <CloseCircleOutlined />,
|
||||||
|
props: {
|
||||||
|
danger: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default demo;
|
54
packages/biz/src/treePanel/demo/mock.tsx
Normal file
54
packages/biz/src/treePanel/demo/mock.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { TreeData } from "@zhst/biz";
|
||||||
|
|
||||||
|
export const boxDataSource: TreeData[] = [
|
||||||
|
{
|
||||||
|
title: '全部盒子',
|
||||||
|
key: '0-0',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: '盒子组1',
|
||||||
|
key: '0-0-0',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: '摄像头1',
|
||||||
|
key: '0-0-0-0',
|
||||||
|
isCamera: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '摄像头2',
|
||||||
|
key: '0-0-0-1',
|
||||||
|
isCamera: true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '盒子组2',
|
||||||
|
key: '0-0-1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: '摄像头4',
|
||||||
|
key: '0-0-1-0',
|
||||||
|
isCamera: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
export const treeData: TreeData[] = [
|
||||||
|
{ key: '0-1-0', title: '分组0-1-0', isLeaf: true, checkable: false },
|
||||||
|
{ key: '0-1-1', title: '分组0-1-1', isLeaf: true, checkable: false },
|
||||||
|
{ key: '0-1-2', title: '分组0-1-2', isLeaf: true, checkable: false },
|
||||||
|
{
|
||||||
|
key: '0-1-3',
|
||||||
|
title: '分组0-1-3',
|
||||||
|
isLeaf: false,
|
||||||
|
children: [
|
||||||
|
{ key: '0-1-3-1', title: '分组0-1-3-1', isLeaf: true, isCamera: true },
|
||||||
|
{ key: '0-1-3-2', title: '分组0-1-3-2', isLeaf: true, isCamera: true },
|
||||||
|
{ key: '0-1-3-3', title: '分组0-1-3-3', isLeaf: true, isCamera: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
24
packages/biz/src/treePanel/demo/normal.tsx
Normal file
24
packages/biz/src/treePanel/demo/normal.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React, { useState, useRef } from 'react';
|
||||||
|
import { TreePanel } from '@zhst/biz';
|
||||||
|
import { boxDataSource } from './mock'
|
||||||
|
|
||||||
|
const demo = () => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '12px', width: '240px', border: '1px solid #09f' }}>
|
||||||
|
<TreePanel
|
||||||
|
data={boxDataSource}
|
||||||
|
treeType='normal'
|
||||||
|
showSelectBar
|
||||||
|
filterSelectProps={{
|
||||||
|
placeholder: '请输入',
|
||||||
|
options: [
|
||||||
|
{ label: '测试', key: '123', value: 'test' }
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default demo;
|
135
packages/biz/src/treePanel/demo/withTags.tsx
Normal file
135
packages/biz/src/treePanel/demo/withTags.tsx
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import React, { useState, useRef } from 'react';
|
||||||
|
import { TreePanel } from '@zhst/biz';
|
||||||
|
import { Badge, Checkbox } from '@zhst/meta'
|
||||||
|
import { treeData, boxDataSource } from './mock'
|
||||||
|
import { ImportOutlined, FolderAddOutlined, CloseCircleOutlined, FilterOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
const demo = () => {
|
||||||
|
const [checkedTags, setCheckedTags] = useState<string[]>([]);
|
||||||
|
const [tagExpandAll, setTagExpandAll] = useState(false);
|
||||||
|
const [showTagPanel, setShowTagPanel] = useState(true);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '12px', width: '240px', border: '1px solid #09f' }}>
|
||||||
|
<TreePanel
|
||||||
|
data={boxDataSource}
|
||||||
|
showTagPanel={showTagPanel}
|
||||||
|
tagExpandAll={tagExpandAll}
|
||||||
|
showSelectBar
|
||||||
|
onTagCheck={(value) => setCheckedTags(pre => {
|
||||||
|
if (pre.includes(value)) {
|
||||||
|
return pre.filter(item => item !== value)
|
||||||
|
} else {
|
||||||
|
return [...pre, value]
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
onResetTags={() => setCheckedTags([])}
|
||||||
|
checkedTags={checkedTags}
|
||||||
|
filterList={[
|
||||||
|
{
|
||||||
|
label: '过滤',
|
||||||
|
key: 'multi',
|
||||||
|
icon: <FilterOutlined />,
|
||||||
|
type: 'dropdown',
|
||||||
|
showTooltip: false,
|
||||||
|
dropdownConfig: {
|
||||||
|
menu: {
|
||||||
|
// 自定义返回项
|
||||||
|
_internalRenderMenuItem: (originNode, menuItemProps, stateProps) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{originNode}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
selectable: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: <p style={{ margin: '0', textAlign: 'center' }} >全部</p>,
|
||||||
|
key: 'all',
|
||||||
|
onClick: () => console.log('多选1')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <Badge status="success" />,
|
||||||
|
label: '多选1',
|
||||||
|
key: 'multi1',
|
||||||
|
onClick: () => console.log('多选1')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '多选2',
|
||||||
|
icon: <Badge status='error' />,
|
||||||
|
key: 'multi2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
extra={(
|
||||||
|
<div>
|
||||||
|
<span><Checkbox>全选</Checkbox></span>
|
||||||
|
<a style={{ float: 'right', color: '#09f' }} >批量操作</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
optionList={[
|
||||||
|
{
|
||||||
|
label: '导入盒子',
|
||||||
|
key: 'import',
|
||||||
|
icon: <ImportOutlined />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '新建组',
|
||||||
|
key: 'add',
|
||||||
|
icon: <FolderAddOutlined />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
key: 'del',
|
||||||
|
icon: <CloseCircleOutlined />,
|
||||||
|
props: {
|
||||||
|
danger: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
tagList={[
|
||||||
|
{
|
||||||
|
label: '标签组1',
|
||||||
|
value: '1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '标签1-1',
|
||||||
|
value: '1-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '标签1-2',
|
||||||
|
value: '1-2',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '标签组2',
|
||||||
|
value: '2',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '标签2-1',
|
||||||
|
value: '2-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '标签2-2',
|
||||||
|
value: '2-2',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
onTagExpand={() => {
|
||||||
|
setTagExpandAll(pre => !pre)
|
||||||
|
setCheckedTags([])
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default demo;
|
131
packages/biz/src/treePanel/index.less
Normal file
131
packages/biz/src/treePanel/index.less
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
.zhst-biz-tree-panel {
|
||||||
|
&-search {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-btns {
|
||||||
|
flex: none;
|
||||||
|
&-btn {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-btns {
|
||||||
|
&-btn {
|
||||||
|
margin-right: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: .3s ease all;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
&-label {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-common {
|
||||||
|
flex: 1;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-import {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tags {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #595959;
|
||||||
|
line-height: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tag {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #595959;
|
||||||
|
font-size: 14px;
|
||||||
|
transition:.3s ease all;
|
||||||
|
|
||||||
|
&_common {
|
||||||
|
margin-top: 6px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 8px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #191919;
|
||||||
|
background-color: #EBEBEB;
|
||||||
|
transition: .3s ease all;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-sizing: content-box;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #09f;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_checked {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #09f;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_fz12 {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_option {
|
||||||
|
float: right;
|
||||||
|
&-btn {
|
||||||
|
margin-left: 12px;
|
||||||
|
color: #09f;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: .3s ease all;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_absolute {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tree {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
}
|
82
packages/biz/src/treePanel/index.md
Normal file
82
packages/biz/src/treePanel/index.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
---
|
||||||
|
category: Components
|
||||||
|
title: TreePanel 树面板
|
||||||
|
toc: content
|
||||||
|
demo:
|
||||||
|
cols: 2
|
||||||
|
group:
|
||||||
|
title: 数据展示
|
||||||
|
---
|
||||||
|
|
||||||
|
## 代码演示
|
||||||
|
|
||||||
|
<code src="./demo/basic.tsx">基本用法</code>
|
||||||
|
<code src="./demo/normal.tsx">普通树</code>
|
||||||
|
<code src="./demo/withTags.tsx">标签面板</code>
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| treeType | 树的类型 | 'directory' 'normal' | directory | --- |
|
||||||
|
| searchInputProps | antd-inputProps | --- | --- | --- |
|
||||||
|
| showOptions | --- | boolean | --- | --- |
|
||||||
|
| treeProps | --- | antd-treeProps | --- | --- |
|
||||||
|
| data | --- | TreeDataNode[] | [] | --- |
|
||||||
|
| onSearch | --- | (e: any) => void | - | --- |
|
||||||
|
| onItemCheck | --- | TreeProps['onCheck'] | - | --- |
|
||||||
|
| onItemSelect | --- | TreeProps['onSelect'] | - | --- |
|
||||||
|
| customImport | 自定义搜索栏边上的过滤图标 | ReactNode 、string | - | --- |
|
||||||
|
| extra | 数组件上方插槽 | ReactNode 、string | --- | --- |
|
||||||
|
| prefixCls | class前缀,用于覆盖class | string | --- | --- |
|
||||||
|
| showSelectBar | 显示搜索框 | boolean | false | --- |
|
||||||
|
| filterSelectProps | 搜索框 | antd-SelectProps | - | --- |
|
||||||
|
| showSearchBar | 显示搜索框 | boolean | false | --- |
|
||||||
|
| noFilter | 是否显示搜索拓展 | boolean | false | --- |
|
||||||
|
| filterList | 过滤插槽列表 | IOption[] | [] | --- |
|
||||||
|
| optionList | 操作按钮列表 | IOption[] | [] | --- |
|
||||||
|
| showTagPanel | 显示标签插槽 | boolean | false | --- |
|
||||||
|
| tagList | 标签列表 | ITag[] | [] | --- |
|
||||||
|
| onSelect | 搜索选中事件 | SelectProps['onChange'] | - | --- |
|
||||||
|
| tagExpandAll | 标签展开状态 | boolean | false | --- |
|
||||||
|
| onTagCheck | 标签点击事件 | (value: string, tag: ITag) => void; | false | --- |
|
||||||
|
| checkedTags | 标签选中状态 | string[] | [] | --- |
|
||||||
|
| onResetTags | 重置标签事件 | () => void | - | --- |
|
||||||
|
| onTagExpand | 标签展开事件 | (e: any) => void | - | --- |
|
||||||
|
| tagFootRender | 标签展开状态 | ReactNode, string | false | --- |
|
||||||
|
|
||||||
|
### IOption
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface IOption {
|
||||||
|
label: string
|
||||||
|
key: string
|
||||||
|
icon?: string | ReactNode
|
||||||
|
disabled?: boolean;
|
||||||
|
showTooltip?: boolean;
|
||||||
|
onClick?: () => void
|
||||||
|
className?: string;
|
||||||
|
dropdownConfig?: DropDownProps
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ITag
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface ITag {
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
icon?: ReactNode
|
||||||
|
parentNode?: string
|
||||||
|
children?: ITag[]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 组件设计
|
||||||
|
|
||||||
|
该组件包含以下功能:
|
||||||
|
|
||||||
|
1. 顶部按钮支持
|
||||||
|
2. 输入框单行展示
|
||||||
|
3. 选择框和筛选框同一行
|
||||||
|
4. 按钮列表
|
7
packages/biz/src/treePanel/index.tsx
Normal file
7
packages/biz/src/treePanel/index.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Created by jiangzhixiong on 2024/06/04
|
||||||
|
*/
|
||||||
|
import TreePanel from './TreePanel'
|
||||||
|
export type { TreePanelProps } from './TreePanel'
|
||||||
|
|
||||||
|
export default TreePanel
|
@ -1,91 +1,245 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { ReactNode, useContext } from 'react';
|
||||||
import { Button, Card, ConfigProvider, theme, Flex, Input, InputProps, TransferProps, TreeDataNode, TreeProps, Tree } from 'antd';
|
import { Button, Input, ConfigProvider, theme, Flex, InputProps, TabsProps, Tabs, ButtonProps, Tree, TreeProps, DataNode as TreeDataNode } from '@zhst/meta'
|
||||||
|
import { IconFont } from '@zhst/icon'
|
||||||
import './index.less'
|
import './index.less'
|
||||||
import { DeleteOutlined, DoubleRightOutlined, SearchOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
const componentName = 'zhst-biz-treeTransfer'
|
const { ConfigContext } = ConfigProvider
|
||||||
|
|
||||||
export interface TreeTransferProps {
|
export interface TreeTransferProps {
|
||||||
|
customizePrefixCls?: string
|
||||||
|
/**
|
||||||
|
* @description 自定义标题
|
||||||
|
* @array []
|
||||||
|
*/
|
||||||
|
titles?: string[] | ReactNode[]
|
||||||
|
/**
|
||||||
|
* @description 边框
|
||||||
|
*/
|
||||||
|
bordered?: boolean
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
* 树的数据结构 ,参考antd-treeData
|
||||||
|
*/
|
||||||
dataSource: TreeDataNode[]
|
dataSource: TreeDataNode[]
|
||||||
|
/**
|
||||||
|
* @description 树的透传参数
|
||||||
|
*/
|
||||||
treeProps?: TreeProps
|
treeProps?: TreeProps
|
||||||
|
/**
|
||||||
|
* 是否显示左侧树面板的搜索
|
||||||
|
*/
|
||||||
|
showLeftSearch?: boolean
|
||||||
|
/**
|
||||||
|
* @description 搜索的透传
|
||||||
|
*/
|
||||||
searchInputProps?: InputProps
|
searchInputProps?: InputProps
|
||||||
|
/**
|
||||||
|
* @description 选中的对象
|
||||||
|
*/
|
||||||
targetItems: TreeDataNode[];
|
targetItems: TreeDataNode[];
|
||||||
|
/**
|
||||||
|
* @description 选中对象的id
|
||||||
|
*/
|
||||||
checkedKeys: string[];
|
checkedKeys: string[];
|
||||||
|
/**
|
||||||
|
* @description 是否显示搜索框边上的过滤条件
|
||||||
|
*/
|
||||||
|
showFilter?: boolean;
|
||||||
|
/**
|
||||||
|
* @description 是否显示左侧看板底部渲染
|
||||||
|
*/
|
||||||
|
showLeftPanelFooter?: boolean;
|
||||||
|
/**
|
||||||
|
* @description 左侧看板底部渲染
|
||||||
|
*/
|
||||||
|
leftPanelFooterRender?: ReactNode | string;
|
||||||
|
/**
|
||||||
|
* @description 是否显示右侧看板底部渲染
|
||||||
|
*/
|
||||||
|
showRightPanelFooter?: boolean;
|
||||||
|
/**
|
||||||
|
* 右侧看板底部渲染
|
||||||
|
* @param events
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
rightPanelFooterRender?: (events: { onOk: TreeTransferProps['onOk'], onReset: TreeTransferProps['onReset'] }) => ReactNode | string;
|
||||||
|
/**
|
||||||
|
* @description 左侧看板宽度
|
||||||
|
*/
|
||||||
|
leftPanelWidth?: string | number;
|
||||||
|
/**
|
||||||
|
* @description 右侧看板宽度
|
||||||
|
*/
|
||||||
|
rightPanelWidth?: string | number;
|
||||||
|
/**
|
||||||
|
* @description 左侧看板滚动高度
|
||||||
|
*/
|
||||||
|
leftPanelScrollY?: number;
|
||||||
|
/**
|
||||||
|
* @description 右侧看板滚动高度
|
||||||
|
*/
|
||||||
|
middleIcon?: ReactNode | string
|
||||||
|
rightPanelScrollY?: number;
|
||||||
|
tabsItems?: TabsProps['items']
|
||||||
|
showLeftTabs?: boolean
|
||||||
|
activeTabKey?: string
|
||||||
|
treeBackgroundColor?: string
|
||||||
|
leftTabsProps?: Partial<TabsProps>
|
||||||
|
customLeftPanelContent?: (data?: TreeDataNode[]) => ReactNode
|
||||||
|
filters?: (ButtonProps & {
|
||||||
|
label?: string;
|
||||||
|
icon?: ReactNode;
|
||||||
|
})[]
|
||||||
|
onLeftTabsChange?: TabsProps['onChange']
|
||||||
onTreeSelect?: TreeProps['onSelect']
|
onTreeSelect?: TreeProps['onSelect']
|
||||||
onTreeCheck?: TreeProps['onCheck']
|
onTreeCheck?: TreeProps['onCheck']
|
||||||
onItemDelete?: (key: string, info?: TreeDataNode) => void
|
onItemDelete?: (key: string, info?: TreeDataNode) => void
|
||||||
onChange?: TransferProps['onChange'];
|
|
||||||
onOk?: (data: any) => void;
|
onOk?: (data: any) => void;
|
||||||
onReset?: () => void;
|
onReset?: () => void;
|
||||||
|
onSearch?: InputProps['onChange']
|
||||||
}
|
}
|
||||||
|
|
||||||
const { useToken } = theme
|
const { useToken } = theme
|
||||||
|
|
||||||
const TreeTransfer: React.FC<TreeTransferProps> = ({
|
const TreeTransfer: React.FC<TreeTransferProps> = (props) => {
|
||||||
dataSource,
|
const { getPrefixCls } = useContext(ConfigContext);
|
||||||
treeProps,
|
const componentName = getPrefixCls('biz-treeTransfer', props.customizePrefixCls);
|
||||||
searchInputProps,
|
const {
|
||||||
targetItems = [],
|
dataSource,
|
||||||
checkedKeys = [],
|
treeProps,
|
||||||
onTreeCheck,
|
titles = ['可选择的范围', '已选择的范围'],
|
||||||
onTreeSelect,
|
bordered,
|
||||||
onItemDelete,
|
searchInputProps,
|
||||||
onOk,
|
showLeftSearch = true,
|
||||||
onReset
|
leftPanelScrollY = 300,
|
||||||
}) => {
|
leftPanelWidth = 500,
|
||||||
|
rightPanelScrollY = 422,
|
||||||
|
rightPanelWidth = 300,
|
||||||
|
targetItems = [],
|
||||||
|
checkedKeys = [],
|
||||||
|
showFilter = false,
|
||||||
|
showLeftPanelFooter,
|
||||||
|
showRightPanelFooter = true,
|
||||||
|
customLeftPanelContent,
|
||||||
|
leftPanelFooterRender,
|
||||||
|
rightPanelFooterRender,
|
||||||
|
showLeftTabs,
|
||||||
|
leftTabsProps,
|
||||||
|
middleIcon,
|
||||||
|
activeTabKey,
|
||||||
|
tabsItems = [
|
||||||
|
{
|
||||||
|
key: 'camera',
|
||||||
|
label: <div className={`${componentName}-left_card_tabs_tab`} style={{ textAlign:'center' }} >摄像头</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'plan',
|
||||||
|
label: <div className={`${componentName}-left_card_tabs_tab`} style={{ textAlign:'center' }} >预案</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'map',
|
||||||
|
label: <div className={`${componentName}-left_card_tabs_tab`} style={{ textAlign:'center' }} >地图框选</div>,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
filters = [],
|
||||||
|
onLeftTabsChange,
|
||||||
|
onTreeCheck,
|
||||||
|
onTreeSelect,
|
||||||
|
onItemDelete,
|
||||||
|
onSearch,
|
||||||
|
onOk,
|
||||||
|
onReset
|
||||||
|
} = props
|
||||||
const { token } = useToken()
|
const { token } = useToken()
|
||||||
const [keyWords, setKeyWords ] = useState('')
|
|
||||||
|
|
||||||
function findNodesWithKeyword(_keyWords: string, _treeData: TreeDataNode[]) {
|
|
||||||
// @ts-ignore
|
|
||||||
function dfs(node: any) {
|
|
||||||
return node.filter((item: { title: string | string[]; }) => item.title.includes(_keyWords))
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = dfs(_treeData)
|
|
||||||
return data || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap={20} className={componentName} align='center' justify='center'>
|
<div className={componentName}>
|
||||||
<div className={`${componentName}-left`}>
|
<div className={`${componentName}-left`}
|
||||||
<Card
|
style={{
|
||||||
|
width: typeof leftPanelWidth === 'number' ? leftPanelWidth + 'px' : leftPanelWidth,
|
||||||
|
border: bordered ? `1px solid ${token.colorBorder}` : 'unset',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
className={`${componentName}-left_card`}
|
className={`${componentName}-left_card`}
|
||||||
title={<div style={{ textAlign: 'center' }} >可选择的范围</div>}
|
style={{ backgroundColor: token.colorBgContainer }}
|
||||||
>
|
|
||||||
<Input prefix={<SearchOutlined />} onChange={e => setKeyWords(e.target.value)} placeholder='请输入设备名称' {...searchInputProps} />
|
|
||||||
<ConfigProvider
|
|
||||||
theme={{
|
|
||||||
components: {
|
|
||||||
Tree: {
|
|
||||||
colorBgContainer: '#FCFCFC'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Tree
|
|
||||||
className={`${componentName}-left_card-tree`}
|
|
||||||
height={420}
|
|
||||||
blockNode
|
|
||||||
checkable
|
|
||||||
checkedKeys={checkedKeys}
|
|
||||||
treeData={findNodesWithKeyword(keyWords, dataSource)}
|
|
||||||
onCheck={(keys, info) => onTreeCheck?.(keys, info)}
|
|
||||||
onSelect={(keys, info) => onTreeSelect?.(keys, info)}
|
|
||||||
{...treeProps}
|
|
||||||
/>
|
|
||||||
</ConfigProvider>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<DoubleRightOutlined/>
|
|
||||||
<div className={`${componentName}-right`}>
|
|
||||||
<Card
|
|
||||||
className={`${componentName}-right_card`}
|
|
||||||
title={<div style={{ textAlign: 'center' }}>已选择的范围</div>}
|
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
className={`${componentName}-left_card_title`}
|
||||||
|
style={{ backgroundColor: '#f7f7f7' }}
|
||||||
|
>
|
||||||
|
{titles?.[0]}
|
||||||
|
</div>
|
||||||
|
{showLeftTabs && (
|
||||||
|
<Tabs
|
||||||
|
activeKey={activeTabKey}
|
||||||
|
items={tabsItems}
|
||||||
|
onChange={onLeftTabsChange}
|
||||||
|
{...leftTabsProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showLeftSearch && (
|
||||||
|
<div
|
||||||
|
className={`${componentName}-left_card_search`}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
className={`${componentName}-left_card_search_input`}
|
||||||
|
prefix={<IconFont icon="icon-sousuo" />}
|
||||||
|
placeholder='请输入设备名称'
|
||||||
|
onChange={onSearch}
|
||||||
|
{...searchInputProps}
|
||||||
|
/>
|
||||||
|
{showFilter && (
|
||||||
|
<div
|
||||||
|
className={`${componentName}-left_card_search_filters`}
|
||||||
|
>
|
||||||
|
{filters?.map(item => (
|
||||||
|
<Button icon={item.icon} {...item} >{item.label}</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{customLeftPanelContent?.(dataSource) || (
|
||||||
|
<Tree
|
||||||
|
className={`${componentName}-left_card-tree`}
|
||||||
|
blockNode
|
||||||
|
checkable
|
||||||
|
height={leftPanelScrollY}
|
||||||
|
checkedKeys={checkedKeys}
|
||||||
|
treeData={dataSource}
|
||||||
|
onCheck={(keys: any, info: any) => onTreeCheck?.(keys, info)}
|
||||||
|
onSelect={(keys: any, info: any) => onTreeSelect?.(keys, info)}
|
||||||
|
{...treeProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showLeftPanelFooter && (
|
||||||
|
<div className={`${componentName}-left_card-footer`}>
|
||||||
|
{leftPanelFooterRender}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={`${componentName}-middle`}>
|
||||||
|
{middleIcon || <IconFont icon="icon-zhankai" />}
|
||||||
|
</div>
|
||||||
|
<div className={`${componentName}-right`}
|
||||||
|
style={{
|
||||||
|
width: typeof rightPanelWidth === 'number' ? rightPanelWidth + 'px' : rightPanelWidth,
|
||||||
|
border: bordered ? `1px solid ${token.colorBorder}` : 'unset',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`${componentName}-right_card`}
|
||||||
|
style={{ backgroundColor: token.colorBgContainer }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`${componentName}-right_card_title`}
|
||||||
|
style={{ backgroundColor: '#f7f7f7' }}
|
||||||
|
>{titles?.[1]}</div>
|
||||||
<div
|
<div
|
||||||
className={`${componentName}-right_card__items`}
|
className={`${componentName}-right_card__items`}
|
||||||
|
style={{ height: typeof rightPanelScrollY === 'number' ? rightPanelScrollY + 'px' : rightPanelScrollY}}
|
||||||
>
|
>
|
||||||
{targetItems.map(item => (
|
{targetItems.map(item => (
|
||||||
<div
|
<div
|
||||||
@ -102,8 +256,7 @@ const TreeTransfer: React.FC<TreeTransferProps> = ({
|
|||||||
>
|
>
|
||||||
{item.title as any}
|
{item.title as any}
|
||||||
<div style={{ float: 'right' }}>
|
<div style={{ float: 'right' }}>
|
||||||
<DeleteOutlined onClick={() => {
|
<IconFont icon="icon-shanchu" onIconClick={() => {
|
||||||
// const { root, keys } = getAllRootKeyById(item.key as string, dataSource)
|
|
||||||
onItemDelete?.(item.key as string, item)
|
onItemDelete?.(item.key as string, item)
|
||||||
}} />
|
}} />
|
||||||
</div>
|
</div>
|
||||||
@ -111,19 +264,25 @@ const TreeTransfer: React.FC<TreeTransferProps> = ({
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<Flex
|
{showRightPanelFooter && (
|
||||||
className={`${componentName}-right_card__btns`}
|
<Flex
|
||||||
>
|
className={`${componentName}-right_card__btns`}
|
||||||
<Button style={{ marginRight: 8, width: '50%' }} disabled={targetItems.length <= 0} onClick={onReset}>重置</Button>
|
>
|
||||||
<Button
|
{rightPanelFooterRender?.({ onOk, onReset }) || (
|
||||||
style={{ width: '50%' }}
|
<>
|
||||||
type='primary'
|
<Button style={{ marginRight: 8, width: '50%' }} disabled={targetItems.length <= 0} onClick={onReset}>重置</Button>
|
||||||
onClick={() => onOk?.(targetItems)}
|
<Button
|
||||||
>确定</Button>
|
style={{ width: '50%' }}
|
||||||
</Flex>
|
type='primary'
|
||||||
</Card>
|
onClick={() => onOk?.(targetItems)}
|
||||||
|
>确定</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,10 +42,18 @@ const App: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TreeTransfer
|
<TreeTransfer
|
||||||
|
leftPanelWidth={800}
|
||||||
|
rightPanelWidth={500}
|
||||||
|
showLeftPanelFooter
|
||||||
dataSource={boxDataSource}
|
dataSource={boxDataSource}
|
||||||
targetItems={targetItems}
|
targetItems={targetItems}
|
||||||
checkedKeys={checkedKeys}
|
checkedKeys={checkedKeys}
|
||||||
onTreeCheck={onTreeCheck}
|
onTreeCheck={onTreeCheck}
|
||||||
|
leftPanelFooterRender={(
|
||||||
|
<div style={{ padding: '6px', color: 'red' }}>
|
||||||
|
自定义脚部dom
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
onItemDelete={onItemDelete}
|
onItemDelete={onItemDelete}
|
||||||
onOk={onOk}
|
onOk={onOk}
|
||||||
onReset={onReset}
|
onReset={onReset}
|
||||||
|
71
packages/biz/src/treeTransfer/demo/mingmou.tsx
Normal file
71
packages/biz/src/treeTransfer/demo/mingmou.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { TreeTransfer } from '@zhst/biz';
|
||||||
|
import { TreeDataNode } from 'antd';
|
||||||
|
import { TreeProps } from 'antd/lib';
|
||||||
|
import { boxDataSource } from './mock'
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
const [targetItems, setTargetItems] = useState<TreeDataNode[]>([]);
|
||||||
|
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const onTreeCheck: TreeProps['onCheck'] = (keys: any, info) => {
|
||||||
|
let _targetItems: TreeDataNode[] = []
|
||||||
|
setCheckedKeys(keys)
|
||||||
|
info.checkedNodes.forEach(o => {
|
||||||
|
o.isLeaf && _targetItems.push(o)
|
||||||
|
})
|
||||||
|
setTargetItems(_targetItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param key
|
||||||
|
* @param param1
|
||||||
|
*/
|
||||||
|
const onItemDelete = (key: any, { keys = [] }: any) => {
|
||||||
|
setCheckedKeys(pre => {
|
||||||
|
const newKeys = pre.filter(_key => _key !== key)
|
||||||
|
console.log('newKeys', newKeys, keys)
|
||||||
|
return newKeys
|
||||||
|
})
|
||||||
|
setTargetItems(pre => pre.filter(o => o.key !== key))
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOk = (data: any) => {
|
||||||
|
console.log('data', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
setCheckedKeys([])
|
||||||
|
setTargetItems([])
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: 24 }}>
|
||||||
|
<TreeTransfer
|
||||||
|
leftPanelWidth={800}
|
||||||
|
rightPanelWidth={500}
|
||||||
|
bordered
|
||||||
|
titles={[
|
||||||
|
<div style={{ textAlign: 'left' }}>可选择的范围(123)</div>,
|
||||||
|
(
|
||||||
|
<div onClick={onReset} style={{ textAlign: 'left' }}>已选范围(123)<a style={{ float: 'right' }}>清空</a></div>
|
||||||
|
),
|
||||||
|
]}
|
||||||
|
showRightPanelFooter={false}
|
||||||
|
dataSource={boxDataSource}
|
||||||
|
targetItems={targetItems}
|
||||||
|
checkedKeys={checkedKeys}
|
||||||
|
onTreeCheck={onTreeCheck}
|
||||||
|
onItemDelete={onItemDelete}
|
||||||
|
onOk={onOk}
|
||||||
|
onReset={onReset}
|
||||||
|
searchInputProps={{
|
||||||
|
onChange: e => console.log('123123')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
57
packages/biz/src/treeTransfer/demo/noSearch.tsx
Normal file
57
packages/biz/src/treeTransfer/demo/noSearch.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { TreeTransfer } from '@zhst/biz';
|
||||||
|
import { TreeDataNode } from 'antd';
|
||||||
|
import { TreeProps } from 'antd/lib';
|
||||||
|
import { boxDataSource } from './mock'
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
const [targetItems, setTargetItems] = useState<TreeDataNode[]>([]);
|
||||||
|
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const onTreeCheck: TreeProps['onCheck'] = (keys: any, info) => {
|
||||||
|
let _targetItems: TreeDataNode[] = []
|
||||||
|
setCheckedKeys(keys)
|
||||||
|
info.checkedNodes.forEach(o => {
|
||||||
|
o.isLeaf && _targetItems.push(o)
|
||||||
|
})
|
||||||
|
setTargetItems(_targetItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param key
|
||||||
|
* @param param1
|
||||||
|
*/
|
||||||
|
const onItemDelete = (key: any, { keys = [] }: any) => {
|
||||||
|
setCheckedKeys(pre => {
|
||||||
|
const newKeys = pre.filter(_key => _key !== key)
|
||||||
|
console.log('newKeys', newKeys, keys)
|
||||||
|
return newKeys
|
||||||
|
})
|
||||||
|
setTargetItems(pre => pre.filter(o => o.key !== key))
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOk = (data: any) => {
|
||||||
|
console.log('data', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
setCheckedKeys([])
|
||||||
|
setTargetItems([])
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TreeTransfer
|
||||||
|
dataSource={boxDataSource}
|
||||||
|
targetItems={targetItems}
|
||||||
|
checkedKeys={checkedKeys}
|
||||||
|
onTreeCheck={onTreeCheck}
|
||||||
|
showLeftSearch={false}
|
||||||
|
onItemDelete={onItemDelete}
|
||||||
|
onOk={onOk}
|
||||||
|
onReset={onReset}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
66
packages/biz/src/treeTransfer/demo/withFilter.tsx
Normal file
66
packages/biz/src/treeTransfer/demo/withFilter.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { TreeTransfer } from '@zhst/biz';
|
||||||
|
import { TreeDataNode } from 'antd';
|
||||||
|
import { IconFont } from '@zhst/icon'
|
||||||
|
import { TreeProps } from 'antd/lib';
|
||||||
|
import { boxDataSource } from './mock'
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
const [targetItems, setTargetItems] = useState<TreeDataNode[]>([]);
|
||||||
|
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const onTreeCheck: TreeProps['onCheck'] = (keys: any, info) => {
|
||||||
|
let _targetItems: TreeDataNode[] = []
|
||||||
|
setCheckedKeys(keys)
|
||||||
|
info.checkedNodes.forEach(o => {
|
||||||
|
o.isLeaf && _targetItems.push(o)
|
||||||
|
})
|
||||||
|
setTargetItems(_targetItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param key
|
||||||
|
* @param param1
|
||||||
|
*/
|
||||||
|
const onItemDelete = (key: any, { keys = [] }: any) => {
|
||||||
|
setCheckedKeys(pre => {
|
||||||
|
const newKeys = pre.filter(_key => _key !== key)
|
||||||
|
console.log('newKeys', newKeys, keys)
|
||||||
|
return newKeys
|
||||||
|
})
|
||||||
|
setTargetItems(pre => pre.filter(o => o.key !== key))
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOk = (data: any) => {
|
||||||
|
console.log('data', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
setCheckedKeys([])
|
||||||
|
setTargetItems([])
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TreeTransfer
|
||||||
|
dataSource={boxDataSource}
|
||||||
|
targetItems={targetItems}
|
||||||
|
checkedKeys={checkedKeys}
|
||||||
|
onTreeCheck={onTreeCheck}
|
||||||
|
showFilter
|
||||||
|
filters={[
|
||||||
|
{
|
||||||
|
label: '',
|
||||||
|
icon: <IconFont icon="icon-bangzhu2" />,
|
||||||
|
danger: true,
|
||||||
|
onClick: () => {}
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onItemDelete={onItemDelete}
|
||||||
|
onOk={onOk}
|
||||||
|
onReset={onReset}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
103
packages/biz/src/treeTransfer/demo/withMap.tsx
Normal file
103
packages/biz/src/treeTransfer/demo/withMap.tsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
import { TreeTransfer } from '@zhst/biz';
|
||||||
|
import { TreeDataNode } from 'antd';
|
||||||
|
import { TreeProps } from 'antd/lib';
|
||||||
|
import { MapBox } from '@zhst/map'
|
||||||
|
import { boxDataSource } from './mock'
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
const [targetItems, setTargetItems] = useState<TreeDataNode[]>([]);
|
||||||
|
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
|
||||||
|
const [activeTabKey, setActiveTabKey] = useState<string>('camera');
|
||||||
|
const mapRef = useRef(null);
|
||||||
|
const [canDraw, setCanDraw] = useState(false)
|
||||||
|
const [toolsBarOpen, setToolsBarOpen] = useState(false)
|
||||||
|
|
||||||
|
// 地图初始化
|
||||||
|
const handleMapLoad = (e: mapboxgl.MapboxEvent<undefined>) => {
|
||||||
|
const map = e.target;
|
||||||
|
|
||||||
|
if (!map) return
|
||||||
|
|
||||||
|
map.flyTo({
|
||||||
|
// center: [120,30],
|
||||||
|
// zoom: map?.getMaxZoom(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTreeCheck: TreeProps['onCheck'] = (keys: any, info) => {
|
||||||
|
let _targetItems: TreeDataNode[] = []
|
||||||
|
setCheckedKeys(keys)
|
||||||
|
info.checkedNodes.forEach(o => {
|
||||||
|
o.isLeaf && _targetItems.push(o)
|
||||||
|
})
|
||||||
|
setTargetItems(_targetItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param key
|
||||||
|
* @param param1
|
||||||
|
*/
|
||||||
|
const onItemDelete = (key: any, { keys = [] }: any) => {
|
||||||
|
setCheckedKeys(pre => {
|
||||||
|
const newKeys = pre.filter(_key => _key !== key)
|
||||||
|
console.log('newKeys', newKeys, keys)
|
||||||
|
return newKeys
|
||||||
|
})
|
||||||
|
setTargetItems(pre => pre.filter(o => o.key !== key))
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOk = (data: any) => {
|
||||||
|
console.log('data', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
setCheckedKeys([])
|
||||||
|
setTargetItems([])
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TreeTransfer
|
||||||
|
dataSource={boxDataSource}
|
||||||
|
targetItems={targetItems}
|
||||||
|
checkedKeys={checkedKeys}
|
||||||
|
leftPanelWidth={800}
|
||||||
|
rightPanelWidth={600}
|
||||||
|
onTreeCheck={onTreeCheck}
|
||||||
|
showLeftTabs
|
||||||
|
activeTabKey={activeTabKey}
|
||||||
|
onLeftTabsChange={val => setActiveTabKey(val)}
|
||||||
|
customLeftPanelContent={(data) => {
|
||||||
|
if (activeTabKey !== 'map') return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
>
|
||||||
|
<MapBox
|
||||||
|
onLoad={handleMapLoad}
|
||||||
|
ref={mapRef}
|
||||||
|
width='100%'
|
||||||
|
height="402px"
|
||||||
|
draw={canDraw}
|
||||||
|
toolsBarOpen={toolsBarOpen}
|
||||||
|
onToolClick={e => {
|
||||||
|
setCanDraw(pre => !pre)
|
||||||
|
setToolsBarOpen(pre => !pre)
|
||||||
|
}}
|
||||||
|
drawerProps={{
|
||||||
|
onActionable: e => console.log('e', e)
|
||||||
|
}}
|
||||||
|
></MapBox>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
onItemDelete={onItemDelete}
|
||||||
|
onOk={onOk}
|
||||||
|
onReset={onReset}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
@ -1,28 +1,90 @@
|
|||||||
.zhst-biz-treeTransfer {
|
.zhst-biz-treeTransfer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
&-left {
|
&-left {
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
&_card {
|
&_card {
|
||||||
width: 500px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
min-height: 544px;
|
min-height: 544px;
|
||||||
background-color: #FCFCFC;
|
background-color: #FCFCFC;
|
||||||
|
border: 2px solid #f7f7f7;
|
||||||
|
|
||||||
|
&_title {
|
||||||
|
padding: 12px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 2px solid #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tabs {
|
||||||
|
.ant-tabs-nav {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.ant-tabs-nav-list {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.ant-tabs-tab {
|
||||||
|
margin: 0;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_search {
|
||||||
|
display: flex;
|
||||||
|
margin: 16px 20px;
|
||||||
|
&_input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
&_filters {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-tree {
|
&-tree {
|
||||||
margin-top: 6px;
|
margin: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-footer {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-middle {
|
||||||
|
padding: 24px;
|
||||||
|
height: 100%;
|
||||||
|
color: #d9d9d9;
|
||||||
|
transform-origin: center center;
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
&-right {
|
&-right {
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
&_card {
|
&_card {
|
||||||
width: 300px;
|
position: relative;
|
||||||
min-height: 544px;
|
min-height: 544px;
|
||||||
background-color: #FCFCFC;
|
background-color: #FCFCFC;
|
||||||
|
border: 2px solid #f7f7f7;
|
||||||
|
|
||||||
|
&_title {
|
||||||
|
padding: 12px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 2px solid #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
&__items {
|
&__items {
|
||||||
padding: 8px 4px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 105px);
|
font-size: 14px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
max-height: 422px;
|
box-sizing: border-box;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
@ -44,7 +106,7 @@
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-top: 1px solid #f0f0f0;
|
border-top: 2px solid #f0f0f0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,42 @@ group:
|
|||||||
## 代码演示
|
## 代码演示
|
||||||
|
|
||||||
<code src="./demo/basic.tsx">基本用法</code>
|
<code src="./demo/basic.tsx">基本用法</code>
|
||||||
|
<code src="./demo/noSearch.tsx">没有搜索框</code>
|
||||||
|
<code src="./demo/withFilter.tsx">加载filter按钮</code>
|
||||||
|
<code src="./demo/withMap.tsx">和地图组件搭配使用</code>
|
||||||
<code src="./demo/withModal.tsx">和Modal组合使用</code>
|
<code src="./demo/withModal.tsx">和Modal组合使用</code>
|
||||||
|
<code src="./demo/mingmou.tsx">明眸同款</code>
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| data | 数据源 | Array[] | [] | - |
|
| titles | 顶部标题 | string[]、ReactNode[] | [] | - |
|
||||||
|
| dataSource | 数据 | TreeDataNode[] | [] | - |
|
||||||
|
| treeProps | 左侧树的props | antd-tree | [] | - |
|
||||||
|
| showLeftSearch | 显示左侧搜索框 | boolean | true | - |
|
||||||
|
| searchInputProps | 左侧搜索透传 | InputProps | {} | - |
|
||||||
|
| targetItems | 选中数据 | TreeDataNode[] | [] | - |
|
||||||
|
| checkedKeys | 选中的key数组 | string[] | [] | - |
|
||||||
|
| showFilter | 是否显示搜索框边上的dom | boolean | false | - |
|
||||||
|
| leftPanelWidth | 左边面板高度 | string、number | - | - |
|
||||||
|
| leftPanelScrollY | 左边面板滚动高度 | number | - | - |
|
||||||
|
| showLeftPanelFooter | 是否显示左边面板的底部 | boolean | false | - |
|
||||||
|
| leftPanelFooterRender | 左边面板底部自定义 | | ReactNode、string | - |
|
||||||
|
| rightPanelWidth | 右边面板高度 | number、string | - | - |
|
||||||
|
| rightPanelScrollY | 右边面板滚动高度 | number | - | - |
|
||||||
|
| showRightPanelFooter | 是否显示右边面板的底部 | boolean | false | - |
|
||||||
|
| rightPanelFooterRender | 右边边面板底部自定义 | ReactNode、string | - | - |
|
||||||
|
| tabsItems | tab列表 | 参考antd-tabs组件的 tabsProps['items'] | - | - |
|
||||||
|
| showLeftTabs | 是否显示tabs组件 | boolean | false | - |
|
||||||
|
| activeTabKey | 当前选中tab | string | - | - |
|
||||||
|
| treeBackgroundColor | 左侧树颜色 | string | #FCFCFC | - |
|
||||||
|
| leftTabsProps | 左侧树tabs的props | tabsProps | - | - |
|
||||||
|
| customLeftPanelContent | 左侧树自定义内容 | (data?: TreeDataNode[]) => ReactNode | - | - |
|
||||||
|
| filters | 左侧过滤自定义 | ButtonProps & { label: string; icon?: string } | - | - |
|
||||||
|
| onLeftTabsChange | 左侧tab点击事件 | TabsProps['onChange'] | - | - |
|
||||||
|
| onTreeSelect | 左侧树点击事件 | TreeProps['onSelect'] | - | - |
|
||||||
|
| onTreeCheck | 左侧树勾选☑️事件 | TreeProps['onCheck'] | - | - |
|
||||||
|
| onOk | 提交事件 | (data: any) => void; | - | - |
|
||||||
|
| onReset | 重置事件 | () => void; | - | - |
|
||||||
|
| onSearch | 左侧树搜索事件 | InputProps['onChange'] | - | - |
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
import React, { FC, useState } from 'react';
|
import React, { FC, useState } from 'react';
|
||||||
import { Modal, ModalProps, Radio, RadioGroupProps, Select, SelectProps, TransferProps, TreeDataNode, TreeProps } from 'antd';
|
import { Modal, ModalProps, Radio, RadioGroupProps, Select, SelectProps, DataNode, TreeProps } from '@zhst/meta';
|
||||||
import TreeTransfer from '../treeTransfer';
|
import TreeTransfer from '../treeTransfer';
|
||||||
import { TreeTransferProps } from '../treeTransfer'
|
import { TreeTransferProps } from '../treeTransfer'
|
||||||
|
|
||||||
export interface TreeTransferModalProps {
|
export interface TreeTransferModalProps {
|
||||||
dataSource: TreeDataNode[]
|
dataSource: DataNode[]
|
||||||
treeProps?: TreeProps
|
treeProps?: TreeProps
|
||||||
targetItems: TreeDataNode[];
|
targetItems: DataNode[];
|
||||||
checkedKeys: string[];
|
checkedKeys: string[];
|
||||||
onTreeSelect?: TreeProps['onSelect']
|
onTreeSelect?: TreeProps['onSelect']
|
||||||
onTreeCheck?: TreeProps['onCheck']
|
onTreeCheck?: TreeProps['onCheck']
|
||||||
onItemDelete?: TreeTransferProps['onItemDelete']
|
onItemDelete?: TreeTransferProps['onItemDelete']
|
||||||
onChange?: TransferProps['onChange'];
|
|
||||||
onOk?: (data: any) => void;
|
onOk?: (data: any) => void;
|
||||||
onReset?: () => void;
|
onReset?: () => void;
|
||||||
open?: boolean
|
open?: boolean
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
# @zhst/biz
|
# @zhst/biz
|
||||||
|
|
||||||
|
## 0.7.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 修改 less to css 配置
|
||||||
|
|
||||||
## 0.6.2
|
## 0.6.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
export var OBJECT_GRNER_THRESHOLD = 0.8; //目标图判断性别阈值
|
|
||||||
export var OBJECT_AGE_TYPE_THRESHOLD = 0.5; //目标图判断年龄段阈值
|
|
||||||
export var MODE_KEY = 'test_mode';
|
|
||||||
//业务约定
|
|
||||||
export var SEARCH_IMG_COUNT = 10; //检索图片的最大个数
|
|
||||||
export var GLOBAL_IS_ITEM_NUMBER_SHOW = false; //是否展示索引
|
|
||||||
export var publicPath = 'hummingbird';
|
|
||||||
export var ENTER_CIRCLE = 'MONITORTYPE_ENTER_CIRCLE';
|
|
||||||
export var OUT_CIRCLE = 'MONITORTYPE_OUT_CIRCLE';
|
|
||||||
export var TEMP = 'MONITORTYPE_TEMP';
|
|
||||||
export var GLOBAL_IS_BOX_VMS_SHOW = true; //是否展示盒子vms
|
|
||||||
|
|
||||||
export var BODY_SEARCH_THRESHOID = 0.45; //形体检索阈值
|
|
||||||
export var RECORD_VERSION = '3.0.0'; //保存记录的版本号
|
|
||||||
|
|
||||||
export var DeviceTab = {
|
|
||||||
EMPTY: 0,
|
|
||||||
REAL_CAMERA: 1,
|
|
||||||
PREPROCESS_CAMERA: 2,
|
|
||||||
//摄像头列表
|
|
||||||
TAG_CAMERA: 3,
|
|
||||||
//预案列表
|
|
||||||
HISTORY_VIDEO_GROUP: 4,
|
|
||||||
//录像回放
|
|
||||||
VIRTUAL_CAMERA: 5,
|
|
||||||
//离线视频
|
|
||||||
REAL_CAMERA_NOFACE: 6,
|
|
||||||
REAL_CAMERA_ONLYFACE: 7,
|
|
||||||
REAL_CAMERA_NOFACE_NOBOX_NODIRECONNECT: 8 // 只有普通摄像头,没有人脸、没有盒子、直连
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
export var TYPE = '';
|
|
@ -1,3 +0,0 @@
|
|||||||
export * from "./camera";
|
|
||||||
export * from "./base";
|
|
||||||
export * from "./user";
|
|
@ -1 +0,0 @@
|
|||||||
export default {};
|
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@zhst/constants",
|
"name": "@zhst/constants",
|
||||||
"version": "0.6.2",
|
"version": "0.7.0",
|
||||||
"description": "常量库",
|
"description": "常量库",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"constants",
|
"constants",
|
||||||
|
@ -1,5 +1,24 @@
|
|||||||
# @zhst/utils
|
# @zhst/utils
|
||||||
|
|
||||||
|
## 0.17.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 修改 less to css 配置
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/request@0.17.0
|
||||||
|
|
||||||
|
## 0.16.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 穿梭框完成,新增 meta 组件
|
||||||
|
- Updated dependencies
|
||||||
|
- @zhst/request@0.16.1
|
||||||
|
|
||||||
## 0.16.0
|
## 0.16.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
1
packages/func/dist/func.min.js
vendored
1
packages/func/dist/func.min.js
vendored
File diff suppressed because one or more lines are too long
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;
|
|
||||||
}[];
|
|
||||||
};
|
|
@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* 设备类型枚举
|
|
||||||
*/
|
|
||||||
export var DeviceType = {
|
|
||||||
VMS: 'vms',
|
|
||||||
DIR: 'dir',
|
|
||||||
CAMERA: 'camera'
|
|
||||||
};
|
|
||||||
export var LOCAL_KEY = 'local'; //离线摄像头key 约定
|
|
||||||
export var DIRE_CONNECT_KEY = 'direconnect';
|
|
||||||
export var BOX_LIST_KEY = 'boxlist';
|
|
||||||
export var VmsplatformOpt = /*#__PURE__*/function (VmsplatformOpt) {
|
|
||||||
VmsplatformOpt[VmsplatformOpt["VMSPLATFORMOPT_ID"] = 0] = "VMSPLATFORMOPT_ID";
|
|
||||||
VmsplatformOpt[VmsplatformOpt["VMSPLATFORMOPT_PLATFORMNAME"] = 1] = "VMSPLATFORMOPT_PLATFORMNAME";
|
|
||||||
VmsplatformOpt[VmsplatformOpt["VMSPLATFORMOPT_PLUGINNAME"] = 2] = "VMSPLATFORMOPT_PLUGINNAME";
|
|
||||||
VmsplatformOpt[VmsplatformOpt["VMSPLATFORMOPT_IP"] = 3] = "VMSPLATFORMOPT_IP";
|
|
||||||
VmsplatformOpt[VmsplatformOpt["VMSPLATFORMOPT_PORT"] = 4] = "VMSPLATFORMOPT_PORT";
|
|
||||||
VmsplatformOpt[VmsplatformOpt["VMSPLATFORMOPT_USERNAME"] = 5] = "VMSPLATFORMOPT_USERNAME";
|
|
||||||
VmsplatformOpt[VmsplatformOpt["VMSPLATFORMOPT_PASSWORD"] = 6] = "VMSPLATFORMOPT_PASSWORD";
|
|
||||||
return VmsplatformOpt;
|
|
||||||
}({});
|
|
||||||
export var OPT = /*#__PURE__*/function (OPT) {
|
|
||||||
OPT[OPT["OR"] = 0] = "OR";
|
|
||||||
OPT[OPT["AND"] = 1] = "AND";
|
|
||||||
OPT[OPT["ORNOT"] = 2] = "ORNOT";
|
|
||||||
OPT[OPT["ANDNOT"] = 3] = "ANDNOT";
|
|
||||||
return OPT;
|
|
||||||
}({});
|
|
||||||
export var DevicemanagerCameraType = /*#__PURE__*/function (DevicemanagerCameraType) {
|
|
||||||
DevicemanagerCameraType[DevicemanagerCameraType["DEVICEMANAGER_CAMERA_TYPE_DEFAULT"] = 0] = "DEVICEMANAGER_CAMERA_TYPE_DEFAULT";
|
|
||||||
DevicemanagerCameraType[DevicemanagerCameraType["DEVICEMANAGER_CAMERA_TYPE_NORMAL"] = 1] = "DEVICEMANAGER_CAMERA_TYPE_NORMAL";
|
|
||||||
DevicemanagerCameraType[DevicemanagerCameraType["DEVICEMANAGER_CAMERA_TYPE_1400"] = 97] = "DEVICEMANAGER_CAMERA_TYPE_1400";
|
|
||||||
DevicemanagerCameraType[DevicemanagerCameraType["DEVICEMANAGER_CAMERA_TYPE_DHGRABBER"] = 98] = "DEVICEMANAGER_CAMERA_TYPE_DHGRABBER";
|
|
||||||
DevicemanagerCameraType[DevicemanagerCameraType["DEVICEMANAGER_CAMERA_TYPE_HKGRABBER"] = 99] = "DEVICEMANAGER_CAMERA_TYPE_HKGRABBER";
|
|
||||||
DevicemanagerCameraType[DevicemanagerCameraType["DEVICEMANAGER_CAMERA_TYPE_LOCAL"] = 100] = "DEVICEMANAGER_CAMERA_TYPE_LOCAL";
|
|
||||||
return DevicemanagerCameraType;
|
|
||||||
}({});
|
|
||||||
|
|
||||||
// 盒子 直连 平台
|
|
||||||
export var BOX_DIRECONNECT_PLATFORM_FILTER = {
|
|
||||||
filtervmsplatformList: [{
|
|
||||||
opt: OPT.OR,
|
|
||||||
vmsplatformOpt: VmsplatformOpt.VMSPLATFORMOPT_PLATFORMNAME,
|
|
||||||
value: 'direconnect'
|
|
||||||
}, {
|
|
||||||
opt: OPT.OR,
|
|
||||||
vmsplatformOpt: VmsplatformOpt.VMSPLATFORMOPT_PLATFORMNAME,
|
|
||||||
value: 'boxlist'
|
|
||||||
}]
|
|
||||||
};
|
|
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[];
|
|
@ -1,142 +0,0 @@
|
|||||||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
||||||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
||||||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
||||||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
||||||
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
||||||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
||||||
import { get, has, isString } from 'lodash-es';
|
|
||||||
import { loop } from "../utils";
|
|
||||||
import { DevicemanagerCameraType, DeviceType } from "./constants";
|
|
||||||
export var isFaceCamera = function isFaceCamera(type) {
|
|
||||||
return [DevicemanagerCameraType.DEVICEMANAGER_CAMERA_TYPE_1400, DevicemanagerCameraType.DEVICEMANAGER_CAMERA_TYPE_HKGRABBER, DevicemanagerCameraType.DEVICEMANAGER_CAMERA_TYPE_DHGRABBER].includes(type);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param value 传入的数据 可以是 item(camera/vms/dirs)/ deviceID
|
|
||||||
* @param isId
|
|
||||||
*/
|
|
||||||
export function getDeviceType(value) {
|
|
||||||
var type;
|
|
||||||
var isDeviceKey = isString(value);
|
|
||||||
if (isDeviceKey) {
|
|
||||||
type = value.split('_')[0];
|
|
||||||
} else {
|
|
||||||
if (has(value, 'longitude')) {
|
|
||||||
type = DeviceType['CAMERA'];
|
|
||||||
}
|
|
||||||
if (has(value, 'ip')) {
|
|
||||||
type = DeviceType['VMS'];
|
|
||||||
}
|
|
||||||
if (!type) {
|
|
||||||
type = DeviceType['DIR'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 后端设备id/vmsid/dirid是三张表 合并在一起不保证唯一 前端生成唯一key
|
|
||||||
* @param id 设备id
|
|
||||||
* @param type 设备类型
|
|
||||||
*/
|
|
||||||
export function deviceIDToDeviceKey(id, type, vmsId) {
|
|
||||||
if (type == DeviceType['DIR']) {
|
|
||||||
return "".concat(type, "_").concat(id, "_").concat(vmsId);
|
|
||||||
} else {
|
|
||||||
return "".concat(type, "_").concat(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 后端设备id/vmsid/dirid是三张表 合并在一起不保证唯一 前端生成唯一key
|
|
||||||
* @param item camera/vms/dirs
|
|
||||||
*/
|
|
||||||
export function deviceToDeviceKey(item) {
|
|
||||||
var deviceKey = '';
|
|
||||||
var type = getDeviceType(item);
|
|
||||||
if (!type) {
|
|
||||||
console.error('device type is null!');
|
|
||||||
}
|
|
||||||
switch (type) {
|
|
||||||
case DeviceType['DIR']:
|
|
||||||
{
|
|
||||||
var dirId = item['dirid'] || item['dirId'];
|
|
||||||
if (!dirId && dirId !== 0) {
|
|
||||||
console.error('dirId type is null!');
|
|
||||||
}
|
|
||||||
var vmsId = get(item, 'extendInfo.vmsPlatformId');
|
|
||||||
if (!vmsId && vmsId !== 0) {
|
|
||||||
console.error('vmsId type is null!');
|
|
||||||
}
|
|
||||||
deviceKey = "".concat(type, "_").concat(dirId, "_").concat(vmsId);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DeviceType['VMS']:
|
|
||||||
deviceKey = "".concat(type, "_").concat(item['id']);
|
|
||||||
break;
|
|
||||||
case DeviceType['CAMERA']:
|
|
||||||
{
|
|
||||||
var _vmsId = get(item, 'extendInfo.vmsPlatformId');
|
|
||||||
if (!_vmsId && _vmsId !== 0) {
|
|
||||||
console.error('vmsId type is null!');
|
|
||||||
}
|
|
||||||
deviceKey = "".concat(type, "_").concat(item.id);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return deviceKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备树key 转 后端设备原始id dirid是string/vms&camera 是number 和后端保持一致
|
|
||||||
* @param deviceKey 设备树的id
|
|
||||||
*/
|
|
||||||
export function deviceKeyToDeviceId(deviceKey) {
|
|
||||||
var _deviceKey$split = deviceKey.split('_'),
|
|
||||||
_deviceKey$split2 = _slicedToArray(_deviceKey$split, 2),
|
|
||||||
type = _deviceKey$split2[0],
|
|
||||||
id = _deviceKey$split2[1];
|
|
||||||
return type === DeviceType['DIR'] ? id : Number(id);
|
|
||||||
}
|
|
||||||
export var getVmsIdByDeviceId = function getVmsIdByDeviceId(key) {
|
|
||||||
var type = getDeviceType(key);
|
|
||||||
var vmsId = '';
|
|
||||||
switch (type) {
|
|
||||||
case DeviceType['CAMERA']:
|
|
||||||
case DeviceType['DIR']:
|
|
||||||
vmsId = key.split('_')[2];
|
|
||||||
break;
|
|
||||||
case DeviceType['VMS']:
|
|
||||||
vmsId = key.split('_')[1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!vmsId) {
|
|
||||||
console.error('vmsid is null!');
|
|
||||||
}
|
|
||||||
return vmsId;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过设备id或设备key在树里面找摄像头
|
|
||||||
* @param ids cameraId
|
|
||||||
* @param deviceTree 树
|
|
||||||
* @param type "id" | "key"
|
|
||||||
*/
|
|
||||||
export var findCamerasByInDeviceTree = function findCamerasByInDeviceTree() {
|
|
||||||
var ids = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
||||||
var deviceTree = arguments.length > 1 ? arguments[1] : undefined;
|
|
||||||
var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'id';
|
|
||||||
var cameraInfoList = [];
|
|
||||||
var _ids = ids.map(function (v) {
|
|
||||||
return String(v);
|
|
||||||
}); //都转string在做判断 保证格式一致
|
|
||||||
loop(deviceTree, function (item) {
|
|
||||||
var isCamera = getDeviceType(get(item, 'key', '')) === DeviceType['CAMERA'];
|
|
||||||
var isMatch = type === 'key' ? _ids.includes(get(item, 'key')) : _ids.includes("".concat(get(item, 'origin.id')));
|
|
||||||
if (isCamera && isMatch) {
|
|
||||||
cameraInfoList.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return cameraInfoList;
|
|
||||||
};
|
|
80
packages/func/es/file/index.d.ts
vendored
80
packages/func/es/file/index.d.ts
vendored
@ -1,80 +0,0 @@
|
|||||||
export type Rect = {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @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;
|
|
||||||
/**
|
|
||||||
* 获取指定字符串后面的部分
|
|
||||||
* @param imageKey v1_开头的字符串
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export declare const getImageKey: (imageKey: string, preFix?: string) => string;
|
|
||||||
/**
|
|
||||||
* 获取图片
|
|
||||||
* @param img 图片的url链接
|
|
||||||
* @param odRect
|
|
||||||
* @returns file
|
|
||||||
*/
|
|
||||||
export declare const getFileByRect: (img: string, odRect: Rect) => Promise<File>;
|
|
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user