@slice_cy/mint 1.2.0
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/README.md +697 -0
- package/app/common/base-response.js +10 -0
- package/app/common/business-error.js +10 -0
- package/app/common/error-code.js +22 -0
- package/app/common/return-utils.js +40 -0
- package/app/common/router-schema-validator.js +25 -0
- package/app/controller/base.js +24 -0
- package/app/controller/project.js +90 -0
- package/app/controller/view.js +13 -0
- package/app/docs/dsl-doc.js +148 -0
- package/app/extend/logger.js +30 -0
- package/app/extend/params-valid.js +6 -0
- package/app/extend/return-utils.js +9 -0
- package/app/middleware/api-sign-verify.js +25 -0
- package/app/middleware/err-handler.js +18 -0
- package/app/middleware/project-handler.js +25 -0
- package/app/pages/boot.js +34 -0
- package/app/pages/dashboard/complex-view/header-view/index.vue +108 -0
- package/app/pages/dashboard/complex-view/iframe-view/index.vue +37 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/index.vue +36 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/index.vue +123 -0
- package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +14 -0
- package/app/pages/dashboard/complex-view/schema-view/components/createForm/index.vue +72 -0
- package/app/pages/dashboard/complex-view/schema-view/components/detailPanel/index.vue +52 -0
- package/app/pages/dashboard/complex-view/schema-view/components/editForm/index.vue +87 -0
- package/app/pages/dashboard/complex-view/schema-view/hook/use-schema.js +113 -0
- package/app/pages/dashboard/complex-view/schema-view/index.vue +114 -0
- package/app/pages/dashboard/complex-view/sider-view/index.vue +116 -0
- package/app/pages/dashboard/entry.dashboard.js +44 -0
- package/app/pages/dashboard/index.vue +60 -0
- package/app/pages/service/index.js +70 -0
- package/app/pages/store/index.js +4 -0
- package/app/pages/store/menu.js +86 -0
- package/app/pages/store/project.js +9 -0
- package/app/pages/utils/index.js +26 -0
- package/app/pages/widgets/header-container/assets/avatar.png +0 -0
- package/app/pages/widgets/header-container/assets/icon.png +0 -0
- package/app/pages/widgets/header-container/index.vue +108 -0
- package/app/pages/widgets/schema-form/components/input/index.vue +155 -0
- package/app/pages/widgets/schema-form/components/input-number/index.vue +148 -0
- package/app/pages/widgets/schema-form/components/select/index.vue +132 -0
- package/app/pages/widgets/schema-form/form-item-cofnig.js +18 -0
- package/app/pages/widgets/schema-form/index.vue +137 -0
- package/app/pages/widgets/schema-search-bar/component/date-range-picker/index.vue +55 -0
- package/app/pages/widgets/schema-search-bar/component/dynamic-select/index.vue +70 -0
- package/app/pages/widgets/schema-search-bar/component/input/index.vue +54 -0
- package/app/pages/widgets/schema-search-bar/component/select/index.vue +65 -0
- package/app/pages/widgets/schema-search-bar/index.vue +143 -0
- package/app/pages/widgets/schema-search-bar/search-item-config.js +20 -0
- package/app/pages/widgets/schema-table/index.vue +198 -0
- package/app/pages/widgets/sider-container/index.vue +30 -0
- package/app/post-router.js +9 -0
- package/app/pre-middleware.js +37 -0
- package/app/public/dist/entry.dashboard.tpl +2 -0
- package/app/public/dist/entry.project-list.tpl +2 -0
- package/app/public/dist/prod/css/async.609.bcbe5779.css +1 -0
- package/app/public/dist/prod/css/async.952.9bc7c180.css +1 -0
- package/app/public/dist/prod/css/common.1a98227b.css +1 -0
- package/app/public/dist/prod/css/entry.project-list.14f64dd9.css +1 -0
- package/app/public/dist/prod/js/142.56646c73.bundle.js +1 -0
- package/app/public/dist/prod/js/609.8270b20d.bundle.js +1 -0
- package/app/public/dist/prod/js/68.2813fafe.bundle.js +1 -0
- package/app/public/dist/prod/js/952.c76c04fb.bundle.js +1 -0
- package/app/public/dist/prod/js/common.9985f827.bundle.js +1 -0
- package/app/public/dist/prod/js/entry.dashboard.cd7591e4.bundle.js +1 -0
- package/app/public/dist/prod/js/entry.project-list.beb0fa9d.bundle.js +1 -0
- package/app/public/dist/prod/js/runtime.5602b906.bundle.js +1 -0
- package/app/public/dist/prod/js/vendor.709fbf32.bundle.js +53 -0
- package/app/public/dist/prod/static/images/avatar.78e68eaa.png +0 -0
- package/app/public/dist/static/logo.png +0 -0
- package/app/public/dist/static/normalize.css +1 -0
- package/app/public/static/logo.png +0 -0
- package/app/public/static/normalize.css +267 -0
- package/app/router/project.js +6 -0
- package/app/router/view.js +5 -0
- package/app/router-schema/project.js +14 -0
- package/app/service/base.js +22 -0
- package/app/service/project.js +49 -0
- package/app/view/entry.tpl +18 -0
- package/app/webpack/config/webpack.base.js +288 -0
- package/app/webpack/config/webpack.dev.js +53 -0
- package/app/webpack/config/webpack.prod.js +106 -0
- package/app/webpack/dev.js +34 -0
- package/app/webpack/libs/block.js +1 -0
- package/app/webpack/prod.js +21 -0
- package/config/config.default.js +5 -0
- package/index.js +37 -0
- package/mint-core/env.js +23 -0
- package/mint-core/index.js +101 -0
- package/mint-core/loader/config.js +59 -0
- package/mint-core/loader/controller.js +59 -0
- package/mint-core/loader/extend.js +42 -0
- package/mint-core/loader/middleware.js +54 -0
- package/mint-core/loader/router-schema.js +39 -0
- package/mint-core/loader/router.js +36 -0
- package/mint-core/loader/service.js +57 -0
- package/model/index.js +94 -0
- package/package.json +93 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<sider-container>
|
|
3
|
+
<template #sider-content>
|
|
4
|
+
<a-menu
|
|
5
|
+
v-model:selected-keys="activeKey"
|
|
6
|
+
mode="vertical"
|
|
7
|
+
:items="menuList"
|
|
8
|
+
@click="onMenuSelect"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
<template #main-content>
|
|
12
|
+
<router-view />
|
|
13
|
+
</template>
|
|
14
|
+
</sider-container>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script setup>
|
|
18
|
+
import { ref, watch, onMounted } from "vue";
|
|
19
|
+
import SiderContainer from "$mintWidgets/sider-container/index.vue";
|
|
20
|
+
import { useRoute, useRouter } from "vue-router";
|
|
21
|
+
import { useMenuStore } from "$mintStore/menu";
|
|
22
|
+
|
|
23
|
+
const route = useRoute();
|
|
24
|
+
const router = useRouter();
|
|
25
|
+
const menuStore = useMenuStore();
|
|
26
|
+
|
|
27
|
+
const activeKey = ref("");
|
|
28
|
+
const menuList = ref([]);
|
|
29
|
+
|
|
30
|
+
watch(
|
|
31
|
+
[() => menuStore.menuList, () => route.query.key],
|
|
32
|
+
() => {
|
|
33
|
+
setMenuList();
|
|
34
|
+
setActiveKey();
|
|
35
|
+
},
|
|
36
|
+
{ deep: true },
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const setMenuList = () => {
|
|
40
|
+
const menuItem = menuStore.findMenuItem({
|
|
41
|
+
key: "key",
|
|
42
|
+
value: route.query.key,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (!menuItem) return;
|
|
46
|
+
|
|
47
|
+
if (menuItem?.siderConfig?.menu) {
|
|
48
|
+
menuList.value = menuStore.transfomMenuList(menuItem.siderConfig.menu);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const onMenuSelect = ({ item }) => {
|
|
53
|
+
handleMenuSelect(item.originItemValue);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handleMenuSelect = (menuItem) => {
|
|
57
|
+
if (route.query.sider_key === menuItem.key) return;
|
|
58
|
+
const { key, moduleType, customConfig } = menuItem;
|
|
59
|
+
const pathMap = {
|
|
60
|
+
sider: "/sider",
|
|
61
|
+
schema: "/schema",
|
|
62
|
+
custom: customConfig?.path,
|
|
63
|
+
iframe: "/iframe",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
router.push({
|
|
67
|
+
path: `/view/dashboard/sider${pathMap[moduleType]}`,
|
|
68
|
+
query: {
|
|
69
|
+
project_key: route.query.project_key,
|
|
70
|
+
key: route.query.key,
|
|
71
|
+
sider_key: key,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const setActiveKey = () => {
|
|
77
|
+
// 获取顶层子菜单
|
|
78
|
+
const topMenu = menuStore.findMenuItem({
|
|
79
|
+
key: "key",
|
|
80
|
+
value: route.query.key,
|
|
81
|
+
});
|
|
82
|
+
if (!topMenu) return;
|
|
83
|
+
|
|
84
|
+
if (!topMenu?.siderConfig?.menu) return;
|
|
85
|
+
// 获取sider侧边栏菜单
|
|
86
|
+
const siderMenuList = topMenu.siderConfig.menu;
|
|
87
|
+
// 获取到就直接设置
|
|
88
|
+
const siderMenuItem = menuStore.findMenuItem(
|
|
89
|
+
{
|
|
90
|
+
key: "key",
|
|
91
|
+
value: route.query.sider_key,
|
|
92
|
+
},
|
|
93
|
+
siderMenuList,
|
|
94
|
+
);
|
|
95
|
+
if (siderMenuItem) {
|
|
96
|
+
activeKey.value = siderMenuItem?.key ? [siderMenuItem.key] : [];
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// 获取不到就取第一项(考虑嵌套情况,递归)
|
|
100
|
+
const firstMenuItem = menuStore.findFirstMenuItem(siderMenuList);
|
|
101
|
+
activeKey.value = firstMenuItem?.key ? [firstMenuItem.key] : [];
|
|
102
|
+
// 兼容url没带sider_key,然后菜单自动高亮了第一项,也需要自动跳转到第一项的路由页面
|
|
103
|
+
handleMenuSelect(firstMenuItem);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
onMounted(() => {
|
|
107
|
+
setMenuList();
|
|
108
|
+
setActiveKey();
|
|
109
|
+
});
|
|
110
|
+
</script>
|
|
111
|
+
|
|
112
|
+
<style lang="less" scoped>
|
|
113
|
+
.sider-view {
|
|
114
|
+
height: 100%;
|
|
115
|
+
}
|
|
116
|
+
</style>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import App from "./index";
|
|
2
|
+
import boot from "../boot";
|
|
3
|
+
// import businessDashboardRouterConfig from "$businessDashboardRouterConfig";
|
|
4
|
+
|
|
5
|
+
const siderRoutes = [
|
|
6
|
+
{
|
|
7
|
+
path: "iframe",
|
|
8
|
+
component: () => import("$mintPage/dashboard/complex-view/iframe-view/index.vue"),
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
path: "schema",
|
|
12
|
+
component: () => import("$mintPage/dashboard/complex-view/schema-view/index.vue"),
|
|
13
|
+
},
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const routes = [
|
|
17
|
+
// 头部菜单路由
|
|
18
|
+
{
|
|
19
|
+
path: "/view/dashboard/iframe",
|
|
20
|
+
component: () => import("$mintPage/dashboard/complex-view/iframe-view/index.vue"),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
path: "/view/dashboard/schema",
|
|
24
|
+
component: () => import("$mintPage/dashboard/complex-view/schema-view/index.vue"),
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
// 侧边栏菜单
|
|
28
|
+
{
|
|
29
|
+
path: "/view/dashboard/sider",
|
|
30
|
+
component: () => import("$mintPage/dashboard/complex-view/sider-view/index.vue"),
|
|
31
|
+
children: siderRoutes,
|
|
32
|
+
},
|
|
33
|
+
// 侧边栏兜底
|
|
34
|
+
{
|
|
35
|
+
path: "/view/dashboard/sider/:chapter",
|
|
36
|
+
component: () => import("$mintPage/dashboard/complex-view/sider-view/index.vue"),
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
// if (typeof businessDashboardRouterConfig === "function") {
|
|
41
|
+
// businessDashboardRouterConfig({ routes, siderRoutes });
|
|
42
|
+
// }
|
|
43
|
+
|
|
44
|
+
boot(App, { routes });
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<header-view
|
|
3
|
+
:project-name="projectName"
|
|
4
|
+
username="管理员"
|
|
5
|
+
@menu-select="onMenuSelect"
|
|
6
|
+
>
|
|
7
|
+
<template #main-content>
|
|
8
|
+
<router-view />
|
|
9
|
+
</template>
|
|
10
|
+
</header-view>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup>
|
|
14
|
+
import HeaderView from "$mintPage/dashboard/complex-view/header-view/index.vue";
|
|
15
|
+
import { onMounted, ref } from "vue";
|
|
16
|
+
import { useRoute, useRouter } from "vue-router";
|
|
17
|
+
import { httpGet } from "$mintService";
|
|
18
|
+
import { useProjectStore } from "$mintStore/project";
|
|
19
|
+
import { useMenuStore } from "$mintStore/menu";
|
|
20
|
+
|
|
21
|
+
const route = useRoute();
|
|
22
|
+
const router = useRouter();
|
|
23
|
+
const projectStore = useProjectStore();
|
|
24
|
+
const menuStore = useMenuStore();
|
|
25
|
+
const projectKey = route.query.project_key;
|
|
26
|
+
const projectName = ref("");
|
|
27
|
+
const loadProject = async () => {
|
|
28
|
+
const { data } = await httpGet("/api/project", { project_key: projectKey });
|
|
29
|
+
projectName.value = data.name;
|
|
30
|
+
menuStore.setMenuList(data.menu);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const loadProjectList = async () => {
|
|
34
|
+
const { data } = await httpGet("/api/project/list", { project_key: projectKey });
|
|
35
|
+
projectStore.setProjectList(data);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const onMenuSelect = (menuItem) => {
|
|
39
|
+
const { key, moduleType, customConfig } = menuItem;
|
|
40
|
+
if (key === route.query.key) return;
|
|
41
|
+
const pathMap = {
|
|
42
|
+
sider: "/sider",
|
|
43
|
+
schema: "/schema",
|
|
44
|
+
custom: customConfig?.path,
|
|
45
|
+
iframe: "/iframe",
|
|
46
|
+
};
|
|
47
|
+
router.push({
|
|
48
|
+
path: `/view/dashboard${pathMap[moduleType]}`,
|
|
49
|
+
query: {
|
|
50
|
+
project_key: projectKey,
|
|
51
|
+
key,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
onMounted(() => {
|
|
57
|
+
loadProject();
|
|
58
|
+
loadProjectList();
|
|
59
|
+
});
|
|
60
|
+
</script>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { message } from "ant-design-vue";
|
|
3
|
+
const md5 = require("md5");
|
|
4
|
+
|
|
5
|
+
const signKey = "mint";
|
|
6
|
+
|
|
7
|
+
// 创建实例
|
|
8
|
+
const service = axios.create({
|
|
9
|
+
baseURL: "", // 环境地址
|
|
10
|
+
timeout: 15000, // 15秒超时
|
|
11
|
+
headers: {
|
|
12
|
+
"Content-Type": "application/json;charset=utf-8",
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// 请求拦截器
|
|
17
|
+
service.interceptors.request.use(
|
|
18
|
+
(config) => {
|
|
19
|
+
// 请求头添加签名
|
|
20
|
+
const st = new Date().getTime();
|
|
21
|
+
const signature = md5(`${signKey}_${st}`);
|
|
22
|
+
config.headers = {
|
|
23
|
+
...config.headers,
|
|
24
|
+
s_sign: signature,
|
|
25
|
+
s_t: st,
|
|
26
|
+
};
|
|
27
|
+
if (config.url.indexOf("/api/proj/") > -1 && window.projectKey) {
|
|
28
|
+
config.headers.project_key = window.projectKey;
|
|
29
|
+
}
|
|
30
|
+
return config;
|
|
31
|
+
},
|
|
32
|
+
(error) => {
|
|
33
|
+
return Promise.reject(error);
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// 响应拦截器
|
|
38
|
+
service.interceptors.response.use(
|
|
39
|
+
(response) => {
|
|
40
|
+
const { data } = response;
|
|
41
|
+
if (data.code === 200 || data.code === 0) {
|
|
42
|
+
return data;
|
|
43
|
+
}
|
|
44
|
+
message.error(data.message);
|
|
45
|
+
return Promise.reject(data);
|
|
46
|
+
},
|
|
47
|
+
(error) => {
|
|
48
|
+
message.error(error.message || "请求失败,请稍后重试");
|
|
49
|
+
return Promise.reject(error);
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// 导出简写请求方法
|
|
54
|
+
export function httpGet(url, params, config = {}) {
|
|
55
|
+
return service.get(url, { params, ...config });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function httpPost(url, data, config = {}) {
|
|
59
|
+
return service.post(url, data, config);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function httpPut(url, data) {
|
|
63
|
+
return service.put(url, data);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function httpDelete(url, params) {
|
|
67
|
+
return service.delete(url, { params });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default service;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { defineStore } from "pinia";
|
|
2
|
+
import { ref } from "vue";
|
|
3
|
+
import cloneDeep from "lodash/cloneDeep";
|
|
4
|
+
export const useMenuStore = defineStore("menu", () => {
|
|
5
|
+
const menuList = ref([]);
|
|
6
|
+
const setMenuList = (list) => {
|
|
7
|
+
menuList.value = list;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 递归从菜单树中获取查询菜单项
|
|
12
|
+
* @param {object} param
|
|
13
|
+
* @param {array} mList
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
const findMenuItem = ({ key, value }, mList = menuList.value) => {
|
|
17
|
+
for (let i = 0; i < mList.length; i++) {
|
|
18
|
+
const menuItem = cloneDeep(mList[i]);
|
|
19
|
+
if (!menuItem) continue;
|
|
20
|
+
|
|
21
|
+
if (menuItem[key] === value) {
|
|
22
|
+
return menuItem;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { menuType, moduleType, siderConfig } = menuItem;
|
|
26
|
+
|
|
27
|
+
if (menuType === "group") {
|
|
28
|
+
const findItem = findMenuItem({ key, value }, menuItem.subMenu);
|
|
29
|
+
if (findItem) return findItem;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (menuType === "module" && moduleType === "sider" && siderConfig?.menu) {
|
|
33
|
+
const findItem = findMenuItem({ key, value }, siderConfig.menu);
|
|
34
|
+
if (findItem) return findItem;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 获取第一个菜单项
|
|
42
|
+
* @param {array} mList
|
|
43
|
+
* @returns
|
|
44
|
+
*/
|
|
45
|
+
const findFirstMenuItem = (mList = menuList.value) => {
|
|
46
|
+
const firstMenuItem = cloneDeep(mList[0]);
|
|
47
|
+
const { menuType, subMenu } = firstMenuItem;
|
|
48
|
+
if (menuType === "group" && subMenu) {
|
|
49
|
+
const findItem = findFirstMenuItem(subMenu);
|
|
50
|
+
if (findItem) return findItem;
|
|
51
|
+
}
|
|
52
|
+
return firstMenuItem;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 负责递归菜单树结构,新增{ label , disabled,children 字段}
|
|
57
|
+
* @param {array} menuList
|
|
58
|
+
* @param {object} options
|
|
59
|
+
* @param {function} options.disabledCallback
|
|
60
|
+
*/
|
|
61
|
+
const transfomMenuList = (mList = menuList.value, options = {}) => {
|
|
62
|
+
return cloneDeep(mList).map((item) => {
|
|
63
|
+
const { subMenu, menuType, moduleType, siderConfig } = item;
|
|
64
|
+
const obj = {
|
|
65
|
+
...item,
|
|
66
|
+
label: item.name,
|
|
67
|
+
disabled: options?.disabledCallback && options.disabledCallback(item),
|
|
68
|
+
};
|
|
69
|
+
if (menuType === "group" && subMenu && subMenu.length) {
|
|
70
|
+
obj.children = transfomMenuList(subMenu, options);
|
|
71
|
+
}
|
|
72
|
+
if (
|
|
73
|
+
menuType === "module" &&
|
|
74
|
+
moduleType === "sider" &&
|
|
75
|
+
siderConfig?.menu &&
|
|
76
|
+
siderConfig?.menu?.length
|
|
77
|
+
) {
|
|
78
|
+
obj.siderConfig.menu = transfomMenuList(siderConfig.menu, options);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return obj;
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return { menuList, setMenuList, findMenuItem, findFirstMenuItem, transfomMenuList };
|
|
86
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineStore } from "pinia";
|
|
2
|
+
import { ref } from "vue";
|
|
3
|
+
export const useProjectStore = defineStore("project", () => {
|
|
4
|
+
const projectList = ref([]);
|
|
5
|
+
const setProjectList = (list) => {
|
|
6
|
+
projectList.value = list;
|
|
7
|
+
};
|
|
8
|
+
return { projectList, setProjectList };
|
|
9
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 过滤配置了 [key]: false 的属性
|
|
3
|
+
* @param {*} schema
|
|
4
|
+
* @param {*} fieldName
|
|
5
|
+
* @returns
|
|
6
|
+
*/
|
|
7
|
+
export const filterNotRenderData = (schema, fieldName) => {
|
|
8
|
+
if (!schema?.properties) return schema;
|
|
9
|
+
return Object.entries(schema.properties).reduce(
|
|
10
|
+
(acc, [key, value]) => {
|
|
11
|
+
if (
|
|
12
|
+
value?.option &&
|
|
13
|
+
Object.hasOwnProperty.call(value.option, fieldName) &&
|
|
14
|
+
!value.option[fieldName]
|
|
15
|
+
) {
|
|
16
|
+
return acc;
|
|
17
|
+
}
|
|
18
|
+
acc.properties[key] = value;
|
|
19
|
+
return acc;
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {},
|
|
24
|
+
},
|
|
25
|
+
);
|
|
26
|
+
};
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<a-layout>
|
|
3
|
+
<a-layout-header class="header">
|
|
4
|
+
<!-- 头部容器 -->
|
|
5
|
+
<div class="header-panel">
|
|
6
|
+
<!-- 左侧 -->
|
|
7
|
+
<div class="left-panel">
|
|
8
|
+
<a-flex
|
|
9
|
+
align="center"
|
|
10
|
+
class="title-panel"
|
|
11
|
+
>
|
|
12
|
+
<img src="./assets/avatar.png" />
|
|
13
|
+
<h2 class="title">{{ projectName }}</h2>
|
|
14
|
+
</a-flex>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- 中间侧 -->
|
|
18
|
+
<div class="middle-panel">
|
|
19
|
+
<slot name="menu-content" />
|
|
20
|
+
</div>
|
|
21
|
+
<!-- 右侧 -->
|
|
22
|
+
<div class="right-panel">
|
|
23
|
+
<slot name="setting-content" />
|
|
24
|
+
<div class="avatar-wrap">
|
|
25
|
+
<img src="./assets/avatar.png" />
|
|
26
|
+
<a-dropdown>
|
|
27
|
+
<a>
|
|
28
|
+
{{ username }}
|
|
29
|
+
<DownOutlined />
|
|
30
|
+
</a>
|
|
31
|
+
<template #overlay>
|
|
32
|
+
<a-menu>
|
|
33
|
+
<a-menu-item>
|
|
34
|
+
<a href="javascript:;">退出登录</a>
|
|
35
|
+
</a-menu-item>
|
|
36
|
+
</a-menu>
|
|
37
|
+
</template>
|
|
38
|
+
</a-dropdown>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</a-layout-header>
|
|
43
|
+
<a-layout-content class="content">
|
|
44
|
+
<slot name="main-content" />
|
|
45
|
+
</a-layout-content>
|
|
46
|
+
</a-layout>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<script setup>
|
|
50
|
+
defineProps({
|
|
51
|
+
projectName: {
|
|
52
|
+
type: String,
|
|
53
|
+
default: "",
|
|
54
|
+
},
|
|
55
|
+
username: {
|
|
56
|
+
type: String,
|
|
57
|
+
default: "",
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<style scoped lang="less">
|
|
63
|
+
.header {
|
|
64
|
+
background: #fff;
|
|
65
|
+
border-bottom: 1px solid #eee;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.content {
|
|
69
|
+
// padding: 48px;
|
|
70
|
+
background: #fff;
|
|
71
|
+
height: calc(100vh - 64px);
|
|
72
|
+
overflow-y: auto;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
img {
|
|
76
|
+
width: 32px;
|
|
77
|
+
height: 32px;
|
|
78
|
+
margin-right: 16px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.header-panel {
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
height: 64px;
|
|
85
|
+
.title-panel {
|
|
86
|
+
.title {
|
|
87
|
+
margin: 0 20px 0 0;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.left-panel {
|
|
92
|
+
min-width: 120px;
|
|
93
|
+
display: flex;
|
|
94
|
+
align-items: center;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.middle-panel {
|
|
98
|
+
flex: 1;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.right-panel {
|
|
102
|
+
min-width: 120px;
|
|
103
|
+
display: flex;
|
|
104
|
+
gap: 12px;
|
|
105
|
+
align-items: center;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
</style>
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="form-item">
|
|
3
|
+
<div class="item-label">
|
|
4
|
+
<a-flex
|
|
5
|
+
:gap="5"
|
|
6
|
+
align-items="center"
|
|
7
|
+
>
|
|
8
|
+
<span
|
|
9
|
+
v-if="schema?.option?.required"
|
|
10
|
+
class="required"
|
|
11
|
+
>
|
|
12
|
+
*
|
|
13
|
+
</span>
|
|
14
|
+
<span>{{ schema.label }}</span>
|
|
15
|
+
</a-flex>
|
|
16
|
+
</div>
|
|
17
|
+
<a-input
|
|
18
|
+
v-model:value="dtoValue"
|
|
19
|
+
:class="[validTip.length ? 'validate-border-error' : '']"
|
|
20
|
+
v-bind="componentAttrs"
|
|
21
|
+
@blur="onBlur"
|
|
22
|
+
@focus="onFocus"
|
|
23
|
+
></a-input>
|
|
24
|
+
|
|
25
|
+
<div
|
|
26
|
+
v-if="validTip.length"
|
|
27
|
+
class="validate-tip-error"
|
|
28
|
+
>
|
|
29
|
+
{{ validTip }}
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup>
|
|
35
|
+
import { watch, onMounted, ref, computed, inject } from "vue";
|
|
36
|
+
const props = defineProps({
|
|
37
|
+
schema: {
|
|
38
|
+
type: Object,
|
|
39
|
+
default: () => ({}),
|
|
40
|
+
},
|
|
41
|
+
schemaKey: {
|
|
42
|
+
type: String,
|
|
43
|
+
default: "",
|
|
44
|
+
},
|
|
45
|
+
model: {
|
|
46
|
+
type: String,
|
|
47
|
+
default: "",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const name = "input";
|
|
52
|
+
|
|
53
|
+
const componentAttrs = computed(() => {
|
|
54
|
+
const { minLength, maxLength, pattern } = props.schema;
|
|
55
|
+
const { placeholder: pValue } = props.schema.option?.uiComponentConfig || {};
|
|
56
|
+
const ruleList = [];
|
|
57
|
+
if (minLength) {
|
|
58
|
+
ruleList.push(`最小长度:${minLength}`);
|
|
59
|
+
}
|
|
60
|
+
if (maxLength) {
|
|
61
|
+
ruleList.push(`最大长度:${maxLength}`);
|
|
62
|
+
}
|
|
63
|
+
if (pattern) {
|
|
64
|
+
ruleList.push(`格式要求:${pattern}`);
|
|
65
|
+
}
|
|
66
|
+
ruleList.push(pValue || "请输入");
|
|
67
|
+
return {
|
|
68
|
+
allowClear: true,
|
|
69
|
+
...(props.schema.option?.uiComponentConfig || {}),
|
|
70
|
+
placeholder: ruleList.join(" | "),
|
|
71
|
+
disabled: props.schema.option?.disabled,
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
watch(
|
|
76
|
+
[() => props.schema, () => props.model, () => props.schemaKey],
|
|
77
|
+
() => {
|
|
78
|
+
initData();
|
|
79
|
+
},
|
|
80
|
+
{ deep: true },
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const dtoValue = ref();
|
|
84
|
+
const getValue = () => {
|
|
85
|
+
return dtoValue.value
|
|
86
|
+
? {
|
|
87
|
+
[props.schemaKey]: dtoValue.value,
|
|
88
|
+
}
|
|
89
|
+
: {};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getAjv = inject("getAjv");
|
|
93
|
+
const validTip = ref("");
|
|
94
|
+
|
|
95
|
+
const validate = async () => {
|
|
96
|
+
validTip.value = "";
|
|
97
|
+
if (props.schema.option?.required && !dtoValue.value) {
|
|
98
|
+
validTip.value = `${props.schema.label}必填`;
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (dtoValue.value) {
|
|
102
|
+
const ajv = await getAjv();
|
|
103
|
+
const validate = ajv.compile(props.schema);
|
|
104
|
+
const valid = validate(dtoValue.value);
|
|
105
|
+
if (!valid && validate.errors && validate.errors[0]) {
|
|
106
|
+
const { keyword, params } = validate.errors[0];
|
|
107
|
+
switch (keyword) {
|
|
108
|
+
case "type":
|
|
109
|
+
validTip.value = `${props.schema.label}类型错误`;
|
|
110
|
+
break;
|
|
111
|
+
case "minLength":
|
|
112
|
+
validTip.value = `${props.schema.label}长度不能小于${params.limit}`;
|
|
113
|
+
break;
|
|
114
|
+
case "maxLength":
|
|
115
|
+
validTip.value = `${props.schema.label}长度不能大于${params.limit}`;
|
|
116
|
+
break;
|
|
117
|
+
case "pattern":
|
|
118
|
+
validTip.value = `${props.schema.label}格式错误`;
|
|
119
|
+
break;
|
|
120
|
+
default:
|
|
121
|
+
validTip.value = `${props.schema.label}不符合要求`;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return true;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const onFocus = () => {
|
|
132
|
+
validTip.value = "";
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const onBlur = () => {
|
|
136
|
+
void validate();
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
function initData() {
|
|
140
|
+
dtoValue.value = props.model || props.schema.option?.default || undefined;
|
|
141
|
+
validTip.value = "";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
onMounted(() => {
|
|
145
|
+
initData();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
defineExpose({
|
|
149
|
+
getValue,
|
|
150
|
+
validate,
|
|
151
|
+
name,
|
|
152
|
+
});
|
|
153
|
+
</script>
|
|
154
|
+
|
|
155
|
+
<style scoped lang="less"></style>
|