mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-06-08 01:17:23 +08:00
Merge branch 'main' of github.com:pure-admin/vue-pure-admin into gitee
This commit is contained in:
commit
227a7b7a2a
@ -12,6 +12,7 @@
|
||||
"Grey": false,
|
||||
"Weak": false,
|
||||
"HideTabs": false,
|
||||
"HideFooter": false,
|
||||
"SidebarStatus": true,
|
||||
"EpThemeColor": "#409EFF",
|
||||
"ShowLogo": true,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,15 @@
|
||||
import iconifyIconOffline from "./src/iconifyIconOffline";
|
||||
import iconifyIconOnline from "./src/iconifyIconOnline";
|
||||
import iconSelect from "./src/Select.vue";
|
||||
import fontIcon from "./src/iconfont";
|
||||
|
||||
/** 本地图标组件 */
|
||||
const IconifyIconOffline = iconifyIconOffline;
|
||||
/** 在线图标组件 */
|
||||
const IconifyIconOnline = iconifyIconOnline;
|
||||
/** iconfont组件 */
|
||||
/** `IconSelect`图标选择器组件 */
|
||||
const IconSelect = iconSelect;
|
||||
/** `iconfont`组件 */
|
||||
const FontIcon = fontIcon;
|
||||
|
||||
export { IconifyIconOffline, IconifyIconOnline, FontIcon };
|
||||
export { IconifyIconOffline, IconifyIconOnline, IconSelect, FontIcon };
|
||||
|
@ -1,7 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { cloneDeep } from "@pureadmin/utils";
|
||||
import { IconJson } from "@/components/ReIcon/data";
|
||||
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
|
||||
import { ref, computed, CSSProperties, toRef, watch } from "vue";
|
||||
import Search from "@iconify-icons/ri/search-eye-line";
|
||||
|
||||
type ParameterCSSProperties = (item?: string) => CSSProperties | undefined;
|
||||
|
||||
defineOptions({
|
||||
@ -16,15 +18,15 @@ const props = defineProps({
|
||||
});
|
||||
const emit = defineEmits<{ (e: "update:modelValue", v: string) }>();
|
||||
|
||||
const visible = ref(false);
|
||||
const inputValue = toRef(props, "modelValue");
|
||||
const iconList = ref(IconJson);
|
||||
const icon = ref("add-location");
|
||||
const icon = ref();
|
||||
const currentActiveType = ref("ep:");
|
||||
// 深拷贝图标数据,前端做搜索
|
||||
const copyIconList = cloneDeep(iconList.value);
|
||||
|
||||
const pageSize = ref(96);
|
||||
const totalPage = ref(0);
|
||||
// 每页显示35个图标
|
||||
const pageSize = ref(35);
|
||||
const currentPage = ref(1);
|
||||
|
||||
// 搜索条件
|
||||
@ -36,8 +38,8 @@ const tabsList = [
|
||||
name: "ep:"
|
||||
},
|
||||
{
|
||||
label: "Font Awesome 4",
|
||||
name: "fa:"
|
||||
label: "Remix Icon",
|
||||
name: "ri:"
|
||||
},
|
||||
{
|
||||
label: "Font Awesome 5 Solid",
|
||||
@ -45,20 +47,14 @@ const tabsList = [
|
||||
}
|
||||
];
|
||||
|
||||
const pageList = computed(() => {
|
||||
if (currentPage.value === 1) {
|
||||
return copyIconList[currentActiveType.value]
|
||||
.filter(v => v.includes(filterValue.value))
|
||||
.slice(currentPage.value - 1, pageSize.value);
|
||||
} else {
|
||||
return copyIconList[currentActiveType.value]
|
||||
.filter(v => v.includes(filterValue.value))
|
||||
.slice(
|
||||
pageSize.value * (currentPage.value - 1),
|
||||
pageSize.value * (currentPage.value - 1) + pageSize.value
|
||||
);
|
||||
}
|
||||
});
|
||||
const pageList = computed(() =>
|
||||
copyIconList[currentActiveType.value]
|
||||
.filter(i => i.includes(filterValue.value))
|
||||
.slice(
|
||||
(currentPage.value - 1) * pageSize.value,
|
||||
currentPage.value * pageSize.value
|
||||
)
|
||||
);
|
||||
|
||||
const iconItemStyle = computed((): ParameterCSSProperties => {
|
||||
return item => {
|
||||
@ -71,50 +67,63 @@ const iconItemStyle = computed((): ParameterCSSProperties => {
|
||||
};
|
||||
});
|
||||
|
||||
function setVal() {
|
||||
currentActiveType.value = props.modelValue.substring(
|
||||
0,
|
||||
props.modelValue.indexOf(":") + 1
|
||||
);
|
||||
icon.value = props.modelValue.substring(props.modelValue.indexOf(":") + 1);
|
||||
}
|
||||
|
||||
function onBeforeEnter() {
|
||||
if (isAllEmpty(icon.value)) return;
|
||||
setVal();
|
||||
// 寻找当前图标在第几页
|
||||
const curIconIndex = copyIconList[currentActiveType.value].findIndex(
|
||||
i => i === icon.value
|
||||
);
|
||||
currentPage.value = Math.ceil((curIconIndex + 1) / pageSize.value);
|
||||
}
|
||||
|
||||
function onAfterLeave() {
|
||||
filterValue.value = "";
|
||||
}
|
||||
|
||||
function handleClick({ props }) {
|
||||
currentPage.value = 1;
|
||||
currentActiveType.value = props.name;
|
||||
emit(
|
||||
"update:modelValue",
|
||||
currentActiveType.value + iconList.value[currentActiveType.value][0]
|
||||
);
|
||||
icon.value = iconList.value[currentActiveType.value][0];
|
||||
}
|
||||
|
||||
function onChangeIcon(item) {
|
||||
icon.value = item;
|
||||
emit("update:modelValue", currentActiveType.value + item);
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
function onCurrentChange(page) {
|
||||
currentPage.value = page;
|
||||
}
|
||||
|
||||
function onClear() {
|
||||
icon.value = "";
|
||||
emit("update:modelValue", "");
|
||||
}
|
||||
|
||||
watch(
|
||||
() => {
|
||||
return props.modelValue;
|
||||
},
|
||||
() => {
|
||||
if (props.modelValue) {
|
||||
currentActiveType.value = props.modelValue.substring(
|
||||
0,
|
||||
props.modelValue.indexOf(":") + 1
|
||||
);
|
||||
icon.value = props.modelValue.substring(
|
||||
props.modelValue.indexOf(":") + 1
|
||||
);
|
||||
}
|
||||
},
|
||||
() => pageList.value,
|
||||
() =>
|
||||
(totalPage.value = copyIconList[currentActiveType.value].filter(i =>
|
||||
i.includes(filterValue.value)
|
||||
).length),
|
||||
{ immediate: true }
|
||||
);
|
||||
watch(
|
||||
() => {
|
||||
return filterValue.value;
|
||||
},
|
||||
() => {
|
||||
currentPage.value = 1;
|
||||
}
|
||||
() => props.modelValue,
|
||||
val => val && setVal(),
|
||||
{ immediate: true }
|
||||
);
|
||||
watch(
|
||||
() => filterValue.value,
|
||||
() => (currentPage.value = 1)
|
||||
);
|
||||
</script>
|
||||
|
||||
@ -129,14 +138,15 @@ watch(
|
||||
:popper-options="{
|
||||
placement: 'auto'
|
||||
}"
|
||||
:visible="visible"
|
||||
@before-enter="onBeforeEnter"
|
||||
@after-leave="onAfterLeave"
|
||||
>
|
||||
<template #reference>
|
||||
<div
|
||||
class="w-[40px] h-[32px] cursor-pointer flex justify-center items-center"
|
||||
@click="visible = !visible"
|
||||
>
|
||||
<IconifyIconOnline :icon="currentActiveType + icon" />
|
||||
<IconifyIconOffline v-if="!icon" :icon="Search" />
|
||||
<IconifyIconOnline v-else :icon="inputValue" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -160,7 +170,7 @@ watch(
|
||||
v-for="(item, key) in pageList"
|
||||
:key="key"
|
||||
:title="item"
|
||||
class="icon-item p-2 cursor-pointer mr-2 mt-1 flex justify-center items-center border border-solid"
|
||||
class="icon-item p-2 cursor-pointer mr-2 mt-1 flex justify-center items-center border border-[#e5e7eb]"
|
||||
:style="iconItemStyle(item)"
|
||||
@click="onChangeIcon(item)"
|
||||
>
|
||||
@ -175,16 +185,31 @@ watch(
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-pagination
|
||||
small
|
||||
:total="copyIconList[currentActiveType].length"
|
||||
:page-size="pageSize"
|
||||
:current-page="currentPage"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
class="flex items-center justify-center h-10"
|
||||
@current-change="onCurrentChange"
|
||||
/>
|
||||
<div
|
||||
class="w-full h-9 flex items-center overflow-auto border-t border-[#e5e7eb]"
|
||||
>
|
||||
<el-pagination
|
||||
class="flex-auto ml-2"
|
||||
:total="totalPage"
|
||||
:current-page="currentPage"
|
||||
:page-size="pageSize"
|
||||
:pager-count="5"
|
||||
layout="pager"
|
||||
background
|
||||
small
|
||||
@current-change="onCurrentChange"
|
||||
/>
|
||||
<el-button
|
||||
class="justify-end mr-2 ml-2"
|
||||
type="danger"
|
||||
size="small"
|
||||
text
|
||||
bg
|
||||
@click="onClear"
|
||||
>
|
||||
清空
|
||||
</el-button>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-input>
|
||||
@ -231,6 +256,14 @@ watch(
|
||||
box-shadow: 0 2px 5px rgb(0 0 0 / 6%);
|
||||
}
|
||||
|
||||
:deep(.el-tabs__nav-wrap::after) {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__nav-wrap) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__content) {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { h, defineComponent } from "vue";
|
||||
import { Icon as IconifyIcon, addIcon } from "@iconify/vue/dist/offline";
|
||||
|
||||
// Iconify Icon在Vue里本地使用(用于内网环境)https://docs.iconify.design/icon-components/vue/offline.html
|
||||
// Iconify Icon在Vue里本地使用(用于内网环境)
|
||||
export default defineComponent({
|
||||
name: "IconifyIconOffline",
|
||||
components: { IconifyIcon },
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import Footer from "./footer/index.vue";
|
||||
import { useGlobal } from "@pureadmin/utils";
|
||||
import backTop from "@/assets/svg/back_top.svg?component";
|
||||
import { h, computed, Transition, defineComponent } from "vue";
|
||||
@ -24,6 +25,10 @@ const hideTabs = computed(() => {
|
||||
return $storage?.configure.hideTabs;
|
||||
});
|
||||
|
||||
const hideFooter = computed(() => {
|
||||
return $storage?.configure.hideFooter;
|
||||
});
|
||||
|
||||
const layout = computed(() => {
|
||||
return $storage?.layout.layout === "vertical";
|
||||
});
|
||||
@ -32,9 +37,15 @@ const getSectionStyle = computed(() => {
|
||||
return [
|
||||
hideTabs.value && layout ? "padding-top: 48px;" : "",
|
||||
!hideTabs.value && layout ? "padding-top: 85px;" : "",
|
||||
hideTabs.value && !layout.value ? "padding-top: 48px" : "",
|
||||
hideTabs.value && !layout.value ? "padding-top: 48px;" : "",
|
||||
!hideTabs.value && !layout.value ? "padding-top: 85px;" : "",
|
||||
props.fixedHeader ? "" : "padding-top: 0;"
|
||||
props.fixedHeader
|
||||
? ""
|
||||
: `padding-top: 0;${
|
||||
hideTabs.value
|
||||
? "min-height: calc(100vh - 48px);"
|
||||
: "min-height: calc(100vh - 86px);"
|
||||
}`
|
||||
];
|
||||
});
|
||||
|
||||
@ -78,30 +89,44 @@ const transitionMain = defineComponent({
|
||||
>
|
||||
<router-view>
|
||||
<template #default="{ Component, route }">
|
||||
<el-scrollbar v-if="props.fixedHeader">
|
||||
<el-scrollbar
|
||||
v-if="props.fixedHeader"
|
||||
:wrap-style="{
|
||||
display: 'flex'
|
||||
}"
|
||||
:view-style="{
|
||||
display: 'flex',
|
||||
flex: 'auto',
|
||||
overflow: 'auto',
|
||||
'flex-direction': 'column'
|
||||
}"
|
||||
>
|
||||
<el-backtop title="回到顶部" target=".app-main .el-scrollbar__wrap">
|
||||
<backTop />
|
||||
</el-backtop>
|
||||
<transitionMain :route="route">
|
||||
<keep-alive
|
||||
v-if="isKeepAlive"
|
||||
:include="usePermissionStoreHook().cachePageList"
|
||||
>
|
||||
<div class="grow">
|
||||
<transitionMain :route="route">
|
||||
<keep-alive
|
||||
v-if="isKeepAlive"
|
||||
:include="usePermissionStoreHook().cachePageList"
|
||||
>
|
||||
<component
|
||||
:is="Component"
|
||||
:key="route.fullPath"
|
||||
class="main-content"
|
||||
/>
|
||||
</keep-alive>
|
||||
<component
|
||||
v-else
|
||||
:is="Component"
|
||||
:key="route.fullPath"
|
||||
class="main-content"
|
||||
/>
|
||||
</keep-alive>
|
||||
<component
|
||||
v-else
|
||||
:is="Component"
|
||||
:key="route.fullPath"
|
||||
class="main-content"
|
||||
/>
|
||||
</transitionMain>
|
||||
</transitionMain>
|
||||
</div>
|
||||
<Footer v-if="!hideFooter" />
|
||||
</el-scrollbar>
|
||||
<div v-else>
|
||||
<div v-else class="grow">
|
||||
<transitionMain :route="route">
|
||||
<keep-alive
|
||||
v-if="isKeepAlive"
|
||||
@ -123,6 +148,9 @@ const transitionMain = defineComponent({
|
||||
</div>
|
||||
</template>
|
||||
</router-view>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<Footer v-if="!hideFooter && !props.fixedHeader" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@ -136,8 +164,9 @@ const transitionMain = defineComponent({
|
||||
|
||||
.app-main-nofixed-header {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
|
29
src/layout/components/footer/index.vue
Normal file
29
src/layout/components/footer/index.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<script lang="ts" setup>
|
||||
import { getConfig } from "@/config";
|
||||
|
||||
const TITLE = getConfig("Title");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer class="layout-footer">
|
||||
MIT © 2020-PRESENT
|
||||
<a
|
||||
class="ml-1 hover:text-primary"
|
||||
href="https://github.com/pure-admin"
|
||||
target="_blank"
|
||||
>
|
||||
{{ TITLE }}
|
||||
</a>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 0 0 8px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
</style>
|
@ -66,6 +66,7 @@ const settings = reactive({
|
||||
tabsVal: $storage.configure.hideTabs,
|
||||
showLogo: $storage.configure.showLogo,
|
||||
showModel: $storage.configure.showModel,
|
||||
hideFooter: $storage.configure.hideFooter,
|
||||
multiTagsCache: $storage.configure.multiTagsCache
|
||||
});
|
||||
|
||||
@ -111,12 +112,20 @@ const weekChange = (value): void => {
|
||||
storageConfigureChange("weak", value);
|
||||
};
|
||||
|
||||
/** 隐藏标签页设置 */
|
||||
const tagsChange = () => {
|
||||
const showVal = settings.tabsVal;
|
||||
storageConfigureChange("hideTabs", showVal);
|
||||
emitter.emit("tagViewsChange", showVal as unknown as string);
|
||||
};
|
||||
|
||||
/** 隐藏页脚设置 */
|
||||
const hideFooterChange = () => {
|
||||
const hideFooter = settings.hideFooter;
|
||||
storageConfigureChange("hideFooter", hideFooter);
|
||||
};
|
||||
|
||||
/** 标签页持久化设置 */
|
||||
const multiTagsCacheChange = () => {
|
||||
const multiTagsCache = settings.multiTagsCache;
|
||||
storageConfigureChange("multiTagsCache", multiTagsCache);
|
||||
@ -218,6 +227,7 @@ onBeforeMount(() => {
|
||||
settings.weakVal &&
|
||||
document.querySelector("html")?.setAttribute("class", "html-weakness");
|
||||
settings.tabsVal && tagsChange();
|
||||
settings.hideFooter && hideFooterChange();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@ -344,6 +354,17 @@ onBeforeMount(() => {
|
||||
@change="tagsChange"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<span class="dark:text-white">隐藏页脚</span>
|
||||
<el-switch
|
||||
v-model="settings.hideFooter"
|
||||
inline-prompt
|
||||
inactive-color="#a6a6a6"
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@change="hideFooterChange"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<span class="dark:text-white">侧边栏Logo</span>
|
||||
<el-switch
|
||||
|
@ -36,6 +36,7 @@ export function useLayout() {
|
||||
grey: $config?.Grey ?? false,
|
||||
weak: $config?.Weak ?? false,
|
||||
hideTabs: $config?.HideTabs ?? false,
|
||||
hideFooter: $config.HideFooter ?? false,
|
||||
showLogo: $config?.ShowLogo ?? true,
|
||||
showModel: $config?.ShowModel ?? "smart",
|
||||
multiTagsCache: $config?.MultiTagsCache ?? false
|
||||
|
@ -24,6 +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,
|
||||
showLogo: config.ShowLogo ?? true,
|
||||
showModel: config.ShowModel ?? "smart",
|
||||
multiTagsCache: config.MultiTagsCache ?? false
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import IconSelect from "@/components/ReIcon/src/Select.vue";
|
||||
import { IconSelect } from "@/components/ReIcon";
|
||||
|
||||
defineOptions({
|
||||
name: "IconSelect"
|
||||
|
@ -22,9 +22,9 @@ onMounted(() => {
|
||||
videoAttributes: {
|
||||
crossOrigin: "anonymous"
|
||||
},
|
||||
url: "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4",
|
||||
url: "//lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/xgplayer-demo.mp4",
|
||||
poster:
|
||||
"https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg",
|
||||
"//lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg",
|
||||
fluid: deviceDetection(),
|
||||
//传入倍速可选数组
|
||||
playbackRate: [0.5, 0.75, 1, 1.5, 2]
|
||||
|
3
types/global.d.ts
vendored
3
types/global.d.ts
vendored
@ -89,6 +89,7 @@ declare global {
|
||||
Grey?: boolean;
|
||||
Weak?: boolean;
|
||||
HideTabs?: boolean;
|
||||
HideFooter?: boolean;
|
||||
SidebarStatus?: boolean;
|
||||
EpThemeColor?: string;
|
||||
ShowLogo?: boolean;
|
||||
@ -125,6 +126,7 @@ declare global {
|
||||
grey?: boolean;
|
||||
weak?: boolean;
|
||||
hideTabs?: boolean;
|
||||
hideFooter?: boolean;
|
||||
sidebarStatus?: boolean;
|
||||
epThemeColor?: string;
|
||||
showLogo?: boolean;
|
||||
@ -158,6 +160,7 @@ declare global {
|
||||
grey?: boolean;
|
||||
weak?: boolean;
|
||||
hideTabs?: boolean;
|
||||
hideFooter?: boolean;
|
||||
showLogo?: boolean;
|
||||
showModel?: string;
|
||||
multiTagsCache?: boolean;
|
||||
|
Loading…
x
Reference in New Issue
Block a user