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

This commit is contained in:
xiaoxian521 2023-06-09 18:07:26 +08:00
commit 2bd7d44119
21 changed files with 2832 additions and 6165 deletions

View File

@ -33,7 +33,6 @@ const include = [
"@howdyjs/mouse-menu",
"@logicflow/extension",
"vue-virtual-scroller",
"element-resize-detector",
"@amap/amap-jsapi-loader",
"el-table-infinite-scroll",
"vue-waterfall-plugin-next",

View File

@ -101,7 +101,7 @@ menus:
hsInfiniteScroll: 表格无限滚动
hsdanmaku: 弹幕组件
hsPureTableBase: 基础用法23个示例
hsPureTableHigh: 高级用法10个示例)
hsPureTableHigh: 高级用法11个示例)
hsTree: 大数据树业务组件
hsMenuoverflow: 目录超出显示 Tooltip 文字提示
hsChildMenuoverflow: 菜单超出显示 Tooltip 文字提示

View File

@ -33,7 +33,7 @@
"@logicflow/core": "^1.2.7",
"@logicflow/extension": "^1.2.7",
"@pureadmin/descriptions": "^1.1.1",
"@pureadmin/table": "^2.2.0",
"@pureadmin/table": "^2.3.2",
"@pureadmin/utils": "^1.9.2",
"@vueuse/core": "^10.1.2",
"@vueuse/motion": "^2.0.0",
@ -47,7 +47,6 @@
"echarts": "^5.4.2",
"el-table-infinite-scroll": "^3.0.1",
"element-plus": "^2.3.5",
"element-resize-detector": "^1.2.4",
"intro.js": "^7.0.1",
"js-cookie": "^3.0.5",
"jsbarcode": "^3.11.5",
@ -87,7 +86,6 @@
"@iconify/vue": "^4.1.1",
"@intlify/unplugin-vue-i18n": "^0.10.0",
"@pureadmin/theme": "^3.0.0",
"@types/element-resize-detector": "1.1.3",
"@types/intro.js": "^5.1.1",
"@types/js-cookie": "^3.0.3",
"@types/mockjs": "^1.0.7",

8656
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +0,0 @@
import { Directive, type DirectiveBinding, type VNode } from "vue";
import elementResizeDetectorMaker from "element-resize-detector";
import type { Erd } from "element-resize-detector";
import { emitter } from "@/utils/mitt";
const erd: Erd = elementResizeDetectorMaker({
strategy: "scroll"
});
export const resize: Directive = {
mounted(el: HTMLElement, binding?: DirectiveBinding, vnode?: VNode) {
erd.listenTo(el, elem => {
const width = elem.offsetWidth;
const height = elem.offsetHeight;
if (binding?.instance) {
emitter.emit("resize", { detail: { width, height } });
} else {
vnode.el.dispatchEvent(
new CustomEvent("resize", { detail: { width, height } })
);
}
});
},
unmounted(el: HTMLElement) {
erd.uninstall(el);
}
};

View File

@ -1,2 +1 @@
export * from "./auth";
export * from "./elResizeDetector";

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref, computed } from "vue";
import { emitter } from "@/utils/mitt";
import { onClickOutside } from "@vueuse/core";
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import Close from "@iconify-icons/ep/close";
const target = ref(null);
@ -27,8 +27,15 @@ onClickOutside(target, (event: any) => {
show.value = false;
});
emitter.on("openPanel", () => {
show.value = true;
onMounted(() => {
emitter.on("openPanel", () => {
show.value = true;
});
});
onBeforeUnmount(() => {
// `openPanel`
emitter.off("openPanel");
});
</script>

View File

@ -7,9 +7,9 @@ import leftCollapse from "./leftCollapse.vue";
import { useNav } from "@/layout/hooks/useNav";
import { storageLocal } from "@pureadmin/utils";
import { responsiveStorageNameSpace } from "@/config";
import { ref, computed, watch, onBeforeMount } from "vue";
import { findRouteByPath, getParentPaths } from "@/router/utils";
import { usePermissionStoreHook } from "@/store/modules/permission";
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";
const route = useRoute();
const showLogo = ref(
@ -51,12 +51,6 @@ function getSubMenuData(path: string) {
getSubMenuData(route.path);
onBeforeMount(() => {
emitter.on("logoChange", key => {
showLogo.value = key;
});
});
watch(
() => [route.path, usePermissionStoreHook().wholeMenus],
() => {
@ -65,6 +59,17 @@ watch(
menuSelect(route.path, routers);
}
);
onMounted(() => {
emitter.on("logoChange", key => {
showLogo.value = key;
});
});
onBeforeUnmount(() => {
// `logoChange`
emitter.off("logoChange");
});
</script>
<template>

View File

@ -8,7 +8,7 @@ import { isEqual, isAllEmpty } from "@pureadmin/utils";
import { handleAliveRoute, getTopMenu } from "@/router/utils";
import { useSettingStoreHook } from "@/store/modules/settings";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { ref, watch, unref, toRaw, nextTick, onBeforeMount } from "vue";
import { ref, watch, unref, toRaw, nextTick, onBeforeUnmount } from "vue";
import { useResizeObserver, useDebounceFn, useFullscreen } from "@vueuse/core";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
@ -465,7 +465,17 @@ function tagOnClick(item) {
// showMenuModel(item?.path, item?.query);
}
onBeforeMount(() => {
watch([route], () => {
activeIndex.value = -1;
dynamicTagView();
});
watch(isFullscreen, () => {
tagsViews[6].icon = Fullscreen;
tagsViews[6].text = $t("buttons.hswholeFullScreen");
});
onMounted(() => {
if (!instance) return;
//
@ -489,19 +499,7 @@ onBeforeMount(() => {
showMenuModel(indexPath);
});
});
});
watch([route], () => {
activeIndex.value = -1;
dynamicTagView();
});
watch(isFullscreen, () => {
tagsViews[6].icon = Fullscreen;
tagsViews[6].text = $t("buttons.hswholeFullScreen");
});
onMounted(() => {
useResizeObserver(
scrollbarDom,
useDebounceFn(() => {
@ -509,6 +507,13 @@ onMounted(() => {
}, 200)
);
});
onBeforeUnmount(() => {
// `tagViewsChange``tagViewsShowModel``changLayoutRoute`
emitter.off("tagViewsChange");
emitter.off("tagViewsShowModel");
emitter.off("changLayoutRoute");
});
</script>
<template>

View File

@ -3,14 +3,15 @@ import "animate.css";
// src/components/ReIcon/src/offlineIcon.ts 使addIcon
import "@/components/ReIcon/src/offlineIcon";
import { setType } from "./types";
import { emitter } from "@/utils/mitt";
import { useLayout } from "./hooks/useLayout";
import { useResizeObserver } from "@vueuse/core";
import { useAppStoreHook } from "@/store/modules/app";
import { useSettingStoreHook } from "@/store/modules/settings";
import { deviceDetection, useDark, useGlobal } from "@pureadmin/utils";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import {
h,
ref,
reactive,
computed,
onMounted,
@ -26,6 +27,7 @@ import Vertical from "./components/sidebar/vertical.vue";
import Horizontal from "./components/sidebar/horizontal.vue";
import backTop from "@/assets/svg/back_top.svg?component";
const appWrapperRef = ref();
const { isDark } = useDark();
const { layout } = useLayout();
const isMobile = deviceDetection();
@ -78,10 +80,10 @@ function toggle(device: string, bool: boolean) {
//
let isAutoCloseSidebar = true;
//
emitter.on("resize", ({ detail }) => {
useResizeObserver(appWrapperRef, entries => {
if (isMobile) return;
const { width } = detail;
const entry = entries[0];
const { width } = entry.contentRect;
width <= 760 ? setTheme("vertical") : setTheme(useAppStoreHook().layout);
/** width app-wrapper
* 0 < width <= 760 隐藏侧边栏
@ -147,7 +149,7 @@ const layoutHeader = defineComponent({
</script>
<template>
<div :class="['app-wrapper', set.classes]" v-resize>
<div ref="appWrapperRef" :class="['app-wrapper', set.classes]">
<div
v-show="
set.device === 'mobile' &&

View File

@ -15,8 +15,10 @@ const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({ user: "", region: "" })
});
// vue prop prop form.vue
// prop 使 prop
// vue prop prop Vue
// reactive ref Ref value reactive
// newFormInline === props.formInline true props.formInline
// props.formInline
// https://cn.vuejs.org/guide/components/props.html#one-way-data-flow
const newFormInline = ref(props.formInline);
</script>

View File

@ -0,0 +1,22 @@
<script setup lang="ts">
import { useVModel } from "@vueuse/core";
// props
export interface FormProps {
data: string;
}
// props
// https://cn.vuejs.org/guide/typescript/composition-api.html#typing-component-props
const props = withDefaults(defineProps<FormProps>(), {
data: () => ""
});
// 使 vueuse
const emit = defineEmits(["update:data"]);
const data = useVModel(props, "data", emit);
</script>
<template>
<el-input class="!w-[220px]" v-model="data" placeholder="请输入内容" />
</template>

View File

@ -3,6 +3,7 @@ import { useRouter } from "vue-router";
import { h, createVNode, ref } from "vue";
import { message } from "@/utils/message";
import forms, { type FormProps } from "./form.vue";
import formPrimitive from "./formPrimitive.vue";
import { cloneDeep, debounce } from "@pureadmin/utils";
import {
addDialog,
@ -316,7 +317,10 @@ function onFormTwoClick() {
addDialog({
width: "30%",
title: "结合Form表单第二种方式",
contentRenderer: () => h(forms, { formInline: formInline.value }),
contentRenderer: () =>
h(forms, {
formInline: formInline.value
}),
closeCallBack: () => {
message(
`当前表单数据为 姓名:${formInline.value.user} 城市:${formInline.value.region}`
@ -338,7 +342,9 @@ function onFormThreeClick() {
width: "30%",
title: "结合Form表单第三种方式",
contentRenderer: () =>
createVNode(forms, { formInline: formThreeInline.value }),
createVNode(forms, {
formInline: formThreeInline.value
}),
closeCallBack: () => {
message(
`当前表单数据为 姓名:${formThreeInline.value.user} 城市:${formThreeInline.value.region}`
@ -373,6 +379,26 @@ function onFormFourClick() {
});
}
// prop primitive demo
const formPrimitiveParam = ref("Hello World");
const resetFormPrimitiveParam = ref(formPrimitiveParam.value);
function onFormPrimitiveFormClick() {
addDialog({
width: "30%",
title: "子组件 prop 为 primitive 类型 demo",
contentRenderer: () =>
h(formPrimitive, {
data: formPrimitiveParam.value,
"onUpdate:data": val => (formPrimitiveParam.value = val)
}),
closeCallBack: () => {
message(`当前表单内容:${formPrimitiveParam.value}`);
//
formPrimitiveParam.value = resetFormPrimitiveParam.value;
}
});
}
function onBeforeCancelClick() {
addDialog({
title: "点击底部取消按钮的回调",
@ -474,6 +500,9 @@ function onBeforeSureClick() {
<el-button @click="onFormFourClick">
结合Form表单第四种方式
</el-button>
<el-button @click="onFormPrimitiveFormClick">
子组件 prop primitive 类型
</el-button>
</el-space>
<el-divider />
<el-space wrap>

View File

@ -0,0 +1,105 @@
import type {
LoadingConfig,
AdaptiveConfig,
PaginationProps
} from "@pureadmin/table";
import { tableData } from "../data";
import { ref, onMounted, reactive } from "vue";
import { clone, delay } from "@pureadmin/utils";
export function useColumns() {
const dataList = ref([]);
const loading = ref(true);
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
/** 分页配置 */
const pagination = reactive<PaginationProps>({
pageSize: 20,
currentPage: 1,
pageSizes: [20, 40, 60],
total: 0,
align: "right",
background: true,
small: false
});
/** 加载动画配置 */
const loadingConfig = reactive<LoadingConfig>({
text: "正在加载第一页...",
viewBox: "-10, -10, 50, 50",
spinner: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`
// svg: "",
// background: rgba()
});
/** 撑满内容区自适应高度相关配置 */
const adaptiveConfig: AdaptiveConfig = {
/** 表格距离页面底部的偏移量,默认值为 `96` */
offsetBottom: 110
/** 是否固定表头,默认值为 `true`如果不想固定表头fixHeader设置为false并且表格要设置table-layout="auto" */
// fixHeader: true
/** 页面 `resize` 时的防抖时间,默认值为 `60` ms */
// timeout: 60
/** 表头的 `z-index`,默认值为 `100` */
// zIndex: 100
};
function onSizeChange(val) {
console.log("onSizeChange", val);
}
function onCurrentChange(val) {
loadingConfig.text = `正在加载第${val}页...`;
loading.value = true;
delay(600).then(() => {
loading.value = false;
});
}
onMounted(() => {
delay(600).then(() => {
const newList = [];
Array.from({ length: 6 }).forEach(() => {
newList.push(clone(tableData, true));
});
newList.flat(Infinity).forEach((item, index) => {
dataList.value.push({ id: index, ...item });
});
pagination.total = dataList.value.length;
loading.value = false;
});
});
return {
loading,
columns,
dataList,
pagination,
loadingConfig,
adaptiveConfig,
onSizeChange,
onCurrentChange
};
}

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import { ref } from "vue";
import { useColumns } from "./columns";
const tableRef = ref();
const {
loading,
columns,
dataList,
pagination,
loadingConfig,
adaptiveConfig,
onSizeChange,
onCurrentChange
} = useColumns();
</script>
<template>
<pure-table
ref="tableRef"
border
adaptive
:adaptiveConfig="adaptiveConfig"
row-key="id"
alignWhole="center"
showOverflowTooltip
:loading="loading"
:loading-config="loadingConfig"
:data="
dataList.slice(
(pagination.currentPage - 1) * pagination.pageSize,
pagination.currentPage * pagination.pageSize
)
"
:columns="columns"
:pagination="pagination"
@page-size-change="onSizeChange"
@page-current-change="onCurrentChange"
/>
</template>

View File

@ -1,3 +1,4 @@
import Adaptive from "./adaptive/index.vue";
import Page from "./page/index.vue";
import RowDrag from "./drag/row/index.vue";
import ColumnDrag from "./drag/column/index.vue";
@ -13,6 +14,12 @@ const rendContent = (val: string) =>
`代码位置src/views/pure-table/high/${val}/index.vue`;
export const list = [
{
key: "adaptive",
content: rendContent("adaptive"),
title: "自适应内容区高度",
component: Adaptive
},
{
key: "page",
content: rendContent("page"),

View File

@ -90,6 +90,8 @@ const {
<pure-table
ref="tableRef"
border
adaptive
:adaptiveConfig="{ offsetBottom: 32 }"
align-whole="center"
row-key="id"
showOverflowTooltip

View File

@ -109,6 +109,7 @@ const {
table-layout="auto"
:loading="loading"
:size="size"
adaptive
:data="dataList"
:columns="dynamicColumns"
:pagination="pagination"

View File

@ -39,7 +39,7 @@ const {
<template>
<div class="main">
<tree class="w-[17%] float-left" />
<div class="float-right w-[81%]">
<div class="float-right w-[82%]">
<el-form
ref="formRef"
:inline="true"
@ -97,6 +97,7 @@ const {
<template v-slot="{ size, dynamicColumns }">
<pure-table
border
adaptive
align-whole="center"
table-layout="auto"
:loading="loading"

View File

@ -87,7 +87,10 @@ onMounted(async () => {
</script>
<template>
<div class="h-full min-h-[780px] bg-bg_color overflow-auto">
<div
class="h-full bg-bg_color overflow-auto"
:style="{ minHeight: `calc(100vh - 133px)` }"
>
<div class="flex items-center h-[34px]">
<p class="flex-1 ml-2 font-bold text-base truncate" title="部门列表">
部门列表

View File

@ -145,7 +145,9 @@ getReleases().then(({ data }) => {
</template>
<el-skeleton animated :rows="7" :loading="loading">
<template #default>
<Github />
<el-scrollbar :height="`calc(${height}px - 35vh - 340px)`">
<Github />
</el-scrollbar>
</template>
</el-skeleton>
</el-card>