@tntd/bread-crumb 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports["default"] = void 0;
8
+ var _index = _interopRequireDefault(require("../../.octopus/zh-TW/index.js"));
9
+ var _default = exports["default"] = {
10
+ TntdBreadCrumb: _index["default"]
11
+ };
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@tntd/bread-crumb",
3
+ "version": "1.0.0",
4
+ "description": "react4.0的面包屑",
5
+ "author": "zefei.zhou <zefei.zhou@tongdun.net>",
6
+ "license": "ISC",
7
+ "scripts": {
8
+ "build": "rm -rf ./es && npx babel src --out-dir es --copy-files"
9
+ },
10
+ "main": "es/index.js",
11
+ "files": [
12
+ "es",
13
+ "src",
14
+ ".octopus"
15
+ ],
16
+ "peerDependencies": {
17
+ "antd": "^3.0.0",
18
+ "tntd": ">=3.1.0",
19
+ "react": ">=16.8.0",
20
+ "dva": ">=2.4.1"
21
+ }
22
+ }
package/readme.md ADDED
@@ -0,0 +1,84 @@
1
+ ### 安装和初始化
2
+
3
+ ```
4
+ npm install @tddc/bread-crumb --save
5
+ ```
6
+
7
+ ### 面包屑
8
+
9
+ ```jsx
10
+ import React, { memo } from 'react';
11
+ import { createBrowserHistory } from 'history';
12
+ import { Router, Route, Switch } from 'react-router-dom';
13
+ import BreadCrumb from '@tddc/bread-crumb';
14
+ import Detail from '../Test/Detail';
15
+ import List from '../Test/List';
16
+
17
+ const BreadCrumbDefault = BreadCrumb(
18
+ (props) => {
19
+ return (
20
+ <Switch>
21
+ <Route name="面包屑" path={`/components/bread-crumb`} exact component={Detail} />
22
+ <Route
23
+ name="组件总览"
24
+ component={List}
25
+ path="/components"
26
+ query={[{ token: 'parentToken' }]}
27
+ />
28
+ </Switch>
29
+ );
30
+ },
31
+ {
32
+ // BreadCrumbCustom:(breadList)=>{
33
+ // console.log(breadList)
34
+ // return <div>{breadList?.map(v=>v?.name)}</div>
35
+ // },
36
+ // BreadCrumbPrototype:{
37
+ // separator:"->",
38
+ // className:"z-c-breadcrumb"
39
+ // }
40
+ },
41
+ );
42
+
43
+ export default memo(BreadCrumbDefault);
44
+ ```
45
+
46
+ #### 🚀 高阶组件
47
+
48
+ ```javascript
49
+ BreadCrumb(
50
+ (props) => {
51
+ return (
52
+ <Switch>
53
+ <Route name="面包屑" path={`/components/bread-crumb`} exact component={Detail} />
54
+ <Route name="组件总览" component={List} path="/components" />
55
+ </Switch>
56
+ );
57
+ },
58
+ {
59
+ breadCrumbCustomName: ({ breadList, path }) => {
60
+ return 'aa';
61
+ },
62
+ // BreadCrumbCustom:(breadList)=>{
63
+ // console.log(breadList)
64
+ // return <div>{breadList?.map(v=>v?.name)}</div>
65
+ // },
66
+ // BreadCrumbPrototype:{
67
+ // separator:"->",
68
+ // className:"z-c-breadcrumb"
69
+ // }
70
+ },
71
+ );
72
+ ```
73
+
74
+ | 参数 | 说明 | 类型 | 默认值 |
75
+ | ------------------- | ------------------------------------------ | --------------- | ------ |
76
+ | component | 子路由定义,可嵌套 | | |
77
+ | BreadCrumbCustom | 自定义面包屑 | (breadList)=>{} | null |
78
+ | BreadCrumbPrototype | 面包屑属性,同 antd 的 BreadCrumb 组件参数 | Object | {} |
79
+ | useCache | 开启缓存模式,记录页面跳转地址 | false | {} |
80
+
81
+ | Route 参数 | 说明 | 类型 | 默认值 |
82
+ | -------------- | ---------------------- | --------------------------- | ------ |
83
+ | query | 需要往下传递的参数 | | |
84
+ | routerItemHide | 当前页不需要展示面包屑 | (location)=>{} 或者 Boolean | false |
package/src/I18N.js ADDED
@@ -0,0 +1,27 @@
1
+ import Cookies from 'universal-cookie';
2
+ import zhCN from '../.octopus/zh-CN';
3
+ import zhTW from '../.octopus/zh-TW';
4
+ import enUS from '../.octopus/en-US';
5
+ import thTH from '../.octopus/th-TH';
6
+ import arEG from '../.octopus/ar-EG';
7
+ import koKR from '../.octopus/ko-KR';
8
+ import esES from '../.octopus/es-ES';
9
+
10
+ const cookies = new Cookies();
11
+
12
+ const mapLocale = {
13
+ 'zh-cn': zhCN,
14
+ 'zh-tw': zhTW,
15
+ en: enUS,
16
+ th: thTH, // 泰语
17
+ ar: arEG, // 阿拉伯语(埃及)
18
+ ko: koKR, // 韩语
19
+ es: esES // 西班牙语
20
+ };
21
+
22
+ export const getLang = () => {
23
+ const lang = cookies.get('lang');
24
+ return lang !== 'cn' ? lang : 'zh-cn';
25
+ };
26
+
27
+ export default mapLocale;
@@ -0,0 +1,93 @@
1
+ ---
2
+ title: react-router4.0的面包屑
3
+ group:
4
+ path: /
5
+ nav:
6
+ title: 组件
7
+ path: /components
8
+ ---
9
+
10
+ ### 安装和初始化
11
+
12
+ ```
13
+ npm install @tddc/bread-crumb --save
14
+ ```
15
+
16
+ ### 面包屑
17
+
18
+ ```jsx
19
+ import React, { memo } from 'react';
20
+ import { createBrowserHistory } from 'history';
21
+ import { Router, Route, Switch } from 'react-router-dom';
22
+ import BreadCrumb from '@tddc/bread-crumb';
23
+ import Detail from '../Test/Detail';
24
+ import List from '../Test/List';
25
+
26
+ const BreadCrumbDefault = BreadCrumb(
27
+ (props) => {
28
+ return (
29
+ <Switch>
30
+ <Route name="面包屑" path={`/components/bread-crumb`} exact component={Detail} />
31
+ <Route
32
+ name="组件总览"
33
+ component={List}
34
+ path="/components"
35
+ query={[{ token: 'parentToken' }]}
36
+ />
37
+ </Switch>
38
+ );
39
+ },
40
+ {
41
+ // BreadCrumbCustom:(breadList)=>{
42
+ // console.log(breadList)
43
+ // return <div>{breadList?.map(v=>v?.name)}</div>
44
+ // },
45
+ // BreadCrumbPrototype:{
46
+ // separator:"->",
47
+ // className:"z-c-breadcrumb"
48
+ // }
49
+ },
50
+ );
51
+
52
+ export default memo(BreadCrumbDefault);
53
+ ```
54
+
55
+ #### 🚀 高阶组件
56
+
57
+ ```javascript
58
+ BreadCrumb(
59
+ (props) => {
60
+ return (
61
+ <Switch>
62
+ <Route name="面包屑" path={`/components/bread-crumb`} exact component={Detail} />
63
+ <Route name="组件总览" component={List} path="/components" />
64
+ </Switch>
65
+ );
66
+ },
67
+ {
68
+ breadCrumbCustomName: ({ breadList, path }) => {
69
+ return 'aa';
70
+ },
71
+ // BreadCrumbCustom:(breadList)=>{
72
+ // console.log(breadList)
73
+ // return <div>{breadList?.map(v=>v?.name)}</div>
74
+ // },
75
+ // BreadCrumbPrototype:{
76
+ // separator:"->",
77
+ // className:"z-c-breadcrumb"
78
+ // }
79
+ },
80
+ );
81
+ ```
82
+
83
+ | 参数 | 说明 | 类型 | 默认值 |
84
+ | ------------------- | ------------------------------------------ | --------------- | ------ |
85
+ | component | 子路由定义,可嵌套 | | |
86
+ | BreadCrumbCustom | 自定义面包屑 | (breadList)=>{} | null |
87
+ | BreadCrumbPrototype | 面包屑属性,同 antd 的 BreadCrumb 组件参数 | Object | {} |
88
+ | useCache | 开启缓存模式,记录页面跳转地址 | false | {} |
89
+
90
+ | Route 参数 | 说明 | 类型 | 默认值 |
91
+ | -------------- | ---------------------- | --------------------------- | ------ |
92
+ | query | 需要往下传递的参数 | | |
93
+ | routerItemHide | 当前页不需要展示面包屑 | (location)=>{} 或者 Boolean | false |
package/src/index.js ADDED
@@ -0,0 +1,331 @@
1
+ import mapLocale, { getLang } from './I18N';
2
+ import { useEffect, useState, useMemo, useRef } from 'react';
3
+ import { Breadcrumb, Icon } from 'tntd';
4
+ import LocaleReceiver from 'antd/es/locale-provider/LocaleReceiver'
5
+ import { withRouter, matchPath, Link } from 'dva/router';
6
+ import './index.less';
7
+ const searchToObject = (search) => {
8
+ let pairs = search.substring(1).split('&');
9
+ let obj = {};
10
+ let pair;
11
+ let i;
12
+ for (i in pairs) {
13
+ if (pairs[i] === '') {
14
+ continue;
15
+ }
16
+
17
+ pair = pairs[i].split('=');
18
+ // obj[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
19
+ obj[decodeURIComponent(pair[0])] = pair[1];
20
+ }
21
+ return obj;
22
+ };
23
+
24
+ const getParams = (params) => {
25
+ let paramStr = '';
26
+ Object.keys(params).forEach((item) => {
27
+ const tempParamStr = encodeURIComponent(params[item]);
28
+ if (paramStr === '') {
29
+ paramStr = `${item}=${tempParamStr}`;
30
+ } else {
31
+ paramStr = `${paramStr}&${item}=${tempParamStr}`;
32
+ }
33
+ });
34
+ return paramStr;
35
+ };
36
+
37
+ const flatten = (arr) => {
38
+ let res = [];
39
+ const curArr = arr?.props?.children;
40
+ for (let i = 0, length = curArr?.length; i < length; i++) {
41
+ if (Array.isArray(curArr[i]?.props?.children)) {
42
+ res = res.concat(flatten(curArr[i]));
43
+ } else {
44
+ res.push(curArr[i]?.props);
45
+ }
46
+ }
47
+ return res;
48
+ };
49
+
50
+ export default (WrapperComponent, rest) => {
51
+ const {
52
+ useMemory,
53
+ memoryHandle,
54
+ useCache, // 使用内部缓存
55
+ defaultSearch = ['currentTab', 'current'],
56
+ BreadCrumbCustom,
57
+ breadCrumbCustomName,
58
+ BreadCrumbPrototype = {},
59
+ showHeader,
60
+ forceNoHeader,
61
+ lang,
62
+ version,
63
+ locale,
64
+ } = rest || {};
65
+ let breadCacheList = [];
66
+
67
+ return withRouter((props) => {
68
+ const { match, location, separator } = props || {};
69
+ const { pathname, search } = location || {};
70
+ const children = WrapperComponent({ ...props });
71
+ const [breadList, setBreadList] = useState([]);
72
+ // 记录链接上需要保留的query参数
73
+ const searchObj = searchToObject(search);
74
+ const curVersion = version || localStorage.getItem('app_version') || 'v3';
75
+
76
+ const breadListRef = useRef();
77
+ breadListRef.current = useMemo(() => {
78
+ return breadList;
79
+ }, [breadList]);
80
+
81
+ const watchReplaceState = (e) => {
82
+ const newBread = (breadListRef.current || []).slice();
83
+ const url = e?.arguments?.[2];
84
+ if (newBread?.length && url) {
85
+ if (url !== newBread[newBread?.length - 1].url) {
86
+ newBread[newBread?.length - 1].url = url;
87
+ setBreadList(newBread);
88
+ breadCacheList = newBread;
89
+ }
90
+ }
91
+ };
92
+ useEffect(() => {
93
+ if (useCache) {
94
+ window.addEventListener('replaceState', watchReplaceState);
95
+ return () => {
96
+ window.removeEventListener('replaceState', watchReplaceState);
97
+ };
98
+ }
99
+ }, [breadList, useCache]);
100
+
101
+ useEffect(() => {
102
+ if (useCache && useMemory && breadList?.length > 0) {
103
+ const localStorageKey = breadList?.[0]?.path;
104
+ if (breadList?.length > 1) {
105
+ if (breadList?.[0]?.path) {
106
+ const lastKey = breadList?.slice(-1)?.[0]?.url;
107
+ localStorage.setItem(
108
+ localStorageKey,
109
+ JSON.stringify({
110
+ [lastKey]: breadList,
111
+ }),
112
+ );
113
+ }
114
+ } else {
115
+ localStorage.removeItem(localStorageKey);
116
+ }
117
+ }
118
+ }, [useCache, useMemory, breadList]);
119
+
120
+ useEffect(() => {
121
+ if (!useCache) {
122
+ const routerArr = [];
123
+ flatten(children)?.forEach((props) => {
124
+ routerArr.push({
125
+ path: props.path === '/' ? match?.path : props.path,
126
+ name: props.name,
127
+ query: props.query,
128
+ ...(props.routerItemHide ? { routerItemHide: props.routerItemHide } : {}),
129
+ });
130
+ });
131
+
132
+ const breadCrumbList = [];
133
+ routerArr?.filter((routeObj) => {
134
+ const { path } = routeObj || {};
135
+ const pathObj = matchPath(pathname, { path });
136
+ if (pathObj) {
137
+ breadCrumbList.push({
138
+ ...pathObj,
139
+ ...(routeObj || {}),
140
+ });
141
+ }
142
+ });
143
+
144
+ breadCrumbList.sort((a, b) => {
145
+ return a.path.length - b.path.length;
146
+ });
147
+
148
+ breadCrumbList?.map((item) => {
149
+ const querySet = new Set();
150
+ let curQuery = [];
151
+ item.query?.map((item1) => {
152
+ const curKey = Object.keys(item1)[0];
153
+ const sourceKey = Object.values(item1)[0];
154
+ curQuery.push(curKey + '=' + searchObj[sourceKey]);
155
+ querySet.add(curKey);
156
+ });
157
+
158
+ // const matched = matchPath(pathname, { path: item.path, exact: true });
159
+ if (defaultSearch?.length) {
160
+ defaultSearch.forEach((defaultKey) => {
161
+ if (!querySet.has(defaultKey)) {
162
+ if (searchObj[defaultKey]) {
163
+ curQuery.push(`${defaultKey}=${searchObj[defaultKey]}`);
164
+ }
165
+ }
166
+ });
167
+ }
168
+ if (curQuery?.length) {
169
+ item.url += '?' + curQuery.join('&');
170
+ }
171
+ });
172
+ setBreadList(breadCrumbList);
173
+ }
174
+ }, [pathname]);
175
+
176
+ useEffect(() => {
177
+ const routerArr = [];
178
+ flatten(children)?.forEach((props) => {
179
+ routerArr.push({
180
+ path: props.path === '/' ? match?.path : props.path,
181
+ name: props.name,
182
+ query: props.query,
183
+ ...(props.routerItemHide ? { routerItemHide: props.routerItemHide } : {}),
184
+ });
185
+ });
186
+
187
+ if (useCache) {
188
+ if (pathname) {
189
+ const curRoute = routerArr?.find((item) => {
190
+ const matched = matchPath(pathname, { path: item.path, exact: true });
191
+ if (matched) {
192
+ return item;
193
+ }
194
+ });
195
+ const routeSort = routerArr.sort((a, b) => {
196
+ return a.path.length - b.path.length;
197
+ });
198
+
199
+ const href = pathname + search;
200
+ // 获取最原始的第一层级数据
201
+ const isIndex = matchPath(pathname, { path: routeSort?.[0]?.path, exact: true });
202
+
203
+ // 如果在缓存中默认取
204
+ if (useMemory && useCache) {
205
+ let curStorageInfo = localStorage.getItem(routeSort?.[0]?.path);
206
+ if (typeof curStorageInfo === 'string') {
207
+ curStorageInfo = JSON.parse(curStorageInfo);
208
+ if (curStorageInfo && curStorageInfo[href]) {
209
+ breadCacheList = curStorageInfo[href];
210
+ }
211
+ if (memoryHandle && typeof memoryHandle === 'function') {
212
+ breadCacheList = memoryHandle({ routeArr: routeSort, href });
213
+ }
214
+ }
215
+ }
216
+
217
+ // 如果是第一层路由例如列表, 查看是否已经在缓存中了
218
+ const indexInfo = isIndex
219
+ ? breadCacheList?.find((item) => item.path === pathname)
220
+ : undefined;
221
+ if (isIndex && !search) {
222
+ breadCacheList = [{ ...curRoute, url: href }];
223
+ } else if (indexInfo) {
224
+ // 如果已经在缓存中了 则更新url 说明search发生变化
225
+ indexInfo.url = href;
226
+ } else {
227
+ const hasIn = breadCacheList?.find((item) => item.url === href);
228
+ if (!hasIn) {
229
+ breadCacheList.push({ ...curRoute, url: href });
230
+ }
231
+ }
232
+ try {
233
+ setBreadList(JSON.parse(JSON.stringify(breadCacheList)));
234
+ } catch (e) {
235
+ console.log('e', e);
236
+ }
237
+ }
238
+ }
239
+ }, [pathname, search, useCache]);
240
+
241
+ const onlyTwoLevels = breadList?.length === 2;
242
+
243
+ const breadClick = (breadIndex) => {
244
+ if (useCache) {
245
+ const breadListTemp = [...breadList];
246
+ breadListTemp.splice(breadIndex + 1);
247
+ breadCacheList = breadListTemp;
248
+ setBreadList(breadListTemp);
249
+ }
250
+ };
251
+
252
+ // 当前隐藏
253
+ const hideCurrentBreadCrumb = useMemo(() => {
254
+ if (breadList?.length) {
255
+ const curBread = breadList.slice(-1)?.[0] || {};
256
+ if (typeof curBread.routerItemHide === 'function') {
257
+ return curBread.routerItemHide(location);
258
+ }
259
+ return curBread.routerItemHide;
260
+ }
261
+ }, [breadList]);
262
+
263
+ return (
264
+ <LocaleReceiver componentName="TntdBreadCrumb">
265
+ {(locale, localeCode) => {
266
+ const I18N = !!Object.keys(locale).length ? locale : (mapLocale[localeCode] || mapLocale[getLang()]);
267
+ return (
268
+ <>
269
+ {(breadList?.length > 1 || showHeader) && !forceNoHeader && !hideCurrentBreadCrumb && (
270
+ <div className={`page-global-header bread-crumb-head ${curVersion}`}>
271
+ {BreadCrumbCustom &&
272
+ !!breadList?.length &&
273
+ BreadCrumbCustom(breadList, getParams(searchObj))}
274
+ {!(BreadCrumbCustom && BreadCrumbCustom(breadList)) && (
275
+ <Breadcrumb
276
+ separator={!onlyTwoLevels ? separator || '>' : ' '}
277
+ className="c-breadcrumb"
278
+ {...(BreadCrumbPrototype || {})}
279
+ >
280
+ {breadList?.map((bread, i) => {
281
+ const { url } = bread;
282
+ let dom = bread?.name;
283
+ if (breadCrumbCustomName && typeof breadCrumbCustomName === 'function') {
284
+ dom =
285
+ breadCrumbCustomName({
286
+ breadList,
287
+ level: i + 1,
288
+ path: url,
289
+ name: bread?.name,
290
+ pathInfo: bread,
291
+ }) || dom;
292
+ }
293
+ if (onlyTwoLevels && i === 0) {
294
+ dom = (
295
+ <>
296
+ <Icon type="left" className="go-back" />
297
+ {I18N.src.index.fanHui}</>
298
+ );
299
+ }
300
+ return (
301
+ <Breadcrumb.Item key={url}>
302
+ {url && i !== breadList?.length - 1 ? (
303
+ <Link
304
+ onClick={() => {
305
+ breadClick(i);
306
+ }}
307
+ to={url}
308
+ >
309
+ {dom}
310
+ </Link>
311
+ ) : (
312
+ dom
313
+ )}
314
+ </Breadcrumb.Item>
315
+ );
316
+ })}
317
+ </Breadcrumb>
318
+ )}
319
+ </div>
320
+ )}
321
+ {!!useCache && (
322
+ <div key={location?.pathname + (location?.search || '')}> {children || null}</div>
323
+ )}
324
+ {!useCache && (children || null)}
325
+ </>
326
+ )
327
+ }}
328
+ </LocaleReceiver>
329
+ );
330
+ });
331
+ };
package/src/index.less ADDED
@@ -0,0 +1,28 @@
1
+ .bread-crumb-head {
2
+ height: 40px;
3
+ padding: 0 20px;
4
+ line-height: 40px;
5
+ background: #fff;
6
+ border: none;
7
+ .h2 {
8
+ font-size: 14px;
9
+ }
10
+ .c-breadcrumb {
11
+ height: 40px;
12
+ line-height: 40px;
13
+ > span {
14
+ font-size: 14px;
15
+ }
16
+ .go-back {
17
+ margin-right: 2px;
18
+ }
19
+ .ant-breadcrumb-separator {
20
+ margin: 0 6px;
21
+ }
22
+ }
23
+ }
24
+ .bread-crumb-body {
25
+ height: ~'calc(100vh - 90px)';
26
+ padding: 16px 20px;
27
+ overflow: auto;
28
+ }
@@ -0,0 +1,5 @@
1
+ import arEG from '../../.octopus/ar-EG/index.js'
2
+
3
+ export default {
4
+ TntdBreadCrumb: arEG
5
+ }
@@ -0,0 +1,5 @@
1
+ import enUS from '../../.octopus/en-US/index.js'
2
+
3
+ export default {
4
+ TntdBreadCrumb: enUS
5
+ }
@@ -0,0 +1,5 @@
1
+ import esES from '../../.octopus/es-ES/index.js'
2
+
3
+ export default {
4
+ TntdBreadCrumb: esES
5
+ }
@@ -0,0 +1,5 @@
1
+ import koKR from '../../.octopus/ko-KR/index.js'
2
+
3
+ export default {
4
+ TntdBreadCrumb: koKR
5
+ }
@@ -0,0 +1,5 @@
1
+ import thTH from '../../.octopus/th-TH/index.js'
2
+
3
+ export default {
4
+ TntdBreadCrumb: thTH
5
+ }
@@ -0,0 +1,5 @@
1
+ import zhCN from '../../.octopus/zh-CN/index.js'
2
+
3
+ export default {
4
+ TntdBreadCrumb: zhCN
5
+ }
@@ -0,0 +1,5 @@
1
+ import zhTW from '../../.octopus/zh-TW/index.js'
2
+
3
+ export default {
4
+ TntdBreadCrumb: zhTW
5
+ }