@yuku123/z-frontend-common 0.1.2 → 0.1.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.
Files changed (43) hide show
  1. package/dist/z-frontend-common.css +1 -0
  2. package/dist/z-frontend-common.es.js +6154 -300
  3. package/dist/z-frontend-common.umd.js +22 -4
  4. package/package.json +4 -3
  5. package/src/components/Ctc/Layout.jsx +328 -0
  6. package/src/components/Ctc/Layout.module.css +145 -0
  7. package/src/components/Ctc/agentTeam/index.tsx +308 -0
  8. package/src/components/Ctc/login/AuthPage.module.css +26 -0
  9. package/src/components/Ctc/login/AuthPage.tsx +235 -0
  10. package/src/components/Ctc/login/index.less +49 -0
  11. package/src/components/Ctc/login/index.tsx +142 -0
  12. package/src/components/Ctc/userPanel/index.tsx +998 -0
  13. package/src/components/Ctc/webide/index.tsx +272 -0
  14. package/src/components/LowCode/LowCodeModel.jsx +962 -0
  15. package/src/components/LowCode/LowCodePage.jsx +31 -0
  16. package/src/components/LowCode/LowCodeRuntime.jsx +335 -0
  17. package/src/components/LowCode/MaterializePage.jsx +235 -0
  18. package/src/components/LowCode/index.js +1 -0
  19. package/src/components/MockPlatform/CurlImportModal.jsx +362 -0
  20. package/src/components/MockPlatform/EndpointsTab.jsx +509 -0
  21. package/src/components/MockPlatform/EnvironmentsTab.jsx +212 -0
  22. package/src/components/MockPlatform/MockTemplateHelper.jsx +200 -0
  23. package/src/components/MockPlatform/OpenApiImportModal.jsx +305 -0
  24. package/src/components/MockPlatform/RecordingsTab.jsx +397 -0
  25. package/src/components/MockPlatform/RequestLogsTab.jsx +239 -0
  26. package/src/components/MockPlatform/ScenariosTab.jsx +236 -0
  27. package/src/components/MockPlatform/TestCasesTab.jsx +462 -0
  28. package/src/components/MockPlatform/index.jsx +127 -0
  29. package/src/components/Overview.jsx +74 -0
  30. package/src/index.js +27 -27
  31. package/src/services/agentTeam.js +7 -0
  32. package/src/services/api.js +84 -0
  33. package/src/services/ctcAc.js +7 -0
  34. package/src/services/ctcAcDomain.js +7 -0
  35. package/src/services/ctcAuthorization.js +7 -0
  36. package/src/services/ctcSurl.js +7 -0
  37. package/src/services/ctcUser.js +7 -0
  38. package/src/services/job.js +7 -0
  39. package/src/services/metaApp.js +7 -0
  40. package/src/services/privateConfig.js +7 -0
  41. package/src/services/request.js +6 -0
  42. package/src/services/webide.js +6 -0
  43. package/src/services/workspace.js +21 -0
@@ -0,0 +1,328 @@
1
+ import React, {useEffect, useRef, useState} from 'react'
2
+ import {Outlet, useLocation, useNavigate} from 'react-router-dom'
3
+ import {Avatar, Cascader, Divider, Dropdown, Layout as AntLayout, Menu, Space, Typography,} from 'antd'
4
+ import {
5
+ AppstoreOutlined,
6
+ AuditOutlined,
7
+ CloudOutlined,
8
+ ClusterOutlined,
9
+ CodeOutlined,
10
+ DownOutlined,
11
+ LinkOutlined,
12
+ LogoutOutlined,
13
+ MenuFoldOutlined,
14
+ MenuUnfoldOutlined,
15
+ RobotOutlined,
16
+ SafetyCertificateOutlined,
17
+ SafetyOutlined,
18
+ SettingOutlined,
19
+ TeamOutlined,
20
+ UserOutlined,
21
+ } from '@ant-design/icons'
22
+ import styles from './Layout.module.css'
23
+ import {getCurrentUser} from '../../services/api'
24
+
25
+ const {Header, Sider, Content} = AntLayout
26
+ const {Text} = Typography
27
+
28
+ const Layout = ({compact = false} = {}) => {
29
+ const location = useLocation()
30
+ const navigate = useNavigate()
31
+ const [collapsed, setCollapsed] = useState(false)
32
+ const [userName, setUserName] = useState('管理员')
33
+ const [userInfo, setUserInfo] = useState(null)
34
+ const [tenantOptions, setTenantOptions] = useState([])
35
+ const [selectedTenant, setSelectedTenant] = useState([])
36
+
37
+ const navigateRef = useRef(navigate)
38
+ navigateRef.current = navigate
39
+
40
+ useEffect(() => {
41
+ const token = localStorage.getItem('token')
42
+ if (!token) {
43
+ navigateRef.current('/login')
44
+ return
45
+ }
46
+
47
+ const userInfoStr = localStorage.getItem('userInfo')
48
+ if (userInfoStr) {
49
+ try {
50
+ const user = JSON.parse(userInfoStr)
51
+ setUserName(user.userName || '管理员')
52
+ setUserInfo(user)
53
+ if (user.tenantCode) {
54
+ setSelectedTenant([user.tenantCode])
55
+ }
56
+ } catch (e) {
57
+ }
58
+ }
59
+ getCurrentUser().then(res => {
60
+ if (res) {
61
+ setUserInfo(res)
62
+ setUserName(res.userName || '管理员')
63
+ localStorage.setItem('userInfo', JSON.stringify(res))
64
+ }
65
+ }).catch(() => {
66
+ })
67
+ setTenantOptions([
68
+ {
69
+ value: 'tenant1',
70
+ label: '租户1',
71
+ children: [
72
+ {value: 'domain1-1', label: '域1-1'},
73
+ {value: 'domain1-2', label: '域1-2'},
74
+ ],
75
+ },
76
+ {
77
+ value: 'tenant2',
78
+ label: '租户2',
79
+ children: [
80
+ {value: 'domain2-1', label: '域2-1'},
81
+ {value: 'domain2-2', label: '域2-2'},
82
+ ],
83
+ },
84
+ ])
85
+ }, [])
86
+
87
+ // 获取当前选中的菜单项
88
+ const getSelectedKeys = () => {
89
+ const path = location.pathname
90
+ if (path.includes('/user')) return ['user']
91
+ if (path.includes('/role')) return ['role']
92
+ if (path.includes('/permission')) return ['permission']
93
+ if (path.includes('/app')) return ['app']
94
+ if (path.includes('/dict')) return ['dict']
95
+ if (path.includes('/audit')) return ['audit']
96
+ if (path.includes('/tenant')) return ['tenant']
97
+ if (path.includes('/domain')) return ['domain']
98
+ if (path.includes('/org')) return ['org']
99
+ if (path.includes('/dept')) return ['dept']
100
+ if (path.includes('/group')) return ['group']
101
+ // 4A 域 (合并后菜单项直接是 4a/* 作为顶级 key)
102
+ if (path.includes('/4a/')) {
103
+ const parts = path.split('/4a/')[1]?.split('/') || []
104
+ if (parts.length > 0) return [`4a/${parts[0]}`]
105
+ }
106
+ // z-webide
107
+ if (path.includes('/webide')) return ['webide']
108
+ // z-agent-team
109
+ if (path.includes('/agent-team')) return ['agent-team']
110
+ return ['4a/account']
111
+ }
112
+
113
+ // 菜单项
114
+ const menuItems = [
115
+ // ===== FEATURE015: 合并 4A 到顶层, 一套菜单 =====
116
+ {
117
+ key: '4a/account',
118
+ icon: <TeamOutlined/>,
119
+ label: '账号管理',
120
+ },
121
+ {
122
+ key: '4a/role',
123
+ icon: <SafetyOutlined/>,
124
+ label: '角色管理',
125
+ },
126
+ {
127
+ key: '4a/application',
128
+ icon: <AppstoreOutlined/>,
129
+ label: '应用管理',
130
+ },
131
+ {
132
+ key: '4a/tenant',
133
+ icon: <TeamOutlined/>,
134
+ label: '租户管理',
135
+ },
136
+ {
137
+ key: '4a/org',
138
+ icon: <ClusterOutlined/>,
139
+ label: '组织管理',
140
+ },
141
+ {
142
+ key: '4a/permission',
143
+ icon: <SafetyCertificateOutlined/>,
144
+ label: '权限管理',
145
+ },
146
+ {
147
+ key: '4a/surl',
148
+ icon: <LinkOutlined/>,
149
+ label: '短链管理',
150
+ },
151
+
152
+ {
153
+ key: 'webide',
154
+ icon: <CodeOutlined/>,
155
+ label: 'WebIDE 容器',
156
+ },
157
+ {
158
+ key: 'agent-team',
159
+ icon: <RobotOutlined/>,
160
+ label: 'Agent 群组 IM',
161
+ },
162
+ {
163
+ type: 'divider',
164
+ },
165
+ {
166
+ key: 'settings',
167
+ icon: <SettingOutlined/>,
168
+ label: '系统设置',
169
+ },
170
+ ]
171
+
172
+ // 处理菜单点击
173
+ const handleMenuClick = ({key}) => {
174
+ navigate(`/ctc/${key}`)
175
+ }
176
+
177
+ return (
178
+ <AntLayout className={styles.layout}>
179
+ {compact ? null : (
180
+ /* 顶部导航栏 (compact 模式: 由主壳 App.jsx 提供) */
181
+ <>
182
+ <Header className={styles.header}>
183
+ <div className={styles.logo}>
184
+ <span className={styles.logoText}>Z-CTC 统一用户中心</span>
185
+ </div>
186
+ <div className={styles.headerRight}>
187
+ <Space size={24}>
188
+ <Dropdown
189
+ overlay={
190
+ <div style={{
191
+ background: '#fff',
192
+ borderRadius: '8px',
193
+ boxShadow: '0 6px 16px rgba(0, 0, 0, 0.12)',
194
+ padding: '16px',
195
+ width: '360px'
196
+ }}>
197
+ <div style={{
198
+ display: 'flex',
199
+ alignItems: 'center',
200
+ gap: '12px',
201
+ marginBottom: '12px'
202
+ }}>
203
+ <span style={{fontSize: '14px', color: '#666'}}>租户域</span>
204
+ <Cascader
205
+ value={selectedTenant}
206
+ onChange={(value) => setSelectedTenant(value)}
207
+ options={tenantOptions}
208
+ placeholder="请选择租户域"
209
+ style={{flex: 1}}
210
+ />
211
+ </div>
212
+ <Divider style={{margin: '12px 0'}}/>
213
+ <div style={{display: 'flex', flexDirection: 'column', gap: '8px'}}>
214
+ <div style={{display: 'flex', fontSize: '14px'}}>
215
+ <span style={{color: '#999', width: '70px'}}>用户名:</span>
216
+ <span>{userInfo?.userName || '-'}</span>
217
+ </div>
218
+ <div style={{display: 'flex', fontSize: '14px'}}>
219
+ <span style={{color: '#999', width: '70px'}}>真实姓名:</span>
220
+ <span>{userInfo?.realName || '-'}</span>
221
+ </div>
222
+ <div style={{display: 'flex', fontSize: '14px'}}>
223
+ <span style={{color: '#999', width: '70px'}}>邮箱:</span>
224
+ <span>{userInfo?.email || '-'}</span>
225
+ </div>
226
+ <div style={{display: 'flex', fontSize: '14px'}}>
227
+ <span style={{color: '#999', width: '70px'}}>手机号:</span>
228
+ <span>{userInfo?.phone || '-'}</span>
229
+ </div>
230
+ <div style={{display: 'flex', fontSize: '14px'}}>
231
+ <span style={{color: '#999', width: '70px'}}>状态:</span>
232
+ <span>{userInfo?.status === 1 ? '正常' : '停用'}</span>
233
+ </div>
234
+ </div>
235
+ <Divider style={{margin: '12px 0'}}/>
236
+ <div style={{display: 'flex', flexDirection: 'column'}}>
237
+ <div style={{
238
+ display: 'flex',
239
+ alignItems: 'center',
240
+ gap: '8px',
241
+ padding: '8px 12px',
242
+ cursor: 'pointer',
243
+ borderRadius: '4px',
244
+ fontSize: '14px'
245
+ }} onClick={() => {
246
+ }}>
247
+ <UserOutlined/> 个人中心
248
+ </div>
249
+ <div style={{
250
+ display: 'flex',
251
+ alignItems: 'center',
252
+ gap: '8px',
253
+ padding: '8px 12px',
254
+ cursor: 'pointer',
255
+ borderRadius: '4px',
256
+ fontSize: '14px'
257
+ }} onClick={() => {
258
+ }}>
259
+ <SettingOutlined/> 系统设置
260
+ </div>
261
+ <div style={{
262
+ display: 'flex',
263
+ alignItems: 'center',
264
+ gap: '8px',
265
+ padding: '8px 12px',
266
+ cursor: 'pointer',
267
+ borderRadius: '4px',
268
+ fontSize: '14px'
269
+ }} onClick={() => {
270
+ localStorage.removeItem('token')
271
+ localStorage.removeItem('userInfo')
272
+ navigate('/ctc/login')
273
+ }}>
274
+ <LogoutOutlined/> 退出登录
275
+ </div>
276
+ </div>
277
+ </div>
278
+ }
279
+ placement="bottomRight"
280
+ trigger={['click']}
281
+ >
282
+ <div className={styles.userInfo}>
283
+ <Avatar icon={<UserOutlined/>} size="small"/>
284
+ <Text className={styles.userName}>{userName}</Text>
285
+ <DownOutlined/>
286
+ </div>
287
+ </Dropdown>
288
+ </Space>
289
+ </div>
290
+ </Header>
291
+ </>
292
+ )}
293
+
294
+ <AntLayout className={styles.mainLayout}>
295
+ {/* 侧边菜单 */}
296
+ <Sider
297
+ trigger={null}
298
+ collapsible
299
+ collapsed={collapsed}
300
+ className={styles.sider}
301
+ width={200}
302
+ >
303
+ <Menu
304
+ mode="inline"
305
+ selectedKeys={getSelectedKeys()}
306
+ defaultOpenKeys={['user', 'role', 'permission', 'audit', 'tenant', 'domain', 'org', 'dept', 'group']}
307
+ className={styles.menu}
308
+ items={menuItems}
309
+ onClick={handleMenuClick}
310
+ />
311
+ <div
312
+ className={styles.collapsedBtn}
313
+ onClick={() => setCollapsed(!collapsed)}
314
+ >
315
+ {collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
316
+ </div>
317
+ </Sider>
318
+
319
+ {/* 主内容区 */}
320
+ <Content className={styles.content}>
321
+ <Outlet/>
322
+ </Content>
323
+ </AntLayout>
324
+ </AntLayout>
325
+ )
326
+ }
327
+
328
+ export default Layout
@@ -0,0 +1,145 @@
1
+ .layout {
2
+ min-height: 100vh;
3
+ }
4
+
5
+ .header {
6
+ display: flex;
7
+ justify-content: space-between;
8
+ align-items: center;
9
+ padding: 0 24px;
10
+ background: #001529;
11
+ height: 48px;
12
+ line-height: 48px;
13
+ }
14
+
15
+ .logo {
16
+ display: flex;
17
+ align-items: center;
18
+ }
19
+
20
+ .logoText {
21
+ color: #fff;
22
+ font-size: 16px;
23
+ font-weight: 600;
24
+ }
25
+
26
+ .headerRight {
27
+ display: flex;
28
+ align-items: center;
29
+ }
30
+
31
+ .userInfo {
32
+ display: flex;
33
+ align-items: center;
34
+ cursor: pointer;
35
+ color: #fff;
36
+ }
37
+
38
+ .userName {
39
+ margin-left: 8px;
40
+ color: #fff;
41
+ }
42
+
43
+ .mainLayout {
44
+ margin-top: 0;
45
+ min-height: calc(100vh - 48px);
46
+ }
47
+
48
+ .sider {
49
+ background: #fff;
50
+ min-height: calc(100vh - 48px);
51
+ }
52
+
53
+ .menu {
54
+ border-right: none;
55
+ }
56
+
57
+ .collapsedBtn {
58
+ position: absolute;
59
+ bottom: 16px;
60
+ left: 50%;
61
+ transform: translateX(-50%);
62
+ cursor: pointer;
63
+ padding: 8px 16px;
64
+ background: #f5f5f5;
65
+ border-radius: 4px;
66
+ color: #666;
67
+ }
68
+
69
+ .collapsedBtn:hover {
70
+ background: #e6f7ff;
71
+ color: #1890ff;
72
+ }
73
+
74
+ .content {
75
+ padding: 24px;
76
+ background: #f5f5f5;
77
+ min-height: calc(100vh - 48px);
78
+ overflow: auto;
79
+ }
80
+
81
+ .userDropdownPanel {
82
+ background: #fff;
83
+ border-radius: 8px;
84
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
85
+ padding: 16px;
86
+ width: 360px;
87
+ }
88
+
89
+ .tenantSection {
90
+ display: flex;
91
+ align-items: center;
92
+ gap: 12px;
93
+ }
94
+
95
+ .tenantLabel {
96
+ font-size: 14px;
97
+ color: #666;
98
+ white-space: nowrap;
99
+ }
100
+
101
+ .tenantCascader {
102
+ flex: 1;
103
+ }
104
+
105
+ .panelDivider {
106
+ margin: 12px 0;
107
+ }
108
+
109
+ .userSection {
110
+ display: flex;
111
+ flex-direction: column;
112
+ gap: 8px;
113
+ }
114
+
115
+ .userInfoRow {
116
+ display: flex;
117
+ font-size: 14px;
118
+ line-height: 1.6;
119
+ }
120
+
121
+ .userInfoLabel {
122
+ color: #999;
123
+ width: 70px;
124
+ flex-shrink: 0;
125
+ }
126
+
127
+ .menuSection {
128
+ display: flex;
129
+ flex-direction: column;
130
+ }
131
+
132
+ .menuItem {
133
+ display: flex;
134
+ align-items: center;
135
+ gap: 8px;
136
+ padding: 8px 12px;
137
+ cursor: pointer;
138
+ border-radius: 4px;
139
+ font-size: 14px;
140
+ color: #333;
141
+ }
142
+
143
+ .menuItem:hover {
144
+ background: #f5f5f5;
145
+ }