Merge branch 'main' of github.com:pure-admin/vue-pure-admin into gitee

This commit is contained in:
xiaoxian521
2023-10-30 23:52:58 +08:00
24 changed files with 402 additions and 81 deletions

View File

@@ -0,0 +1,7 @@
import reAnimateSelector from "./src/index.vue";
import { withInstall } from "@pureadmin/utils";
/** [animate.css](https://animate.style/) 选择器组件 */
export const ReAnimateSelector = withInstall(reAnimateSelector);
export default ReAnimateSelector;

View File

@@ -0,0 +1,114 @@
export const animates = [
/* Attention seekers */
"bounce",
"flash",
"pulse",
"rubberBand",
"shakeX",
"headShake",
"swing",
"tada",
"wobble",
"jello",
"heartBeat",
/* Back entrances */
"backInDown",
"backInLeft",
"backInRight",
"backInUp",
/* Back exits */
"backOutDown",
"backOutLeft",
"backOutRight",
"backOutUp",
/* Bouncing entrances */
"bounceIn",
"bounceInDown",
"bounceInLeft",
"bounceInRight",
"bounceInUp",
/* Bouncing exits */
"bounceOut",
"bounceOutDown",
"bounceOutLeft",
"bounceOutRight",
"bounceOutUp",
/* Fading entrances */
"fadeIn",
"fadeInDown",
"fadeInDownBig",
"fadeInLeft",
"fadeInLeftBig",
"fadeInRight",
"fadeInRightBig",
"fadeInUp",
"fadeInUpBig",
"fadeInTopLeft",
"fadeInTopRight",
"fadeInBottomLeft",
"fadeInBottomRight",
/* Fading exits */
"fadeOut",
"fadeOutDown",
"fadeOutDownBig",
"fadeOutLeft",
"fadeOutLeftBig",
"fadeOutRight",
"fadeOutRightBig",
"fadeOutUp",
"fadeOutUpBig",
"fadeOutTopLeft",
"fadeOutTopRight",
"fadeOutBottomRight",
"fadeOutBottomLeft",
/* Flippers */
"flip",
"flipInX",
"flipInY",
"flipOutX",
"flipOutY",
/* Lightspeed */
"lightSpeedInRight",
"lightSpeedInLeft",
"lightSpeedOutRight",
"lightSpeedOutLeft",
/* Rotating entrances */
"rotateIn",
"rotateInDownLeft",
"rotateInDownRight",
"rotateInUpLeft",
"rotateInUpRight",
/* Rotating exits */
"rotateOut",
"rotateOutDownLeft",
"rotateOutDownRight",
"rotateOutUpLeft",
"rotateOutUpRight",
/* Specials */
"hinge",
"jackInTheBox",
"rollIn",
"rollOut",
/* Zooming entrances */
"zoomIn",
"zoomInDown",
"zoomInLeft",
"zoomInRight",
"zoomInUp",
/* Zooming exits */
"zoomOut",
"zoomOutDown",
"zoomOutLeft",
"zoomOutRight",
"zoomOutUp",
/* Sliding entrances */
"slideInDown",
"slideInLeft",
"slideInRight",
"slideInUp",
/* Sliding exits */
"slideOutDown",
"slideOutLeft",
"slideOutRight",
"slideOutUp"
];

View File

@@ -0,0 +1,127 @@
<script setup lang="ts">
import { animates } from "./animate";
import { ref, computed, toRef } from "vue";
import { cloneDeep } from "@pureadmin/utils";
defineOptions({
name: "ReAnimateSelector"
});
const props = defineProps({
modelValue: {
require: false,
type: String
}
});
const emit = defineEmits<{ (e: "update:modelValue", v: string) }>();
const inputValue = toRef(props, "modelValue");
const animatesList = ref(animates);
const copyAnimatesList = cloneDeep(animatesList);
const animateClass = computed(() => {
return [
"mt-1",
"flex",
"border",
"w-[130px]",
"h-[100px]",
"items-center",
"cursor-pointer",
"transition-all",
"justify-center",
"border-[#e5e7eb]",
"hover:text-primary",
"hover:duration-[700ms]"
];
});
const animateStyle = computed(
() => (i: string) =>
inputValue.value === i
? {
borderColor: "var(--el-color-primary)",
color: "var(--el-color-primary)"
}
: ""
);
function onChangeIcon(animate: string) {
emit("update:modelValue", animate);
}
function onClear() {
emit("update:modelValue", "");
}
function filterMethod(value: any) {
animatesList.value = copyAnimatesList.value.filter((i: string | any[]) =>
i.includes(value)
);
}
const animateMap = ref({});
function onMouseEnter(index: string | number) {
animateMap.value[index] = animateMap.value[index]?.loading
? Object.assign({}, animateMap.value[index], {
loading: false
})
: Object.assign({}, animateMap.value[index], {
loading: true
});
}
function onMouseleave() {
animateMap.value = {};
}
</script>
<template>
<el-select
:model-value="inputValue"
placeholder="请选择动画"
clearable
filterable
@clear="onClear"
:filter-method="filterMethod"
>
<template #empty>
<div class="w-[280px]">
<el-scrollbar
noresize
height="212px"
:view-style="{ overflow: 'hidden' }"
class="border-t border-[#e5e7eb]"
>
<ul class="flex flex-wrap justify-around mb-1">
<li
v-for="(animate, index) in animatesList"
:key="index"
:class="animateClass"
:style="animateStyle(animate)"
@mouseenter.prevent="onMouseEnter(index)"
@mouseleave.prevent="onMouseleave"
@click="onChangeIcon(animate)"
>
<h4
:class="[
`animate__animated animate__${
animateMap[index]?.loading
? animate + ' animate__infinite'
: ''
} `
]"
>
{{ animate }}
</h4>
</li>
</ul>
<el-empty
v-show="animatesList.length === 0"
description="暂无动画"
:image-size="60"
/>
</el-scrollbar>
</div>
</template>
</el-select>
</template>

View File

@@ -2,31 +2,33 @@ import "./index.css";
import {
h,
ref,
toRef,
watch,
nextTick,
defineComponent,
getCurrentInstance
} from "vue";
import type { OptionsType } from "./type";
import { isFunction, useDark } from "@pureadmin/utils";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { isFunction, isNumber, useDark } from "@pureadmin/utils";
const props = {
options: {
type: Array<OptionsType>,
default: () => []
},
/** 默认选中,按照第一个索引为 `0` 的模式 */
defaultValue: {
type: Number,
default: 0
/** 默认选中,按照第一个索引为 `0` 的模式,可选(`modelValue`只有传`number`类型时才为响应式) */
modelValue: {
type: undefined,
require: false,
default: "0"
}
};
export default defineComponent({
name: "ReSegmented",
props,
emits: ["change"],
emits: ["change", "update:modelValue"],
setup(props, { emit }) {
const width = ref(0);
const translateX = ref(0);
@@ -35,12 +37,16 @@ export default defineComponent({
const curMouseActive = ref(-1);
const segmentedItembg = ref("");
const instance = getCurrentInstance()!;
const curIndex = ref(props.defaultValue);
const curIndex = isNumber(props.modelValue)
? toRef(props, "modelValue")
: ref(0);
function handleChange({ option, index }, event: Event) {
if (option.disabled) return;
event.preventDefault();
curIndex.value = index;
isNumber(props.modelValue)
? emit("update:modelValue", index)
: (curIndex.value = index);
segmentedItembg.value = "";
emit("change", { index, option });
}

View File

@@ -21,6 +21,7 @@ const {
logout,
backTopMenu,
onPanel,
getLogo,
username,
userAvatar,
avatarsStyle,
@@ -43,7 +44,7 @@ nextTick(() => {
class="horizontal-header"
>
<div class="horizontal-header-left" @click="backTopMenu">
<img src="/logo.svg" alt="logo" />
<img :src="getLogo()" alt="logo" />
<span>{{ title }}</span>
</div>
<el-menu

View File

@@ -6,7 +6,7 @@ const props = defineProps({
collapse: Boolean
});
const { title } = useNav();
const { title, getLogo } = useNav();
</script>
<template>
@@ -19,7 +19,7 @@ const { title } = useNav();
class="sidebar-logo-link"
:to="getTopMenu()?.path ?? '/'"
>
<img src="/logo.svg" alt="logo" />
<img :src="getLogo()" alt="logo" />
<span class="sidebar-title">{{ title }}</span>
</router-link>
<router-link
@@ -29,7 +29,7 @@ const { title } = useNav();
class="sidebar-logo-link"
:to="getTopMenu()?.path ?? '/'"
>
<img src="/logo.svg" alt="logo" />
<img :src="getLogo()" alt="logo" />
<span class="sidebar-title">{{ title }}</span>
</router-link>
</transition>

View File

@@ -6,10 +6,10 @@ import { useTags } from "../../hooks/useTag";
import { routerArrays } from "@/layout/types";
import { handleAliveRoute, getTopMenu } from "@/router/utils";
import { useSettingStoreHook } from "@/store/modules/settings";
import { useResizeObserver, useFullscreen } from "@vueuse/core";
import { isEqual, isAllEmpty, debounce } from "@pureadmin/utils";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { ref, watch, unref, toRaw, nextTick, onBeforeUnmount } from "vue";
import { useResizeObserver, useFullscreen, onClickOutside } from "@vueuse/core";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
@@ -49,6 +49,7 @@ const {
const tabDom = ref();
const containerDom = ref();
const scrollbarDom = ref();
const contextmenuRef = ref();
const isShowArrow = ref(false);
const topPath = getTopMenu()?.path;
const { VITE_HIDE_HOME } = import.meta.env;
@@ -328,6 +329,7 @@ function handleCommand(command: any) {
/** 触发右键中菜单的点击事件 */
function selectTag(key, item) {
closeMenu();
onClickDrop(key, item, currentSelect.value);
}
@@ -462,6 +464,10 @@ function tagOnClick(item) {
// showMenuModel(item?.path, item?.query);
}
onClickOutside(contextmenuRef, closeMenu, {
detectIframe: true
});
watch(route, () => {
activeIndex.value = -1;
dynamicTagView();
@@ -558,6 +564,7 @@ onBeforeUnmount(() => {
<transition name="el-zoom-in-top">
<ul
v-show="visible"
ref="contextmenuRef"
:key="Math.random()"
:style="getContextMenuStyle"
class="contextmenu"

View File

@@ -58,7 +58,6 @@ onMounted(() => {
.frame {
position: absolute;
inset: 0;
z-index: 998;
.frame-iframe {
box-sizing: border-box;

View File

@@ -36,7 +36,7 @@ export function useLayout() {
grey: $config?.Grey ?? false,
weak: $config?.Weak ?? false,
hideTabs: $config?.HideTabs ?? false,
hideFooter: $config.HideFooter ?? false,
hideFooter: $config.HideFooter ?? true,
showLogo: $config?.ShowLogo ?? true,
showModel: $config?.ShowModel ?? "smart",
multiTagsCache: $config?.MultiTagsCache ?? false

View File

@@ -124,6 +124,11 @@ export function useNav() {
return remainingPaths.includes(path);
}
/** 获取`logo` */
function getLogo() {
return new URL("/logo.svg", import.meta.url).href;
}
return {
title,
device,
@@ -139,6 +144,7 @@ export function useNav() {
menuSelect,
handleResize,
resolvePath,
getLogo,
isCollapse,
pureApp,
username,

View File

@@ -1,7 +1,6 @@
import {
ref,
unref,
watch,
computed,
reactive,
onMounted,
@@ -9,7 +8,6 @@ import {
getCurrentInstance
} from "vue";
import { tagsViewsType } from "../types";
import { useEventListener } from "@vueuse/core";
import { useRoute, useRouter } from "vue-router";
import { transformI18n, $t } from "@/plugins/i18n";
import { responsiveStorageNameSpace } from "@/config";
@@ -215,13 +213,6 @@ export function useTags() {
}
});
watch(
() => visible.value,
() => {
useEventListener(document, "click", closeMenu);
}
);
return {
route,
router,

View File

@@ -105,6 +105,15 @@ export default {
title: $t("menus.hscropping")
}
},
{
path: "/components/animatecss",
name: "AnimateCss",
component: () => import("@/views/components/animatecss/index.vue"),
meta: {
title: $t("menus.hsanimatecss"),
extraIcon: "IF-pure-iconfont-new svg"
}
},
{
path: "/components/countTo",
name: "CountTo",

View File

@@ -73,7 +73,7 @@ class PureHttp {
return config;
}
/** 请求白名单放置一些不需要token的接口通过设置请求白名单防止token过期后再请求造成的死循环问题 */
const whiteList = ["/refreshToken", "/login"];
const whiteList = ["/refresh-token", "/login"];
return whiteList.find(url => url === config.url)
? config
: new Promise(resolve => {

View File

@@ -24,7 +24,7 @@ export const injectResponsiveStorage = (app: App, config: PlatformConfigs) => {
grey: config.Grey ?? false,
weak: config.Weak ?? false,
hideTabs: config.HideTabs ?? false,
hideFooter: config.HideFooter ?? false,
hideFooter: config.HideFooter ?? true,
showLogo: config.ShowLogo ?? true,
showModel: config.ShowModel ?? "smart",
multiTagsCache: config.MultiTagsCache ?? false

View File

@@ -0,0 +1,34 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import ReAnimateSelector from "@/components/ReAnimateSelector";
defineOptions({
name: "AnimateCss"
});
const icon = ref("");
watch(icon, () => {
console.log("icon", icon.value);
});
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<span class="font-medium">
<el-link
href="https://animate.style/"
target="_blank"
style="margin: 0 4px 5px; font-size: 16px"
>
animate.css
</el-link>
选择器组件
</span>
</div>
</template>
<ReAnimateSelector v-model="icon" />
</el-card>
</template>

View File

@@ -1,5 +1,5 @@
<script setup lang="tsx">
import { h } from "vue";
import { h, ref } from "vue";
import { message } from "@/utils/message";
import HomeFilled from "@iconify-icons/ep/home-filled";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
@@ -10,51 +10,43 @@ defineOptions({
});
/** 基础用法 */
const value = ref(4); // 必须为number类型
const optionsBasis: Array<OptionsType> = [
{
label: "周一",
value: 1
label: "周一"
},
{
label: "周二",
value: 2
label: "周二"
},
{
label: "周三",
value: 3
label: "周三"
},
{
label: "周四",
value: 4
label: "周四"
},
{
label: "周五",
value: 5
label: "周五"
}
];
/** 禁用 */
const optionsDisabled: Array<OptionsType> = [
{
label: "周一",
value: 1
label: "周一"
},
{
label: "周二",
value: 2
label: "周二"
},
{
label: "周三",
value: 3,
disabled: true
},
{
label: "周四",
value: 4
label: "周四"
},
{
label: "周五",
value: 5,
disabled: true
}
];
@@ -63,26 +55,21 @@ const optionsDisabled: Array<OptionsType> = [
const optionsIcon: Array<OptionsType> = [
{
label: "周一",
value: 1,
icon: HomeFilled
},
{
label: "周二",
value: 2
label: "周二"
},
{
label: "周三",
value: 3,
icon: "terminalWindowLine"
},
{
label: "周四",
value: 4,
icon: "streamline-emojis:airplane"
},
{
label: "周五",
value: 5,
icon: "streamline-emojis:2"
}
];
@@ -90,23 +77,18 @@ const optionsIcon: Array<OptionsType> = [
/** 只设置图标 */
const optionsOnlyIcon: Array<OptionsType> = [
{
value: 1,
icon: HomeFilled
},
{
value: 2,
icon: "terminalWindowLine"
},
{
value: 3,
icon: "streamline-emojis:cow-face"
},
{
value: 4,
icon: "streamline-emojis:airplane"
},
{
value: 5,
icon: "streamline-emojis:2"
}
];
@@ -121,8 +103,7 @@ const optionsLabel: Array<OptionsType> = [
})}
<p>周一</p>
</div>
),
value: 1
)
},
{
label: () => (
@@ -132,8 +113,7 @@ const optionsLabel: Array<OptionsType> = [
})}
<p>周二</p>
</div>
),
value: 2
)
},
{
label: () => (
@@ -143,8 +123,7 @@ const optionsLabel: Array<OptionsType> = [
})}
<p>周三</p>
</div>
),
value: 3
)
}
];
@@ -179,11 +158,15 @@ function onChange({ index, option }) {
<span class="font-medium">分段控制器</span>
</div>
</template>
<p class="mb-2">基础用法</p>
<Segmented :options="optionsBasis" />
<p class="mb-2">
基础用法v-model<span class="text-primary">
{{ optionsBasis[value].label }}
</span>
</p>
<Segmented :options="optionsBasis" v-model="value" />
<el-divider />
<p class="mb-2">默认选中和禁用</p>
<Segmented :options="optionsDisabled" :defaultValue="2" />
<p class="mb-2">禁用</p>
<Segmented :options="optionsDisabled" />
<el-divider />
<p class="mb-2">设置图标</p>
<Segmented :options="optionsIcon" />