mirror of https://github.com/halo-dev/halo-admin
parent
ad10465da2
commit
9c9a2ea5c4
|
@ -82,6 +82,7 @@ function handleClose() {
|
|||
class="modal-content transform transition-all"
|
||||
>
|
||||
<div class="modal-header">
|
||||
<slot name="header">
|
||||
<div class="modal-header-title">{{ title }}</div>
|
||||
<div class="modal-header-actions flex flex-row">
|
||||
<slot name="actions"></slot>
|
||||
|
@ -89,6 +90,7 @@ function handleClose() {
|
|||
<IconClose />
|
||||
</div>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
<div :class="bodyClass" class="modal-body">
|
||||
<slot />
|
||||
|
|
|
@ -1,3 +1,26 @@
|
|||
<script lang="ts" setup>
|
||||
import { VRoutesMenu } from "@/components/base/menu";
|
||||
import { VTag } from "@/components/base/tag";
|
||||
import { VModal } from "@/components/base/modal";
|
||||
import { VInput } from "@/components/base/input";
|
||||
import { menus, minimenus } from "@/router/menus.config";
|
||||
import logo from "@/assets/logo.svg";
|
||||
import { IconMore, IconSearch, IconUserSettings } from "@/core/icons";
|
||||
import { RouterView, useRoute, useRouter } from "vue-router";
|
||||
import { ref } from "vue";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const moreMenuVisible = ref(false);
|
||||
const moreMenuRootVisible = ref(false);
|
||||
const spotlight = ref(false);
|
||||
|
||||
const handleRouteToProfile = () => {
|
||||
router.push({ name: "Profile" });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full">
|
||||
<aside
|
||||
|
@ -10,6 +33,7 @@
|
|||
<div class="px-3">
|
||||
<div
|
||||
class="flex cursor-pointer items-center rounded bg-gray-100 p-2 text-gray-400 transition-all hover:text-gray-900"
|
||||
@click="spotlight = true"
|
||||
>
|
||||
<span class="mr-3">
|
||||
<IconSearch />
|
||||
|
@ -135,27 +159,13 @@
|
|||
</Teleport>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<VModal v-model:visible="spotlight" :width="600">
|
||||
<template #header>
|
||||
<VInput placeholder="全局搜索" size="lg"></VInput>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
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, IconSearch, IconUserSettings } from "@/core/icons";
|
||||
import { RouterView, useRoute, useRouter } from "vue-router";
|
||||
import { ref } from "vue";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const moreMenuVisible = ref(false);
|
||||
const moreMenuRootVisible = ref(false);
|
||||
|
||||
const handleRouteToProfile = () => {
|
||||
router.push({ name: "Profile" });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.navbar {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { BasicLayout } from "@/layouts";
|
||||
import { VPageHeader } from "@/components/base/header";
|
||||
import { VButton } from "@/components/base/button";
|
||||
import { VCard } from "@/components/base/card";
|
||||
import { VTabbar } from "@/components/base/tabs";
|
||||
import { IconSettings } from "@/core/icons";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
@ -49,17 +50,19 @@ const handleTabChange = (id: string) => {
|
|||
</template>
|
||||
</VPageHeader>
|
||||
|
||||
<div class="p-4">
|
||||
<div class="m-0 md:m-4">
|
||||
<VCard>
|
||||
<template #header>
|
||||
<VTabbar
|
||||
v-model:active-id="activeTab"
|
||||
:items="SettingTabs"
|
||||
class="w-full !rounded-none"
|
||||
type="outline"
|
||||
@change="handleTabChange"
|
||||
></VTabbar>
|
||||
|
||||
<div>
|
||||
</template>
|
||||
<RouterView></RouterView>
|
||||
</div>
|
||||
</VCard>
|
||||
</div>
|
||||
</BasicLayout>
|
||||
</template>
|
||||
|
|
|
@ -90,7 +90,7 @@ export const menus: MenuGroupType[] = [
|
|||
},
|
||||
{
|
||||
name: "设置",
|
||||
path: "/settings/general",
|
||||
path: "/settings",
|
||||
icon: IconSettings,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -13,7 +13,7 @@ import AttachmentList from "../views/contents/attachments/AttachmentList.vue";
|
|||
|
||||
import ThemeDetail from "../views/interface/themes/ThemeDetail.vue";
|
||||
import MenuList from "../views/interface/menus/MenuList.vue";
|
||||
import Visual from "../views/interface/visual/Visual.vue";
|
||||
import Visual from "../views/interface/themes/Visual.vue";
|
||||
|
||||
import PluginList from "../views/system/plugins/PluginList.vue";
|
||||
import PluginDetail from "../views/system/plugins/PluginDetail.vue";
|
||||
|
|
|
@ -34,7 +34,7 @@ const handleChangeTheme = (theme: any) => {
|
|||
v-model:visible="changeTheme"
|
||||
:body-class="['!p-0']"
|
||||
:width="888"
|
||||
title="切换主题"
|
||||
title="已安装的主题"
|
||||
>
|
||||
<ul class="flex flex-col divide-y divide-gray-100" role="list">
|
||||
<li
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
<script lang="ts" setup>
|
||||
import { VButton } from "@/components/base/button";
|
||||
import { VModal } from "@/components/base/modal";
|
||||
import { VCard } from "@/components/base/card";
|
||||
import { VOption, VSelect } from "@/components/base/select";
|
||||
import { VInput } from "@/components/base/input";
|
||||
import { VTextarea } from "@/components/base/textarea";
|
||||
import { VSpace } from "@/components/base/space";
|
||||
import { VTabbar, VTabItem, VTabs } from "@/components/base/tabs";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import {
|
||||
IconComputer,
|
||||
IconPhone,
|
||||
IconSave,
|
||||
IconSettings,
|
||||
IconTablet,
|
||||
} from "@/core/icons";
|
||||
|
||||
const activeId = ref("general");
|
||||
const deviceActiveId = ref("desktop");
|
||||
const attachmentSelectVisible = ref(false);
|
||||
const settingRootVisible = ref(false);
|
||||
const settingVisible = ref(false);
|
||||
const devices = ref([
|
||||
{
|
||||
id: "desktop",
|
||||
icon: IconComputer,
|
||||
},
|
||||
{
|
||||
id: "tablet",
|
||||
icon: IconTablet,
|
||||
},
|
||||
{
|
||||
id: "phone",
|
||||
icon: IconPhone,
|
||||
},
|
||||
]);
|
||||
|
||||
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]";
|
||||
});
|
||||
|
||||
const attachments = Array.from(new Array(50), (_, index) => index).map(
|
||||
(index) => {
|
||||
return {
|
||||
id: index,
|
||||
name: `attachment-${index}`,
|
||||
url: `https://picsum.photos/1000/700?random=${index}`,
|
||||
size: "1.2MB",
|
||||
type: "image/png",
|
||||
strategy: "本地存储",
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener(
|
||||
"message",
|
||||
function receiveMessageFromIframePage(event) {
|
||||
if (event.data === "select_image") {
|
||||
attachmentSelectVisible.value = true;
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VModal
|
||||
v-model:visible="attachmentSelectVisible"
|
||||
:width="1240"
|
||||
title="选择附件"
|
||||
>
|
||||
<div class="w-full">
|
||||
<ul
|
||||
class="grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-3 md:grid-cols-2 xl:grid-cols-8 2xl:grid-cols-8"
|
||||
role="list"
|
||||
>
|
||||
<li
|
||||
v-for="(attachment, index) in attachments"
|
||||
:key="index"
|
||||
class="relative"
|
||||
>
|
||||
<VCard :body-class="['!p-0']">
|
||||
<div
|
||||
class="group aspect-w-10 aspect-h-7 block w-full cursor-pointer overflow-hidden bg-gray-100"
|
||||
>
|
||||
<img
|
||||
:src="attachment.url"
|
||||
alt=""
|
||||
class="pointer-events-none object-cover group-hover:opacity-75"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
class="pointer-events-none block truncate px-2 py-1 text-sm font-medium text-gray-700"
|
||||
>
|
||||
{{ attachment.name }}
|
||||
</p>
|
||||
</VCard>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<template #footer>
|
||||
<VButton type="secondary">确定</VButton>
|
||||
</template>
|
||||
</VModal>
|
||||
<div class="flex h-screen">
|
||||
<div class="flex-1">
|
||||
<div
|
||||
class="grid h-16 grid-cols-2 items-center bg-white py-2 px-4 drop-shadow-sm sm:grid-cols-3"
|
||||
>
|
||||
<div>
|
||||
<h2 class="truncate text-xl font-bold text-gray-800">
|
||||
<span>Anatole</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="hidden justify-center sm:flex">
|
||||
<VTabbar
|
||||
v-model:active-id="deviceActiveId"
|
||||
:items="devices"
|
||||
type="outline"
|
||||
></VTabbar>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<VSpace>
|
||||
<VButton type="default" @click="settingVisible = true">
|
||||
<template #icon>
|
||||
<IconSettings class="h-full w-full" />
|
||||
</template>
|
||||
设置
|
||||
</VButton>
|
||||
<VButton type="secondary">
|
||||
<template #icon>
|
||||
<IconSave class="h-full w-full" />
|
||||
</template>
|
||||
保存
|
||||
</VButton>
|
||||
</VSpace>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex h-full items-center justify-center"
|
||||
style="height: calc(100vh - 4rem)"
|
||||
>
|
||||
<iframe
|
||||
:class="iframeClasses"
|
||||
class="border-none transition-all duration-300"
|
||||
src="http://localhost:8090"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-show="settingRootVisible"
|
||||
class="drawer-wrapper fixed top-0 left-0 z-[99999] flex h-full w-full flex-row items-end justify-center"
|
||||
>
|
||||
<transition
|
||||
enter-active-class="ease-out duration-200"
|
||||
enter-from-class="opacity-0"
|
||||
enter-to-class="opacity-100"
|
||||
leave-active-class="ease-in duration-100"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
@before-enter="settingRootVisible = true"
|
||||
@after-leave="settingRootVisible = false"
|
||||
>
|
||||
<div
|
||||
v-show="settingVisible"
|
||||
class="drawer-layer absolute top-0 left-0 h-full w-full flex-none bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
@click="settingVisible = false"
|
||||
></div>
|
||||
</transition>
|
||||
<transition
|
||||
enter-active-class="transform transition ease-in-out duration-300"
|
||||
enter-from-class="translate-y-full"
|
||||
enter-to-class="translate-y-0"
|
||||
leave-active-class="transform transition ease-in-out duration-300"
|
||||
leave-from-class="translate-y-0"
|
||||
leave-to-class="translate-y-full"
|
||||
>
|
||||
<div
|
||||
v-show="settingVisible"
|
||||
class="drawer-content relative flex h-3/4 w-screen flex-col items-stretch overflow-y-auto rounded-t-md bg-white shadow-xl"
|
||||
>
|
||||
<div class="drawer-body">
|
||||
<div class="h-full w-full overflow-y-auto bg-white drop-shadow-sm">
|
||||
<VTabs v-model:active-id="activeId" type="outline">
|
||||
<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:col-span-2 sm:mt-0">
|
||||
<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:col-span-2 sm:mt-0">
|
||||
<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:col-span-2 sm:mt-0">
|
||||
<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:col-span-2 sm:mt-0">
|
||||
<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>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
|
@ -1,211 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import { VButton } from "@/components/base/button";
|
||||
import { VInput } from "@/components/base/input";
|
||||
import { VOption, VSelect } from "@/components/base/select";
|
||||
import { VModal } from "@/components/base/modal";
|
||||
import { VTextarea } from "@/components/base/textarea";
|
||||
import { VCard } from "@/components/base/card";
|
||||
import { VTabbar, VTabItem, VTabs } from "@/components/base/tabs";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { IconComputer, IconPhone, IconTablet } from "@/core/icons";
|
||||
|
||||
const activeId = ref("general");
|
||||
const deviceActiveId = ref("desktop");
|
||||
const attachmentSelectVisible = ref(false);
|
||||
const devices = ref([
|
||||
{
|
||||
id: "desktop",
|
||||
icon: IconComputer,
|
||||
},
|
||||
{
|
||||
id: "tablet",
|
||||
icon: IconTablet,
|
||||
},
|
||||
{
|
||||
id: "phone",
|
||||
icon: IconPhone,
|
||||
},
|
||||
]);
|
||||
|
||||
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]";
|
||||
});
|
||||
|
||||
const attachments = Array.from(new Array(50), (_, index) => index).map(
|
||||
(index) => {
|
||||
return {
|
||||
id: index,
|
||||
name: `attachment-${index}`,
|
||||
url: `https://picsum.photos/1000/700?random=${index}`,
|
||||
size: "1.2MB",
|
||||
type: "image/png",
|
||||
strategy: "本地存储",
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener(
|
||||
"message",
|
||||
function receiveMessageFromIframePage(event) {
|
||||
if (event.data === "select_image") {
|
||||
attachmentSelectVisible.value = true;
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VModal
|
||||
v-model:visible="attachmentSelectVisible"
|
||||
:width="1240"
|
||||
title="选择附件"
|
||||
>
|
||||
<div class="w-full">
|
||||
<ul
|
||||
class="grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-3 md:grid-cols-2 xl:grid-cols-8 2xl:grid-cols-8"
|
||||
role="list"
|
||||
>
|
||||
<li
|
||||
v-for="(attachment, index) in attachments"
|
||||
:key="index"
|
||||
class="relative"
|
||||
>
|
||||
<VCard :body-class="['!p-0']">
|
||||
<div
|
||||
class="group aspect-w-10 aspect-h-7 block w-full cursor-pointer overflow-hidden bg-gray-100"
|
||||
>
|
||||
<img
|
||||
:src="attachment.url"
|
||||
alt=""
|
||||
class="pointer-events-none object-cover group-hover:opacity-75"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
class="pointer-events-none block truncate px-2 py-1 text-sm font-medium text-gray-700"
|
||||
>
|
||||
{{ attachment.name }}
|
||||
</p>
|
||||
</VCard>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<template #footer>
|
||||
<VButton type="secondary">确定</VButton>
|
||||
</template>
|
||||
</VModal>
|
||||
<div class="flex h-screen">
|
||||
<div class="h-full w-96 overflow-y-auto bg-white drop-shadow-sm">
|
||||
<VTabs v-model:active-id="activeId" type="outline">
|
||||
<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:col-span-2 sm:mt-0">
|
||||
<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:col-span-2 sm:mt-0">
|
||||
<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:col-span-2 sm:mt-0">
|
||||
<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:col-span-2 sm:mt-0">
|
||||
<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 h-16 items-center justify-between bg-white p-2 drop-shadow-sm"
|
||||
>
|
||||
<div>
|
||||
<h2 class="truncate text-xl font-bold text-gray-800">
|
||||
<span>Anatole</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div>
|
||||
<VTabbar
|
||||
v-model:active-id="deviceActiveId"
|
||||
:items="devices"
|
||||
type="outline"
|
||||
></VTabbar>
|
||||
</div>
|
||||
<div>
|
||||
<VButton type="secondary">保存</VButton>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex h-full items-center justify-center"
|
||||
style="height: calc(100vh - 4rem)"
|
||||
>
|
||||
<iframe
|
||||
:class="iframeClasses"
|
||||
class="border-none transition-all duration-200"
|
||||
src="http://localhost:8090"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
Loading…
Reference in New Issue