Skip to content

Commit 0b8c296

Browse files
committed
feat: complete SPA creation page design
1 parent 0e292e7 commit 0b8c296

7 files changed

Lines changed: 771 additions & 60 deletions

File tree

web/src/router/index.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,20 @@ export const constantRoutes = [
4545
component: () => import('@/views/app/index'),
4646
meta: {title: '应用', icon: 'app'},
4747
},
48-
{
49-
path: 'create',
50-
name: '创建应用',
51-
component: () => import('@/views/app/create'),
52-
meta: {title: '创建应用', icon: 'app'},
53-
hidden: true
54-
}]
48+
{
49+
path: 'create',
50+
name: '创建应用',
51+
component: () => import('@/views/app/create'),
52+
meta: {title: '创建应用', icon: 'app'},
53+
hidden: true
54+
},
55+
{
56+
path: ':id',
57+
name: 'AppDetail',
58+
component: () => import('@/views/app/appDetail'),
59+
hidden: true
60+
},
61+
]
5562
},
5663
{
5764
path: '/identity',

web/src/views/app/appDetail.vue

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
<template>
2+
<div class="app-detail-page">
3+
4+
<!-- 顶部基础信息栏 -->
5+
<div class="app-header">
6+
<el-button
7+
type="text"
8+
icon="el-icon-arrow-left"
9+
class="back-btn"
10+
@click="$router.back()"
11+
>返回</el-button>
12+
13+
<div class="app-meta">
14+
<div class="app-name">{{ app.name }}</div>
15+
<el-tag size="mini">{{ app.type }}</el-tag>
16+
<div class="app-id">Client ID:{{ app.clientId }}</div>
17+
</div>
18+
</div>
19+
20+
<!-- 新建引导 Banner -->
21+
<el-alert
22+
v-if="isFromCreate && showGuide"
23+
type="success"
24+
show-icon
25+
closable
26+
class="guide-banner"
27+
title="应用已创建成功"
28+
description="请完成协议配置、API 授权和角色分配,以便应用可以正常使用。"
29+
@close="showGuide = false"
30+
/>
31+
32+
<!-- Tabs -->
33+
<el-tabs v-model="activeTab" class="detail-tabs">
34+
35+
<!-- 概览 -->
36+
<el-tab-pane label="概览" name="overview">
37+
<div class="overview">
38+
39+
<el-card class="overview-card">
40+
<div class="card-title">配置进度</div>
41+
42+
<ul class="checklist">
43+
<li
44+
v-for="item in checklist"
45+
:key="item.key"
46+
:class="{ done: item.done }"
47+
@click="goTo(item.tab)"
48+
>
49+
<i
50+
:class="item.done ? 'el-icon-check' : 'el-icon-warning-outline'"
51+
/>
52+
{{ item.label }}
53+
</li>
54+
</ul>
55+
</el-card>
56+
</div>
57+
</el-tab-pane>
58+
59+
<!-- 协议 -->
60+
<el-tab-pane label="协议" name="protocol">
61+
<el-card>协议配置区域(OIDC / OAuth2)</el-card>
62+
</el-tab-pane>
63+
64+
<!-- API 授权 -->
65+
<el-tab-pane label="API 授权" name="api">
66+
<el-card>API 授权配置区域</el-card>
67+
</el-tab-pane>
68+
69+
<!-- 角色 -->
70+
<el-tab-pane label="角色" name="role">
71+
<el-card>角色分配区域</el-card>
72+
</el-tab-pane>
73+
74+
</el-tabs>
75+
</div>
76+
</template>
77+
78+
<script>
79+
export default {
80+
name: 'AppDetail',
81+
82+
data() {
83+
return {
84+
activeTab: 'overview',
85+
showGuide: true,
86+
87+
// 模拟应用数据
88+
app: {
89+
name: '单页应用程序',
90+
type: 'SPA',
91+
clientId: 'spa_xxx_123',
92+
protocolConfigured: false,
93+
apiAuthorized: false,
94+
roleAssigned: false
95+
}
96+
}
97+
},
98+
99+
computed: {
100+
isFromCreate() {
101+
return this.$route.query.from === 'create'
102+
},
103+
104+
checklist() {
105+
return [
106+
{
107+
key: 'protocol',
108+
label: '配置协议',
109+
done: this.app.protocolConfigured,
110+
tab: 'protocol'
111+
},
112+
{
113+
key: 'api',
114+
label: '授权 API',
115+
done: this.app.apiAuthorized,
116+
tab: 'api'
117+
},
118+
{
119+
key: 'role',
120+
label: '分配角色',
121+
done: this.app.roleAssigned,
122+
tab: 'role'
123+
}
124+
]
125+
}
126+
},
127+
128+
created() {
129+
if (this.isFromCreate) {
130+
this.activeTab = 'protocol'
131+
}
132+
},
133+
134+
methods: {
135+
goTo(tab) {
136+
this.activeTab = tab
137+
}
138+
}
139+
}
140+
</script>
141+
142+
<style scoped lang="scss">
143+
.app-detail-page {
144+
padding: 24px 32px;
145+
}
146+
147+
.app-header {
148+
display: flex;
149+
align-items: center;
150+
margin-bottom: 16px;
151+
152+
.back-btn {
153+
margin-right: 16px;
154+
}
155+
156+
.app-meta {
157+
.app-name {
158+
font-size: 20px;
159+
font-weight: 600;
160+
margin-bottom: 4px;
161+
}
162+
163+
.app-id {
164+
font-size: 13px;
165+
color: #888;
166+
}
167+
}
168+
}
169+
170+
.guide-banner {
171+
margin-bottom: 16px;
172+
}
173+
174+
.detail-tabs {
175+
background: #fff;
176+
}
177+
178+
.overview {
179+
padding: 8px 0;
180+
181+
.overview-card {
182+
max-width: 480px;
183+
}
184+
185+
.card-title {
186+
font-weight: 600;
187+
margin-bottom: 12px;
188+
}
189+
190+
.checklist {
191+
list-style: none;
192+
padding: 0;
193+
margin: 0;
194+
195+
li {
196+
display: flex;
197+
align-items: center;
198+
padding: 8px 0;
199+
cursor: pointer;
200+
color: #333;
201+
202+
i {
203+
margin-right: 8px;
204+
}
205+
206+
&.done {
207+
color: #67c23a;
208+
}
209+
}
210+
}
211+
}
212+
</style>

web/src/views/app/create.vue

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
<template>
22
<div class="app-create-page">
33

4+
<!-- 顶部导航 -->
5+
<div class="page-header">
6+
<div class="back-btn" @click="goBack">
7+
<i class="el-icon-arrow-left" />
8+
<span>返回</span>
9+
</div>
10+
</div>
11+
412
<!-- 顶部标题 -->
513
<h2 class="page-title">创建新应用程序</h2>
614

@@ -64,12 +72,20 @@
6472

6573
</div>
6674

75+
<!--单页应用程序创建弹框-->
76+
<create-spa-dialog
77+
:visible.sync="showSpaDialog"
78+
@created="onCreated"
79+
/>
6780
</div>
6881
</template>
6982

7083
<script>
84+
import CreateSpaDialog from './createSpaDialog.vue'
85+
7186
export default {
7287
name: 'AppCreatePage',
88+
components: { CreateSpaDialog },
7389
7490
data() {
7591
return {
@@ -137,7 +153,8 @@ export default {
137153
],
138154
proto: ['OIDC', 'CAS', 'OAuth2']
139155
}
140-
]
156+
],
157+
showSpaDialog: false
141158
}
142159
},
143160
@@ -160,7 +177,27 @@ export default {
160177
},
161178
162179
selectApp(item) {
163-
this.$message.success('选择了:' + item.name)
180+
if (item.id === 1) {
181+
this.showSpaDialog = true
182+
}
183+
},
184+
185+
goBack() {
186+
// 优先返回上一页,否则回到应用列表
187+
if (this.$router && this.$router.history.current.from) {
188+
this.$router.back()
189+
} else {
190+
this.$router.push('/app')
191+
}
192+
},
193+
194+
onCreated(appId) {
195+
// 创建成功后,进入应用详情页
196+
this.$router.push({
197+
name: 'AppDetail',
198+
params: { id: appId },
199+
query: { from: 'create' }
200+
})
164201
}
165202
}
166203
}
@@ -171,6 +208,34 @@ export default {
171208
padding: 30px 40px;
172209
color: #333;
173210
211+
.page-header {
212+
margin-bottom: 6px;
213+
214+
.back-btn {
215+
display: inline-flex;
216+
align-items: center;
217+
218+
font-size: 14px;
219+
color: #606266;
220+
cursor: pointer;
221+
222+
i {
223+
margin-right: 6px;
224+
font-size: 16px;
225+
}
226+
227+
padding: 6px 10px;
228+
border-radius: 8px;
229+
230+
transition: background 0.15s ease, color 0.15s ease;
231+
232+
&:hover {
233+
background: #f5f7fa;
234+
color: #409eff;
235+
}
236+
}
237+
}
238+
174239
.page-title {
175240
font-size: 26px;
176241
font-weight: 600;

0 commit comments

Comments
 (0)