@siven-li/elpis 1.0.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.
Files changed (78) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc +55 -0
  3. package/README.md +201 -0
  4. package/app/controller/base.js +37 -0
  5. package/app/controller/project.js +65 -0
  6. package/app/controller/view.js +20 -0
  7. package/app/extend/logger.js +35 -0
  8. package/app/middleware/api-params-verify.js +97 -0
  9. package/app/middleware/api-sign-verify.js +30 -0
  10. package/app/middleware/error-handler.js +34 -0
  11. package/app/middleware/project-handler.js +22 -0
  12. package/app/middleware.js +37 -0
  13. package/app/pages/assets/custom.css +16 -0
  14. package/app/pages/boot.js +53 -0
  15. package/app/pages/common/curl.js +74 -0
  16. package/app/pages/common/utils.js +2 -0
  17. package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +35 -0
  18. package/app/pages/dashboard/complex-view/header-view/header-view.vue +132 -0
  19. package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +45 -0
  20. package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +37 -0
  21. package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +130 -0
  22. package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +23 -0
  23. package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +90 -0
  24. package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +110 -0
  25. package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +133 -0
  26. package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +125 -0
  27. package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +97 -0
  28. package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +35 -0
  29. package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +127 -0
  30. package/app/pages/dashboard/dashboard.vue +94 -0
  31. package/app/pages/dashboard/entry.dashboard.js +43 -0
  32. package/app/pages/store/index.js +3 -0
  33. package/app/pages/store/menu.js +59 -0
  34. package/app/pages/store/project.js +17 -0
  35. package/app/pages/widgets/header-container/assets/avatar.png +0 -0
  36. package/app/pages/widgets/header-container/assets/logo.png +0 -0
  37. package/app/pages/widgets/header-container/header-container.vue +124 -0
  38. package/app/pages/widgets/schema-form/complex-view/input/input.vue +143 -0
  39. package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +141 -0
  40. package/app/pages/widgets/schema-form/complex-view/select/select.vue +127 -0
  41. package/app/pages/widgets/schema-form/form-item-config.js +22 -0
  42. package/app/pages/widgets/schema-form/schema-form.vue +129 -0
  43. package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +49 -0
  44. package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +65 -0
  45. package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +43 -0
  46. package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +50 -0
  47. package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +133 -0
  48. package/app/pages/widgets/schema-search-bar/search-item-config.js +26 -0
  49. package/app/pages/widgets/schema-table/schema-table.vue +234 -0
  50. package/app/pages/widgets/sider-container/sider-container.vue +32 -0
  51. package/app/public/static/logo.png +0 -0
  52. package/app/public/static/normalize.css +239 -0
  53. package/app/router/project.js +6 -0
  54. package/app/router/view.js +9 -0
  55. package/app/router-schema/project.js +30 -0
  56. package/app/service/base.js +12 -0
  57. package/app/service/project.js +44 -0
  58. package/app/view/entry.tpl +26 -0
  59. package/app/webpack/config/webpack.base.js +194 -0
  60. package/app/webpack/config/webpack.dev.js +58 -0
  61. package/app/webpack/config/webpack.prod.js +114 -0
  62. package/app/webpack/dev.js +48 -0
  63. package/app/webpack/libs/blank.js +1 -0
  64. package/app/webpack/prod.js +18 -0
  65. package/config/config.default.js +3 -0
  66. package/elpis-core/env.js +20 -0
  67. package/elpis-core/index.js +88 -0
  68. package/elpis-core/loader/config.js +55 -0
  69. package/elpis-core/loader/controller.js +56 -0
  70. package/elpis-core/loader/extend.js +45 -0
  71. package/elpis-core/loader/middleware.js +58 -0
  72. package/elpis-core/loader/router-schema.js +38 -0
  73. package/elpis-core/loader/router.js +40 -0
  74. package/elpis-core/loader/service.js +56 -0
  75. package/index.js +39 -0
  76. package/model/index.js +104 -0
  77. package/package.json +93 -0
  78. package/test/controll/project.test.js +203 -0
@@ -0,0 +1,110 @@
1
+ <template>
2
+ <el-drawer
3
+ v-model="isShow"
4
+ direction="rtl"
5
+ :destroy-on-close="true"
6
+ :size="550"
7
+ >
8
+ <template #header>
9
+ <h3>
10
+ {{ title }}
11
+ </h3>
12
+ </template>
13
+ <template #default>
14
+ <el-card
15
+ v-loading="loading"
16
+ shadow="always"
17
+ class="detail-panel"
18
+ >
19
+ <el-row
20
+ v-for="(item, key) in components[name]?.schema?.properties"
21
+ :key="key"
22
+ type="flex"
23
+ class="row-item"
24
+ >
25
+ <el-row class="item-label">
26
+ {{ item.label }}:
27
+ </el-row>
28
+ <el-row class="item-value">
29
+ {{ dtoModel[key] }}
30
+ </el-row>
31
+ </el-row>
32
+ </el-card>
33
+ </template>
34
+ </el-drawer>
35
+ </template>
36
+ <script setup>
37
+ import { ref, inject } from 'vue';
38
+ import $curl from '$elpisCommon/curl';
39
+
40
+ const {
41
+ api,
42
+ components
43
+ } = inject('schemaViewData');
44
+
45
+ const name = ref('detailPanel');
46
+
47
+ const isShow = ref(false);
48
+ const loading = ref(false);
49
+ const title = ref('');
50
+ const mainKey = ref('');
51
+ const mainValue = ref('');
52
+ const dtoModel = ref();
53
+
54
+ const show = (rowData) => {
55
+ const { config } = components.value[name.value];
56
+
57
+ title.value = config.title;
58
+ mainKey.value = config.mainKey;
59
+ mainValue.value = rowData[mainKey.value];
60
+ dtoModel.value = {};
61
+
62
+ isShow.value = true;
63
+
64
+ fetchFormData();
65
+ }
66
+
67
+ const fetchFormData = async () => {
68
+ if (loading.value) return;
69
+
70
+ loading.value = true;
71
+
72
+ const res = await $curl({
73
+ method: 'get',
74
+ url: api.value,
75
+ query: {
76
+ [mainKey.value]: mainValue.value
77
+ }
78
+ })
79
+
80
+ loading.value = false;
81
+
82
+ if (!res || !res.success || !res.data) return;
83
+
84
+ dtoModel.value = res.data;
85
+ }
86
+
87
+ defineExpose({
88
+ name,
89
+ show
90
+ })
91
+ </script>
92
+ <style lang="less" scoped>
93
+ .detail-panel {
94
+ border: 1px solid #a6a6a6;
95
+ padding: 30px;
96
+ .row-item {
97
+ height: 40px;
98
+ line-height: 40px;
99
+ font-size: 20px;
100
+ .item-label {
101
+ width: 120px;
102
+ margin-right: 20px;
103
+ color: #ffffff;
104
+ }
105
+ .item-value {
106
+ color: #d2dae4;
107
+ }
108
+ }
109
+ }
110
+ </style>
@@ -0,0 +1,133 @@
1
+ <template>
2
+ <el-drawer
3
+ v-model="isShow"
4
+ direction="rtl"
5
+ :destroy-on-close="true"
6
+ :size="550"
7
+ >
8
+ <template #header>
9
+ <h3 class="title">
10
+ {{ title }}
11
+ </h3>
12
+ </template>
13
+ <template #default>
14
+ <SchemaForm
15
+ ref="schemaFormRef"
16
+ v-loading="loading"
17
+ :schema="components[name]?.schema"
18
+ :model="dtoModel"
19
+ />
20
+ </template>
21
+ <template #footer>
22
+ <el-button
23
+ type="primary"
24
+ @click="save"
25
+ >
26
+ {{ saveBtnText }}
27
+ </el-button>
28
+ </template>
29
+ </el-drawer>
30
+ </template>
31
+ <script setup>
32
+ import { ref, inject } from 'vue';
33
+ import { ElNotification } from 'element-plus';
34
+ import $curl from '$elpisCommon/curl';
35
+ import SchemaForm from '$elpisWidgets/schema-form/schema-form.vue'
36
+
37
+ const {
38
+ api,
39
+ components
40
+ } = inject('schemaViewData');
41
+
42
+ const emit = defineEmits(['command']);
43
+
44
+ const name = ref('editForm');
45
+
46
+ const isShow = ref(false);
47
+ const loading = ref(false);
48
+ const schemaFormRef = ref(null);
49
+ const title = ref('');
50
+ const saveBtnText = ref('');
51
+ const mainKey = ref('');
52
+ const mainValue = ref();
53
+ const dtoModel = ref({});
54
+
55
+ const show = (rowData) => {
56
+ const {
57
+ title: titleCon,
58
+ saveBtnText: saveBtnTextCon,
59
+ mainKey: mainKeyCon
60
+ } = components.value[name.value].config;
61
+ title.value = titleCon;
62
+ saveBtnText.value = saveBtnTextCon;
63
+ mainKey.value = mainKeyCon;
64
+ mainValue.value = rowData[mainKeyCon];
65
+ dtoModel.value = {};
66
+
67
+ isShow.value = true;
68
+
69
+ featchFormData();
70
+ }
71
+
72
+ const close = () => {
73
+ isShow.value = false;
74
+ }
75
+
76
+ const featchFormData = async () => {
77
+ if (loading.value) return;
78
+ loading.value = true;
79
+
80
+ const res = await $curl({
81
+ method: 'get',
82
+ url: api.value,
83
+ query: {
84
+ [mainKey.value]: mainValue.value
85
+ }
86
+ })
87
+
88
+ loading.value = false;
89
+
90
+ if (!res || !res.success || !res.data) return;
91
+ dtoModel.value = res.data;
92
+ }
93
+
94
+ const save = async () => {
95
+ if (loading.value) return;
96
+
97
+ if (!schemaFormRef.value.validate()) return;
98
+
99
+ loading.value = true;
100
+
101
+ const res = await $curl({
102
+ method: 'put',
103
+ url: api.value,
104
+ data: {
105
+ [mainKey.value]: mainValue.value,
106
+ ...schemaFormRef.value.getValue()
107
+ }
108
+ })
109
+
110
+ loading.value = false;
111
+
112
+ if (!res || !res.success) return;
113
+
114
+ ElNotification({
115
+ title: '修改成功',
116
+ message: '修改成功',
117
+ type: 'success'
118
+ });
119
+ close();
120
+ emit('command', {
121
+ event: 'loadTableData'
122
+ });
123
+
124
+ }
125
+
126
+ defineExpose({
127
+ name,
128
+ show
129
+ })
130
+ </script>
131
+ <style lang="less" scoped>
132
+
133
+ </style>
@@ -0,0 +1,125 @@
1
+ import { ref, watch, onMounted, nextTick } from 'vue';
2
+ import { useRoute } from 'vue-router';
3
+ import { useMenuStore } from '$elpisStore/menu.js';
4
+
5
+ export const useSchema = function() {
6
+ const route = useRoute();
7
+ const menuStore = useMenuStore();
8
+
9
+ const api = ref('');
10
+ const tableSchema = ref({});
11
+ const tableConfig = ref();
12
+ const searchSchema = ref({});
13
+ const searchConfig = ref();
14
+ const components = ref({});
15
+
16
+ // 构造 schemaConfig 相关配置,输送给 schemaView 解析
17
+ const buildData = () => {
18
+ const { key, sider_key: siderKey } = route.query;
19
+ const menuItem = menuStore.findMenuItem({
20
+ key: 'key',
21
+ value: siderKey ?? key
22
+ })
23
+
24
+ if (menuItem && menuItem.schemaConfig) {
25
+ const { schemaConfig: sConfig } = menuItem;
26
+
27
+ const configSchema = JSON.parse(JSON.stringify(sConfig.schema));
28
+ api.value = sConfig.api ?? '';
29
+ tableSchema.value = {};
30
+ tableConfig.value = undefined;
31
+ searchSchema.value = {};
32
+ searchConfig.value = undefined;
33
+ components.value = {};
34
+
35
+ nextTick(() => {
36
+ // 构造 tableSchema 和 tableConfig
37
+ tableSchema.value = buildDtoSchema(configSchema, 'table');
38
+ tableConfig.value = sConfig.tableConfig;
39
+
40
+ // 构造 searchSchema 和 searchConfig
41
+ const dtoSearchSchema = buildDtoSchema(configSchema, 'search');
42
+ // 如果跳转过来的路由上带有 search 参数,则页面默认搜索参数相关数据
43
+ for (const key in dtoSearchSchema.properties) {
44
+ if (route.query[key] !== undefined) {
45
+ dtoSearchSchema.properties[key].default = route.query[key];
46
+ }
47
+ };
48
+ searchSchema.value = dtoSearchSchema;
49
+ searchConfig.value = sConfig.searchConfig;
50
+
51
+ // 构造 components = {key: { schema, config } }
52
+ const { componentConfig } = sConfig;
53
+ if (componentConfig && Object.keys(componentConfig).length > 0) {
54
+ const dtoComponents = {};
55
+ for (const comName in componentConfig) {
56
+ dtoComponents[comName] = {
57
+ schema: buildDtoSchema(configSchema, comName),
58
+ config: componentConfig[comName]
59
+ }
60
+ }
61
+ components.value = dtoComponents;
62
+ }
63
+ })
64
+ }
65
+ };
66
+
67
+ // 通用构建 schema 方法
68
+ const buildDtoSchema = (_schema, comName) => {
69
+ if (!_schema?.properties) return {};
70
+
71
+ const dtoSchema = {
72
+ type: 'object',
73
+ properties: {}
74
+ };
75
+
76
+ // 提取有效 schema 信息
77
+ for (const key in _schema.properties) {
78
+ const props = _schema.properties[key];
79
+ if (props[`${comName}Option`]) {
80
+ let dtoProps = {};
81
+ // 提取 props 中非 option 的部分,存放到 dtoProps 中
82
+ for (const pKey in props) {
83
+ if (pKey.indexOf('Option') < 0) {
84
+ dtoProps[pKey] = props[pKey];
85
+ }
86
+ }
87
+ // 处理 comName Option
88
+ dtoProps = Object.assign({}, dtoProps, { option: props[`${comName}Option`] });
89
+
90
+ // 处理 required 字段
91
+ const { required } = _schema;
92
+ if (required && required.find(pk => pk === key)) {
93
+ dtoProps.option.required = true;
94
+ }
95
+
96
+ dtoSchema.properties[key] = dtoProps;
97
+ }
98
+ }
99
+
100
+ return dtoSchema;
101
+ }
102
+
103
+ watch([
104
+ () => route.query.key,
105
+ () => route.query.sider_key,
106
+ () => menuStore.menuList
107
+ ], () => {
108
+ buildData();
109
+ }, {
110
+ deep: true
111
+ });
112
+
113
+ onMounted(() => {
114
+ buildData();
115
+ })
116
+
117
+ return {
118
+ api,
119
+ tableSchema,
120
+ tableConfig,
121
+ searchSchema,
122
+ searchConfig,
123
+ components
124
+ }
125
+ }
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <el-row class="schema-view">
3
+ <search-panel
4
+ v-if="searchSchema?.properties && Object.keys(searchSchema.properties).length > 0"
5
+ @search="onSearch"
6
+ />
7
+ <table-panel
8
+ ref="tablePanelRef"
9
+ @operate="onTableOperate"
10
+ />
11
+ <component
12
+ :is="ComponentConfig[key]?.component"
13
+ v-for="(item, key) in components"
14
+ :key="key"
15
+ ref="comListRef"
16
+ @command="onComponentCommand"
17
+ />
18
+ </el-row>
19
+ </template>
20
+
21
+ <script setup>
22
+ import { ref, provide } from 'vue';
23
+ import SearchPanel from './complex-view/search-panel/search-panel.vue';
24
+ import TablePanel from './complex-view/table-panel/table-panel.vue';
25
+ import ComponentConfig from './components/component-config.js'
26
+ import { useSchema } from './hook/schema';
27
+
28
+ const {
29
+ api,
30
+ tableSchema,
31
+ tableConfig,
32
+ searchSchema,
33
+ searchConfig,
34
+ components
35
+ } = useSchema();
36
+
37
+ const apiParams = ref({});
38
+ provide('schemaViewData', {
39
+ api,
40
+ apiParams,
41
+ tableSchema,
42
+ tableConfig,
43
+ searchSchema,
44
+ searchConfig,
45
+ components
46
+ });
47
+
48
+ const tablePanelRef = ref(null);
49
+ const comListRef = ref([]);
50
+
51
+ const onSearch = (searchValObj) => {
52
+ apiParams.value = searchValObj;
53
+ }
54
+
55
+ // showComponent 展示动态组件
56
+ const showComponent = ({ btnConfig, rowData }) => {
57
+ const { comName } = btnConfig.eventOption;
58
+ if (!comName) return console.error('没配置组件名');
59
+
60
+ const comRef = comListRef.value.find(item => item.name === comName);
61
+
62
+ if (!comRef || typeof comRef.show !== 'function') return console.error(`找不到组件:${comName}`);
63
+
64
+ comRef.show(rowData);
65
+
66
+ }
67
+
68
+ // table 事件映射
69
+ const EventHandlerMap = {
70
+ showComponent: showComponent
71
+ }
72
+
73
+ const onTableOperate = ({ btnConfig, rowData }) => {
74
+ const { eventKey } = btnConfig;
75
+ if (EventHandlerMap[eventKey]) {
76
+ EventHandlerMap[eventKey]({ btnConfig, rowData });
77
+ }
78
+ }
79
+
80
+ // 相应组件事件
81
+ const onComponentCommand = (data) => {
82
+ const { event } = data;
83
+ if (event === 'loadTableData') {
84
+ tablePanelRef.value.loadTableData();
85
+ }
86
+ }
87
+
88
+ </script>
89
+
90
+ <style lang="less" scoped>
91
+ .schema-view {
92
+ display: flex;
93
+ flex-direction: column;
94
+ width: 100%;
95
+ height: 100%;
96
+ }
97
+ </style>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <el-sub-menu :index="menuItem.key">
3
+ <template #title>
4
+ {{ menuItem.name }}
5
+ </template>
6
+ <div
7
+ v-for="item in menuItem.subMenu"
8
+ :key="item.key"
9
+ >
10
+ <sub-menu
11
+ v-if="item.subMenu && item.subMenu.length > 0"
12
+ :menu-item="item"
13
+ />
14
+ <el-menu-item
15
+ v-else
16
+ :index="item.key"
17
+ >
18
+ {{ item.name }}
19
+ </el-menu-item>
20
+ </div>
21
+ </el-sub-menu>
22
+ </template>
23
+
24
+ <script setup>
25
+ const { menuItem } = defineProps({
26
+ menuItem: {
27
+ type: Object,
28
+ required: true
29
+ }
30
+ })
31
+ </script>
32
+
33
+ <style lang="less" scoped>
34
+
35
+ </style>
@@ -0,0 +1,127 @@
1
+ <template>
2
+ <sider-container>
3
+ <template #menu-content>
4
+ <el-menu
5
+ :default-active="activeKey"
6
+ :ellipsis="false"
7
+ @select="onMenuSelect"
8
+ >
9
+ <template v-for="item in menuList">
10
+ <!-- group -->
11
+ <sub-menu
12
+ v-if="item.subMenu && item.subMenu.length > 0"
13
+ :key="item.key"
14
+ :menu-item="item"
15
+ />
16
+ <!-- module -->
17
+ <el-menu-item
18
+ v-else
19
+ :key="item.name + item.key"
20
+ :index="item.key"
21
+ >
22
+ {{ item.name }}
23
+ </el-menu-item>
24
+ </template>
25
+ </el-menu>
26
+ </template>
27
+ <template #main-content>
28
+ <router-view />
29
+ </template>
30
+ </sider-container>
31
+ </template>
32
+
33
+ <script setup>
34
+ import { ref, watch, onMounted } from 'vue';
35
+ import { useRouter, useRoute } from 'vue-router';
36
+ import { useMenuStore } from '$elpisStore/menu.js';
37
+ import SiderContainer from '$elpisWidgets/sider-container/sider-container.vue';
38
+ import SubMenu from './complex-view/sub-menu/sub-menu.vue'
39
+
40
+ const router = useRouter();
41
+ const route = useRoute();
42
+ const menuStore = useMenuStore();
43
+
44
+ const menuList = ref([]);
45
+ const activeKey = ref('');
46
+
47
+ const setMenuList = () => {
48
+ const menuItem = menuStore.findMenuItem({
49
+ key: 'key',
50
+ value: route.query.key
51
+ });
52
+ if (menuItem && menuItem.siderConfig && menuItem.siderConfig.menu) {
53
+ menuList.value = menuItem.siderConfig.menu;
54
+ }
55
+ }
56
+ const setActiveKey = () => {
57
+ let siderMenuItem = menuStore.findMenuItem({
58
+ key: 'key',
59
+ value: route.query.sider_key
60
+ });
61
+
62
+ // 如果首次加载 sider-view,用户未选中左侧菜单,需要默认选中第一个
63
+ if (!siderMenuItem) {
64
+ const hMenuItem = menuStore.findMenuItem({
65
+ key: 'key',
66
+ value: route.query.key
67
+ })
68
+ if (hMenuItem && hMenuItem.siderConfig && hMenuItem.siderConfig.menu) {
69
+ const siderMenuList = hMenuItem.siderConfig.menu;
70
+ siderMenuItem = menuStore.findFirstMenuItem(siderMenuList); // 找出左侧菜单中第一项
71
+ if (siderMenuItem) {
72
+ handleMenuSelect(siderMenuItem.key);
73
+ }
74
+ }
75
+ }
76
+
77
+ activeKey.value = siderMenuItem?.key;
78
+ }
79
+
80
+ watch(() => route.query.key, () => {
81
+ setMenuList();
82
+ setActiveKey();
83
+ })
84
+ watch(() => menuStore.menuList, () => {
85
+ setMenuList();
86
+ setActiveKey();
87
+ }, { deep: true })
88
+
89
+ const onMenuSelect = (menuKey) => {
90
+ handleMenuSelect(menuKey);
91
+ }
92
+
93
+ const handleMenuSelect = (menuKey) => {
94
+ const menuItem = menuStore.findMenuItem({
95
+ key: 'key',
96
+ value: menuKey
97
+ });
98
+
99
+ const { moduleType, key, customConfig } = menuItem;
100
+
101
+ // 如果是当前页面,不处理
102
+ if (route.query.sider_key === key) return;
103
+
104
+ const pathMap = {
105
+ iframe: '/iframe',
106
+ schema: '/schema',
107
+ custom: customConfig?.path
108
+ };
109
+ router.push({
110
+ path: `/view/dashboard/sider${pathMap[moduleType]}`,
111
+ query: {
112
+ key: route.query.key,
113
+ proj_key: route.query.proj_key,
114
+ sider_key: key
115
+ }
116
+ })
117
+ }
118
+
119
+ onMounted(() => {
120
+ setMenuList();
121
+ setActiveKey();
122
+ })
123
+ </script>
124
+
125
+ <style lang="less" scoped>
126
+
127
+ </style>