zyf-model-engine 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/.editorconfig +15 -0
  2. package/.env +13 -0
  3. package/.env.development +20 -0
  4. package/.env.production +25 -0
  5. package/.env.test +25 -0
  6. package/.eslintignore +15 -0
  7. package/.eslintrc-auto-import.json +65 -0
  8. package/.eslintrc.cjs +66 -0
  9. package/.husky/commit-msg +4 -0
  10. package/.husky/pre-commit +5 -0
  11. package/.prettierignore +9 -0
  12. package/.prettierrc.cjs +41 -0
  13. package/.stylelintignore +4 -0
  14. package/.stylelintrc.cjs +40 -0
  15. package/.vscode/extensions.json +14 -0
  16. package/.vscode/settings.json +92 -0
  17. package/CHANGELOG.md +246 -0
  18. package/LICENSE +21 -0
  19. package/README.md +205 -0
  20. package/auto-imports.d.ts +132 -0
  21. package/build/getEnv.ts +46 -0
  22. package/build/plugins.ts +138 -0
  23. package/build/proxy.ts +28 -0
  24. package/commitlint.config.cjs +84 -0
  25. package/components.d.ts +28 -0
  26. package/index.html +105 -0
  27. package/lint-staged.config.cjs +8 -0
  28. package/package.json +108 -0
  29. package/postcss.config.cjs +5 -0
  30. package/public/logo.png +0 -0
  31. package/src/App.vue +16 -0
  32. package/src/api/config/servicePort.ts +3 -0
  33. package/src/api/enums/httpEnum.ts +35 -0
  34. package/src/api/helper/axiosCancel.ts +50 -0
  35. package/src/api/helper/axiosFileCancel.ts +62 -0
  36. package/src/api/helper/checkStatus.ts +97 -0
  37. package/src/api/index.ts +142 -0
  38. package/src/api/interface/index.ts +27 -0
  39. package/src/assets/global-icons/icon-demo.svg +1 -0
  40. package/src/assets/iconfont/iconfont.scss +44 -0
  41. package/src/assets/iconfont/iconfont.ttf +0 -0
  42. package/src/assets/images/empty-img.png +0 -0
  43. package/src/assets/option-icons/add.svg +1 -0
  44. package/src/assets/option-icons/check.svg +1 -0
  45. package/src/assets/option-icons/config.svg +1 -0
  46. package/src/assets/option-icons/del.svg +1 -0
  47. package/src/assets/option-icons/detail.svg +1 -0
  48. package/src/assets/option-icons/edit.svg +2 -0
  49. package/src/assets/option-icons/export.svg +1 -0
  50. package/src/assets/option-icons/folder.svg +1 -0
  51. package/src/assets/option-icons/full-screen.svg +1 -0
  52. package/src/assets/option-icons/more.svg +1 -0
  53. package/src/assets/option-icons/return.svg +1 -0
  54. package/src/assets/option-icons/search.svg +1 -0
  55. package/src/components/CSvgIcon/index.scss +23 -0
  56. package/src/components/CSvgIcon/index.vue +74 -0
  57. package/src/components/CTree/index.scss +48 -0
  58. package/src/components/CTree/index.vue +176 -0
  59. package/src/components/CTree/interface/index.ts +6 -0
  60. package/src/components/Dialog/index.scss +74 -0
  61. package/src/components/Dialog/index.vue +87 -0
  62. package/src/components/Empty/index.scss +16 -0
  63. package/src/components/Empty/index.vue +43 -0
  64. package/src/components/Grid/components/GridItem.vue +68 -0
  65. package/src/components/Grid/index.vue +167 -0
  66. package/src/components/Grid/interface/index.ts +6 -0
  67. package/src/components/Loading/index.scss +67 -0
  68. package/src/components/Loading/index.vue +13 -0
  69. package/src/components/Overflow/index.vue +76 -0
  70. package/src/components/ProTable/components/ColSetting.vue +45 -0
  71. package/src/components/ProTable/components/Pagination.vue +29 -0
  72. package/src/components/ProTable/components/SearchForm/components/SearchFormItem.vue +93 -0
  73. package/src/components/ProTable/components/SearchForm/index.scss +12 -0
  74. package/src/components/ProTable/components/SearchForm/index.vue +91 -0
  75. package/src/components/ProTable/components/TableColumn.vue +56 -0
  76. package/src/components/ProTable/index.scss +14 -0
  77. package/src/components/ProTable/index.vue +274 -0
  78. package/src/components/ProTable/interface/index.ts +86 -0
  79. package/src/components/ScrollBox/index.vue +44 -0
  80. package/src/components/SvgIcon/index.vue +20 -0
  81. package/src/components/Tabs/index.vue +142 -0
  82. package/src/config/index.ts +12 -0
  83. package/src/config/nprogress.ts +12 -0
  84. package/src/config/piniaPersist.ts +19 -0
  85. package/src/config/serviceLoading.ts +45 -0
  86. package/src/enums/httpEnum.ts +24 -0
  87. package/src/hooks/interface/index.ts +32 -0
  88. package/src/hooks/useOnline.ts +27 -0
  89. package/src/hooks/useTable.ts +167 -0
  90. package/src/main.ts +35 -0
  91. package/src/plugins/button/button.vue +56 -0
  92. package/src/plugins/index.js +26 -0
  93. package/src/plugins/index.vue +24 -0
  94. package/src/plugins/msg/msg.vue +56 -0
  95. package/src/stores/commonStore.ts +9 -0
  96. package/src/stores/index.ts +8 -0
  97. package/src/stores/interface/index.ts +1 -0
  98. package/src/styles/common.scss +87 -0
  99. package/src/styles/element.scss +233 -0
  100. package/src/styles/reset.scss +20 -0
  101. package/src/styles/var.scss +2 -0
  102. package/src/typings/global.d.ts +37 -0
  103. package/src/typings/window.d.ts +8 -0
  104. package/src/utils/color.ts +59 -0
  105. package/src/utils/commonUtils.ts +175 -0
  106. package/src/utils/errorHandler.ts +28 -0
  107. package/src/utils/is.ts +147 -0
  108. package/src/utils/layoutUtils.ts +83 -0
  109. package/src/utils/localUtils.ts +42 -0
  110. package/src/utils/serviceDict.ts +9 -0
  111. package/src/vite-env.d.ts +6 -0
  112. package/tsconfig.json +50 -0
  113. package/vite.config.ts +76 -0
@@ -0,0 +1,176 @@
1
+ <template>
2
+ <div class="tree">
3
+ <el-tree
4
+ ref="treeRef"
5
+ v-bind="{
6
+ data: treeData,
7
+ highlightCurren: true,
8
+ nodeKey: 'id',
9
+ defaultExpandAll: true,
10
+ indent: 7,
11
+ props: {
12
+ children: 'children',
13
+ label: 'name'
14
+ },
15
+ checkStrictly: true,
16
+ emptyText: '暂无数据',
17
+ ...$attrs
18
+ }"
19
+ @check="onCheckTree"
20
+ :current-node-key="currentNodeKey"
21
+ >
22
+ <template v-if="slotDefault" #default="{ node, data }">
23
+ <div class="custom-tree-node">
24
+ <slot :data="data" :node="node"></slot>
25
+ </div>
26
+ </template>
27
+ <template #empty>
28
+ <empty />
29
+ </template>
30
+ </el-tree>
31
+ </div>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import { ElTree } from "element-plus";
36
+ import { PropType } from "vue";
37
+ import { fetchType as myFetchType } from "@/api/interface/index";
38
+ import { treeDataProps } from "./interface/index";
39
+ const props = defineProps({
40
+ //列表请求方法
41
+ fetch: {
42
+ type: [Function, Array<treeDataProps>, String],
43
+ default: () => {
44
+ return [];
45
+ }
46
+ },
47
+ fetchType: {
48
+ type: String as PropType<myFetchType>,
49
+ default: () => "post"
50
+ },
51
+ // 列表请求参数
52
+ fetchParams: {
53
+ type: Object,
54
+ default: () => {
55
+ return {};
56
+ }
57
+ },
58
+ // 当前选择的节点
59
+ currentNodeKey: {
60
+ type: [String, Number],
61
+ default: () => ""
62
+ },
63
+ //是否监听请求方法
64
+ requestAuto: {
65
+ type: Boolean,
66
+ default: () => true
67
+ }
68
+ });
69
+
70
+ const emit = defineEmits(["loadAfter", "onChecked"]);
71
+ //判断<slot/>是否有传值
72
+ const slotDefault = !!useSlots().default;
73
+
74
+ const treeData = ref<treeDataProps[]>([]);
75
+ // 加载table数据
76
+ const loadData = async () => {
77
+ if (props.fetch) {
78
+ try {
79
+ if (["[object AsyncFunction]", "[object String]"].includes(Object.prototype.toString.call(props.fetch))) {
80
+ let res = typeof "string"
81
+ ? await http[props.fetchType](props.fetch as string, props.fetchParams || {})
82
+ : await (props.fetch as any)(props.fetchParams);
83
+ if (Array.isArray(res)) {
84
+ treeData.value = res;
85
+ } else {
86
+ treeData.value = [];
87
+ }
88
+ nextTick(() => {
89
+ emit("loadAfter", res);
90
+ });
91
+ } else {
92
+ treeData.value = props.fetch as treeDataProps[];
93
+ }
94
+ } catch (e) {
95
+ console.log(e);
96
+ }
97
+ }
98
+ };
99
+
100
+ const treeRef = ref<InstanceType<typeof ElTree>>();
101
+
102
+ // 获取被勾选项的id数组
103
+ const getCheckedKeys = () => {
104
+ return treeRef.value?.getCheckedKeys();
105
+ };
106
+ // 设置被勾选项的id数组
107
+ const setCheckedKeys = (val: (string | number)[]) => {
108
+ treeRef.value?.setCheckedKeys(val);
109
+ };
110
+ // 获取被勾选前选中节点的数组
111
+ const getCheckedNodes = () => {
112
+ return treeRef.value?.getCheckedNodes();
113
+ };
114
+ // 设置目前勾选的节点
115
+ const setCheckedNodes = (val: any[]) => {
116
+ treeRef.value?.setCheckedNodes(val);
117
+ };
118
+ // 设置目前勾选的节点 key/data
119
+ const setChecked = (key: string, checked: boolean, deep = true) => {
120
+ treeRef.value?.setChecked(key, checked, deep);
121
+ };
122
+
123
+ const onCheckTree = (data: any, checked: any, indeterminate: any) => {
124
+ let obj = { data, checked, indeterminate, allChecked: getCheckedKeys(), allCheckedNode: getCheckedNodes() };
125
+ emit("onChecked", obj);
126
+ };
127
+
128
+ const onTreeFilter = (val: string | number) => {
129
+ if (treeData.value?.length) {
130
+ // 将不符合输入名称多选框字列表隐藏掉
131
+ treeRef.value?.filter(val);
132
+ }
133
+ };
134
+ watch(
135
+ [() => props.fetch, () => props.fetchParams],
136
+ () => {
137
+ loadData();
138
+ },
139
+ {
140
+ deep: true
141
+ }
142
+ );
143
+ watch(
144
+ () => props.currentNodeKey,
145
+ newV => {
146
+ if (newV) {
147
+ if (treeRef.value) {
148
+ treeRef.value.setCurrentKey(newV);
149
+ }
150
+ } else {
151
+ if (treeRef.value) {
152
+ treeRef.value.setCurrentKey(undefined);
153
+ }
154
+ }
155
+ }
156
+ );
157
+ onMounted(() => {
158
+ props.requestAuto && loadData();
159
+ });
160
+
161
+ defineExpose({
162
+ element: treeRef,
163
+ treeData,
164
+ loadData,
165
+ getCheckedKeys,
166
+ setCheckedKeys,
167
+ getCheckedNodes,
168
+ setCheckedNodes,
169
+ setChecked,
170
+ onTreeFilter
171
+ });
172
+ </script>
173
+
174
+ <style scoped lang="scss">
175
+ @import "./index.scss";
176
+ </style>
@@ -0,0 +1,6 @@
1
+ export interface treeDataProps {
2
+ id: string;
3
+ name: string;
4
+ children?: treeDataProps[];
5
+ [key: string]: any;
6
+ }
@@ -0,0 +1,74 @@
1
+ .dialog-box {
2
+ :deep(.my-dialog) {
3
+ display: flex;
4
+ flex-direction: column;
5
+ .el-dialog__header {
6
+ padding: 20px;
7
+ padding-right: 36px;
8
+ margin: 0;
9
+ border-bottom: 1px solid #eeeeee;
10
+ .el-dialog__title {
11
+ font-size: 16px;
12
+ font-weight: bold;
13
+ line-height: 19px;
14
+ color: #333333;
15
+ }
16
+ }
17
+ .el-dialog__body {
18
+ position: relative;
19
+ box-sizing: border-box;
20
+ flex: 1;
21
+ padding: 0 !important;
22
+ .dialog-body {
23
+ padding: 20px 0 20px 20px;
24
+ .dialog-close-icon {
25
+ position: absolute;
26
+ top: 20px;
27
+ right: 20px;
28
+ z-index: 1;
29
+ cursor: pointer;
30
+ }
31
+ .dialog-close-icon:hover {
32
+ color: $primary-color;
33
+ }
34
+ > div {
35
+ width: 100%;
36
+ max-height: 760px;
37
+ padding-right: 20px;
38
+ overflow-y: auto;
39
+ }
40
+ .btn-group {
41
+ display: flex;
42
+ justify-content: flex-end;
43
+ .el-button {
44
+ display: flex;
45
+ align-items: center;
46
+ padding-right: 14px;
47
+ padding-left: 14px;
48
+ .icon {
49
+ width: 16px;
50
+ height: 16px;
51
+ margin-right: 4px;
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+ .el-dialog__footer {
58
+ padding: 0 30px 30px;
59
+ }
60
+ }
61
+ :deep(.hide-title) {
62
+ .el-dialog__header {
63
+ display: none !important;
64
+ }
65
+ .dialog-body {
66
+ > div {
67
+ width: 100%;
68
+ max-height: 800px;
69
+ padding-right: 30px;
70
+ overflow-y: auto;
71
+ }
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,87 @@
1
+ <template>
2
+ <div class="dialog-box" v-if="visible">
3
+ <el-dialog
4
+ :model-value="visible"
5
+ :width="toWidthRatio()"
6
+ align-center
7
+ :title="title"
8
+ :before-close="onClose"
9
+ :destroy-on-close="true"
10
+ :show-close="showCloseIcon"
11
+ :class="hideTitle ? 'my-dialog hide-title' : 'my-dialog'"
12
+ :modal="modal"
13
+ :close-on-click-modal="closeOnClickModal"
14
+ :append-to-body="appendToBody"
15
+ close-on-press-escape
16
+ >
17
+ <template #header v-if="slotHeader">
18
+ <slot name="header"></slot>
19
+ </template>
20
+ <div class="dialog-body" :style="{ width: '100%' }">
21
+ <section v-if="hideTitle" class="dialog-close-icon" @click="onClose">
22
+ <Close style="width: 1em; height: 1em" />
23
+ </section>
24
+ <slot></slot>
25
+ </div>
26
+ <template #footer v-if="slotFooter">
27
+ <slot name="footer"></slot>
28
+ </template>
29
+ </el-dialog>
30
+ </div>
31
+ </template>
32
+
33
+ <script setup lang="ts">
34
+ const props = defineProps({
35
+ visible: {
36
+ type: Boolean,
37
+ required: true
38
+ },
39
+ title: {
40
+ type: String,
41
+ default: () => "默认标题"
42
+ },
43
+ width: {
44
+ type: [Number, String],
45
+ default: () => 600
46
+ },
47
+ showCloseIcon: {
48
+ type: Boolean,
49
+ default: () => true
50
+ },
51
+ // 控制是否隐藏 header 栏, 默认为 true
52
+ hideTitle: {
53
+ type: Boolean,
54
+ default: () => false
55
+ },
56
+ modal: {
57
+ type: Boolean,
58
+ default: () => true
59
+ },
60
+ appendToBody: {
61
+ type: Boolean,
62
+ default: () => false
63
+ },
64
+ closeOnClickModal: {
65
+ type: Boolean,
66
+ default: () => true
67
+ }
68
+ });
69
+
70
+ const emit = defineEmits(["onClose"]);
71
+ //判断<slot/>是否有传值
72
+ const slotHeader = !!useSlots().header;
73
+ const slotFooter = !!useSlots().footer;
74
+ const toWidthRatio = () => {
75
+ return typeof props.width === "number" ? (props.width / 1920) * 100 + "vw" : props.width;
76
+ };
77
+ const onClose = () => {
78
+ if (axiosFileController.size() > 0) {
79
+ axiosFileController.removeAllPending();
80
+ }
81
+ emit("onClose");
82
+ };
83
+ </script>
84
+
85
+ <style scoped lang="scss">
86
+ @import "./index.scss";
87
+ </style>
@@ -0,0 +1,16 @@
1
+ .empty-box {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ justify-content: center;
6
+ width: 100%;
7
+ height: 100%;
8
+ p {
9
+ margin: 0;
10
+ margin-top: 9px;
11
+ font-size: 14px;
12
+ font-weight: 400;
13
+ line-height: 16px;
14
+ color: #999999;
15
+ }
16
+ }
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <div ref="emptyRef" class="empty-box flx-center" :style="{ width, height }">
3
+ <img src="../../assets/images/empty-img.png" alt="" :style="refSize" />
4
+ <p class="title">{{ title }}</p>
5
+ </div>
6
+ </template>
7
+ <script setup lang="ts">
8
+ const props = defineProps({
9
+ width: {
10
+ type: String,
11
+ default: () => "100%"
12
+ },
13
+ height: {
14
+ type: String,
15
+ default: () => "100%"
16
+ },
17
+ imgStyle: {
18
+ type: Object,
19
+ default: () => {}
20
+ },
21
+ title: {
22
+ type: String,
23
+ default: () => "暂无数据"
24
+ }
25
+ });
26
+ const emptyRef = ref();
27
+ const refSize = ref<Record<string, any>>({
28
+ height: "30%"
29
+ });
30
+ watchEffect(() => {
31
+ if (emptyRef.value) {
32
+ refSize.value =
33
+ emptyRef.value.offsetHeight > emptyRef.value.offsetWidth
34
+ ? { width: "30%" }
35
+ : {
36
+ height: "30%"
37
+ };
38
+ }
39
+ });
40
+ </script>
41
+ <style scoped lang="scss">
42
+ @import "./index.scss";
43
+ </style>
@@ -0,0 +1,68 @@
1
+ <template>
2
+ <div v-show="isShow" :style="style">
3
+ <slot></slot>
4
+ </div>
5
+ </template>
6
+ <script setup lang="ts" name="GridItem">
7
+ import { computed, inject, Ref, ref, useAttrs, watch } from "vue";
8
+ import { BreakPoint, Responsive } from "../interface/index";
9
+
10
+ type Props = {
11
+ offset?: number;
12
+ span?: number;
13
+ suffix?: boolean;
14
+ xs?: Responsive;
15
+ sm?: Responsive;
16
+ md?: Responsive;
17
+ lg?: Responsive;
18
+ xl?: Responsive;
19
+ };
20
+
21
+ const props = withDefaults(defineProps<Props>(), {
22
+ offset: 0,
23
+ span: 1,
24
+ suffix: false,
25
+ xs: undefined,
26
+ sm: undefined,
27
+ md: undefined,
28
+ lg: undefined,
29
+ xl: undefined
30
+ });
31
+
32
+ const attrs = useAttrs() as { index: string };
33
+ const isShow = ref(true);
34
+
35
+ // 注入断点
36
+ const breakPoint = inject<Ref<BreakPoint>>("breakPoint", ref("xl"));
37
+ const shouldHiddenIndex = inject<Ref<number>>("shouldHiddenIndex", ref(-1));
38
+ watch(
39
+ () => [shouldHiddenIndex.value, breakPoint.value],
40
+ n => {
41
+ if (!!attrs.index) {
42
+ isShow.value = !(n[0] !== -1 && parseInt(attrs.index) >= Number(n[0]));
43
+ }
44
+ },
45
+ { immediate: true }
46
+ );
47
+
48
+ const gap = inject("gap", 0);
49
+ const cols = inject("cols", ref(4));
50
+ const style = computed(() => {
51
+ let span = props[breakPoint.value]?.span ?? props.span;
52
+ let offset = props[breakPoint.value]?.offset ?? props.offset;
53
+ if (props.suffix) {
54
+ return {
55
+ gridColumnStart: cols.value - span - offset + 1,
56
+ gridColumnEnd: `span ${span + offset}`,
57
+ marginLeft: offset !== 0 ? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})` : "unset"
58
+ };
59
+ } else {
60
+ return {
61
+ gridColumn: `span ${span + offset > cols.value ? cols.value : span + offset}/span ${
62
+ span + offset > cols.value ? cols.value : span + offset
63
+ }`,
64
+ marginLeft: offset !== 0 ? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})` : "unset"
65
+ };
66
+ }
67
+ });
68
+ </script>
@@ -0,0 +1,167 @@
1
+ <template>
2
+ <div :style="style">
3
+ <slot></slot>
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts" name="Grid">
8
+ import {
9
+ ref,
10
+ watch,
11
+ useSlots,
12
+ computed,
13
+ provide,
14
+ onBeforeMount,
15
+ onMounted,
16
+ onUnmounted,
17
+ onDeactivated,
18
+ onActivated,
19
+ VNodeArrayChildren,
20
+ VNode
21
+ } from "vue";
22
+ import type { BreakPoint } from "./interface/index";
23
+
24
+ type Props = {
25
+ cols?: number | Record<BreakPoint, number>;
26
+ collapsed?: boolean;
27
+ collapsedRows?: number;
28
+ gap?: [number, number] | number;
29
+ };
30
+
31
+ const props = withDefaults(defineProps<Props>(), {
32
+ cols: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }),
33
+ collapsed: false,
34
+ collapsedRows: 1,
35
+ gap: 0
36
+ });
37
+
38
+ onBeforeMount(() => props.collapsed && findIndex());
39
+ onMounted(() => {
40
+ resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent);
41
+ window.addEventListener("resize", resize);
42
+ });
43
+ onActivated(() => {
44
+ resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent);
45
+ window.addEventListener("resize", resize);
46
+ });
47
+ onUnmounted(() => {
48
+ window.removeEventListener("resize", resize);
49
+ });
50
+ onDeactivated(() => {
51
+ window.removeEventListener("resize", resize);
52
+ });
53
+
54
+ // 监听屏幕变化
55
+ const resize = (e: UIEvent) => {
56
+ let width = (e.target as Window).innerWidth;
57
+ switch (!!width) {
58
+ case width < 768:
59
+ breakPoint.value = "xs";
60
+ break;
61
+ case width >= 768 && width < 992:
62
+ breakPoint.value = "sm";
63
+ break;
64
+ case width >= 992 && width < 1200:
65
+ breakPoint.value = "md";
66
+ break;
67
+ case width >= 1200 && width < 1920:
68
+ breakPoint.value = "lg";
69
+ break;
70
+ case width >= 1920:
71
+ breakPoint.value = "xl";
72
+ break;
73
+ }
74
+ };
75
+
76
+ // 注入 gap 间距
77
+ provide("gap", Array.isArray(props.gap) ? props.gap[0] : props.gap);
78
+
79
+ // 注入响应式断点
80
+ let breakPoint = ref<BreakPoint>("xl");
81
+ provide("breakPoint", breakPoint);
82
+
83
+ // 注入要开始折叠的 index
84
+ const hiddenIndex = ref(-1);
85
+ provide("shouldHiddenIndex", hiddenIndex);
86
+
87
+ // 注入 cols
88
+ const gridCols = computed(() => {
89
+ if (typeof props.cols === "object") return props.cols[breakPoint.value] ?? props.cols;
90
+ return props.cols;
91
+ });
92
+ provide("cols", gridCols);
93
+
94
+ // 寻找需要开始折叠的字段 index
95
+ const slots = useSlots().default!();
96
+
97
+ const findIndex = () => {
98
+ let fields: VNodeArrayChildren = [];
99
+ let suffix: VNode | null = null;
100
+ slots.forEach((slot: any) => {
101
+ // suffix
102
+ if (typeof slot.type === "object" && slot.type.name === "GridItem" && slot.props?.suffix !== undefined) suffix = slot;
103
+ // slot children
104
+ if (typeof slot.type === "symbol" && Array.isArray(slot.children)) fields.push(...slot.children);
105
+ });
106
+
107
+ // 计算 suffix 所占用的列
108
+ let suffixCols = 0;
109
+ if (suffix) {
110
+ suffixCols =
111
+ ((suffix as VNode).props![breakPoint.value]?.span ?? (suffix as VNode).props?.span ?? 1) +
112
+ ((suffix as VNode).props![breakPoint.value]?.offset ?? (suffix as VNode).props?.offset ?? 0);
113
+ }
114
+ try {
115
+ let find = false;
116
+ fields.reduce((prev = 0, current, index) => {
117
+ prev +=
118
+ ((current as VNode)!.props![breakPoint.value]?.span ?? (current as VNode)!.props?.span ?? 1) +
119
+ ((current as VNode)!.props![breakPoint.value]?.offset ?? (current as VNode)!.props?.offset ?? 0);
120
+ if (Number(prev) > props.collapsedRows * (gridCols.value as any) - suffixCols) {
121
+ hiddenIndex.value = index;
122
+ find = true;
123
+ throw "find it";
124
+ }
125
+ return prev;
126
+ }, 0);
127
+ if (!find) hiddenIndex.value = -1;
128
+ } catch (e) {
129
+ // console.warn(e);
130
+ }
131
+ };
132
+
133
+ // 断点变化时 执行 findIndex
134
+ watch(
135
+ () => breakPoint.value,
136
+ () => {
137
+ if (props.collapsed) findIndex();
138
+ }
139
+ );
140
+
141
+ // 监听 collapsed
142
+ watch(
143
+ () => props.collapsed,
144
+ value => {
145
+ if (value) return findIndex();
146
+ hiddenIndex.value = -1;
147
+ }
148
+ );
149
+
150
+ // 设置间距
151
+ const gridGap = computed(() => {
152
+ if (typeof props.gap === "number") return `${props.gap}px`;
153
+ if (Array.isArray(props.gap)) return `${props.gap[1]}px ${props.gap[0]}px`;
154
+ return "unset";
155
+ });
156
+
157
+ // 设置 style
158
+ const style = computed(() => {
159
+ return {
160
+ display: "grid",
161
+ gridGap: gridGap.value,
162
+ gridTemplateColumns: `repeat(${gridCols.value}, minmax(0, 1fr))`
163
+ };
164
+ });
165
+
166
+ defineExpose({ breakPoint });
167
+ </script>
@@ -0,0 +1,6 @@
1
+ export type BreakPoint = "xs" | "sm" | "md" | "lg" | "xl";
2
+
3
+ export type Responsive = {
4
+ span?: number;
5
+ offset?: number;
6
+ };