mirror of https://github.com/halo-dev/halo-admin
parent
f825047eb5
commit
cd338f0b1f
|
@ -45,10 +45,10 @@
|
|||
"homepage": "https://github.com/halo-dev/halo-admin/tree/next/packages/components#readme",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@iconify-json/ri": "^1.1.2",
|
||||
"@iconify-json/ri": "^1.1.3",
|
||||
"@rollup/plugin-typescript": "^8.3.3",
|
||||
"histoire": "^0.7.8",
|
||||
"unplugin-icons": "^0.14.5",
|
||||
"unplugin-icons": "^0.14.6",
|
||||
"vite-plugin-dts": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -13,3 +13,4 @@ export * from "./components/tabs";
|
|||
export * from "./components/tag";
|
||||
export * from "./components/textarea";
|
||||
export * from "./components/switch";
|
||||
export * from "./components/dialog";
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<script lang="ts" setup>
|
||||
import { VButton } from "@/components/button";
|
||||
import { VDialog } from "@/components/dialog";
|
||||
|
||||
const initState = () => {
|
||||
return {
|
||||
visible: false,
|
||||
title: "提示",
|
||||
description: "是否确定删除这篇文章?",
|
||||
confirmText: "确定",
|
||||
cancelText: "取消",
|
||||
type: "info",
|
||||
onConfirm: () => {
|
||||
alert("已删除");
|
||||
},
|
||||
onCancel: () => {
|
||||
alert("已取消");
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<Story :init-state="initState" title="Dialog">
|
||||
<template #default="{ state }">
|
||||
<VButton type="danger" @click="state.visible = true">删除</VButton>
|
||||
<VDialog
|
||||
:cancel-text="state.cancelText"
|
||||
:confirm-text="state.confirmText"
|
||||
:description="state.description"
|
||||
:title="state.title"
|
||||
:type="state.type"
|
||||
:visible="state.visible"
|
||||
></VDialog>
|
||||
</template>
|
||||
</Story>
|
||||
</template>
|
|
@ -0,0 +1,128 @@
|
|||
<script lang="ts" setup>
|
||||
import { VModal } from "@/components/modal";
|
||||
import { VSpace } from "@/components/space";
|
||||
import { VButton } from "@/components/button";
|
||||
import {
|
||||
IconCheckboxCircle,
|
||||
IconClose,
|
||||
IconErrorWarning,
|
||||
IconForbidLine,
|
||||
IconInformation,
|
||||
} from "@/icons/icons";
|
||||
import type { PropType } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import type { Type } from "@/components/dialog/interface";
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String as PropType<Type>,
|
||||
default: "info",
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: "提示",
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: "确定",
|
||||
},
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: "取消",
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
onConfirm: {
|
||||
type: Function as PropType<() => void>,
|
||||
},
|
||||
onCancel: {
|
||||
type: Function as PropType<() => void>,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:visible", "close"]);
|
||||
|
||||
const icons = {
|
||||
success: {
|
||||
icon: IconCheckboxCircle,
|
||||
color: "green",
|
||||
},
|
||||
info: {
|
||||
icon: IconInformation,
|
||||
color: "blue",
|
||||
},
|
||||
warning: {
|
||||
icon: IconErrorWarning,
|
||||
color: "orange",
|
||||
},
|
||||
error: {
|
||||
icon: IconForbidLine,
|
||||
color: "red",
|
||||
},
|
||||
};
|
||||
const icon = computed(() => icons[props.type]);
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const handleCancel = () => {
|
||||
if (props.onCancel) {
|
||||
props.onCancel();
|
||||
}
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (props.onConfirm) {
|
||||
loading.value = true;
|
||||
await props.onConfirm();
|
||||
}
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
loading.value = false;
|
||||
emit("update:visible", false);
|
||||
emit("close");
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<VModal :visible="visible" :width="450" @close="handleCancel()">
|
||||
<div class="flex justify-between items-start py-2 mb-2">
|
||||
<div>
|
||||
<div
|
||||
:class="`ring-${icon.color}-100`"
|
||||
class="inline-flex rounded-full bg-teal-50 p-1.5 ring-4"
|
||||
>
|
||||
<component
|
||||
:is="icon.icon"
|
||||
:class="`text-${icon.color}-500`"
|
||||
class="w-5 h-5"
|
||||
></component>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<IconClose class="cursor-pointer" @click="handleCancel" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex-1 flex flex-col items-stretch gap-2">
|
||||
<div class="text-base text-gray-900 font-bold">{{ title }}</div>
|
||||
<div class="text-sm text-gray-700">{{ description }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<VSpace>
|
||||
<VButton :loading="loading" type="secondary" @click="handleConfirm">
|
||||
{{ confirmText }}
|
||||
</VButton>
|
||||
<VButton @click="handleCancel">{{ cancelText }}</VButton>
|
||||
</VSpace>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
|
@ -0,0 +1,26 @@
|
|||
<script lang="ts" setup>
|
||||
import { VDialog } from "./index";
|
||||
import { provide, ref } from "vue";
|
||||
import type { useDialogOptions } from "./interface";
|
||||
import { DialogProviderProvideKey } from "@/components/dialog/interface";
|
||||
|
||||
const options = ref<useDialogOptions>({
|
||||
visible: false,
|
||||
title: "",
|
||||
});
|
||||
|
||||
provide(DialogProviderProvideKey, options);
|
||||
</script>
|
||||
<template>
|
||||
<slot />
|
||||
<VDialog
|
||||
v-model:visible="options.visible"
|
||||
:cancel-text="options.cancelText"
|
||||
:confirm-text="options.confirmText"
|
||||
:description="options.description"
|
||||
:onCancel="options.onCancel"
|
||||
:onConfirm="options.onConfirm"
|
||||
:title="options.title"
|
||||
:type="options.type"
|
||||
></VDialog>
|
||||
</template>
|
|
@ -0,0 +1,9 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { VDialog } from "../index";
|
||||
|
||||
describe("Dialog", () => {
|
||||
it("should render", () => {
|
||||
expect(mount(VDialog)).toBeDefined();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
export { default as VDialog } from "./Dialog.vue";
|
||||
export { default as VDialogProvider } from "./DialogProvider.vue";
|
||||
export * from "./use-dialog";
|
|
@ -0,0 +1,15 @@
|
|||
export type Type = "success" | "info" | "warning" | "error";
|
||||
export const DialogProviderProvideKey = "DIALOG_PROVIDER_PROVIDE_KEY";
|
||||
|
||||
export interface useDialogOptions {
|
||||
type?: Type;
|
||||
visible: boolean;
|
||||
title: string;
|
||||
description?: string;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
onConfirm?: () => void;
|
||||
onCancel?: () => void;
|
||||
}
|
||||
|
||||
export type useDialogUserOptions = Omit<useDialogOptions, "type" | "visible">;
|
|
@ -0,0 +1,36 @@
|
|||
import type { Ref } from "vue";
|
||||
import { inject } from "vue";
|
||||
import type {
|
||||
Type,
|
||||
useDialogOptions,
|
||||
useDialogUserOptions,
|
||||
} from "@/components/dialog/interface";
|
||||
import { DialogProviderProvideKey } from "@/components/dialog/interface";
|
||||
|
||||
interface useDialogReturn {
|
||||
success: (options: useDialogUserOptions) => void;
|
||||
info: (options: useDialogUserOptions) => void;
|
||||
warning: (options: useDialogUserOptions) => void;
|
||||
error: (options: useDialogUserOptions) => void;
|
||||
}
|
||||
|
||||
export function useDialog(): useDialogReturn {
|
||||
const dialogOptions = inject<Ref<useDialogOptions>>(DialogProviderProvideKey);
|
||||
|
||||
if (!dialogOptions) {
|
||||
throw new Error("DialogProvider is not mounted");
|
||||
}
|
||||
|
||||
const createDialog = (type: Type) => (options: useDialogUserOptions) => {
|
||||
dialogOptions.value = { ...dialogOptions.value, ...options };
|
||||
dialogOptions.value.type = type;
|
||||
dialogOptions.value.visible = true;
|
||||
};
|
||||
|
||||
return {
|
||||
success: createDialog("success"),
|
||||
info: createDialog("info"),
|
||||
warning: createDialog("warning"),
|
||||
error: createDialog("error"),
|
||||
};
|
||||
}
|
|
@ -46,7 +46,7 @@ function handleClose() {
|
|||
}
|
||||
</script>
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<Teleport to="body" :disabled="true">
|
||||
<div
|
||||
v-show="rootVisible"
|
||||
:class="wrapperClasses"
|
||||
|
@ -81,7 +81,7 @@ function handleClose() {
|
|||
:style="contentStyles"
|
||||
class="modal-content transform transition-all"
|
||||
>
|
||||
<div class="modal-header">
|
||||
<div v-if="$slots.header || title" class="modal-header">
|
||||
<slot name="header">
|
||||
<div class="modal-header-title">{{ title }}</div>
|
||||
<div class="modal-header-actions flex flex-row">
|
||||
|
|
|
@ -79,6 +79,8 @@ import IconShieldUser from "~icons/ri/shield-user-line";
|
|||
import IconGitBranch from "~icons/ri/git-branch-line";
|
||||
// @ts-ignore
|
||||
import IconStopCircle from "~icons/ri/stop-circle-line";
|
||||
// @ts-ignore
|
||||
import IconForbidLine from "~icons/ri/forbid-line";
|
||||
|
||||
export {
|
||||
IconDashboard,
|
||||
|
@ -121,4 +123,5 @@ export {
|
|||
IconShieldUser,
|
||||
IconGitBranch,
|
||||
IconStopCircle,
|
||||
IconForbidLine,
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import axios from "axios";
|
||||
|
||||
const token =
|
||||
"eyJhbGciOiJSUzUxMiJ9.eyJpc3MiOiJIYWxvIE93bmVyIiwic3ViIjoiYWRtaW4iLCJleHAiOjE2NTY0NzM3NTMsImlhdCI6MTY1NjM4NzM1Mywic2NvcGUiOlsiUk9MRV9zdXBlci1yb2xlIl19.vJ7l6jo3CJv7h4XTDvqjY70qngpaiiYhJL7_vXNPRY2Rz2NdmosMzy-BIRYPRuJnhU33uQ5LjXY5K2YwyQinrIsT66JsfckuYi6slAQY2rUC3929wC3gBcMJp9Z--VGRA701vDoecGWnGh68XR-RCK_uGcMnNhC4A1bCsb-nrn4TYdUndM0Aa2OsGP6G0IjzuyvXwwoAGB2JojBdGzXVz2KujHsaELziAZ2Kx78wsEIREN0pZGXnapB1-0nqgjLQ9VuGl62bRAkyzirwbasgB6Tk8njz-TR_uIL-smyWt_LUwX5I97lrM5VCtMmBlT999_Zi8MgzWTHeEUobzbI4ng";
|
||||
"eyJhbGciOiJSUzUxMiJ9.eyJpc3MiOiJIYWxvIE93bmVyIiwic3ViIjoiYWRtaW4iLCJleHAiOjE2NTY1NjAzMjQsImlhdCI6MTY1NjQ3MzkyNCwic2NvcGUiOlsiUk9MRV9zdXBlci1yb2xlIl19.CloSZG47VmzUd7uLUHb1q-0mmu0SE3zoRVHVAFeN4P9izXc-Sy2ZZc_3QKQPb4HyfWV1HoFDr2GeOKudbkAJIBvvJ1rr-NLh1szj0le06aeK1U2RqANgR0oI4HxSWrPu9Mnz-r04L1D-KrrmnG7votQ6ekeTPwlKIyEI6zl-0NlCqGdv0g3FV37dDbu8-9QrgtTOvRcpymLYGJ-vY2CqGm1z7vNbgMSneIhbRxe6lXfwkbvjtCp3P3Mkj5_14cAt_FQEUHGYnZZH_dNtdU2NZBpMXIgZYE0CaTBes4ciH1N62cwIT05iBTN_tdmDwEsg-BYjvI5IqVmrt2CwT_IcBg"
|
||||
const axiosInstance = axios.create({
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
|
|
@ -111,16 +111,16 @@ importers:
|
|||
|
||||
packages/components:
|
||||
specifiers:
|
||||
'@iconify-json/ri': ^1.1.2
|
||||
'@iconify-json/ri': ^1.1.3
|
||||
'@rollup/plugin-typescript': ^8.3.3
|
||||
histoire: ^0.7.8
|
||||
unplugin-icons: ^0.14.5
|
||||
unplugin-icons: ^0.14.6
|
||||
vite-plugin-dts: ^1.2.0
|
||||
devDependencies:
|
||||
'@iconify-json/ri': 1.1.2
|
||||
'@iconify-json/ri': 1.1.3
|
||||
'@rollup/plugin-typescript': 8.3.3
|
||||
histoire: 0.7.8
|
||||
unplugin-icons: 0.14.5
|
||||
unplugin-icons: 0.14.6
|
||||
vite-plugin-dts: 1.2.0
|
||||
|
||||
packages/shared:
|
||||
|
@ -1532,8 +1532,8 @@ packages:
|
|||
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
||||
dev: true
|
||||
|
||||
/@iconify-json/ri/1.1.2:
|
||||
resolution: {integrity: sha512-qLlDrpkxJi+aUxY2xipKC08aI2seaxFsrEWC3Wm5WJxTtZco+G8SUBpzNpQaKzjN4IwpzcZv8T37Q3aREXDk/A==}
|
||||
/@iconify-json/ri/1.1.3:
|
||||
resolution: {integrity: sha512-YQ45kQNpuHc2bso4fDGhooWou43qy7njD/I5l7vpjcujb+P/K2BfLASbWYTTUKu6lMersuFmO8F7NdGzy6eGWw==}
|
||||
dependencies:
|
||||
'@iconify/types': 1.1.0
|
||||
dev: true
|
||||
|
@ -6640,8 +6640,8 @@ packages:
|
|||
engines: {node: '>= 0.8'}
|
||||
dev: true
|
||||
|
||||
/unplugin-icons/0.14.5:
|
||||
resolution: {integrity: sha512-fxi/fuBZXtZu64L8iAPj+ecu/rnSvTbfR14RO44xIWdsI/Ohpzs9Gve7+nHIgD6JFrdtCfzGnXWBEVPbMGWX3A==}
|
||||
/unplugin-icons/0.14.6:
|
||||
resolution: {integrity: sha512-8sxDiL4l+TV4zufZfrskgHZZSDFoGOCBgYsefRMM4inQ3Z6KhgMSuNyew7U7D/xG//rwxgD7bN+Dv+YAZEEfEw==}
|
||||
peerDependencies:
|
||||
'@svgr/core': '>=5.5.0'
|
||||
'@vue/compiler-sfc': ^3.0.2
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<script lang="ts" setup>
|
||||
import { RouterView } from "vue-router";
|
||||
import { VDialogProvider } from "@halo-dev/components";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
<VDialogProvider>
|
||||
<RouterView />
|
||||
</VDialogProvider>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
Loading…
Reference in New Issue