Skip to content

Commit 35d2274

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 5d169ef + 812b7ab commit 35d2274

File tree

15 files changed

+132
-45
lines changed

15 files changed

+132
-45
lines changed

Diff for: package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"color": "^4.2.3",
5454
"echarts": "^5.5.0",
5555
"element-plus": "^2.7.3",
56+
"exceljs": "^4.4.0",
5657
"lodash-es": "^4.17.21",
5758
"net": "^1.0.2",
5859
"nprogress": "^0.2.0",
@@ -64,8 +65,7 @@
6465
"stompjs": "^2.3.3",
6566
"vue": "^3.4.27",
6667
"vue-i18n": "9.9.1",
67-
"vue-router": "^4.3.2",
68-
"xlsx": "^0.18.5"
68+
"vue-router": "^4.3.2"
6969
},
7070
"devDependencies": {
7171
"@commitlint/cli": "^18.6.1",

Diff for: src/assets/icons/backtop.svg

+1
Loading

Diff for: src/components/PageContent/index.vue

+58-5
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,18 @@
5959
<template v-for="item in defaultToolbar" :key="item">
6060
<!-- 刷新 -->
6161
<template v-if="item === 'refresh'">
62-
<el-button icon="refresh" circle @click="handleToolbar(item)" />
62+
<el-button
63+
icon="refresh"
64+
circle
65+
title="刷新"
66+
@click="handleToolbar(item)"
67+
/>
6368
</template>
64-
<!-- 列设置 -->
69+
<!-- 筛选列 -->
6570
<template v-else-if="item === 'filter'">
6671
<el-popover placement="bottom" trigger="click">
6772
<template #reference>
68-
<el-button icon="Operation" circle />
73+
<el-button icon="Operation" circle title="筛选列" />
6974
</template>
7075
<el-scrollbar max-height="350px">
7176
<template v-for="col in cols" :key="col">
@@ -78,9 +83,23 @@
7883
</el-scrollbar>
7984
</el-popover>
8085
</template>
86+
<!-- 导出 -->
87+
<template v-else-if="item === 'exports'">
88+
<el-button
89+
icon="FolderOpened"
90+
circle
91+
title="导出"
92+
@click="handleToolbar(item)"
93+
/>
94+
</template>
8195
<!-- 搜索 -->
8296
<template v-else-if="item === 'search'">
83-
<el-button icon="search" circle @click="handleToolbar(item)" />
97+
<el-button
98+
icon="search"
99+
circle
100+
title="搜索"
101+
@click="handleToolbar(item)"
102+
/>
84103
</template>
85104
</template>
86105
</div>
@@ -285,6 +304,7 @@
285304
</template>
286305

287306
<script setup lang="ts">
307+
import ExcelJS from "exceljs";
288308
import { ref, reactive } from "vue";
289309
import { useDateFormat } from "@vueuse/core";
290310
import { hasAuth } from "@/plugins/permission";
@@ -352,7 +372,7 @@ export interface IContentConfig<T = any> {
352372
}
353373
>;
354374
// 表格工具栏右侧图标
355-
defaultToolbar?: ("refresh" | "filter" | "search")[];
375+
defaultToolbar?: ("refresh" | "filter" | "exports" | "search")[];
356376
// table组件列属性(额外的属性templet,operat,slotName)
357377
cols: Array<{
358378
type?: "default" | "selection" | "index" | "expand";
@@ -417,6 +437,7 @@ const toolbar = props.contentConfig.toolbar ?? ["add", "delete"];
417437
const defaultToolbar = props.contentConfig.defaultToolbar ?? [
418438
"refresh",
419439
"filter",
440+
"exports",
420441
"search",
421442
];
422443
// 表格列
@@ -487,12 +508,44 @@ function handleDelete(id?: number | string) {
487508
}
488509
});
489510
}
511+
// 导出
512+
function handleExports() {
513+
const workbook = new ExcelJS.Workbook();
514+
const worksheet = workbook.addWorksheet("sheet");
515+
const columns: Partial<ExcelJS.Column>[] = [];
516+
cols.value.forEach((col) => {
517+
if (col.label && col.prop) {
518+
columns.push({ header: col.label, key: col.prop });
519+
}
520+
});
521+
worksheet.columns = columns;
522+
worksheet.addRows(pageData.value);
523+
workbook.xlsx
524+
.writeBuffer()
525+
.then((buffer) => {
526+
const blob = new Blob([buffer], {
527+
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
528+
});
529+
const downloadUrl = window.URL.createObjectURL(blob);
530+
const downloadLink = document.createElement("a");
531+
downloadLink.href = downloadUrl;
532+
downloadLink.download = props.contentConfig.pageName;
533+
document.body.appendChild(downloadLink);
534+
downloadLink.click();
535+
document.body.removeChild(downloadLink);
536+
window.URL.revokeObjectURL(downloadUrl);
537+
})
538+
.catch((error) => console.log("Error writing excel export", error));
539+
}
490540
// 操作栏
491541
function handleToolbar(name: string) {
492542
switch (name) {
493543
case "refresh":
494544
handleRefresh();
495545
break;
546+
case "exports":
547+
handleExports();
548+
break;
496549
case "search":
497550
emit("searchClick");
498551
break;

Diff for: src/components/PageModal/index.vue

+8-4
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@
100100
:prop="item.prop"
101101
:formData="formData"
102102
:attrs="item.attrs"
103-
></slot>
103+
>
104+
</slot>
104105
</template>
105106
</el-form-item>
106107
</template>
@@ -224,7 +225,8 @@
224225
:prop="item.prop"
225226
:formData="formData"
226227
:attrs="item.attrs"
227-
></slot>
228+
>
229+
</slot>
228230
</template>
229231
</el-form-item>
230232
</template>
@@ -251,7 +253,7 @@ import type {
251253
DrawerProps,
252254
} from "element-plus";
253255
import { useThrottleFn } from "@vueuse/core";
254-
import { reactive, ref, watch, watchEffect } from "vue";
256+
import { reactive, ref, watch, watchEffect, nextTick } from "vue";
255257
256258
// 对象类型
257259
export type IObject = Record<string, any>;
@@ -398,7 +400,9 @@ const handleSubmit = useThrottleFn(() => {
398400
function handleCloseModal() {
399401
modalVisible.value = false;
400402
formRef.value?.resetFields();
401-
formRef.value?.clearValidate();
403+
nextTick(() => {
404+
formRef.value?.clearValidate();
405+
});
402406
}
403407
// 显示modal
404408
function setModalVisible(data: IObject = {}) {

Diff for: src/lang/index.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { App } from "vue";
12
import { createI18n } from "vue-i18n";
23
import { useAppStoreHook } from "@/store/modules/app";
34
// 本地语言包
@@ -22,4 +23,9 @@ const i18n = createI18n({
2223
globalInjection: true,
2324
});
2425

26+
// 全局注册 i18n
27+
export function setupI18n(app: App<Element>) {
28+
app.use(i18n);
29+
}
30+
2531
export default i18n;

Diff for: src/layout/index.vue

+8-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
</div>
2929
<AppMain />
3030
<Settings v-if="defaultSettings.showSettings" />
31+
<!-- 返回顶部 -->
32+
<el-backtop target=".main-container">
33+
<svg-icon icon-class="backtop" size="24px" />
34+
</el-backtop>
3135
</div>
3236
</div>
3337

@@ -39,6 +43,10 @@
3943
</div>
4044
<AppMain />
4145
<Settings v-if="defaultSettings.showSettings" />
46+
<!-- 返回顶部 -->
47+
<el-backtop target=".main-container">
48+
<svg-icon icon-class="backtop" size="24px" />
49+
</el-backtop>
4250
</div>
4351
</div>
4452
</template>
@@ -248,10 +256,6 @@ watch(route, () => {
248256
}
249257
250258
&.layout-mix {
251-
.fixed-header {
252-
left: $sidebar-width-collapsed;
253-
}
254-
255259
.sidebar-container {
256260
width: 100% !important;
257261
}

Diff for: src/main.ts

+3-15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { createApp } from "vue";
22
import App from "./App.vue";
3-
import router from "@/router";
4-
import { setupStore } from "@/store";
5-
import { setupDirective } from "@/directive";
6-
import { setupElIcons, setupI18n, setupPermission } from "@/plugins";
3+
import setupPlugins from "@/plugins";
74

85
// 本地SVG图标
96
import "virtual:svg-icons-register";
@@ -15,14 +12,5 @@ import "uno.css";
1512
import "animate.css";
1613

1714
const app = createApp(App);
18-
// 全局注册 自定义指令(directive)
19-
setupDirective(app);
20-
// 全局注册 状态管理(store)
21-
setupStore(app);
22-
// 全局注册Element-plus图标
23-
setupElIcons(app);
24-
// 国际化
25-
setupI18n(app);
26-
// 注册动态路由
27-
setupPermission();
28-
app.use(router).mount("#app");
15+
app.use(setupPlugins);
16+
app.mount("#app");

Diff for: src/plugins/i18n.ts

-7
This file was deleted.

Diff for: src/plugins/index.ts

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1-
export * from "./icons";
2-
export * from "./i18n";
3-
export * from "./permission";
1+
import { setupDirective } from "@/directive";
2+
import { setupI18n } from "@/lang";
3+
import { setupRouter } from "@/router";
4+
import { setupStore } from "@/store";
5+
import type { App } from "vue";
6+
import { setupElIcons } from "./icons";
7+
import { setupPermission } from "./permission";
8+
9+
export default {
10+
install(app: App<Element>) {
11+
// 自定义指令(directive)
12+
setupDirective(app);
13+
// 路由(router)
14+
setupRouter(app);
15+
// 状态管理(store)
16+
setupStore(app);
17+
// 国际化
18+
setupI18n(app);
19+
// Element-plus图标
20+
setupElIcons(app);
21+
// 路由守卫
22+
setupPermission();
23+
},
24+
};

Diff for: src/router/index.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { App } from "vue";
12
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
23

34
export const Layout = () => import("@/layout/index.vue");
@@ -117,6 +118,11 @@ const router = createRouter({
117118
scrollBehavior: () => ({ left: 0, top: 0 }),
118119
});
119120

121+
// 全局注册 router
122+
export function setupRouter(app: App<Element>) {
123+
app.use(router);
124+
}
125+
120126
/**
121127
* 重置路由
122128
*/

Diff for: src/styles/index.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
@use "./reset";
22

33
.app-container {
4-
padding: 10px;
4+
padding: 15px;
55
}
66

77
.search-container {

Diff for: src/typings/auto-imports.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -325,12 +325,14 @@ declare module "vue" {
325325
interface GlobalComponents {}
326326
interface ComponentCustomProperties {
327327
readonly EffectScope: UnwrapRef<(typeof import("vue"))["EffectScope"]>;
328+
readonly ElForm: UnwrapRef<(typeof import("element-plus/es"))["ElForm"]>;
328329
readonly ElMessage: UnwrapRef<
329330
(typeof import("element-plus/es"))["ElMessage"]
330331
>;
331332
readonly ElMessageBox: UnwrapRef<
332333
(typeof import("element-plus/es"))["ElMessageBox"]
333334
>;
335+
readonly ElTree: UnwrapRef<(typeof import("element-plus/es"))["ElTree"]>;
334336
readonly acceptHMRUpdate: UnwrapRef<
335337
(typeof import("pinia"))["acceptHMRUpdate"]
336338
>;
@@ -1053,12 +1055,14 @@ declare module "@vue/runtime-core" {
10531055
interface GlobalComponents {}
10541056
interface ComponentCustomProperties {
10551057
readonly EffectScope: UnwrapRef<(typeof import("vue"))["EffectScope"]>;
1058+
readonly ElForm: UnwrapRef<(typeof import("element-plus/es"))["ElForm"]>;
10561059
readonly ElMessage: UnwrapRef<
10571060
(typeof import("element-plus/es"))["ElMessage"]
10581061
>;
10591062
readonly ElMessageBox: UnwrapRef<
10601063
(typeof import("element-plus/es"))["ElMessageBox"]
10611064
>;
1065+
readonly ElTree: UnwrapRef<(typeof import("element-plus/es"))["ElTree"]>;
10621066
readonly acceptHMRUpdate: UnwrapRef<
10631067
(typeof import("pinia"))["acceptHMRUpdate"]
10641068
>;

Diff for: src/typings/components.d.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ declare module "vue" {
1414
DeptTree: (typeof import("./../views/system/user/components/dept-tree.vue"))["default"];
1515
Dictionary: (typeof import("./../components/Dictionary/index.vue"))["default"];
1616
DictItem: (typeof import("./../views/system/dict/components/dict-item.vue"))["default"];
17+
ElBacktop: (typeof import("element-plus/es"))["ElBacktop"];
1718
ElBreadcrumb: (typeof import("element-plus/es"))["ElBreadcrumb"];
1819
ElBreadcrumbItem: (typeof import("element-plus/es"))["ElBreadcrumbItem"];
1920
ElButton: (typeof import("element-plus/es"))["ElButton"];
@@ -52,12 +53,16 @@ declare module "vue" {
5253
ElSwitch: (typeof import("element-plus/es"))["ElSwitch"];
5354
ElTable: (typeof import("element-plus/es"))["ElTable"];
5455
ElTableColumn: (typeof import("element-plus/es"))["ElTableColumn"];
56+
ElTabPane: (typeof import("element-plus/es"))["ElTabPane"];
57+
ElTabs: (typeof import("element-plus/es"))["ElTabs"];
5558
ElTag: (typeof import("element-plus/es"))["ElTag"];
59+
ElText: (typeof import("element-plus/es"))["ElText"];
5660
ElTooltip: (typeof import("element-plus/es"))["ElTooltip"];
5761
ElTree: (typeof import("element-plus/es"))["ElTree"];
5862
ElTreeSelect: (typeof import("element-plus/es"))["ElTreeSelect"];
5963
ElUpload: (typeof import("element-plus/es"))["ElUpload"];
6064
ElWatermark: (typeof import("element-plus/es"))["ElWatermark"];
65+
Form: (typeof import("./../components/PageModal/Form.vue"))["default"];
6166
FunnelChart: (typeof import("./../views/dashboard/components/FunnelChart.vue"))["default"];
6267
GithubCorner: (typeof import("./../components/GithubCorner/index.vue"))["default"];
6368
Hamburger: (typeof import("./../components/Hamburger/index.vue"))["default"];
@@ -69,10 +74,11 @@ declare module "vue" {
6974
IEpDownload: (typeof import("~icons/ep/download"))["default"];
7075
IEpEdit: (typeof import("~icons/ep/edit"))["default"];
7176
IEpPlus: (typeof import("~icons/ep/plus"))["default"];
77+
IEpPosition: (typeof import("~icons/ep/position"))["default"];
78+
IEpQuestionFilled: (typeof import("~icons/ep/question-filled"))["default"];
7279
IEpRefresh: (typeof import("~icons/ep/refresh"))["default"];
7380
IEpRefreshLeft: (typeof import("~icons/ep/refresh-left"))["default"];
7481
IEpSearch: (typeof import("~icons/ep/search"))["default"];
75-
IEpSetting: (typeof import("~icons/ep/setting"))["default"];
7682
IEpTop: (typeof import("~icons/ep/top"))["default"];
7783
IEpUploadFilled: (typeof import("~icons/ep/upload-filled"))["default"];
7884
LangSelect: (typeof import("./../components/LangSelect/index.vue"))["default"];

Diff for: src/typings/shims-vue.d.ts

-1
This file was deleted.

0 commit comments

Comments
 (0)