@gm-pc/react 1.9.2-beta.2 → 1.10.4
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.
- package/package.json +5 -3
- package/src/component/nav/style.less +1 -1
- package/src/component/v_browser/context/browser.ts +7 -0
- package/src/component/v_browser/context/browserWindow.ts +7 -0
- package/src/component/v_browser/hooks/useWindowEffect.ts +40 -0
- package/src/component/v_browser/index.ts +4 -0
- package/src/component/v_browser/stories.tsx +10 -0
- package/src/component/v_browser/types.ts +39 -0
- package/src/component/v_browser/ui/iframe_portal.tsx +28 -0
- package/src/component/v_browser/ui/index.tsx +204 -0
- package/src/component/v_browser/ui/style.less +118 -0
- package/src/component/v_browser/ui/window_wrapper.tsx +66 -0
- package/src/component/v_browser/v_browser.tsx +264 -0
- package/src/index.ts +1 -0
- package/src/svg/vbrowser-tab-delete.svg +9 -0
- package/src/svg/vbrowser-tab-left.svg +9 -0
- package/src/svg/vbrowser-tab-right.svg +9 -0
- package/yarn-error.log +267 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gm-pc/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.4",
|
|
4
4
|
"description": "观麦前端基础组件库",
|
|
5
5
|
"author": "liyatang <liyatang@qq.com>",
|
|
6
6
|
"homepage": "https://github.com/gmfe/gm-pc#readme",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@gm-common/hooks": "^2.10.0",
|
|
26
26
|
"@gm-common/tool": "^2.10.0",
|
|
27
|
-
"@gm-pc/locales": "^1.
|
|
27
|
+
"@gm-pc/locales": "^1.10.4",
|
|
28
28
|
"big.js": "^6.0.1",
|
|
29
29
|
"classnames": "^2.2.5",
|
|
30
30
|
"lodash": "^4.17.19",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"react-window": "^1.8.5"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
+
"@types/react-router-dom": "^5.3.3",
|
|
36
37
|
"react": "^16.14.0",
|
|
37
38
|
"react-dom": "^16.14.0"
|
|
38
39
|
},
|
|
@@ -44,7 +45,8 @@
|
|
|
44
45
|
"moment": "^2.29.1",
|
|
45
46
|
"react": "^16.14.0",
|
|
46
47
|
"react-dom": "^16.14.0",
|
|
48
|
+
"react-router-dom": "^5.2.0",
|
|
47
49
|
"react-window": "^1.8.5"
|
|
48
50
|
},
|
|
49
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "9422d0611d5399c0fe0ed3a924b3f429c01cdfc0"
|
|
50
52
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { reaction } from 'mobx'
|
|
2
|
+
import React, { useEffect, useContext } from 'react'
|
|
3
|
+
import BrowserContext from '../context/browser'
|
|
4
|
+
import BrowserWindowContext from '../context/browserWindow'
|
|
5
|
+
|
|
6
|
+
type Noop = () => void
|
|
7
|
+
/**
|
|
8
|
+
* 多窗口中的子页面组件因为被缓存,原来的useEffect受到影响: effect仅在子窗口创建时触发,销毁函数仅在窗口关闭时触发;子窗口失活时依然会观察deps,并触发effect;
|
|
9
|
+
*
|
|
10
|
+
* 根据情况可以考虑使用useWindowEffect来替换useEffect,功能: 窗口激活时、deps更新时,触发fn执行; 窗口失活时执行fn返回的销毁函数; 如果子窗口为失活状态,不观察deps的更新;
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
export default function useWindowEffect(fn: () => Noop | void, deps: Array<any>) {
|
|
14
|
+
const browser = useContext(BrowserContext)
|
|
15
|
+
const browserWindow = useContext(BrowserWindowContext)
|
|
16
|
+
|
|
17
|
+
let cb: Noop | void
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!browser || !browserWindow) {
|
|
20
|
+
console.warn('useWindowEffect需要在VBrowser中使用, 否则将回退到useEffect')
|
|
21
|
+
cb = fn()
|
|
22
|
+
return cb
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const alive = browserWindow.path === browser.activeWindow?.path
|
|
26
|
+
if (!alive) return
|
|
27
|
+
|
|
28
|
+
const dispose = reaction(
|
|
29
|
+
() => browser.activeWindow,
|
|
30
|
+
(cur, pre) => {
|
|
31
|
+
const activate = cur?.path === browserWindow.path
|
|
32
|
+
const deactivate = pre?.path === browserWindow.path
|
|
33
|
+
if (activate) cb = fn()
|
|
34
|
+
if (deactivate) cb && cb()
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
cb = fn()
|
|
38
|
+
return dispose
|
|
39
|
+
}, deps)
|
|
40
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface VBrowserWindow {
|
|
4
|
+
path: string
|
|
5
|
+
query?: { [key: string]: string }
|
|
6
|
+
/** 窗口图标 */
|
|
7
|
+
faviconURL?: string
|
|
8
|
+
/** 窗口名,如果不存在将尝试从vBrowser的autoTitle中取 */
|
|
9
|
+
title?: ReactNode | Element
|
|
10
|
+
/** 可否关闭,默认为true */
|
|
11
|
+
closeable?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface VBrowserProps {
|
|
15
|
+
/** 窗口数量限制 */
|
|
16
|
+
maxLength?: number
|
|
17
|
+
/** 这里的路由将全屏显示,且不会呈现在Tab栏中 */
|
|
18
|
+
onReady?: Function
|
|
19
|
+
/** 重新打开vBrowser时恢复已打开窗口, 默认为true */
|
|
20
|
+
restore?: boolean
|
|
21
|
+
/** 窗口变化事件 */
|
|
22
|
+
onChange?: (
|
|
23
|
+
from: VBrowserWindow | undefined,
|
|
24
|
+
to: VBrowserWindow,
|
|
25
|
+
windows: VBrowserWindow[]
|
|
26
|
+
) => void
|
|
27
|
+
/** 窗口打开/切换前调用,返回false会阻止窗口打开/切换 */
|
|
28
|
+
auth?: (from?: VBrowserWindow, to?: VBrowserWindow) => Promise<boolean> | boolean
|
|
29
|
+
onError?: (error: { code: number; message: string }) => void
|
|
30
|
+
autoTitle?: (path: string) => string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface CacheItem {
|
|
34
|
+
vNode: JSX.Element & {
|
|
35
|
+
ref: {
|
|
36
|
+
current: HTMLDivElement | undefined
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React, { FC, useEffect, useState } from 'react'
|
|
2
|
+
import { pages } from '../v_browser'
|
|
3
|
+
|
|
4
|
+
interface IframePortalProps {}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 此组件使用在Switch中,用来访问任意子页面,子页面路径通过url参数path指定
|
|
8
|
+
*/
|
|
9
|
+
const IframePortal: FC<IframePortalProps> = () => {
|
|
10
|
+
const [component, setComponent] = useState<JSX.Element | null>(null)
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const query = new URLSearchParams(location.href.split('?')[1] || '')
|
|
14
|
+
const path = query.get('path')
|
|
15
|
+
const page = pages.find((p) => p.path === path)
|
|
16
|
+
if (!page) throw new Error('[VWindow] page not found: ' + path)
|
|
17
|
+
page.loader().then((module) => {
|
|
18
|
+
const Component = module.default
|
|
19
|
+
setComponent(<Component />)
|
|
20
|
+
console.timeEnd('IframePortal')
|
|
21
|
+
return null
|
|
22
|
+
})
|
|
23
|
+
}, [])
|
|
24
|
+
|
|
25
|
+
return component
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default IframePortal
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/* eslint-disable dot-notation */
|
|
2
|
+
import React, { createRef, FC, useContext, useEffect, useRef, useState } from 'react'
|
|
3
|
+
import { createPortal } from 'react-dom'
|
|
4
|
+
import { observer } from 'mobx-react'
|
|
5
|
+
import classNames from 'classnames'
|
|
6
|
+
import Context from '../context/browser'
|
|
7
|
+
import { pages } from '../v_browser'
|
|
8
|
+
import WindowWrapper from './window_wrapper'
|
|
9
|
+
import Delete from '../../../svg/vbrowser-tab-delete.svg'
|
|
10
|
+
import Left from '../../../svg/vbrowser-tab-left.svg'
|
|
11
|
+
import Right from '../../../svg/vbrowser-tab-right.svg'
|
|
12
|
+
import { clamp, throttle } from 'lodash'
|
|
13
|
+
import BrowserWindowContext from '../context/browserWindow'
|
|
14
|
+
import './style.less'
|
|
15
|
+
|
|
16
|
+
const VBrowserContainer: FC<{ className?: string }> = observer(
|
|
17
|
+
({ className, ...props }) => {
|
|
18
|
+
const containerRef = createRef<HTMLDivElement>()
|
|
19
|
+
const browser = useContext(Context)
|
|
20
|
+
const leftInterval = useRef<number>()
|
|
21
|
+
const rightInterval = useRef<number>()
|
|
22
|
+
|
|
23
|
+
const [state, setState] = useState({ scrollLeft: 0, scrollWidth: 0, width: 0 })
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const container = containerRef.current
|
|
27
|
+
if (container) {
|
|
28
|
+
container.appendChild(browser['_portal'])
|
|
29
|
+
}
|
|
30
|
+
browser.mounted = true
|
|
31
|
+
}, [])
|
|
32
|
+
|
|
33
|
+
// #region Tabs滚动处理
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const wrapper: HTMLDivElement | null = document.querySelector(
|
|
36
|
+
'.v-browser-tabs-items'
|
|
37
|
+
)
|
|
38
|
+
if (!wrapper) return
|
|
39
|
+
const onScroll = throttle(() => {
|
|
40
|
+
setState({
|
|
41
|
+
width: wrapper.getBoundingClientRect().width,
|
|
42
|
+
scrollWidth: wrapper.scrollWidth,
|
|
43
|
+
scrollLeft: wrapper.scrollLeft,
|
|
44
|
+
})
|
|
45
|
+
}, 20)
|
|
46
|
+
wrapper.addEventListener('scroll', onScroll)
|
|
47
|
+
onScroll()
|
|
48
|
+
return () => {
|
|
49
|
+
wrapper.removeEventListener('scroll', onScroll)
|
|
50
|
+
}
|
|
51
|
+
}, [browser.windows.length])
|
|
52
|
+
|
|
53
|
+
const _handleScroll = (offset: number) => {
|
|
54
|
+
const wrapper = document.querySelector('.v-browser-tabs-items')
|
|
55
|
+
if (!wrapper) return
|
|
56
|
+
wrapper.scrollTo({
|
|
57
|
+
left: clamp(wrapper.scrollLeft + offset, 0, wrapper.scrollWidth),
|
|
58
|
+
behavior: 'smooth',
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
// #endregion
|
|
62
|
+
|
|
63
|
+
const portal = useRef(
|
|
64
|
+
createPortal(
|
|
65
|
+
pages.map((page, i) => {
|
|
66
|
+
return (
|
|
67
|
+
<BrowserWindowContext.Provider key={i} value={page}>
|
|
68
|
+
<WindowWrapper key={i} path={page.path} />
|
|
69
|
+
</BrowserWindowContext.Provider>
|
|
70
|
+
)
|
|
71
|
+
}),
|
|
72
|
+
browser['_portal']
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
return (
|
|
76
|
+
<div
|
|
77
|
+
className={classNames('v-browser tw-relative', {
|
|
78
|
+
'hiding-tabs': browser['_hidingTabs'],
|
|
79
|
+
})}
|
|
80
|
+
>
|
|
81
|
+
<div
|
|
82
|
+
className={classNames(
|
|
83
|
+
'v-browser-tabs tw-w-full tw-flex tw-items-center tw-overflow-x-auto tw-shadow',
|
|
84
|
+
{ 'tw-hidden': browser['_hidingTabs'] }
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
87
|
+
<div
|
|
88
|
+
className={classNames('v-browser-tabs-left tw-font-sm tw-px-2.5', {
|
|
89
|
+
disabled: state.scrollLeft === 0,
|
|
90
|
+
})}
|
|
91
|
+
// onClick={(e) => {
|
|
92
|
+
// e.stopPropagation()
|
|
93
|
+
// _handleScroll(-150)
|
|
94
|
+
// }}
|
|
95
|
+
onMouseEnter={() => {
|
|
96
|
+
leftInterval.current = setInterval(() => {
|
|
97
|
+
_handleScroll(-15)
|
|
98
|
+
}, 0) as any
|
|
99
|
+
}}
|
|
100
|
+
onMouseLeave={() => {
|
|
101
|
+
clearInterval(leftInterval.current)
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
<Left
|
|
105
|
+
className={classNames(
|
|
106
|
+
'v-browser-tabs-arrow tw-w-3.5 tw-h-3.5 tw-text-gray-500',
|
|
107
|
+
{}
|
|
108
|
+
)}
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
<div className='v-browser-tabs-items tw-w-full tw-h-full tw-flex tw-items-center'>
|
|
112
|
+
{browser.windows.map((w, i) => {
|
|
113
|
+
return (
|
|
114
|
+
<div
|
|
115
|
+
key={i}
|
|
116
|
+
className={classNames(
|
|
117
|
+
'v-browser-tabs-items-item tw-font-sm tw-px-2.5 tw-flex-shrink-0 tw-flex tw-items-center tw-truncate tw-w-min',
|
|
118
|
+
{
|
|
119
|
+
active: i === browser.activeIndex,
|
|
120
|
+
border:
|
|
121
|
+
i !== browser.windows.length - 1 &&
|
|
122
|
+
i !== browser.activeIndex &&
|
|
123
|
+
i !== browser.activeIndex - 1,
|
|
124
|
+
}
|
|
125
|
+
)}
|
|
126
|
+
data-tab-id={w.path}
|
|
127
|
+
onClick={() => browser.switchWindow(w)}
|
|
128
|
+
>
|
|
129
|
+
<div
|
|
130
|
+
className='tw-flex-grow tw-text-sm'
|
|
131
|
+
style={{ minWidth: typeof w.title !== 'string' ? 'auto' : '100px' }}
|
|
132
|
+
>
|
|
133
|
+
<div className='tw-flex tw-items-center'>
|
|
134
|
+
{w.faviconURL && (
|
|
135
|
+
<img
|
|
136
|
+
src={w.faviconURL}
|
|
137
|
+
className='tw-w-3.5 tw-h-3.5 tw-mr-0.5'
|
|
138
|
+
style={{ color: '#6A6A6A' }}
|
|
139
|
+
/>
|
|
140
|
+
)}
|
|
141
|
+
<span>{w.title || '-'}</span>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div className='tw-pl-2 tw-flex tw-items-center'>
|
|
146
|
+
<Delete
|
|
147
|
+
className={classNames(
|
|
148
|
+
'v-browser-tabs-items-item-close tw-w-3.5 tw-h-3.5',
|
|
149
|
+
{
|
|
150
|
+
'tw-hidden': !w.closeable,
|
|
151
|
+
}
|
|
152
|
+
)}
|
|
153
|
+
onClick={(e) => {
|
|
154
|
+
e.stopPropagation()
|
|
155
|
+
browser.close(w)
|
|
156
|
+
}}
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
)
|
|
161
|
+
})}
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div
|
|
165
|
+
className={classNames('v-browser-tabs-right tw-font-sm tw-px-2.5', {
|
|
166
|
+
disabled: state.scrollWidth - state.width - state.scrollLeft === 0,
|
|
167
|
+
})}
|
|
168
|
+
// onClick={(e) => {
|
|
169
|
+
// e.stopPropagation()
|
|
170
|
+
// _handleScroll(150)
|
|
171
|
+
// }}
|
|
172
|
+
onMouseEnter={() => {
|
|
173
|
+
rightInterval.current = setInterval(() => {
|
|
174
|
+
_handleScroll(+15)
|
|
175
|
+
}, 0) as any
|
|
176
|
+
}}
|
|
177
|
+
onMouseLeave={() => {
|
|
178
|
+
clearInterval(rightInterval.current)
|
|
179
|
+
}}
|
|
180
|
+
>
|
|
181
|
+
<Right
|
|
182
|
+
className={classNames(
|
|
183
|
+
'v-browser-tabs-arrow tw-w-3.5 tw-h-3.5 tw-text-gray-500'
|
|
184
|
+
)}
|
|
185
|
+
/>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div className='v-browser-window-wrapper'>
|
|
190
|
+
<div
|
|
191
|
+
className={classNames('v-browser-window', className, {
|
|
192
|
+
'hiding-tabs': browser['_hidingTabs'],
|
|
193
|
+
})}
|
|
194
|
+
ref={containerRef}
|
|
195
|
+
{...props}
|
|
196
|
+
/>
|
|
197
|
+
{portal.current}
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
export default VBrowserContainer
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--v-browser-tabs-bg: white;
|
|
3
|
+
--v-browser-tabs-text-color: #333;
|
|
4
|
+
--v-browser-tabs-height: 40px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.v-browser {
|
|
8
|
+
height: calc(100vh - var(--gm-framework-size-top-right-height));
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
|
|
11
|
+
&-tabs {
|
|
12
|
+
position: relative;
|
|
13
|
+
height: var(--v-browser-tabs-height);
|
|
14
|
+
background-color: var(--v-browser-tabs-bg);
|
|
15
|
+
z-index: 100;
|
|
16
|
+
color: var(--v-browser-tabs-text-color);
|
|
17
|
+
user-select: none;
|
|
18
|
+
padding: 0 40px;
|
|
19
|
+
|
|
20
|
+
&::-webkit-scrollbar {
|
|
21
|
+
display: none; /* for Chrome, Safari, and Opera */
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&-left,
|
|
25
|
+
&-right {
|
|
26
|
+
width: 40px;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
position: absolute;
|
|
29
|
+
background-color: var(--v-browser-tabs-bg);
|
|
30
|
+
height: var(--v-browser-tabs-height);
|
|
31
|
+
display: flex;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
align-items: center;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&-left {
|
|
37
|
+
left: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&-right {
|
|
41
|
+
right: 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&-items {
|
|
45
|
+
overflow-x: auto;
|
|
46
|
+
|
|
47
|
+
&::-webkit-scrollbar {
|
|
48
|
+
display: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&-item {
|
|
52
|
+
height: 100%;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
position: relative;
|
|
55
|
+
|
|
56
|
+
&.active {
|
|
57
|
+
background-color: #dae7ff !important;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&.border::after {
|
|
61
|
+
content: '';
|
|
62
|
+
position: absolute;
|
|
63
|
+
right: 0;
|
|
64
|
+
width: 1px;
|
|
65
|
+
height: 24px;
|
|
66
|
+
background-color: #e8ecf3;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&:hover::after {
|
|
70
|
+
content: none;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
&-close:hover {
|
|
74
|
+
filter: brightness(0);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&-left,
|
|
80
|
+
&-right,
|
|
81
|
+
&-items-item {
|
|
82
|
+
&:hover {
|
|
83
|
+
background-color: #f7f7f7;
|
|
84
|
+
|
|
85
|
+
.v-browser-tabs-arrow {
|
|
86
|
+
filter: brightness(0);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
&.disabled {
|
|
91
|
+
opacity: 0.25;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&.hiding-tabs {
|
|
97
|
+
height: 100vh;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&-window {
|
|
101
|
+
.v-browser-window-content {
|
|
102
|
+
height:
|
|
103
|
+
calc(
|
|
104
|
+
100vh - var(--gm-framework-size-top-right-height) - var(--v-browser-tabs-height)
|
|
105
|
+
);
|
|
106
|
+
overflow: auto;
|
|
107
|
+
background-color: white;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
&.hiding-tabs {
|
|
111
|
+
height: 100vh;
|
|
112
|
+
|
|
113
|
+
.v-browser-window-content {
|
|
114
|
+
height: 100vh;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* eslint-disable dot-notation */
|
|
2
|
+
import { get } from 'lodash'
|
|
3
|
+
import { observer } from 'mobx-react'
|
|
4
|
+
import React, { createRef, FC, useContext, useEffect } from 'react'
|
|
5
|
+
import BrowserContext from '../context/browser'
|
|
6
|
+
import { CacheItem } from '../types'
|
|
7
|
+
import { pages } from '../v_browser'
|
|
8
|
+
|
|
9
|
+
interface WindowWrapperProps {
|
|
10
|
+
path: string
|
|
11
|
+
}
|
|
12
|
+
const WindowWrapper: FC<WindowWrapperProps> = ({ path }) => {
|
|
13
|
+
const placeholder = (<div className='v-browser-placeholder' />) as CacheItem['vNode']
|
|
14
|
+
const browser = useContext(BrowserContext)
|
|
15
|
+
const w = browser.windows.find((w) => w.path === path)
|
|
16
|
+
|
|
17
|
+
const _handleShow = (vNode: CacheItem['vNode']) => {
|
|
18
|
+
const active = browser.activeWindow?.path === path
|
|
19
|
+
if (vNode?.ref.current) vNode.ref.current.style.display = 'none'
|
|
20
|
+
// 激活窗口
|
|
21
|
+
if (vNode && active) {
|
|
22
|
+
if (!vNode.ref.current) return
|
|
23
|
+
vNode.ref.current.style.display = 'block'
|
|
24
|
+
browser['_fire']('show', w!)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const active = browser.activeWindow?.path === path
|
|
30
|
+
const vNode = get(browser['_cache'], [path, 'vNode'])
|
|
31
|
+
if (!vNode && active) {
|
|
32
|
+
// 创建缓存
|
|
33
|
+
const page = pages.find((p) => p.path === path)
|
|
34
|
+
if (!page) throw new Error('[VBrowser] page not found: ' + path)
|
|
35
|
+
page.loader().then((module) => {
|
|
36
|
+
const Component = module.default
|
|
37
|
+
const vNode = (
|
|
38
|
+
<div
|
|
39
|
+
className='v-browser-window-content'
|
|
40
|
+
data-vbrowser-window={path}
|
|
41
|
+
ref={createRef()}
|
|
42
|
+
>
|
|
43
|
+
<Component />
|
|
44
|
+
</div>
|
|
45
|
+
) as CacheItem['vNode']
|
|
46
|
+
browser['_setCache'](path, { vNode })
|
|
47
|
+
browser['_fire']('show', w!)
|
|
48
|
+
return null
|
|
49
|
+
})
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_handleShow(vNode)
|
|
54
|
+
}, [browser.activeWindow?.path])
|
|
55
|
+
|
|
56
|
+
// 创建缓存完成
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const vNode = get(browser['_cache'], [path, 'vNode'])
|
|
59
|
+
if (!vNode) return
|
|
60
|
+
_handleShow(vNode)
|
|
61
|
+
}, [browser['_cache'][path]])
|
|
62
|
+
|
|
63
|
+
return get(browser['_cache'], [path, 'vNode']) || placeholder
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default observer(WindowWrapper)
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { makeAutoObservable, observable, reaction, when } from 'mobx'
|
|
3
|
+
import VBrowserContainer from './ui/index'
|
|
4
|
+
import BrowserContext from './context/browser'
|
|
5
|
+
import { CacheItem, VBrowserProps, VBrowserWindow } from './types'
|
|
6
|
+
import { parse, stringify } from 'querystring'
|
|
7
|
+
import { isEqual } from 'lodash'
|
|
8
|
+
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
const req = require.context('@/pages', true, __AUTO_ROUTER_REG__, 'lazy')
|
|
11
|
+
export const pages = req.keys().map((key) => {
|
|
12
|
+
return {
|
|
13
|
+
path: key.replace(/^\./, '').replace('/index.page.tsx', ''),
|
|
14
|
+
loader: () => Promise.resolve(req(key)),
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const STORAGE_KEY = 'vbrowser-cache'
|
|
19
|
+
|
|
20
|
+
type EventName = 'error' | 'change' | 'close' | 'show'
|
|
21
|
+
|
|
22
|
+
interface VBrowser {
|
|
23
|
+
on(e: 'error', fn: (err: Error) => void): void
|
|
24
|
+
on(e: 'change', fn: (to: VBrowserWindow, from: VBrowserWindow) => void): void
|
|
25
|
+
on(e: 'close', fn: (w: VBrowserWindow) => void): void
|
|
26
|
+
on(e: 'show', fn: (w: VBrowserWindow) => void): void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class VBrowser implements VBrowser {
|
|
30
|
+
constructor(props: VBrowserProps) {
|
|
31
|
+
this.props = props
|
|
32
|
+
this.props.restore = this.props.restore ?? true
|
|
33
|
+
this.ui = (
|
|
34
|
+
<BrowserContext.Provider value={this}>
|
|
35
|
+
<VBrowserContainer />
|
|
36
|
+
</BrowserContext.Provider>
|
|
37
|
+
)
|
|
38
|
+
this._loadStash().then(async () => {
|
|
39
|
+
await when(() => this.mounted)
|
|
40
|
+
this.props.onReady && this.props.onReady()
|
|
41
|
+
return null
|
|
42
|
+
})
|
|
43
|
+
makeAutoObservable(this, { ui: false, windows: observable.shallow })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
props!: VBrowserProps
|
|
47
|
+
|
|
48
|
+
/** 窗口列表 */
|
|
49
|
+
windows: VBrowserWindow[] = []
|
|
50
|
+
/** 选中窗口索引 */
|
|
51
|
+
activeIndex = -1
|
|
52
|
+
/** 选中窗口 */
|
|
53
|
+
get activeWindow() {
|
|
54
|
+
return this.windows[this.activeIndex] as VBrowserWindow | undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 切换已打开窗口 */
|
|
58
|
+
switchWindow(w: number | VBrowserWindow) {
|
|
59
|
+
const oldWindow = this.activeWindow
|
|
60
|
+
if (typeof w === 'number') {
|
|
61
|
+
this.activeIndex = w
|
|
62
|
+
} else {
|
|
63
|
+
const index = this.windows.findIndex((item) => item.path === w.path)
|
|
64
|
+
if (index === -1) {
|
|
65
|
+
throw new Error(`窗口不存在(${w.path} )`)
|
|
66
|
+
}
|
|
67
|
+
this.activeIndex = index
|
|
68
|
+
}
|
|
69
|
+
setTimeout(() => this._scrollToActiveTab(), 100)
|
|
70
|
+
|
|
71
|
+
this.props.onChange &&
|
|
72
|
+
this.props.onChange(oldWindow, this.windows[this.activeIndex], this.windows)
|
|
73
|
+
this._stash()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** 打开新子窗口,路由已打开则判断query是否相等,相等则切换,不相等则销毁重新加载,未打开则新建子窗口
|
|
77
|
+
*
|
|
78
|
+
* target为'_blank'时,新开浏览器窗口
|
|
79
|
+
*/
|
|
80
|
+
async open(
|
|
81
|
+
w: VBrowserWindow | string,
|
|
82
|
+
{ target = '_self' }: { target: '_self' | '_blank' } = { target: '_self' }
|
|
83
|
+
) {
|
|
84
|
+
await when(() => this.mounted)
|
|
85
|
+
// #region 格式化
|
|
86
|
+
if (typeof w === 'string') {
|
|
87
|
+
w = { path: w.split('?')[0], query: parse(w.split('?')[1]) as {} }
|
|
88
|
+
}
|
|
89
|
+
w.closeable = w.closeable ?? true
|
|
90
|
+
w.title = w.title ?? (this.props.autoTitle && this.props.autoTitle(w.path)) ?? '-'
|
|
91
|
+
// #endregion
|
|
92
|
+
|
|
93
|
+
// #region 鉴权
|
|
94
|
+
let pass = true
|
|
95
|
+
const auth = this.props.auth || (() => true)
|
|
96
|
+
// @ts-ignore
|
|
97
|
+
if (auth[Symbol.toStringTag] === 'AsyncFunction') {
|
|
98
|
+
pass = (await auth(this.activeWindow, w as VBrowserWindow)) ?? false
|
|
99
|
+
} else {
|
|
100
|
+
pass = (auth(this.activeWindow, w as VBrowserWindow) as boolean) ?? false
|
|
101
|
+
}
|
|
102
|
+
if (!pass) return this._fire('error', new Error('鉴权失败'))
|
|
103
|
+
if (!pages.find((p) => p.path === (w as VBrowserWindow).path)) {
|
|
104
|
+
console.error(w.path, '页面不存在')
|
|
105
|
+
}
|
|
106
|
+
// #endregion
|
|
107
|
+
|
|
108
|
+
// #region 打开新子窗口
|
|
109
|
+
if (target === '_self') {
|
|
110
|
+
const index = this.windows.findIndex(
|
|
111
|
+
(item) => item.path === (w as VBrowserWindow).path
|
|
112
|
+
)
|
|
113
|
+
if (index === -1) {
|
|
114
|
+
if (this.props.maxLength && this.windows.length >= this.props.maxLength) {
|
|
115
|
+
this.props.onError &&
|
|
116
|
+
this.props.onError({ code: 0, message: '超过最大允许的窗口数量' })
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
this.windows.push(w)
|
|
120
|
+
} else {
|
|
121
|
+
/**
|
|
122
|
+
* 打开已存在的相同路由的子窗口,此时情况比较复杂
|
|
123
|
+
* 当前浏览器窗口中不能开多个相同路由的子窗口,因为存在store共用冲突
|
|
124
|
+
* 如果参数相同,切换到已存在的窗口
|
|
125
|
+
* 如果不相同,销毁旧窗口重新新建,以重新触发其中的生命周期
|
|
126
|
+
*/
|
|
127
|
+
const oldW = this.windows[index]
|
|
128
|
+
if (!isEqual(oldW.query, w.query)) {
|
|
129
|
+
// 删除缓存,使其需要重建
|
|
130
|
+
delete this._cache[w.path]
|
|
131
|
+
}
|
|
132
|
+
Object.assign(this.windows[index], w)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.switchWindow(w as VBrowserWindow)
|
|
136
|
+
}
|
|
137
|
+
// #endregion
|
|
138
|
+
|
|
139
|
+
// #region 打开新浏览器窗口
|
|
140
|
+
if (target === '_blank') {
|
|
141
|
+
// 避免继承
|
|
142
|
+
const currentCache = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || '{}')
|
|
143
|
+
const newCache = { windows: [] as VBrowserWindow[], activeIndex: 0 }
|
|
144
|
+
newCache.windows.push(...this.windows.filter((w) => !w.closeable))
|
|
145
|
+
newCache.windows.push(w)
|
|
146
|
+
newCache.activeIndex = newCache.windows.findIndex(
|
|
147
|
+
(item) => item.path === (w as VBrowserWindow).path
|
|
148
|
+
)
|
|
149
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(newCache))
|
|
150
|
+
window.open(`#${w.path}?${stringify(w.query || {})}`, '_blank')
|
|
151
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(currentCache))
|
|
152
|
+
}
|
|
153
|
+
// #endregion
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** 关闭窗口 */
|
|
157
|
+
close(i: number | VBrowserWindow) {
|
|
158
|
+
if (typeof i !== 'number') {
|
|
159
|
+
i = this.windows.findIndex((item) => item.path === (i as VBrowserWindow).path)
|
|
160
|
+
}
|
|
161
|
+
const originWindows = this.windows.slice()
|
|
162
|
+
const activeWindow = this.activeWindow
|
|
163
|
+
this.windows.splice(i, 1)
|
|
164
|
+
delete this._cache[i]
|
|
165
|
+
if (this.activeIndex === i) {
|
|
166
|
+
const left = originWindows.slice(0, i).reverse()[0]
|
|
167
|
+
const right = originWindows.slice(i + 1, originWindows.length)[0]
|
|
168
|
+
const to = this.windows.findIndex(
|
|
169
|
+
(w) => w.path === left?.path || w.path === right?.path
|
|
170
|
+
)
|
|
171
|
+
this.switchWindow(to)
|
|
172
|
+
} else {
|
|
173
|
+
this.switchWindow(this.windows.indexOf(activeWindow!))
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private _scrollToActiveTab() {
|
|
178
|
+
const tabEl: HTMLDivElement | null = document.querySelector(
|
|
179
|
+
`[data-tab-id="${this.activeWindow?.path}"]`
|
|
180
|
+
)
|
|
181
|
+
if (!tabEl) return
|
|
182
|
+
tabEl.scrollIntoView()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
readonly ui!: JSX.Element
|
|
186
|
+
|
|
187
|
+
/** ui是否已挂载 */
|
|
188
|
+
mounted = false
|
|
189
|
+
|
|
190
|
+
/** 是否隐藏标签栏 */
|
|
191
|
+
private _hidingTabs = false
|
|
192
|
+
|
|
193
|
+
private readonly _portal = document.createElement('div')
|
|
194
|
+
|
|
195
|
+
/** 窗口组件的缓存 */
|
|
196
|
+
private _cache: { [key: string]: CacheItem } = {}
|
|
197
|
+
/** 设置缓存 */
|
|
198
|
+
private _setCache(key: string, { vNode }: CacheItem) {
|
|
199
|
+
// key被重新设置会使原有的子窗口被销毁
|
|
200
|
+
this._cache[key] = { vNode }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// 保存已开窗口
|
|
204
|
+
private async _stash() {
|
|
205
|
+
const windows = this.windows.slice().map((item) => ({ ...item }))
|
|
206
|
+
const string = JSON.stringify({
|
|
207
|
+
windows,
|
|
208
|
+
activeIndex: this.activeIndex,
|
|
209
|
+
})
|
|
210
|
+
sessionStorage.setItem(STORAGE_KEY, string)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 恢复已开窗口
|
|
214
|
+
private _loadStash() {
|
|
215
|
+
return new Promise((resolve, reject) => {
|
|
216
|
+
const { windows = [], activeIndex = 0 } = JSON.parse(
|
|
217
|
+
sessionStorage.getItem(STORAGE_KEY) || '{}'
|
|
218
|
+
)
|
|
219
|
+
if (windows.length === 0) return resolve(undefined)
|
|
220
|
+
this.windows = windows
|
|
221
|
+
const onShow = (w: VBrowserWindow) => {
|
|
222
|
+
this.switchWindow(activeIndex)
|
|
223
|
+
this.off('show', onShow)
|
|
224
|
+
resolve(undefined)
|
|
225
|
+
}
|
|
226
|
+
this.on('show', onShow)
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** 隐藏标签栏 */
|
|
231
|
+
hideTabs() {
|
|
232
|
+
this._hidingTabs = true
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** 显示标签栏 */
|
|
236
|
+
showTabs() {
|
|
237
|
+
this._hidingTabs = false
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private _events: {
|
|
241
|
+
[eventName: string]: Array<{ event: Function }>
|
|
242
|
+
} = {}
|
|
243
|
+
|
|
244
|
+
on(eventName: EventName, event: Function) {
|
|
245
|
+
if (!this._events[eventName]) {
|
|
246
|
+
this._events[eventName] = []
|
|
247
|
+
}
|
|
248
|
+
this._events[eventName].push({ event })
|
|
249
|
+
return this
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
off(eventName: EventName, event: Function) {
|
|
253
|
+
const events = this._events[eventName] || []
|
|
254
|
+
const index = events.findIndex((item) => item.event === event)
|
|
255
|
+
events.splice(index, 1)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private _fire(eventName: EventName, ...args: any[]) {
|
|
259
|
+
const events = this._events[eventName] || []
|
|
260
|
+
events.forEach((item) => item.event(...args))
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export default VBrowser
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>icon/tab-delete</title>
|
|
4
|
+
<g id="控件" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
5
|
+
<g id="icon/tab-delete" fill="#6a6a6a">
|
|
6
|
+
<path d="M10,4 C10.4142136,4 10.75,4.33578644 10.75,4.75 L10.75,9.25 L15.25,9.25 C15.6642136,9.25 16,9.58578644 16,10 C16,10.4142136 15.6642136,10.75 15.25,10.75 L10.75,10.75 L10.75,15.25 C10.75,15.6642136 10.4142136,16 10,16 C9.58578644,16 9.25,15.6642136 9.25,15.25 L9.25,10.75 L4.75,10.75 C4.33578644,10.75 4,10.4142136 4,10 C4,9.58578644 4.33578644,9.25 4.75,9.25 L9.25,9.25 L9.25,4.75 C9.25,4.33578644 9.58578644,4 10,4 Z" id="形状结合" transform="translate(10.000000, 10.000000) rotate(-315.000000) translate(-10.000000, -10.000000) "></path>
|
|
7
|
+
</g>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>icon/左右滑动/禁用@2x</title>
|
|
4
|
+
<g id="控件" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
|
|
5
|
+
<g id="icon/左右滑动/禁用" stroke="#575757" stroke-width="1.5">
|
|
6
|
+
<path d="M7.8822128,6.07106781 L7.8822128,13.8710678 C7.8822128,13.9815248 7.97175585,14.0710678 8.0822128,14.0710678 L15.8822128,14.0710678 L15.8822128,14.0710678" id="路径-4" transform="translate(11.882213, 10.071068) rotate(-315.000000) translate(-11.882213, -10.071068) "></path>
|
|
7
|
+
</g>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>icon/左右滑动/禁用@2x</title>
|
|
4
|
+
<g id="控件" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
|
|
5
|
+
<g id="icon/左右滑动/右/禁用" stroke="#575757" stroke-width="1.5">
|
|
6
|
+
<path d="M3.8822128,6.07106781 L3.8822128,13.8710678 C3.8822128,13.9815248 3.97175585,14.0710678 4.0822128,14.0710678 L11.8822128,14.0710678 L11.8822128,14.0710678" id="路径-4" transform="translate(7.882213, 10.071068) rotate(-135.000000) translate(-7.882213, -10.071068) "></path>
|
|
7
|
+
</g>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
package/yarn-error.log
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
Arguments:
|
|
2
|
+
/usr/local/bin/node /usr/local/bin/yarn add react-router-dom^5.2.0 --peer
|
|
3
|
+
|
|
4
|
+
PATH:
|
|
5
|
+
/opt/intel/openvino_2021/deployment_tools/model_optimizer:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/usr/local/go/bin:/opt/X11/bin:/Library/Apple/usr/bin:/Users/jawei/google-cloud-sdk/bin:/Users/jawei/opt/anaconda3/bin:/Users/jawei/opt/anaconda3/condabin:/Library/Frameworks/Python.framework/Versions/3.8/bin:/Users/jawei/flutter/bin:/Users/yuanzhiying/Library/flutter/bin:/usr/local/go/bin
|
|
6
|
+
|
|
7
|
+
Yarn version:
|
|
8
|
+
1.22.10
|
|
9
|
+
|
|
10
|
+
Node version:
|
|
11
|
+
14.15.1
|
|
12
|
+
|
|
13
|
+
Platform:
|
|
14
|
+
darwin x64
|
|
15
|
+
|
|
16
|
+
Trace:
|
|
17
|
+
Error: https://registry.npm.taobao.org/react-router-dom%5E5.2.0: [NOT_FOUND] react-router-dom^5.2.0 not found
|
|
18
|
+
at Request.params.callback [as _callback] (/usr/local/lib/node_modules/yarn/lib/cli.js:66988:18)
|
|
19
|
+
at Request.self.callback (/usr/local/lib/node_modules/yarn/lib/cli.js:140662:22)
|
|
20
|
+
at Request.emit (events.js:315:20)
|
|
21
|
+
at Request.<anonymous> (/usr/local/lib/node_modules/yarn/lib/cli.js:141634:10)
|
|
22
|
+
at Request.emit (events.js:315:20)
|
|
23
|
+
at Gunzip.<anonymous> (/usr/local/lib/node_modules/yarn/lib/cli.js:141556:12)
|
|
24
|
+
at Object.onceWrapper (events.js:421:28)
|
|
25
|
+
at Gunzip.emit (events.js:315:20)
|
|
26
|
+
at endReadableNT (_stream_readable.js:1327:12)
|
|
27
|
+
at processTicksAndRejections (internal/process/task_queues.js:80:21)
|
|
28
|
+
|
|
29
|
+
npm manifest:
|
|
30
|
+
{
|
|
31
|
+
"name": "@gm-pc/react",
|
|
32
|
+
"version": "1.7.4-alpha.0",
|
|
33
|
+
"description": "观麦前端基础组件库",
|
|
34
|
+
"author": "liyatang <liyatang@qq.com>",
|
|
35
|
+
"homepage": "https://github.com/gmfe/gm-pc#readme",
|
|
36
|
+
"license": "ISC",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"main": "src/index.ts",
|
|
41
|
+
"module": "src/index.ts",
|
|
42
|
+
"types": "src/index.ts",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/gmfe/gm-pc.git"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/gmfe/gm-pc/issues"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@gm-common/hooks": "^2.10.0",
|
|
55
|
+
"@gm-common/tool": "^2.10.0",
|
|
56
|
+
"@gm-pc/locales": "^1.7.4-alpha.0",
|
|
57
|
+
"big.js": "^6.0.1",
|
|
58
|
+
"classnames": "^2.2.5",
|
|
59
|
+
"lodash": "^4.17.19",
|
|
60
|
+
"modern-normalize": "^1.0.0",
|
|
61
|
+
"moment": "^2.29.1",
|
|
62
|
+
"react-window": "^1.8.5"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/react-router-dom": "^5.3.3",
|
|
66
|
+
"react": "^16.14.0",
|
|
67
|
+
"react-dom": "^16.14.0"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"big.js": "^6.0.1",
|
|
71
|
+
"classnames": "^2.2.5",
|
|
72
|
+
"lodash": "^4.17.19",
|
|
73
|
+
"modern-normalize": "^1.0.0",
|
|
74
|
+
"moment": "^2.29.1",
|
|
75
|
+
"react": "^16.14.0",
|
|
76
|
+
"react-dom": "^16.14.0",
|
|
77
|
+
"react-router-dom": "^5.2.0",
|
|
78
|
+
"react-window": "^1.8.5"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
yarn manifest:
|
|
83
|
+
No manifest
|
|
84
|
+
|
|
85
|
+
Lockfile:
|
|
86
|
+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
87
|
+
# yarn lockfile v1
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
"@babel/runtime@^7.0.0":
|
|
91
|
+
version "7.12.1"
|
|
92
|
+
resolved "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.12.1.tgz?cache=0&sync_timestamp=1602799933339&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fruntime%2Fdownload%2F%40babel%2Fruntime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740"
|
|
93
|
+
integrity sha1-tBFqa2cR0BCy2tO3tuQ78bmVR0A=
|
|
94
|
+
dependencies:
|
|
95
|
+
regenerator-runtime "^0.13.4"
|
|
96
|
+
|
|
97
|
+
"@gm-common/hooks@^2.10.0":
|
|
98
|
+
version "2.10.0"
|
|
99
|
+
resolved "https://registry.npmmirror.com/@gm-common/hooks/download/@gm-common/hooks-2.10.0.tgz#c67e02875b4d266ef911e7828257d957e923826b"
|
|
100
|
+
integrity sha1-xn4Ch1tNJm75EeeCglfZV+kjgms=
|
|
101
|
+
dependencies:
|
|
102
|
+
"@gm-common/tool" "^2.10.0"
|
|
103
|
+
ts-config-gm-react-app "^3.4.5"
|
|
104
|
+
|
|
105
|
+
"@gm-common/tool@^2.10.0":
|
|
106
|
+
version "2.10.0"
|
|
107
|
+
resolved "https://registry.npmmirror.com/@gm-common/tool/download/@gm-common/tool-2.10.0.tgz#f4f512233ff1118eff8bbdeca9762f0e65ce26bd"
|
|
108
|
+
integrity sha1-9PUSIz/xEY7/i73sqXYvDmXOJr0=
|
|
109
|
+
dependencies:
|
|
110
|
+
lodash "^4.17.20"
|
|
111
|
+
|
|
112
|
+
"@gm-pc/locales@^1.7.4-alpha.0":
|
|
113
|
+
version "1.8.6-alpha.0"
|
|
114
|
+
resolved "https://registry.npmmirror.com/@gm-pc/locales/-/locales-1.8.6-alpha.0.tgz#9fc9918454ee774c0d61ff41f5b33343411dd3c3"
|
|
115
|
+
integrity sha512-iNAdAuu8bg7F3xB7Vr0DgvY+jpJ1lxkOTAOw+2BK3ljI1eRTbIYkqvazX9ImrdcaB+7R0sbDGUMeR8Ca7iRY3w==
|
|
116
|
+
|
|
117
|
+
"@types/history@^4.7.11":
|
|
118
|
+
version "4.7.11"
|
|
119
|
+
resolved "https://registry.npmmirror.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
|
|
120
|
+
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
|
|
121
|
+
|
|
122
|
+
"@types/prop-types@*":
|
|
123
|
+
version "15.7.4"
|
|
124
|
+
resolved "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
|
|
125
|
+
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
|
|
126
|
+
|
|
127
|
+
"@types/react-router-dom@^5.3.3":
|
|
128
|
+
version "5.3.3"
|
|
129
|
+
resolved "https://registry.npmmirror.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
|
|
130
|
+
integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
|
|
131
|
+
dependencies:
|
|
132
|
+
"@types/history" "^4.7.11"
|
|
133
|
+
"@types/react" "*"
|
|
134
|
+
"@types/react-router" "*"
|
|
135
|
+
|
|
136
|
+
"@types/react-router@*":
|
|
137
|
+
version "5.1.18"
|
|
138
|
+
resolved "https://registry.npmmirror.com/@types/react-router/-/react-router-5.1.18.tgz#c8851884b60bc23733500d86c1266e1cfbbd9ef3"
|
|
139
|
+
integrity sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==
|
|
140
|
+
dependencies:
|
|
141
|
+
"@types/history" "^4.7.11"
|
|
142
|
+
"@types/react" "*"
|
|
143
|
+
|
|
144
|
+
"@types/react@*":
|
|
145
|
+
version "17.0.39"
|
|
146
|
+
resolved "https://registry.npmmirror.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce"
|
|
147
|
+
integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==
|
|
148
|
+
dependencies:
|
|
149
|
+
"@types/prop-types" "*"
|
|
150
|
+
"@types/scheduler" "*"
|
|
151
|
+
csstype "^3.0.2"
|
|
152
|
+
|
|
153
|
+
"@types/scheduler@*":
|
|
154
|
+
version "0.16.2"
|
|
155
|
+
resolved "https://registry.npmmirror.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
|
|
156
|
+
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
|
|
157
|
+
|
|
158
|
+
big.js@^6.0.1:
|
|
159
|
+
version "6.0.1"
|
|
160
|
+
resolved "https://registry.npm.taobao.org/big.js/download/big.js-6.0.1.tgz#9e0a2e8b1825ce006cd4a096d6f294738cd5cff6"
|
|
161
|
+
integrity sha1-ngouixglzgBs1KCW1vKUc4zVz/Y=
|
|
162
|
+
|
|
163
|
+
classnames@^2.2.5:
|
|
164
|
+
version "2.2.6"
|
|
165
|
+
resolved "https://registry.npm.taobao.org/classnames/download/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
|
166
|
+
integrity sha1-Q5Nb/90pHzJtrQogUwmzjQD2UM4=
|
|
167
|
+
|
|
168
|
+
csstype@^3.0.2:
|
|
169
|
+
version "3.0.10"
|
|
170
|
+
resolved "https://registry.npmmirror.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5"
|
|
171
|
+
integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==
|
|
172
|
+
|
|
173
|
+
"js-tokens@^3.0.0 || ^4.0.0":
|
|
174
|
+
version "4.0.0"
|
|
175
|
+
resolved "https://registry.npm.taobao.org/js-tokens/download/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
|
176
|
+
integrity sha1-GSA/tZmR35jjoocFDUZHzerzJJk=
|
|
177
|
+
|
|
178
|
+
lodash@^4.17.19, lodash@^4.17.20:
|
|
179
|
+
version "4.17.20"
|
|
180
|
+
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
|
|
181
|
+
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
|
|
182
|
+
|
|
183
|
+
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
|
184
|
+
version "1.4.0"
|
|
185
|
+
resolved "https://registry.npm.taobao.org/loose-envify/download/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
|
186
|
+
integrity sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=
|
|
187
|
+
dependencies:
|
|
188
|
+
js-tokens "^3.0.0 || ^4.0.0"
|
|
189
|
+
|
|
190
|
+
"memoize-one@>=3.1.1 <6":
|
|
191
|
+
version "5.1.1"
|
|
192
|
+
resolved "https://registry.npm.taobao.org/memoize-one/download/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
|
|
193
|
+
integrity sha1-BHtuMZm1COrsA1BN5xIpuOsddcA=
|
|
194
|
+
|
|
195
|
+
modern-normalize@^1.0.0:
|
|
196
|
+
version "1.0.0"
|
|
197
|
+
resolved "https://registry.npm.taobao.org/modern-normalize/download/modern-normalize-1.0.0.tgz#539d84a1e141338b01b346f3e27396d0ed17601e"
|
|
198
|
+
integrity sha1-U52EoeFBM4sBs0bz4nOW0O0XYB4=
|
|
199
|
+
|
|
200
|
+
moment@^2.29.1:
|
|
201
|
+
version "2.29.1"
|
|
202
|
+
resolved "https://registry.npm.taobao.org/moment/download/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
|
203
|
+
integrity sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M=
|
|
204
|
+
|
|
205
|
+
object-assign@^4.1.1:
|
|
206
|
+
version "4.1.1"
|
|
207
|
+
resolved "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
|
208
|
+
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
|
209
|
+
|
|
210
|
+
prop-types@^15.6.2:
|
|
211
|
+
version "15.7.2"
|
|
212
|
+
resolved "https://registry.npm.taobao.org/prop-types/download/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
|
213
|
+
integrity sha1-UsQedbjIfnK52TYOAga5ncv/psU=
|
|
214
|
+
dependencies:
|
|
215
|
+
loose-envify "^1.4.0"
|
|
216
|
+
object-assign "^4.1.1"
|
|
217
|
+
react-is "^16.8.1"
|
|
218
|
+
|
|
219
|
+
react-dom@^16.14.0:
|
|
220
|
+
version "16.14.0"
|
|
221
|
+
resolved "https://registry.npm.taobao.org/react-dom/download/react-dom-16.14.0.tgz?cache=0&sync_timestamp=1603367590403&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-dom%2Fdownload%2Freact-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
|
|
222
|
+
integrity sha1-etg47Cmnd/s8dcOhkPZhz5Kri4k=
|
|
223
|
+
dependencies:
|
|
224
|
+
loose-envify "^1.1.0"
|
|
225
|
+
object-assign "^4.1.1"
|
|
226
|
+
prop-types "^15.6.2"
|
|
227
|
+
scheduler "^0.19.1"
|
|
228
|
+
|
|
229
|
+
react-is@^16.8.1:
|
|
230
|
+
version "16.13.1"
|
|
231
|
+
resolved "https://registry.npm.taobao.org/react-is/download/react-is-16.13.1.tgz?cache=0&sync_timestamp=1603367576715&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-is%2Fdownload%2Freact-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
|
232
|
+
integrity sha1-eJcppNw23imZ3BVt1sHZwYzqVqQ=
|
|
233
|
+
|
|
234
|
+
react-window@^1.8.5:
|
|
235
|
+
version "1.8.6"
|
|
236
|
+
resolved "https://registry.npm.taobao.org/react-window/download/react-window-1.8.6.tgz#d011950ac643a994118632665aad0c6382e2a112"
|
|
237
|
+
integrity sha1-0BGVCsZDqZQRhjJmWq0MY4LioRI=
|
|
238
|
+
dependencies:
|
|
239
|
+
"@babel/runtime" "^7.0.0"
|
|
240
|
+
memoize-one ">=3.1.1 <6"
|
|
241
|
+
|
|
242
|
+
react@^16.14.0:
|
|
243
|
+
version "16.14.0"
|
|
244
|
+
resolved "https://registry.npm.taobao.org/react/download/react-16.14.0.tgz?cache=0&sync_timestamp=1603367592109&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact%2Fdownload%2Freact-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
|
|
245
|
+
integrity sha1-lNd23dCqo32j7aj8W2sYpMmjEU0=
|
|
246
|
+
dependencies:
|
|
247
|
+
loose-envify "^1.1.0"
|
|
248
|
+
object-assign "^4.1.1"
|
|
249
|
+
prop-types "^15.6.2"
|
|
250
|
+
|
|
251
|
+
regenerator-runtime@^0.13.4:
|
|
252
|
+
version "0.13.7"
|
|
253
|
+
resolved "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.13.7.tgz?cache=0&sync_timestamp=1595456311465&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
|
|
254
|
+
integrity sha1-ysLazIoepnX+qrrriugziYrkb1U=
|
|
255
|
+
|
|
256
|
+
scheduler@^0.19.1:
|
|
257
|
+
version "0.19.1"
|
|
258
|
+
resolved "https://registry.npm.taobao.org/scheduler/download/scheduler-0.19.1.tgz?cache=0&sync_timestamp=1603367591660&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fscheduler%2Fdownload%2Fscheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
|
|
259
|
+
integrity sha1-Tz4u0sGn1laB9MhU+oxaHMtA8ZY=
|
|
260
|
+
dependencies:
|
|
261
|
+
loose-envify "^1.1.0"
|
|
262
|
+
object-assign "^4.1.1"
|
|
263
|
+
|
|
264
|
+
ts-config-gm-react-app@^3.4.5:
|
|
265
|
+
version "3.4.7"
|
|
266
|
+
resolved "https://registry.npmjs.org/ts-config-gm-react-app/-/ts-config-gm-react-app-3.4.7.tgz#51decbf847c991ae8e3bd950f3f84cfb0523ba4b"
|
|
267
|
+
integrity sha512-1XOMmAIEiUr2R0JTasF30zj5ZRZgVTOx/ycZJOh2xusQCYC4n1/wGbXfEzm8Dzhv2YSWHEWPVHpytAM3mYsu9Q==
|