diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 083049bd..093106c9 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,3 +1,21 @@ + diff --git a/frontend/src/components/plugin/CreateEditPluginDialog.vue b/frontend/src/components/plugin/CreateEditPluginDialog.vue new file mode 100644 index 00000000..14446ec7 --- /dev/null +++ b/frontend/src/components/plugin/CreateEditPluginDialog.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/frontend/src/components/plugin/PluginForm.vue b/frontend/src/components/plugin/PluginForm.vue new file mode 100644 index 00000000..4665f6b6 --- /dev/null +++ b/frontend/src/components/plugin/PluginForm.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/frontend/src/components/plugin/plugin.ts b/frontend/src/components/plugin/plugin.ts new file mode 100644 index 00000000..652e38de --- /dev/null +++ b/frontend/src/components/plugin/plugin.ts @@ -0,0 +1,30 @@ +import {readonly} from 'vue'; +import {Store} from 'vuex'; +import useForm from '@/components/form/form'; +import usePluginService from '@/services/plugin/pluginService'; +import {getDefaultFormComponentData} from '@/utils/form'; + +type Plugin = CPlugin; + +// get new plugin +export const getNewPlugin = (): Plugin => { + return {}; +}; + +// form component data +const formComponentData = getDefaultFormComponentData(getNewPlugin); + +const usePlugin = (store: Store) => { + // store + const ns = 'plugin'; + + // form rules + const formRules = readonly({}); + + return { + ...useForm(ns, store, usePluginService(store), formComponentData), + formRules, + }; +}; + +export default usePlugin; diff --git a/frontend/src/constants/plugin.ts b/frontend/src/constants/plugin.ts new file mode 100644 index 00000000..f0b65473 --- /dev/null +++ b/frontend/src/constants/plugin.ts @@ -0,0 +1,2 @@ +export const PLUGIN_UI_COMPONENT_TYPE_VIEW = 'view'; +export const PLUGIN_UI_COMPONENT_TYPE_TAB = 'tab'; diff --git a/frontend/src/interfaces/models/plugin.d.ts b/frontend/src/interfaces/models/plugin.d.ts new file mode 100644 index 00000000..24bd0acd --- /dev/null +++ b/frontend/src/interfaces/models/plugin.d.ts @@ -0,0 +1,20 @@ +interface CPlugin extends BaseModel { + name?: string; + description?: string; + type?: string; + proto?: string; + active?: boolean; + endpoint?: string; + cmd?: string; + ui_components?: PluginUIComponent[]; + ui_sidebar_navs?: MenuItem[]; +} + +interface PluginUIComponent { + name?: string; + title?: string; + src?: string; + type?: string; + path?: string; + parent_paths?: string[]; +} diff --git a/frontend/src/interfaces/services/request.d.ts b/frontend/src/interfaces/services/request.d.ts index 920f5614..43b54d16 100644 --- a/frontend/src/interfaces/services/request.d.ts +++ b/frontend/src/interfaces/services/request.d.ts @@ -2,6 +2,7 @@ interface ListRequestParams { page?: number; size?: number; conditions?: FilterConditionData[] | string; + all?: boolean | string | number; } interface BatchRequestPayload { diff --git a/frontend/src/interfaces/store/index.d.ts b/frontend/src/interfaces/store/index.d.ts index b641e26f..afa8ed18 100644 --- a/frontend/src/interfaces/store/index.d.ts +++ b/frontend/src/interfaces/store/index.d.ts @@ -14,6 +14,7 @@ declare global { schedule: ScheduleStoreState; user: UserStoreState; token: TokenStoreState; + plugin: PluginStoreState; } type StoreGetter = (state: S, getters: StoreGetter, rootState: RootStoreState, rootGetters: any) => T; @@ -103,6 +104,7 @@ declare global { collapseSidebar: StoreMutation>; expandActions: StoreMutation>; collapseActions: StoreMutation>; + setTabs: StoreMutation; setAfterSave: StoreMutation, (() => Promise)[]>; } @@ -133,7 +135,8 @@ declare global { | 'tag' | 'dataCollection' | 'user' - | 'token'; + | 'token' + | 'plugin'; type ListStoreNamespace = 'node' | 'project' @@ -143,7 +146,8 @@ declare global { | 'dataCollection' | 'schedule' | 'user' - | 'token'; + | 'token' + | 'plugin'; interface StoreContext { namespace: StoreNamespace; diff --git a/frontend/src/interfaces/store/modules/layout.d.ts b/frontend/src/interfaces/store/modules/layout.d.ts index dc57f98e..392787db 100644 --- a/frontend/src/interfaces/store/modules/layout.d.ts +++ b/frontend/src/interfaces/store/modules/layout.d.ts @@ -26,6 +26,7 @@ declare global { } interface LayoutStoreMutations extends MutationTree { + setMenuItems: StoreMutation; setSideBarCollapsed: StoreMutation; setTabs: StoreMutation; setActiveTabId: StoreMutation; diff --git a/frontend/src/interfaces/store/modules/node.d.ts b/frontend/src/interfaces/store/modules/node.d.ts index 4a91d440..b8d093fc 100644 --- a/frontend/src/interfaces/store/modules/node.d.ts +++ b/frontend/src/interfaces/store/modules/node.d.ts @@ -1,5 +1,3 @@ -type Node = CNode; - type NodeStoreModule = BaseModule; type NodeStoreState = BaseStoreState; diff --git a/frontend/src/interfaces/store/modules/plugin.d.ts b/frontend/src/interfaces/store/modules/plugin.d.ts new file mode 100644 index 00000000..c11cc4aa --- /dev/null +++ b/frontend/src/interfaces/store/modules/plugin.d.ts @@ -0,0 +1,9 @@ +type PluginStoreModule = BaseModule; + +type PluginStoreState = BaseStoreState; + +type PluginStoreGetters = BaseStoreGetters; + +type PluginStoreMutations = BaseStoreMutations; + +type PluginStoreActions = BaseStoreActions; diff --git a/frontend/src/layouts/BasicLayout.vue b/frontend/src/layouts/BasicLayout.vue index eb5def30..4f1fdd9a 100644 --- a/frontend/src/layouts/BasicLayout.vue +++ b/frontend/src/layouts/BasicLayout.vue @@ -1,22 +1,23 @@ + + diff --git a/frontend/src/views/plugin/detail/tabs/PluginDetailTabOverview.vue b/frontend/src/views/plugin/detail/tabs/PluginDetailTabOverview.vue new file mode 100644 index 00000000..56271f24 --- /dev/null +++ b/frontend/src/views/plugin/detail/tabs/PluginDetailTabOverview.vue @@ -0,0 +1,24 @@ + + + diff --git a/frontend/src/views/plugin/list/PluginList.vue b/frontend/src/views/plugin/list/PluginList.vue new file mode 100644 index 00000000..1d556468 --- /dev/null +++ b/frontend/src/views/plugin/list/PluginList.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/frontend/src/views/plugin/list/pluginList.ts b/frontend/src/views/plugin/list/pluginList.ts new file mode 100644 index 00000000..d9174b52 --- /dev/null +++ b/frontend/src/views/plugin/list/pluginList.ts @@ -0,0 +1,123 @@ +import useList from '@/layouts/list'; +import {useStore} from 'vuex'; +import {getDefaultUseListOptions, setupListComponent} from '@/utils/list'; +import {computed, h} from 'vue'; +import {TABLE_COLUMN_NAME_ACTIONS} from '@/constants/table'; +import {ElMessageBox} from 'element-plus'; +import usePluginService from '@/services/plugin/pluginService'; +import NavLink from '@/components/nav/NavLink.vue'; +import {useRouter} from 'vue-router'; + +type Plugin = CPlugin; + +const usePluginList = () => { + // router + const router = useRouter(); + + // store + const ns = 'plugin'; + const store = useStore(); + const {commit} = store; + + // services + const { + getList, + deleteById, + } = usePluginService(store); + + // nav actions + const navActions = computed(() => [ + { + name: 'common', + children: [ + { + buttonType: 'label', + label: 'New Plugin', + tooltip: 'New Plugin', + icon: ['fa', 'plus'], + type: 'success', + onClick: () => { + commit(`${ns}/showDialog`, 'create'); + } + } + ] + } + ]); + + // table columns + const tableColumns = computed>(() => [ + { + key: 'name', // name + label: 'Name', + icon: ['fa', 'font'], + width: '150', + value: (row: Plugin) => h(NavLink, { + path: `/plugins/${row._id}`, + label: row.name, + }), + hasSort: true, + hasFilter: true, + allowFilterSearch: true, + }, + { + key: 'description', + label: 'Description', + icon: ['fa', 'comment-alt'], + width: 'auto', + hasFilter: true, + allowFilterSearch: true, + }, + { + key: TABLE_COLUMN_NAME_ACTIONS, + label: 'Actions', + fixed: 'right', + width: '200', + buttons: [ + { + type: 'primary', + icon: ['fa', 'search'], + tooltip: 'View', + onClick: (row) => { + router.push(`/plugins/${row._id}`); + }, + }, + // { + // type: 'info', + // size: 'mini', + // icon: ['fa', 'clone'], + // tooltip: 'Clone', + // onClick: (row) => { + // console.log('clone', row); + // } + // }, + { + type: 'danger', + size: 'mini', + icon: ['fa', 'trash-alt'], + tooltip: 'Delete', + disabled: (row: Plugin) => !!row.active, + onClick: async (row: Plugin) => { + const res = await ElMessageBox.confirm('Are you sure to delete?', 'Delete'); + if (res) { + await deleteById(row._id as string); + } + await getList(); + }, + }, + ], + disableTransfer: true, + } + ]); + + // options + const opts = getDefaultUseListOptions(navActions, tableColumns); + + // init + setupListComponent(ns, store, []); + + return { + ...useList(ns, store, opts) + }; +}; + +export default usePluginList;