mirror of https://github.com/halo-dev/halo-admin
feat: add visual theme editing page ui
Signed-off-by: Ryan Wang <i@ryanc.cc>pull/581/head
parent
87642a5698
commit
5b24b06859
|
@ -40,19 +40,19 @@ const handleChange = (id: number | string) => {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="tabbar-wrapper" :class="classes">
|
||||
<div :class="classes" class="tabbar-wrapper">
|
||||
<div class="tabbar-items">
|
||||
<div
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
class="tabbar-item"
|
||||
:class="{ 'tabbar-item-active': item[idKey] === activeId }"
|
||||
class="tabbar-item"
|
||||
@click="handleChange(item[idKey])"
|
||||
>
|
||||
<div v-if="item.icon" class="tabbar-item-icon">
|
||||
<component :is="item.icon" />
|
||||
</div>
|
||||
<div class="tabbar-item-label">
|
||||
<div v-if="item[labelKey]" class="tabbar-item-label">
|
||||
{{ item[labelKey] }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -74,15 +74,12 @@ const handleChange = (id: number | string) => {
|
|||
@apply transition-all;
|
||||
@apply text-base;
|
||||
@apply justify-center;
|
||||
@apply gap-2;
|
||||
|
||||
.tabbar-item-label,
|
||||
.tabbar-item-icon {
|
||||
@apply self-center;
|
||||
}
|
||||
|
||||
.tabbar-item-icon {
|
||||
@apply mr-2;
|
||||
}
|
||||
}
|
||||
|
||||
&.tabbar-default {
|
||||
|
@ -93,6 +90,7 @@ const handleChange = (id: number | string) => {
|
|||
margin-bottom: -2px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
@apply h-10;
|
||||
@apply px-5;
|
||||
|
@ -113,6 +111,7 @@ const handleChange = (id: number | string) => {
|
|||
@apply gap-1;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
@apply h-10;
|
||||
@apply px-9;
|
||||
|
|
|
@ -57,6 +57,14 @@ import IconList from "~icons/ri/list-unordered";
|
|||
import IconGrid from "~icons/ri/grid-line";
|
||||
// @ts-ignore
|
||||
import IconCheckboxFill from "~icons/ri/checkbox-circle-fill";
|
||||
// @ts-ignore
|
||||
import IconSearch from "~icons/ri/search-2-line";
|
||||
// @ts-ignore
|
||||
import IconComputer from "~icons/ri/computer-line";
|
||||
// @ts-ignore
|
||||
import IconPhone from "~icons/ri/smartphone-line";
|
||||
// @ts-ignore
|
||||
import IconTablet from "~icons/ri/tablet-line";
|
||||
|
||||
export {
|
||||
IconDashboard,
|
||||
|
@ -88,4 +96,8 @@ export {
|
|||
IconCheckboxFill,
|
||||
IconArrowUpLine,
|
||||
IconArrowDownLine,
|
||||
IconSearch,
|
||||
IconComputer,
|
||||
IconPhone,
|
||||
IconTablet
|
||||
};
|
||||
|
|
|
@ -7,6 +7,17 @@
|
|||
<div class="logo flex justify-center py-5">
|
||||
<img :src="logo" alt="Halo Logo" style="width: 78px" />
|
||||
</div>
|
||||
<div class="px-3">
|
||||
<div
|
||||
class="flex p-2 text-gray-400 items-center transition-all bg-gray-100 rounded cursor-pointer hover:text-gray-900"
|
||||
>
|
||||
<span class="mr-3">
|
||||
<IconSearch />
|
||||
</span>
|
||||
<span class="flex-1 text-base select-none font-normal">搜索</span>
|
||||
<div class="text-sm">⌘+K</div>
|
||||
</div>
|
||||
</div>
|
||||
<VRoutesMenu :menus="menus" />
|
||||
<div class="current-profile">
|
||||
<div class="profile-avatar">
|
||||
|
@ -131,7 +142,7 @@ import { VRoutesMenu } from "@/components/base/menu";
|
|||
import { VTag } from "@/components/base/tag";
|
||||
import { menus, minimenus } from "@/router/menus.config";
|
||||
import logo from "@/assets/logo.svg";
|
||||
import { IconMore, IconUserSettings } from "@/core/icons";
|
||||
import { IconMore, IconSearch, IconUserSettings } from "@/core/icons";
|
||||
import { RouterView, useRoute, useRouter } from "vue-router";
|
||||
import { ref } from "vue";
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ export const routes: Array<RouteRecordRaw> = [
|
|||
},
|
||||
{
|
||||
path: "/visual",
|
||||
component: BasicLayout,
|
||||
component: BlankLayout,
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
|
|
|
@ -1 +1,144 @@
|
|||
<template>Visual</template>
|
||||
<script lang="ts" setup>
|
||||
import { VButton } from "@/components/base/button";
|
||||
import { VInput } from "@/components/base/input";
|
||||
import { VOption, VSelect } from "@/components/base/select";
|
||||
import { VTextarea } from "@/components/base/textarea";
|
||||
import { VTabbar, VTabItem, VTabs } from "@/components/base/tabs";
|
||||
import { computed, ref } from "vue";
|
||||
import { IconComputer, IconPhone, IconTablet } from "@/core/icons";
|
||||
|
||||
const activeId = ref("general");
|
||||
const deviceActiveId = ref("desktop");
|
||||
|
||||
const iframeClasses = computed(() => {
|
||||
if (deviceActiveId.value === "desktop") {
|
||||
return "w-full h-full";
|
||||
}
|
||||
if (deviceActiveId.value === "tablet") {
|
||||
return "w-2/3 h-2/3";
|
||||
}
|
||||
// phone
|
||||
return "w-96 h-[50rem]";
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="h-screen flex">
|
||||
<div class="h-full bg-white w-96 drop-shadow-sm overflow-y-auto">
|
||||
<VTabs v-model:active-id="activeId">
|
||||
<VTabItem id="general" class="p-3" label="基础设置">
|
||||
<form>
|
||||
<div class="space-y-8 divide-y divide-gray-200 sm:space-y-5">
|
||||
<div class="space-y-6 sm:space-y-5">
|
||||
<div class="space-y-2">
|
||||
<label
|
||||
class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2"
|
||||
for="first-name"
|
||||
>
|
||||
Halo 当前版本:
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<VInput model-value="1.5.3"></VInput>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<label
|
||||
class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2"
|
||||
for="last-name"
|
||||
>
|
||||
首页图片:
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<VInput
|
||||
model-value="https://halo.run/upload/2022/03/support-team.svg"
|
||||
></VInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</VTabItem>
|
||||
<VTabItem id="style" class="p-3" label="样式设置">
|
||||
<form>
|
||||
<div class="space-y-8 divide-y divide-gray-200 sm:space-y-5">
|
||||
<div class="space-y-6 sm:space-y-5">
|
||||
<div class="space-y-2">
|
||||
<label
|
||||
class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2"
|
||||
for="first-name"
|
||||
>
|
||||
文章代码高亮语言:
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<VTextarea></VTextarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<label
|
||||
class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2"
|
||||
for="last-name"
|
||||
>
|
||||
文章代码高亮主题:
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<VSelect>
|
||||
<VOption value="java">Java</VOption>
|
||||
<VOption value="c">C</VOption>
|
||||
<VOption value="go">Go</VOption>
|
||||
<VOption value="javascript">JavaScript</VOption>
|
||||
</VSelect>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</VTabItem>
|
||||
</VTabs>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div
|
||||
class="flex items-center justify-between p-2 bg-white h-16 drop-shadow-sm"
|
||||
>
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-gray-800 truncate">
|
||||
<span>Anatole</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div>
|
||||
<VTabbar
|
||||
v-model:active-id="deviceActiveId"
|
||||
:items="[
|
||||
{
|
||||
id: 'desktop',
|
||||
icon: IconComputer,
|
||||
},
|
||||
{
|
||||
id: 'tablet',
|
||||
icon: IconTablet,
|
||||
},
|
||||
{
|
||||
id: 'phone',
|
||||
icon: IconPhone,
|
||||
},
|
||||
]"
|
||||
type="outline"
|
||||
></VTabbar>
|
||||
</div>
|
||||
<div>
|
||||
<VButton type="secondary">保存</VButton>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="h-full flex justify-center items-center"
|
||||
style="height: calc(100vh - 4rem)"
|
||||
>
|
||||
<iframe
|
||||
:class="iframeClasses"
|
||||
class="border-none"
|
||||
src="http://localhost:8090"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue