Skip to content

Commit ab46d7a

Browse files
feat: layout
1 parent 06f834b commit ab46d7a

32 files changed

+3409
-10
lines changed

ui/src/layout/app-main/index.vue

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<template>
2+
<router-view v-slot="{ Component }">
3+
<transition appear name="fade-transform" mode="out-in">
4+
<keep-alive :include="cachedViews">
5+
<component :is="Component" :key="route.path" />
6+
</keep-alive>
7+
</transition>
8+
</router-view>
9+
</template>
10+
11+
<script setup lang="ts">
12+
import { ref, onBeforeUpdate } from 'vue'
13+
import { useRoute } from 'vue-router'
14+
15+
const route = useRoute()
16+
17+
const cachedViews: any = ref([])
18+
onBeforeUpdate(() => {
19+
const { name, meta } = route
20+
if (name && !cachedViews.value.includes(name)) {
21+
cachedViews.value.push(name)
22+
}
23+
})
24+
</script>
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
<template>
2+
<div class="breadcrumb ml-4 mt-4 mb-12 flex">
3+
<back-button :to="activeMenu" class="mt-4"></back-button>
4+
<el-dropdown
5+
placement="bottom"
6+
trigger="click"
7+
@command="changeMenu"
8+
class="w-full"
9+
style="display: block"
10+
>
11+
<div class="breadcrumb-hover flex-between cursor">
12+
<div class="flex align-center">
13+
<AppAvatar
14+
v-if="isApplication && isAppIcon(current?.icon)"
15+
shape="square"
16+
:size="24"
17+
style="background: none"
18+
class="mr-8"
19+
>
20+
<img :src="current?.icon" alt="" />
21+
</AppAvatar>
22+
<AppAvatar
23+
v-else-if="isApplication"
24+
:name="current?.name"
25+
pinyinColor
26+
shape="square"
27+
class="mr-8"
28+
:size="24"
29+
/>
30+
31+
<AppAvatar
32+
v-else-if="isDataset && current?.type === '1'"
33+
class="mr-8 avatar-purple"
34+
shape="square"
35+
:size="24"
36+
>
37+
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
38+
</AppAvatar>
39+
<AppAvatar
40+
v-else-if="isDataset && current?.type === '2'"
41+
class="mr-8 avatar-purple"
42+
shape="square"
43+
:size="24"
44+
style="background: none"
45+
>
46+
<img src="@/assets/logo_lark.svg" style="width: 100%" alt="" />
47+
</AppAvatar>
48+
<AppAvatar v-else class="mr-8 avatar-blue" shape="square" :size="24">
49+
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
50+
</AppAvatar>
51+
<div class="ellipsis" :title="current?.name">{{ current?.name }}</div>
52+
</div>
53+
54+
<el-button text>
55+
<el-icon><CaretBottom /></el-icon>
56+
</el-button>
57+
</div>
58+
<template #dropdown>
59+
<el-scrollbar>
60+
<div style="max-height: 400px">
61+
<el-dropdown-menu>
62+
<template v-for="(item, index) in list" :key="index">
63+
<div :class="item.id === id ? 'dropdown-active' : ''">
64+
<el-dropdown-item :command="item.id">
65+
<div class="flex align-center">
66+
<AppAvatar
67+
v-if="isApplication && isAppIcon(item?.icon)"
68+
shape="square"
69+
:size="24"
70+
style="background: none"
71+
class="mr-8"
72+
>
73+
<img :src="item?.icon" alt="" />
74+
</AppAvatar>
75+
76+
<AppAvatar
77+
v-else-if="isApplication"
78+
:name="item.name"
79+
pinyinColor
80+
class="mr-12"
81+
shape="square"
82+
:size="24"
83+
/>
84+
<AppAvatar
85+
v-else-if="isDataset && item.type === '1'"
86+
class="mr-12 avatar-purple"
87+
shape="square"
88+
:size="24"
89+
>
90+
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
91+
</AppAvatar>
92+
<AppAvatar
93+
v-else-if="isDataset && item.type === '2'"
94+
class="mr-8 avatar-purple"
95+
shape="square"
96+
:size="24"
97+
style="background: none"
98+
>
99+
<img src="@/assets/logo_lark.svg" style="width: 100%" alt="" />
100+
</AppAvatar>
101+
<AppAvatar v-else class="mr-12 avatar-blue" shape="square" :size="24">
102+
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
103+
</AppAvatar>
104+
<span class="ellipsis" :title="item?.name"> {{ item?.name }}</span>
105+
</div>
106+
</el-dropdown-item>
107+
</div>
108+
</template>
109+
</el-dropdown-menu>
110+
</div>
111+
</el-scrollbar>
112+
<div class="breadcrumb__footer border-t" style="padding: 8px 11px; min-width: 200px">
113+
<template v-if="isApplication">
114+
<div class="w-full text-left cursor" @click="openCreateDialog">
115+
<el-button link>
116+
<el-icon class="mr-4"><Plus /></el-icon>
117+
{{ $t('views.application.createApplication') }}
118+
</el-button>
119+
</div>
120+
</template>
121+
<template v-else-if="isDataset">
122+
<div class="w-full text-left cursor" @click="openCreateDialog">
123+
<el-button link>
124+
<el-icon class="mr-4"><Plus /></el-icon> {{ $t('views.dataset.createDataset') }}
125+
</el-button>
126+
</div>
127+
</template>
128+
</div>
129+
</template>
130+
</el-dropdown>
131+
</div>
132+
<CreateApplicationDialog ref="CreateApplicationDialogRef" @refresh="refresh" />
133+
<CreateDatasetDialog ref="CreateDatasetDialogRef" @refresh="refresh" />
134+
</template>
135+
136+
<script setup lang="ts">
137+
import { ref, onMounted, computed } from 'vue'
138+
import { onBeforeRouteLeave, useRouter, useRoute } from 'vue-router'
139+
import CreateApplicationDialog from '@/views/application/component/CreateApplicationDialog.vue'
140+
import CreateDatasetDialog from '@/views/dataset/component/CreateDatasetDialog.vue'
141+
import { isAppIcon, isWorkFlow } from '@/utils/application'
142+
import useStore from '@/stores'
143+
const { common, dataset, application } = useStore()
144+
const route = useRoute()
145+
const router = useRouter()
146+
const {
147+
meta: { activeMenu },
148+
params: { id }
149+
} = route as any
150+
151+
onBeforeRouteLeave((to, from) => {
152+
common.saveBreadcrumb(null)
153+
})
154+
155+
const CreateDatasetDialogRef = ref()
156+
const CreateApplicationDialogRef = ref()
157+
const list = ref<any[]>([])
158+
const loading = ref(false)
159+
160+
const breadcrumbData = computed(() => common.breadcrumb)
161+
162+
const current = computed(() => {
163+
return list.value?.filter((v) => v.id === id)?.[0]
164+
})
165+
166+
const isApplication = computed(() => {
167+
return activeMenu.includes('application')
168+
})
169+
const isDataset = computed(() => {
170+
return activeMenu.includes('dataset')
171+
})
172+
173+
function openCreateDialog() {
174+
if (isDataset.value) {
175+
CreateDatasetDialogRef.value.open()
176+
} else if (isApplication.value) {
177+
CreateApplicationDialogRef.value.open()
178+
}
179+
}
180+
181+
function changeMenu(id: string) {
182+
const lastMatched = route.matched[route.matched.length - 1]
183+
if (lastMatched) {
184+
if (isDataset.value) {
185+
router.push({ name: lastMatched.name, params: { id: id } })
186+
} else if (isApplication.value) {
187+
const type = list.value?.filter((v) => v.id === id)?.[0]?.type
188+
if (
189+
isWorkFlow(type) &&
190+
(lastMatched.name === 'AppSetting' || lastMatched.name === 'AppHitTest')
191+
) {
192+
router.push({ path: `/application/${id}/${type}/overview` })
193+
} else {
194+
router.push({
195+
name: lastMatched.name,
196+
params: { id: id, type: type }
197+
})
198+
}
199+
}
200+
}
201+
}
202+
203+
function getDataset() {
204+
loading.value = true
205+
dataset
206+
.asyncGetAllDataset()
207+
.then((res: any) => {
208+
list.value = res.data
209+
common.saveBreadcrumb(list.value)
210+
loading.value = false
211+
})
212+
.catch(() => {
213+
loading.value = false
214+
})
215+
}
216+
function getApplication() {
217+
loading.value = true
218+
application
219+
.asyncGetAllApplication()
220+
.then((res: any) => {
221+
list.value = res.data
222+
common.saveBreadcrumb(list.value)
223+
loading.value = false
224+
})
225+
.catch(() => {
226+
loading.value = false
227+
})
228+
}
229+
function refresh() {
230+
common.saveBreadcrumb(null)
231+
}
232+
onMounted(() => {
233+
if (!breadcrumbData.value) {
234+
if (isDataset.value) {
235+
getDataset()
236+
} else if (isApplication.value) {
237+
getApplication()
238+
}
239+
} else {
240+
list.value = breadcrumbData.value
241+
}
242+
})
243+
</script>
244+
245+
<style scoped lang="scss">
246+
:deep(.dropdown-active) {
247+
background-color: var(--el-dropdown-menuItem-hover-fill);
248+
.el-dropdown-menu__item {
249+
color: var(--el-dropdown-menuItem-hover-color);
250+
}
251+
}
252+
.breadcrumb {
253+
.breadcrumb-hover {
254+
padding: 4px;
255+
border-radius: 4px;
256+
&:hover {
257+
background: var(--el-color-primary-light-9);
258+
color: var(--el-color-primary);
259+
}
260+
}
261+
&__footer {
262+
&:hover {
263+
background-color: var(--app-text-color-light-1);
264+
}
265+
}
266+
}
267+
</style>

ui/src/layout/components/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export { default as Sidebar } from './sidebar/index.vue'
2+
export { default as AppMain } from './app-main/index.vue'
3+
export { default as TopBar } from './top-bar/index.vue'
4+
export { default as AppHeader } from './app-header/index.vue'

0 commit comments

Comments
 (0)