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",
|
"homepage": "https://github.com/halo-dev/halo-admin/tree/next/packages/components#readme",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/ri": "^1.1.2",
|
"@iconify-json/ri": "^1.1.3",
|
||||||
"@rollup/plugin-typescript": "^8.3.3",
|
"@rollup/plugin-typescript": "^8.3.3",
|
||||||
"histoire": "^0.7.8",
|
"histoire": "^0.7.8",
|
||||||
"unplugin-icons": "^0.14.5",
|
"unplugin-icons": "^0.14.6",
|
||||||
"vite-plugin-dts": "^1.2.0"
|
"vite-plugin-dts": "^1.2.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
|
@ -13,3 +13,4 @@ export * from "./components/tabs";
|
||||||
export * from "./components/tag";
|
export * from "./components/tag";
|
||||||
export * from "./components/textarea";
|
export * from "./components/textarea";
|
||||||
export * from "./components/switch";
|
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>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Teleport to="body">
|
<Teleport to="body" :disabled="true">
|
||||||
<div
|
<div
|
||||||
v-show="rootVisible"
|
v-show="rootVisible"
|
||||||
:class="wrapperClasses"
|
:class="wrapperClasses"
|
||||||
|
@ -81,7 +81,7 @@ function handleClose() {
|
||||||
:style="contentStyles"
|
:style="contentStyles"
|
||||||
class="modal-content transform transition-all"
|
class="modal-content transform transition-all"
|
||||||
>
|
>
|
||||||
<div class="modal-header">
|
<div v-if="$slots.header || title" class="modal-header">
|
||||||
<slot name="header">
|
<slot name="header">
|
||||||
<div class="modal-header-title">{{ title }}</div>
|
<div class="modal-header-title">{{ title }}</div>
|
||||||
<div class="modal-header-actions flex flex-row">
|
<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";
|
import IconGitBranch from "~icons/ri/git-branch-line";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import IconStopCircle from "~icons/ri/stop-circle-line";
|
import IconStopCircle from "~icons/ri/stop-circle-line";
|
||||||
|
// @ts-ignore
|
||||||
|
import IconForbidLine from "~icons/ri/forbid-line";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
IconDashboard,
|
IconDashboard,
|
||||||
|
@ -121,4 +123,5 @@ export {
|
||||||
IconShieldUser,
|
IconShieldUser,
|
||||||
IconGitBranch,
|
IconGitBranch,
|
||||||
IconStopCircle,
|
IconStopCircle,
|
||||||
|
IconForbidLine,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
const token =
|
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({
|
const axiosInstance = axios.create({
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
|
|
|
@ -111,16 +111,16 @@ importers:
|
||||||
|
|
||||||
packages/components:
|
packages/components:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@iconify-json/ri': ^1.1.2
|
'@iconify-json/ri': ^1.1.3
|
||||||
'@rollup/plugin-typescript': ^8.3.3
|
'@rollup/plugin-typescript': ^8.3.3
|
||||||
histoire: ^0.7.8
|
histoire: ^0.7.8
|
||||||
unplugin-icons: ^0.14.5
|
unplugin-icons: ^0.14.6
|
||||||
vite-plugin-dts: ^1.2.0
|
vite-plugin-dts: ^1.2.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@iconify-json/ri': 1.1.2
|
'@iconify-json/ri': 1.1.3
|
||||||
'@rollup/plugin-typescript': 8.3.3
|
'@rollup/plugin-typescript': 8.3.3
|
||||||
histoire: 0.7.8
|
histoire: 0.7.8
|
||||||
unplugin-icons: 0.14.5
|
unplugin-icons: 0.14.6
|
||||||
vite-plugin-dts: 1.2.0
|
vite-plugin-dts: 1.2.0
|
||||||
|
|
||||||
packages/shared:
|
packages/shared:
|
||||||
|
@ -1532,8 +1532,8 @@ packages:
|
||||||
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@iconify-json/ri/1.1.2:
|
/@iconify-json/ri/1.1.3:
|
||||||
resolution: {integrity: sha512-qLlDrpkxJi+aUxY2xipKC08aI2seaxFsrEWC3Wm5WJxTtZco+G8SUBpzNpQaKzjN4IwpzcZv8T37Q3aREXDk/A==}
|
resolution: {integrity: sha512-YQ45kQNpuHc2bso4fDGhooWou43qy7njD/I5l7vpjcujb+P/K2BfLASbWYTTUKu6lMersuFmO8F7NdGzy6eGWw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@iconify/types': 1.1.0
|
'@iconify/types': 1.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -6640,8 +6640,8 @@ packages:
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/unplugin-icons/0.14.5:
|
/unplugin-icons/0.14.6:
|
||||||
resolution: {integrity: sha512-fxi/fuBZXtZu64L8iAPj+ecu/rnSvTbfR14RO44xIWdsI/Ohpzs9Gve7+nHIgD6JFrdtCfzGnXWBEVPbMGWX3A==}
|
resolution: {integrity: sha512-8sxDiL4l+TV4zufZfrskgHZZSDFoGOCBgYsefRMM4inQ3Z6KhgMSuNyew7U7D/xG//rwxgD7bN+Dv+YAZEEfEw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@svgr/core': '>=5.5.0'
|
'@svgr/core': '>=5.5.0'
|
||||||
'@vue/compiler-sfc': ^3.0.2
|
'@vue/compiler-sfc': ^3.0.2
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { RouterView } from "vue-router";
|
import { RouterView } from "vue-router";
|
||||||
|
import { VDialogProvider } from "@halo-dev/components";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<RouterView />
|
<VDialogProvider>
|
||||||
|
<RouterView />
|
||||||
|
</VDialogProvider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
Loading…
Reference in New Issue