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.
- package/.editorconfig +15 -0
- package/.env +13 -0
- package/.env.development +20 -0
- package/.env.production +25 -0
- package/.env.test +25 -0
- package/.eslintignore +15 -0
- package/.eslintrc-auto-import.json +65 -0
- package/.eslintrc.cjs +66 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +5 -0
- package/.prettierignore +9 -0
- package/.prettierrc.cjs +41 -0
- package/.stylelintignore +4 -0
- package/.stylelintrc.cjs +40 -0
- package/.vscode/extensions.json +14 -0
- package/.vscode/settings.json +92 -0
- package/CHANGELOG.md +246 -0
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/auto-imports.d.ts +132 -0
- package/build/getEnv.ts +46 -0
- package/build/plugins.ts +138 -0
- package/build/proxy.ts +28 -0
- package/commitlint.config.cjs +84 -0
- package/components.d.ts +28 -0
- package/index.html +105 -0
- package/lint-staged.config.cjs +8 -0
- package/package.json +108 -0
- package/postcss.config.cjs +5 -0
- package/public/logo.png +0 -0
- package/src/App.vue +16 -0
- package/src/api/config/servicePort.ts +3 -0
- package/src/api/enums/httpEnum.ts +35 -0
- package/src/api/helper/axiosCancel.ts +50 -0
- package/src/api/helper/axiosFileCancel.ts +62 -0
- package/src/api/helper/checkStatus.ts +97 -0
- package/src/api/index.ts +142 -0
- package/src/api/interface/index.ts +27 -0
- package/src/assets/global-icons/icon-demo.svg +1 -0
- package/src/assets/iconfont/iconfont.scss +44 -0
- package/src/assets/iconfont/iconfont.ttf +0 -0
- package/src/assets/images/empty-img.png +0 -0
- package/src/assets/option-icons/add.svg +1 -0
- package/src/assets/option-icons/check.svg +1 -0
- package/src/assets/option-icons/config.svg +1 -0
- package/src/assets/option-icons/del.svg +1 -0
- package/src/assets/option-icons/detail.svg +1 -0
- package/src/assets/option-icons/edit.svg +2 -0
- package/src/assets/option-icons/export.svg +1 -0
- package/src/assets/option-icons/folder.svg +1 -0
- package/src/assets/option-icons/full-screen.svg +1 -0
- package/src/assets/option-icons/more.svg +1 -0
- package/src/assets/option-icons/return.svg +1 -0
- package/src/assets/option-icons/search.svg +1 -0
- package/src/components/CSvgIcon/index.scss +23 -0
- package/src/components/CSvgIcon/index.vue +74 -0
- package/src/components/CTree/index.scss +48 -0
- package/src/components/CTree/index.vue +176 -0
- package/src/components/CTree/interface/index.ts +6 -0
- package/src/components/Dialog/index.scss +74 -0
- package/src/components/Dialog/index.vue +87 -0
- package/src/components/Empty/index.scss +16 -0
- package/src/components/Empty/index.vue +43 -0
- package/src/components/Grid/components/GridItem.vue +68 -0
- package/src/components/Grid/index.vue +167 -0
- package/src/components/Grid/interface/index.ts +6 -0
- package/src/components/Loading/index.scss +67 -0
- package/src/components/Loading/index.vue +13 -0
- package/src/components/Overflow/index.vue +76 -0
- package/src/components/ProTable/components/ColSetting.vue +45 -0
- package/src/components/ProTable/components/Pagination.vue +29 -0
- package/src/components/ProTable/components/SearchForm/components/SearchFormItem.vue +93 -0
- package/src/components/ProTable/components/SearchForm/index.scss +12 -0
- package/src/components/ProTable/components/SearchForm/index.vue +91 -0
- package/src/components/ProTable/components/TableColumn.vue +56 -0
- package/src/components/ProTable/index.scss +14 -0
- package/src/components/ProTable/index.vue +274 -0
- package/src/components/ProTable/interface/index.ts +86 -0
- package/src/components/ScrollBox/index.vue +44 -0
- package/src/components/SvgIcon/index.vue +20 -0
- package/src/components/Tabs/index.vue +142 -0
- package/src/config/index.ts +12 -0
- package/src/config/nprogress.ts +12 -0
- package/src/config/piniaPersist.ts +19 -0
- package/src/config/serviceLoading.ts +45 -0
- package/src/enums/httpEnum.ts +24 -0
- package/src/hooks/interface/index.ts +32 -0
- package/src/hooks/useOnline.ts +27 -0
- package/src/hooks/useTable.ts +167 -0
- package/src/main.ts +35 -0
- package/src/plugins/button/button.vue +56 -0
- package/src/plugins/index.js +26 -0
- package/src/plugins/index.vue +24 -0
- package/src/plugins/msg/msg.vue +56 -0
- package/src/stores/commonStore.ts +9 -0
- package/src/stores/index.ts +8 -0
- package/src/stores/interface/index.ts +1 -0
- package/src/styles/common.scss +87 -0
- package/src/styles/element.scss +233 -0
- package/src/styles/reset.scss +20 -0
- package/src/styles/var.scss +2 -0
- package/src/typings/global.d.ts +37 -0
- package/src/typings/window.d.ts +8 -0
- package/src/utils/color.ts +59 -0
- package/src/utils/commonUtils.ts +175 -0
- package/src/utils/errorHandler.ts +28 -0
- package/src/utils/is.ts +147 -0
- package/src/utils/layoutUtils.ts +83 -0
- package/src/utils/localUtils.ts +42 -0
- package/src/utils/serviceDict.ts +9 -0
- package/src/vite-env.d.ts +6 -0
- package/tsconfig.json +50 -0
- package/vite.config.ts +76 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
<!-- 📚📚📚 Pro-Table 文档: https://juejin.cn/post/7166068828202336263 -->
|
|
2
|
+
|
|
3
|
+
<template>
|
|
4
|
+
<div class="pro-table-box">
|
|
5
|
+
<!-- 查询表单 card -->
|
|
6
|
+
<SearchForm
|
|
7
|
+
v-show="isShowSearch"
|
|
8
|
+
:search="search"
|
|
9
|
+
:reset="reset"
|
|
10
|
+
:columns="searchColumns"
|
|
11
|
+
:search-param="searchParam"
|
|
12
|
+
:search-col="searchCol"
|
|
13
|
+
:use-card="useCard"
|
|
14
|
+
/>
|
|
15
|
+
|
|
16
|
+
<!-- 表格内容 card -->
|
|
17
|
+
<div class="table-main" :class="{ card: useCard }">
|
|
18
|
+
<!-- 表格头部 操作按钮 -->
|
|
19
|
+
<div class="table-header">
|
|
20
|
+
<div class="header-button-lf">
|
|
21
|
+
<slot name="tableHeader" :selected-list-ids="selectedListIds" :selected-list="selectedList" :is-selected="isSelected" />
|
|
22
|
+
</div>
|
|
23
|
+
<div v-if="toolButton && !useCustomTable" class="header-button-ri">
|
|
24
|
+
<slot name="toolButton">
|
|
25
|
+
<el-button :icon="Refresh" circle @click="getTableList" />
|
|
26
|
+
<el-button v-if="columns.length" :icon="Operation" circle @click="openColSetting" />
|
|
27
|
+
<el-button v-if="searchColumns.length" :icon="Search" circle @click="isShowSearch = !isShowSearch" />
|
|
28
|
+
</slot>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- 表格主体 -->
|
|
33
|
+
<el-table
|
|
34
|
+
v-if="!useCustomTable"
|
|
35
|
+
ref="tableRef"
|
|
36
|
+
v-bind="$attrs"
|
|
37
|
+
:data="data ?? tableData"
|
|
38
|
+
:border="border"
|
|
39
|
+
:row-key="rowKey"
|
|
40
|
+
:tree-props="treeProps"
|
|
41
|
+
@row-click="rowClick"
|
|
42
|
+
@selection-change="selectionChange"
|
|
43
|
+
:highlight-current-row="highlightCurrentRow || typeof currentChange === 'function'"
|
|
44
|
+
@current-change="currentChange"
|
|
45
|
+
>
|
|
46
|
+
<!-- 默认插槽 -->
|
|
47
|
+
<slot></slot>
|
|
48
|
+
<template v-for="item in tableColumns" :key="item">
|
|
49
|
+
<!-- selection || index || expand -->
|
|
50
|
+
<el-table-column
|
|
51
|
+
v-if="item.type && ['selection', 'index', 'expand'].includes(item.type) && item.isShow"
|
|
52
|
+
v-bind="item"
|
|
53
|
+
:align="item.align ?? 'center'"
|
|
54
|
+
:reserve-selection="item.type == 'selection'"
|
|
55
|
+
>
|
|
56
|
+
<!-- 拓展插槽 -->
|
|
57
|
+
<template v-if="item.type == 'expand'" #default="scope">
|
|
58
|
+
<component :is="item.render" v-bind="scope" v-if="item.render"> </component>
|
|
59
|
+
<slot v-else :name="item.type" v-bind="scope"></slot>
|
|
60
|
+
</template>
|
|
61
|
+
</el-table-column>
|
|
62
|
+
<!-- 其他插槽 -->
|
|
63
|
+
<TableColumn v-if="!item.type && item.prop && item.isShow" :column="item">
|
|
64
|
+
<template v-for="slot in Object.keys($slots)" #[slot]="scope">
|
|
65
|
+
<slot :name="slot" v-bind="scope"></slot>
|
|
66
|
+
</template>
|
|
67
|
+
</TableColumn>
|
|
68
|
+
</template>
|
|
69
|
+
<!-- 插入表格最后一行之后的插槽 -->
|
|
70
|
+
<template #append>
|
|
71
|
+
<slot name="append"> </slot>
|
|
72
|
+
</template>
|
|
73
|
+
<!-- 无数据 -->
|
|
74
|
+
<template #empty>
|
|
75
|
+
<div class="table-empty">
|
|
76
|
+
<empty />
|
|
77
|
+
</div>
|
|
78
|
+
</template>
|
|
79
|
+
</el-table>
|
|
80
|
+
<!-- 自定义表格 -->
|
|
81
|
+
<div v-else class="el-table">
|
|
82
|
+
<el-scrollbar v-if="data?.length ?? tableData?.length">
|
|
83
|
+
<slot :data="data ?? tableData" />
|
|
84
|
+
</el-scrollbar>
|
|
85
|
+
<empty v-else />
|
|
86
|
+
</div>
|
|
87
|
+
<!-- 分页组件 -->
|
|
88
|
+
<slot name="pagination">
|
|
89
|
+
<Pagination
|
|
90
|
+
v-if="pagination"
|
|
91
|
+
:pageable="pageable"
|
|
92
|
+
:handle-size-change="handleSizeChange"
|
|
93
|
+
:handle-current-change="handleCurrentChange"
|
|
94
|
+
/>
|
|
95
|
+
</slot>
|
|
96
|
+
</div>
|
|
97
|
+
<!-- 列设置 -->
|
|
98
|
+
<ColSetting v-if="toolButton" ref="colRef" v-model:col-setting="colSetting" />
|
|
99
|
+
</div>
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
<script setup lang="ts" name="ProTable">
|
|
103
|
+
import { ref, watch, computed, provide, onMounted } from "vue";
|
|
104
|
+
import { ElTable } from "element-plus";
|
|
105
|
+
import { useTable } from "@/hooks/useTable";
|
|
106
|
+
import { useSelection } from "@/hooks/useSelection";
|
|
107
|
+
import { BreakPoint } from "@/components/Grid/interface";
|
|
108
|
+
import { ColumnProps, tableTreeProps } from "@/components/ProTable/interface";
|
|
109
|
+
import { fetchType as myFetchType } from "@/api/interface/index";
|
|
110
|
+
import { Refresh, Operation, Search } from "@element-plus/icons-vue";
|
|
111
|
+
import { filterEnum, formatValue, handleProp, handleRowAccordingToProp } from "@/utils/layoutUtils";
|
|
112
|
+
import SearchForm from "./components/SearchForm/index.vue";
|
|
113
|
+
import Pagination from "./components/Pagination.vue";
|
|
114
|
+
import ColSetting from "./components/ColSetting.vue";
|
|
115
|
+
import TableColumn from "./components/TableColumn.vue";
|
|
116
|
+
|
|
117
|
+
export interface ProTableProps {
|
|
118
|
+
useCustomTable?: boolean; // 是否使用表格 如果为true 则使用插槽的方式传入自定义表格渲染方式
|
|
119
|
+
useCard?: boolean; // 是否启用卡片样式
|
|
120
|
+
columns: ColumnProps[]; // 列配置项 ==> 必传
|
|
121
|
+
data?: any[]; // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传
|
|
122
|
+
requestApi?: string | ((params: any) => Promise<any>); // 请求表格数据的 api ==> 非必传
|
|
123
|
+
requestAuto?: boolean; // 是否自动执行请求 api ==> 非必传(默认为true)
|
|
124
|
+
requestError?: (params: any) => void; // 表格 api 请求错误监听 ==> 非必传
|
|
125
|
+
dataCallback?: (data: any) => any; // 返回数据的回调函数,可以对数据进行处理 ==> 非必传
|
|
126
|
+
rowClick?: (data: any) => any; // 当某一行被点击时会触发该事件 ==> 非必传
|
|
127
|
+
highlightCurrentRow?: boolean; // 是否高亮当前行 ==> 非必传
|
|
128
|
+
currentChange?: (data: any) => any; // 当表格的当前行发生变化的时候会触发该事件 ==> 非必传
|
|
129
|
+
title?: string; // 表格标题,目前只在打印的时候用到 ==> 非必传
|
|
130
|
+
pagination?: boolean; // 是否需要分页组件 ==> 非必传(默认为true)
|
|
131
|
+
tableLimit?: number; //数据每页个数
|
|
132
|
+
initParam?: any; // 初始化请求参数 ==> 非必传(默认为{})
|
|
133
|
+
border?: boolean; // 是否带有纵向边框 ==> 非必传(默认为true)
|
|
134
|
+
toolButton?: boolean; // 是否显示表格功能按钮 ==> 非必传(默认为true)
|
|
135
|
+
rowKey?: string; // 行数据的 Key,用来优化 Table 的渲染,当表格数据多选时,所指定的 id ==> 非必传(默认为 id)
|
|
136
|
+
searchCol?: number | Record<BreakPoint, number>; // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }
|
|
137
|
+
treeProps?: tableTreeProps; //渲染嵌套数据的配置选项
|
|
138
|
+
fetchType?: myFetchType;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 接受父组件参数,配置默认值
|
|
142
|
+
const props = withDefaults(defineProps<ProTableProps>(), {
|
|
143
|
+
useCustomTable: false,
|
|
144
|
+
useCard: true,
|
|
145
|
+
columns: () => [],
|
|
146
|
+
requestAuto: true,
|
|
147
|
+
pagination: true,
|
|
148
|
+
tableLimit: 10,
|
|
149
|
+
initParam: {},
|
|
150
|
+
border: false,
|
|
151
|
+
toolButton: true,
|
|
152
|
+
rowKey: "id",
|
|
153
|
+
searchCol: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }),
|
|
154
|
+
treeProps: () => ({ hasChildren: "hasChildren", children: "children" }),
|
|
155
|
+
fetchType: "post"
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// 是否显示搜索模块
|
|
159
|
+
const isShowSearch = ref(true);
|
|
160
|
+
|
|
161
|
+
// 表格 DOM 元素
|
|
162
|
+
const tableRef = ref<InstanceType<typeof ElTable>>();
|
|
163
|
+
|
|
164
|
+
// 表格多选 Hooks 多选框
|
|
165
|
+
const { selectionChange, selectedList, selectedListIds, isSelected } = useSelection(props.rowKey);
|
|
166
|
+
|
|
167
|
+
// 清空选中数据列表
|
|
168
|
+
const clearSelection = () => tableRef.value!.clearSelection();
|
|
169
|
+
|
|
170
|
+
// 初始化请求
|
|
171
|
+
onMounted(() => props.requestAuto && getTableList());
|
|
172
|
+
|
|
173
|
+
// 接收 columns 并设置为响应式
|
|
174
|
+
const tableColumns = ref<ColumnProps[]>(props.columns);
|
|
175
|
+
|
|
176
|
+
// 定义 enumMap 存储 enum 值(避免异步请求无法格式化单元格内容 || 无法填充搜索下拉选择)
|
|
177
|
+
const enumMap = ref(new Map<string, { [key: string]: any }[]>());
|
|
178
|
+
provide("enumMap", enumMap);
|
|
179
|
+
const setEnumMap = async (col: ColumnProps) => {
|
|
180
|
+
if (!col.enum) return;
|
|
181
|
+
// 如果当前 enum 为后台数据需要请求数据,则调用该请求接口,并存储到 enumMap
|
|
182
|
+
if (typeof col.enum !== "function") return enumMap.value.set(col.prop!, col.enum!);
|
|
183
|
+
const res = await col.enum();
|
|
184
|
+
enumMap.value.set(col.prop!, res);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// 扁平化 columns
|
|
188
|
+
const flatColumnsFunc = (columns: ColumnProps[], flatArr: ColumnProps[] = []) => {
|
|
189
|
+
columns.forEach(async col => {
|
|
190
|
+
if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children));
|
|
191
|
+
flatArr.push(col);
|
|
192
|
+
// 给每一项 column 添加 isShow && isFilterEnum 默认属性
|
|
193
|
+
col.isShow = col.isShow ?? true;
|
|
194
|
+
col.isFilterEnum = col.isFilterEnum ?? true;
|
|
195
|
+
// 设置 enumMap
|
|
196
|
+
setEnumMap(col);
|
|
197
|
+
});
|
|
198
|
+
return flatArr.filter(item => !item._children?.length);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// flatColumns
|
|
202
|
+
const flatColumns = ref<ColumnProps[]>();
|
|
203
|
+
flatColumns.value = flatColumnsFunc(tableColumns.value);
|
|
204
|
+
|
|
205
|
+
// 过滤需要搜索的配置项
|
|
206
|
+
const searchColumns = flatColumns.value.filter(item => item.search?.el || item.search?.render);
|
|
207
|
+
|
|
208
|
+
// 设置搜索表单排序默认值 && 设置搜索表单项的默认值
|
|
209
|
+
searchColumns.forEach((column, index) => {
|
|
210
|
+
column.search!.order = column.search!.order ?? index + 2;
|
|
211
|
+
if (column.search?.defaultValue !== undefined && column.search?.defaultValue !== null) {
|
|
212
|
+
searchInitParam.value[column.search.key ?? handleProp(column.prop!)] = column.search?.defaultValue;
|
|
213
|
+
searchParam.value[column.search.key ?? handleProp(column.prop!)] = column.search?.defaultValue;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// 排序搜索表单项
|
|
218
|
+
searchColumns.sort((a, b) => a.search!.order! - b.search!.order!);
|
|
219
|
+
|
|
220
|
+
// 列设置 ==> 过滤掉不需要设置的列
|
|
221
|
+
const colRef = ref();
|
|
222
|
+
const colSetting = tableColumns.value!.filter(
|
|
223
|
+
item => !["selection", "index", "expand"].includes(item.type!) && item.prop !== "operation" && item.isShow
|
|
224
|
+
);
|
|
225
|
+
const openColSetting = () => colRef.value.openColSetting();
|
|
226
|
+
|
|
227
|
+
// 表格操作 Hooks
|
|
228
|
+
const {
|
|
229
|
+
tableData,
|
|
230
|
+
pageable,
|
|
231
|
+
searchParam,
|
|
232
|
+
searchInitParam,
|
|
233
|
+
getTableList,
|
|
234
|
+
search,
|
|
235
|
+
reset,
|
|
236
|
+
handleSizeChange,
|
|
237
|
+
handleCurrentChange,
|
|
238
|
+
totalParam
|
|
239
|
+
} = useTable(
|
|
240
|
+
props.initParam,
|
|
241
|
+
props.pagination,
|
|
242
|
+
props.fetchType,
|
|
243
|
+
props.tableLimit,
|
|
244
|
+
props.requestApi,
|
|
245
|
+
props.dataCallback,
|
|
246
|
+
props.requestError
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
// 监听页面 initParam 改化,重新获取表格数据
|
|
250
|
+
watch([() => props.initParam], getTableList, { deep: true });
|
|
251
|
+
|
|
252
|
+
// 暴露给父组件的参数和方法(外部需要什么,都可以从这里暴露出去)
|
|
253
|
+
defineExpose({
|
|
254
|
+
element: tableRef,
|
|
255
|
+
tableData,
|
|
256
|
+
pageable,
|
|
257
|
+
searchParam,
|
|
258
|
+
searchInitParam,
|
|
259
|
+
getTableList,
|
|
260
|
+
search,
|
|
261
|
+
reset,
|
|
262
|
+
handleSizeChange,
|
|
263
|
+
handleCurrentChange,
|
|
264
|
+
clearSelection,
|
|
265
|
+
enumMap,
|
|
266
|
+
isSelected,
|
|
267
|
+
selectedList,
|
|
268
|
+
selectedListIds,
|
|
269
|
+
totalParam
|
|
270
|
+
});
|
|
271
|
+
</script>
|
|
272
|
+
<style lang="scss" scoped>
|
|
273
|
+
@import "./index.scss";
|
|
274
|
+
</style>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { VNode, ComponentPublicInstance } from "vue";
|
|
2
|
+
import { BreakPoint, Responsive } from "@/components/Grid/interface";
|
|
3
|
+
import { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
|
|
4
|
+
import { ProTableProps } from "@/components/ProTable/index.vue";
|
|
5
|
+
import ProTable from "@/components/ProTable/index.vue";
|
|
6
|
+
|
|
7
|
+
export interface tableTreeProps {
|
|
8
|
+
hasChildren: string;
|
|
9
|
+
children: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface EnumProps {
|
|
13
|
+
label?: string; // 选项框显示的文字
|
|
14
|
+
value?: string | number | boolean | any[]; // 选项框值
|
|
15
|
+
disabled?: boolean; // 是否禁用此选项
|
|
16
|
+
tagType?: string; // 当 tag 为 true 时,此选择会指定 tag 显示类型
|
|
17
|
+
children?: EnumProps[]; // 为树形选择时,可以通过 children 属性指定子选项
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type TypeProps = "index" | "selection" | "expand";
|
|
22
|
+
|
|
23
|
+
export type SearchType =
|
|
24
|
+
| "input"
|
|
25
|
+
| "input-number"
|
|
26
|
+
| "select"
|
|
27
|
+
| "select-v2"
|
|
28
|
+
| "tree-select"
|
|
29
|
+
| "cascader"
|
|
30
|
+
| "date-picker"
|
|
31
|
+
| "time-picker"
|
|
32
|
+
| "time-select"
|
|
33
|
+
| "switch"
|
|
34
|
+
| "slider";
|
|
35
|
+
|
|
36
|
+
export type SearchRenderScope = {
|
|
37
|
+
searchParam: { [key: string]: any };
|
|
38
|
+
placeholder: string;
|
|
39
|
+
clearable: boolean;
|
|
40
|
+
options: EnumProps[];
|
|
41
|
+
data: EnumProps[];
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type SearchProps = {
|
|
45
|
+
el?: SearchType; // 当前项搜索框的类型
|
|
46
|
+
props?: any; // 搜索项参数,根据 element plus 官方文档来传递,该属性所有值会透传到组件
|
|
47
|
+
key?: string; // 当搜索项 key 不为 prop 属性时,可通过 key 指定
|
|
48
|
+
order?: number; // 搜索项排序(从大到小)
|
|
49
|
+
span?: number; // 搜索项所占用的列数,默认为1列
|
|
50
|
+
offset?: number; // 搜索字段左侧偏移列数
|
|
51
|
+
defaultValue?: string | number | boolean | any[]; // 搜索项默认值
|
|
52
|
+
render?: (scope: SearchRenderScope) => VNode; // 自定义搜索内容渲染(tsx语法)
|
|
53
|
+
} & Partial<Record<BreakPoint, Responsive>>;
|
|
54
|
+
|
|
55
|
+
export type FieldNamesProps = {
|
|
56
|
+
label: string;
|
|
57
|
+
value: string;
|
|
58
|
+
children?: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type RenderScope<T> = {
|
|
62
|
+
row: T;
|
|
63
|
+
$index: number;
|
|
64
|
+
column: TableColumnCtx<T>;
|
|
65
|
+
[key: string]: any;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type HeaderRenderScope<T> = {
|
|
69
|
+
$index: number;
|
|
70
|
+
column: TableColumnCtx<T>;
|
|
71
|
+
[key: string]: any;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export interface ColumnProps<T = any> extends Partial<Omit<TableColumnCtx<T>, "children" | "renderCell" | "renderHeader">> {
|
|
75
|
+
tag?: boolean; // 是否是标签展示
|
|
76
|
+
isShow?: boolean; // 是否显示在表格当中
|
|
77
|
+
search?: SearchProps | undefined; // 搜索项配置
|
|
78
|
+
enum?: EnumProps[] | ((params?: any) => Promise<any>); // 枚举类型(字典)
|
|
79
|
+
isFilterEnum?: boolean; // 当前单元格值是否根据 enum 格式化(示例:enum 只作为搜索项数据)
|
|
80
|
+
fieldNames?: FieldNamesProps; // 指定 label && value && children 的 key 值
|
|
81
|
+
headerRender?: (scope: HeaderRenderScope<T>) => VNode; // 自定义表头内容渲染(tsx语法)
|
|
82
|
+
render?: (scope: RenderScope<T>) => VNode | string; // 自定义单元格内容渲染(tsx语法)
|
|
83
|
+
_children?: ColumnProps<T>[]; // 多级表头
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type ProTableInstance = Omit<InstanceType<typeof ProTable>, keyof ComponentPublicInstance | keyof ProTableProps>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="scroll-box" :style="{ width: `calc(100% + ${rightDistance})` }">
|
|
3
|
+
<el-scrollbar
|
|
4
|
+
ref="scrollRef"
|
|
5
|
+
:view-style="{
|
|
6
|
+
paddingRight: rightDistance
|
|
7
|
+
}"
|
|
8
|
+
:height="height"
|
|
9
|
+
:always="always"
|
|
10
|
+
>
|
|
11
|
+
<slot />
|
|
12
|
+
</el-scrollbar>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
height: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: () => "100%"
|
|
21
|
+
},
|
|
22
|
+
maxHeight: {
|
|
23
|
+
type: String,
|
|
24
|
+
default: () => "100%"
|
|
25
|
+
},
|
|
26
|
+
rightDistance: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: () => "0px"
|
|
29
|
+
},
|
|
30
|
+
always: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: () => false
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const scrollRef = ref();
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<style lang="scss" scoped>
|
|
39
|
+
.scroll-box {
|
|
40
|
+
position: relative;
|
|
41
|
+
width: 100%;
|
|
42
|
+
height: 100%;
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg class="icon" :style="iconStyle" aria-hidden="true">
|
|
3
|
+
<use :xlink:href="symbolId" />
|
|
4
|
+
</svg>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts" name="SvgIcon">
|
|
8
|
+
import { computed, CSSProperties } from "vue";
|
|
9
|
+
interface SvgProps {
|
|
10
|
+
name: string; // 图标的名称 ==> 必传
|
|
11
|
+
prefix?: string; // 图标的前缀 ==> 非必传(默认为"icon")
|
|
12
|
+
iconStyle?: CSSProperties; // 图标的样式 ==> 非必传
|
|
13
|
+
}
|
|
14
|
+
const props = withDefaults(defineProps<SvgProps>(), {
|
|
15
|
+
prefix: "icon",
|
|
16
|
+
iconStyle: () => ({ width: "1em", height: "1em" })
|
|
17
|
+
});
|
|
18
|
+
const symbolId = computed(() => `#${props.prefix}-${props.name}`);
|
|
19
|
+
</script>
|
|
20
|
+
<style></style>
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="{ 'full-box': full, tab: true }">
|
|
3
|
+
<el-tabs
|
|
4
|
+
v-if="tabPans?.length"
|
|
5
|
+
ref="tabsRef"
|
|
6
|
+
:model-value="activeName"
|
|
7
|
+
@tab-click="onTabClick"
|
|
8
|
+
@tab-change="onTabChange"
|
|
9
|
+
:before-leave="onBeforeLeave"
|
|
10
|
+
@tab-remove="onRemoveTab"
|
|
11
|
+
v-bind="config"
|
|
12
|
+
>
|
|
13
|
+
<div class="tab-pane-class" style="width: 100%; height: 100%">
|
|
14
|
+
<el-tab-pane
|
|
15
|
+
v-for="(item, index) in tabPans as any"
|
|
16
|
+
:key="index"
|
|
17
|
+
:lazy="lazy"
|
|
18
|
+
:label="item[prop.label]"
|
|
19
|
+
:name="item[prop.name] ?? index"
|
|
20
|
+
:disabled="item.disabled"
|
|
21
|
+
>
|
|
22
|
+
<template #label v-if="item?.labelRender || item?.useLabel">
|
|
23
|
+
<component v-if="item?.labelRender" :is="item?.labelRender" v-bind="item"> </component>
|
|
24
|
+
<slot v-if="item?.useLabel" :label="item[prop.label]" :pane-name="item[prop.name] ?? index" :item="item" />
|
|
25
|
+
</template>
|
|
26
|
+
<slot
|
|
27
|
+
v-if="[item[prop.name], index].includes(activeName)"
|
|
28
|
+
:item="item"
|
|
29
|
+
:index="index"
|
|
30
|
+
:key="item[prop.name] ?? index"
|
|
31
|
+
/>
|
|
32
|
+
</el-tab-pane>
|
|
33
|
+
</div>
|
|
34
|
+
</el-tabs>
|
|
35
|
+
<empty v-else />
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
const props = defineProps({
|
|
40
|
+
full: {
|
|
41
|
+
type: Boolean,
|
|
42
|
+
default: () => true
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
tabPans: {
|
|
46
|
+
type: Array,
|
|
47
|
+
required: true
|
|
48
|
+
},
|
|
49
|
+
activeName: {
|
|
50
|
+
type: [Number, String],
|
|
51
|
+
default: () => 0
|
|
52
|
+
},
|
|
53
|
+
config: {
|
|
54
|
+
type: Object,
|
|
55
|
+
default: () => {}
|
|
56
|
+
},
|
|
57
|
+
// 配置选项
|
|
58
|
+
prop: {
|
|
59
|
+
type: Object,
|
|
60
|
+
default: () => {
|
|
61
|
+
return {
|
|
62
|
+
label: "name",
|
|
63
|
+
name: "id"
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// 标签是否延迟渲染
|
|
69
|
+
lazy: {
|
|
70
|
+
type: Boolean,
|
|
71
|
+
default: () => true
|
|
72
|
+
},
|
|
73
|
+
beforeLeave: {
|
|
74
|
+
type: Function,
|
|
75
|
+
default: () => {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const emit = defineEmits(["onTabClick", "onTabChange", "onRemoveTab"]);
|
|
81
|
+
|
|
82
|
+
const onBeforeLeave = async (activeName: any, oldActiveName: any) => {
|
|
83
|
+
return await props.beforeLeave(activeName, oldActiveName);
|
|
84
|
+
};
|
|
85
|
+
const onTabClick = (pane: any, e: any) => {
|
|
86
|
+
emit("onTabClick", pane.props.name, pane, e);
|
|
87
|
+
};
|
|
88
|
+
const onTabChange = (name: any) => {
|
|
89
|
+
emit("onTabChange", name);
|
|
90
|
+
};
|
|
91
|
+
const onRemoveTab = (name: any) => {
|
|
92
|
+
emit("onRemoveTab", name);
|
|
93
|
+
};
|
|
94
|
+
const tabsRef = ref();
|
|
95
|
+
defineExpose({ tabsRef });
|
|
96
|
+
</script>
|
|
97
|
+
<style lang="scss" scoped>
|
|
98
|
+
.tab {
|
|
99
|
+
:deep(.el-tabs) {
|
|
100
|
+
.el-tabs__nav {
|
|
101
|
+
.el-tabs__item {
|
|
102
|
+
font-size: 14px;
|
|
103
|
+
font-weight: 500;
|
|
104
|
+
color: #8c9094;
|
|
105
|
+
}
|
|
106
|
+
.is-active {
|
|
107
|
+
font-family: MiSans-Regular, MiSans;
|
|
108
|
+
font-size: 14px;
|
|
109
|
+
font-weight: 400;
|
|
110
|
+
color: $primary-color;
|
|
111
|
+
}
|
|
112
|
+
.el-tabs__active-bar {
|
|
113
|
+
background-color: $primary-color;
|
|
114
|
+
}
|
|
115
|
+
.is-disabled {
|
|
116
|
+
color: #d7d7d7;
|
|
117
|
+
cursor: not-allowed;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
.el-tabs__nav-wrap::after {
|
|
121
|
+
display: none;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
.full-box {
|
|
126
|
+
height: 100%;
|
|
127
|
+
:deep(.el-tabs) {
|
|
128
|
+
display: flex;
|
|
129
|
+
flex-direction: column;
|
|
130
|
+
height: 100%;
|
|
131
|
+
.el-tabs__content {
|
|
132
|
+
flex: 1;
|
|
133
|
+
.el-tab-pane {
|
|
134
|
+
height: 100%;
|
|
135
|
+
.main {
|
|
136
|
+
height: 100%;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// ? 全局默认配置项
|
|
2
|
+
// 项目名称区分 智能建造基础版
|
|
3
|
+
export const DEMO_KEY: string = import.meta.env.VITE_GLOB_DEMO_KEY as string;
|
|
4
|
+
export const TITLE: string = import.meta.env.VITE_GLOB_APP_TITLE as string;
|
|
5
|
+
|
|
6
|
+
// 默认主题颜色
|
|
7
|
+
export const DEFAULT_PRIMARY: string = "#00A5A7";
|
|
8
|
+
|
|
9
|
+
// 路由白名单地址(必须是本地存在的路由 staticRouter.ts 中)
|
|
10
|
+
export const ROUTER_WHITE_LIST: string[] = ["/500"];
|
|
11
|
+
|
|
12
|
+
export const USE_LOCAL_FILE_ROUTER = true;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import NProgress from "nprogress";
|
|
2
|
+
import "nprogress/nprogress.css";
|
|
3
|
+
|
|
4
|
+
NProgress.configure({
|
|
5
|
+
easing: "ease", // 动画方式
|
|
6
|
+
speed: 500, // 递增进度条的速度
|
|
7
|
+
showSpinner: true, // 是否显示加载ico
|
|
8
|
+
trickleSpeed: 200, // 自动递增间隔
|
|
9
|
+
minimum: 0.3 // 初始化时的最小百分比
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export default NProgress;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PersistedStateOptions } from "pinia-plugin-persistedstate";
|
|
2
|
+
import { DEMO_KEY } from "./index";
|
|
3
|
+
/**
|
|
4
|
+
* @description pinia 持久化参数配置
|
|
5
|
+
* @param {String} key 存储到持久化的 name
|
|
6
|
+
* @param {Array} paths 需要持久化的 state name
|
|
7
|
+
* @return persist
|
|
8
|
+
* */
|
|
9
|
+
const piniaPersistConfig = (key: string, paths?: string[]) => {
|
|
10
|
+
const persist: PersistedStateOptions = {
|
|
11
|
+
key: DEMO_KEY + key,
|
|
12
|
+
storage: localStorage,
|
|
13
|
+
// storage: sessionStorage,
|
|
14
|
+
paths
|
|
15
|
+
};
|
|
16
|
+
return persist;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default piniaPersistConfig;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ElLoading } from "element-plus";
|
|
2
|
+
|
|
3
|
+
/* 全局请求 loading */
|
|
4
|
+
let loadingInstance: ReturnType<typeof ElLoading.service>;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @description 开启 Loading
|
|
8
|
+
* */
|
|
9
|
+
const startLoading = () => {
|
|
10
|
+
loadingInstance = ElLoading.service({
|
|
11
|
+
fullscreen: true,
|
|
12
|
+
lock: true,
|
|
13
|
+
text: "Loading",
|
|
14
|
+
background: "rgba(0, 0, 0, 0.7)"
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @description 结束 Loading
|
|
20
|
+
* */
|
|
21
|
+
const endLoading = () => {
|
|
22
|
+
loadingInstance.close();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @description 显示全屏加载
|
|
27
|
+
* */
|
|
28
|
+
let needLoadingRequestCount = 0;
|
|
29
|
+
export const showFullScreenLoading = () => {
|
|
30
|
+
if (needLoadingRequestCount === 0) {
|
|
31
|
+
startLoading();
|
|
32
|
+
}
|
|
33
|
+
needLoadingRequestCount++;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @description 隐藏全屏加载
|
|
38
|
+
* */
|
|
39
|
+
export const tryHideFullScreenLoading = () => {
|
|
40
|
+
if (needLoadingRequestCount <= 0) return;
|
|
41
|
+
needLoadingRequestCount--;
|
|
42
|
+
if (needLoadingRequestCount === 0) {
|
|
43
|
+
endLoading();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description:请求方法
|
|
3
|
+
*/
|
|
4
|
+
export enum RequestEnum {
|
|
5
|
+
GET = "GET",
|
|
6
|
+
POST = "POST",
|
|
7
|
+
PATCH = "PATCH",
|
|
8
|
+
PUT = "PUT",
|
|
9
|
+
DELETE = "DELETE"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @description:常用的 contentTyp 类型
|
|
14
|
+
*/
|
|
15
|
+
export enum ContentTypeEnum {
|
|
16
|
+
// json
|
|
17
|
+
JSON = "application/json;charset=UTF-8",
|
|
18
|
+
// text
|
|
19
|
+
TEXT = "text/plain;charset=UTF-8",
|
|
20
|
+
// form-data 一般配合qs
|
|
21
|
+
FORM_URLENCODED = "application/x-www-form-urlencoded;charset=UTF-8",
|
|
22
|
+
// form-data 上传
|
|
23
|
+
FORM_DATA = "multipart/form-data;charset=UTF-8"
|
|
24
|
+
}
|