├── .gitignore
├── LICENSE
├── README.md
├── conf
├── app.conf
└── prometheus.yml
├── controllers
├── base.go
└── prometheus.go
├── frontend
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── .stylelintrc.js
├── README.md
├── config
│ ├── config.js
│ ├── defaultSettings.js
│ ├── plugin.config.js
│ └── themePluginConfig.js
├── jest-puppeteer.config.js
├── jest.config.js
├── jsconfig.json
├── mock
│ ├── notices.js
│ ├── prometheus.js
│ ├── route.js
│ └── user.js
├── package.json
├── public
│ ├── favicon.png
│ └── icons
│ │ ├── icon-128x128.png
│ │ ├── icon-192x192.png
│ │ └── icon-512x512.png
├── src
│ ├── assets
│ │ └── logo.svg
│ ├── components
│ │ ├── Authorized
│ │ │ ├── Authorized.jsx
│ │ │ ├── AuthorizedRoute.jsx
│ │ │ ├── CheckPermissions.jsx
│ │ │ ├── PromiseRender.jsx
│ │ │ ├── Secured.jsx
│ │ │ ├── index.jsx
│ │ │ └── renderAuthorize.js
│ │ ├── GlobalHeader
│ │ │ ├── AvatarDropdown.jsx
│ │ │ ├── NoticeIconView.jsx
│ │ │ ├── RightContent.jsx
│ │ │ └── index.less
│ │ ├── HeaderDropdown
│ │ │ ├── index.jsx
│ │ │ └── index.less
│ │ ├── HeaderSearch
│ │ │ ├── index.jsx
│ │ │ └── index.less
│ │ ├── NoticeIcon
│ │ │ ├── NoticeList.jsx
│ │ │ ├── NoticeList.less
│ │ │ ├── index.jsx
│ │ │ └── index.less
│ │ ├── PageLoading
│ │ │ └── index.jsx
│ │ └── SelectLang
│ │ │ ├── index.jsx
│ │ │ └── index.less
│ ├── e2e
│ │ ├── __mocks__
│ │ │ └── antd-pro-merge-less.js
│ │ ├── baseLayout.e2e.js
│ │ └── topMenu.e2e.js
│ ├── global.jsx
│ ├── global.less
│ ├── layouts
│ │ ├── BasicLayout.jsx
│ │ ├── BlankLayout.jsx
│ │ ├── SecurityLayout.jsx
│ │ ├── UserLayout.jsx
│ │ └── UserLayout.less
│ ├── locales
│ │ ├── en-US.js
│ │ ├── en-US
│ │ │ ├── component.js
│ │ │ ├── globalHeader.js
│ │ │ ├── menu.js
│ │ │ ├── pwa.js
│ │ │ ├── settingDrawer.js
│ │ │ └── settings.js
│ │ ├── pt-BR.js
│ │ ├── pt-BR
│ │ │ ├── component.js
│ │ │ ├── globalHeader.js
│ │ │ ├── menu.js
│ │ │ ├── pwa.js
│ │ │ ├── settingDrawer.js
│ │ │ └── settings.js
│ │ ├── zh-CN.js
│ │ ├── zh-CN
│ │ │ ├── component.js
│ │ │ ├── globalHeader.js
│ │ │ ├── menu.js
│ │ │ ├── pwa.js
│ │ │ ├── settingDrawer.js
│ │ │ └── settings.js
│ │ ├── zh-TW.js
│ │ └── zh-TW
│ │ │ ├── component.js
│ │ │ ├── globalHeader.js
│ │ │ ├── menu.js
│ │ │ ├── pwa.js
│ │ │ ├── settingDrawer.js
│ │ │ └── settings.js
│ ├── manifest.json
│ ├── models
│ │ ├── global.js
│ │ ├── login.js
│ │ ├── prometheus.js
│ │ ├── setting.js
│ │ └── user.js
│ ├── pages
│ │ ├── 404.jsx
│ │ ├── Admin.jsx
│ │ ├── Authorized.jsx
│ │ ├── Welcome.jsx
│ │ ├── Welcome.less
│ │ ├── account
│ │ │ ├── center
│ │ │ │ ├── Center.less
│ │ │ │ ├── _mock.js
│ │ │ │ ├── components
│ │ │ │ │ ├── Applications
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── ArticleListContent
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── Articles
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── AvatarList
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ └── Projects
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── index.jsx
│ │ │ │ ├── model.js
│ │ │ │ └── service.js
│ │ │ └── settings
│ │ │ │ ├── _mock.js
│ │ │ │ ├── components
│ │ │ │ ├── BaseView.less
│ │ │ │ ├── GeographicView.jsx
│ │ │ │ ├── GeographicView.less
│ │ │ │ ├── PhoneView.jsx
│ │ │ │ ├── PhoneView.less
│ │ │ │ ├── base.jsx
│ │ │ │ ├── binding.jsx
│ │ │ │ ├── notification.jsx
│ │ │ │ └── security.jsx
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── geographic
│ │ │ │ ├── city.json
│ │ │ │ └── province.json
│ │ │ │ ├── index.jsx
│ │ │ │ ├── locales
│ │ │ │ ├── en-US.js
│ │ │ │ ├── zh-CN.js
│ │ │ │ └── zh-TW.js
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ └── style.less
│ │ ├── dashboard
│ │ │ ├── analysis
│ │ │ │ ├── _mock.js
│ │ │ │ ├── components
│ │ │ │ │ ├── Charts
│ │ │ │ │ │ ├── Bar
│ │ │ │ │ │ │ └── index.jsx
│ │ │ │ │ │ ├── ChartCard
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── Field
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── Gauge
│ │ │ │ │ │ │ └── index.jsx
│ │ │ │ │ │ ├── MiniArea
│ │ │ │ │ │ │ └── index.jsx
│ │ │ │ │ │ ├── MiniBar
│ │ │ │ │ │ │ └── index.jsx
│ │ │ │ │ │ ├── MiniProgress
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── Pie
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── TagCloud
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── TimelineChart
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── WaterWave
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── autoHeight.jsx
│ │ │ │ │ │ ├── bizcharts.d.ts
│ │ │ │ │ │ ├── bizcharts.jsx
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── IntroduceRow.jsx
│ │ │ │ │ ├── NumberInfo
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── OfflineData.jsx
│ │ │ │ │ ├── PageLoading
│ │ │ │ │ │ └── index.jsx
│ │ │ │ │ ├── ProportionSales.jsx
│ │ │ │ │ ├── SalesCard.jsx
│ │ │ │ │ ├── TopSearch.jsx
│ │ │ │ │ └── Trend
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── index.jsx
│ │ │ │ ├── locales
│ │ │ │ │ ├── en-US.js
│ │ │ │ │ ├── pt-BR.js
│ │ │ │ │ ├── zh-CN.js
│ │ │ │ │ └── zh-TW.js
│ │ │ │ ├── model.jsx
│ │ │ │ ├── service.jsx
│ │ │ │ ├── style.less
│ │ │ │ └── utils
│ │ │ │ │ ├── Yuan.jsx
│ │ │ │ │ ├── utils.js
│ │ │ │ │ └── utils.less
│ │ │ ├── monitor
│ │ │ │ ├── _mock.js
│ │ │ │ ├── components
│ │ │ │ │ ├── ActiveChart
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ └── Charts
│ │ │ │ │ │ ├── Gauge
│ │ │ │ │ │ └── index.jsx
│ │ │ │ │ │ ├── Map
│ │ │ │ │ │ └── index.jsx
│ │ │ │ │ │ ├── MiniArea
│ │ │ │ │ │ └── index.jsx
│ │ │ │ │ │ ├── Pie
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── TagCloud
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── WaterWave
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── autoHeight.jsx
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── index.jsx
│ │ │ │ ├── locales
│ │ │ │ │ ├── en-US.js
│ │ │ │ │ ├── pt-BR.js
│ │ │ │ │ ├── zh-CN.js
│ │ │ │ │ └── zh-TW.js
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ └── style.less
│ │ │ └── workplace
│ │ │ │ ├── _mock.js
│ │ │ │ ├── components
│ │ │ │ ├── EditableLinkGroup
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ │ └── Radar
│ │ │ │ │ ├── autoHeight.jsx
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── index.jsx
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ └── style.less
│ │ ├── document.ejs
│ │ ├── editor
│ │ │ ├── flow
│ │ │ │ ├── common
│ │ │ │ │ └── IconFont
│ │ │ │ │ │ └── index.js
│ │ │ │ ├── components
│ │ │ │ │ ├── EditorContextMenu
│ │ │ │ │ │ ├── FlowContextMenu.jsx
│ │ │ │ │ │ ├── KoniContextMenu.jsx
│ │ │ │ │ │ ├── MenuItem.jsx
│ │ │ │ │ │ ├── MindContextMenu.jsx
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── EditorDetailPanel
│ │ │ │ │ │ ├── DetailForm.jsx
│ │ │ │ │ │ ├── FlowDetailPanel.jsx
│ │ │ │ │ │ ├── KoniDetailPanel.jsx
│ │ │ │ │ │ ├── MindDetailPanel.jsx
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── EditorItemPanel
│ │ │ │ │ │ ├── FlowItemPanel.jsx
│ │ │ │ │ │ ├── KoniItemPanel.jsx
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── EditorMinimap
│ │ │ │ │ │ └── index.jsx
│ │ │ │ │ └── EditorToolbar
│ │ │ │ │ │ ├── FlowToolbar.jsx
│ │ │ │ │ │ ├── KoniToolbar.jsx
│ │ │ │ │ │ ├── MindToolbar.jsx
│ │ │ │ │ │ ├── ToolbarButton.jsx
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ ├── index.jsx
│ │ │ │ ├── index.less
│ │ │ │ └── locales
│ │ │ │ │ ├── en-US.js
│ │ │ │ │ └── zh-CN.js
│ │ │ ├── koni
│ │ │ │ ├── common
│ │ │ │ │ └── IconFont
│ │ │ │ │ │ └── index.js
│ │ │ │ ├── components
│ │ │ │ │ ├── EditorContextMenu
│ │ │ │ │ │ ├── FlowContextMenu.jsx
│ │ │ │ │ │ ├── KoniContextMenu.jsx
│ │ │ │ │ │ ├── MenuItem.jsx
│ │ │ │ │ │ ├── MindContextMenu.jsx
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── EditorDetailPanel
│ │ │ │ │ │ ├── DetailForm.jsx
│ │ │ │ │ │ ├── FlowDetailPanel.jsx
│ │ │ │ │ │ ├── KoniDetailPanel.jsx
│ │ │ │ │ │ ├── MindDetailPanel.jsx
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── EditorItemPanel
│ │ │ │ │ │ ├── FlowItemPanel.jsx
│ │ │ │ │ │ ├── KoniItemPanel.jsx
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── EditorMinimap
│ │ │ │ │ │ └── index.jsx
│ │ │ │ │ └── EditorToolbar
│ │ │ │ │ │ ├── FlowToolbar.jsx
│ │ │ │ │ │ ├── KoniToolbar.jsx
│ │ │ │ │ │ ├── MindToolbar.jsx
│ │ │ │ │ │ ├── ToolbarButton.jsx
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ ├── index.jsx
│ │ │ │ ├── index.less
│ │ │ │ └── locales
│ │ │ │ │ ├── en-US.js
│ │ │ │ │ └── zh-CN.js
│ │ │ └── mind
│ │ │ │ ├── common
│ │ │ │ └── IconFont
│ │ │ │ │ └── index.js
│ │ │ │ ├── components
│ │ │ │ ├── EditorContextMenu
│ │ │ │ │ ├── FlowContextMenu.jsx
│ │ │ │ │ ├── KoniContextMenu.jsx
│ │ │ │ │ ├── MenuItem.jsx
│ │ │ │ │ ├── MindContextMenu.jsx
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ │ ├── EditorDetailPanel
│ │ │ │ │ ├── DetailForm.jsx
│ │ │ │ │ ├── FlowDetailPanel.jsx
│ │ │ │ │ ├── KoniDetailPanel.jsx
│ │ │ │ │ ├── MindDetailPanel.jsx
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ │ ├── EditorItemPanel
│ │ │ │ │ ├── FlowItemPanel.jsx
│ │ │ │ │ ├── KoniItemPanel.jsx
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ │ ├── EditorMinimap
│ │ │ │ │ └── index.jsx
│ │ │ │ └── EditorToolbar
│ │ │ │ │ ├── FlowToolbar.jsx
│ │ │ │ │ ├── KoniToolbar.jsx
│ │ │ │ │ ├── MindToolbar.jsx
│ │ │ │ │ ├── ToolbarButton.jsx
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ │ ├── index.jsx
│ │ │ │ ├── index.less
│ │ │ │ ├── locales
│ │ │ │ ├── en-US.js
│ │ │ │ └── zh-CN.js
│ │ │ │ └── worldCup2018.json
│ │ ├── exception
│ │ │ ├── 403
│ │ │ │ ├── index.jsx
│ │ │ │ └── locales
│ │ │ │ │ ├── en-US.js
│ │ │ │ │ ├── pt-BR.js
│ │ │ │ │ ├── zh-CN.js
│ │ │ │ │ └── zh-TW.js
│ │ │ ├── 404
│ │ │ │ ├── index.jsx
│ │ │ │ └── locales
│ │ │ │ │ ├── en-US.js
│ │ │ │ │ ├── pt-BR.js
│ │ │ │ │ ├── zh-CN.js
│ │ │ │ │ └── zh-TW.js
│ │ │ └── 500
│ │ │ │ ├── index.jsx
│ │ │ │ └── locales
│ │ │ │ ├── en-US.js
│ │ │ │ ├── pt-BR.js
│ │ │ │ ├── zh-CN.js
│ │ │ │ └── zh-TW.js
│ │ ├── form
│ │ │ ├── advanced-form
│ │ │ │ ├── _mock.js
│ │ │ │ ├── components
│ │ │ │ │ ├── FooterToolbar
│ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ └── index.less
│ │ │ │ │ └── TableForm.jsx
│ │ │ │ ├── index.jsx
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ └── style.less
│ │ │ ├── basic-form
│ │ │ │ ├── _mock.js
│ │ │ │ ├── index.jsx
│ │ │ │ ├── locales
│ │ │ │ │ ├── en-US.js
│ │ │ │ │ ├── pt-BR.js
│ │ │ │ │ ├── zh-CN.js
│ │ │ │ │ └── zh-TW.js
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ └── style.less
│ │ │ └── step-form
│ │ │ │ ├── _mock.js
│ │ │ │ ├── components
│ │ │ │ ├── Step1
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ │ ├── Step2
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ │ └── Step3
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ │ ├── index.jsx
│ │ │ │ ├── locales
│ │ │ │ ├── en-US.js
│ │ │ │ ├── pt-BR.js
│ │ │ │ ├── zh-CN.js
│ │ │ │ └── zh-TW.js
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ └── style.less
│ │ ├── list
│ │ │ ├── basic-list
│ │ │ │ ├── _mock.js
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── index.jsx
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ ├── style.less
│ │ │ │ └── utils
│ │ │ │ │ └── utils.less
│ │ │ ├── card-list
│ │ │ │ ├── _mock.js
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── index.jsx
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ ├── style.less
│ │ │ │ └── utils
│ │ │ │ │ └── utils.less
│ │ │ ├── search
│ │ │ │ ├── applications
│ │ │ │ │ ├── _mock.js
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── StandardFormRow
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ └── TagSelect
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── data.d.ts
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ ├── model.js
│ │ │ │ │ ├── service.js
│ │ │ │ │ ├── style.less
│ │ │ │ │ └── utils
│ │ │ │ │ │ └── utils.less
│ │ │ │ ├── articles
│ │ │ │ │ ├── _mock.js
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── ArticleListContent
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── StandardFormRow
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ └── TagSelect
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── data.d.ts
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ ├── model.js
│ │ │ │ │ ├── service.js
│ │ │ │ │ └── style.less
│ │ │ │ ├── index.jsx
│ │ │ │ ├── projects
│ │ │ │ │ ├── _mock.js
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── AvatarList
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ ├── StandardFormRow
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ │ └── TagSelect
│ │ │ │ │ │ │ ├── index.jsx
│ │ │ │ │ │ │ └── index.less
│ │ │ │ │ ├── data.d.ts
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ ├── model.js
│ │ │ │ │ ├── service.js
│ │ │ │ │ ├── style.less
│ │ │ │ │ └── utils
│ │ │ │ │ │ └── utils.less
│ │ │ │ └── utils
│ │ │ │ │ └── utils.less
│ │ │ └── table-list
│ │ │ │ ├── _mock.js
│ │ │ │ ├── components
│ │ │ │ ├── CreateForm.jsx
│ │ │ │ └── UpdateForm.jsx
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── index.jsx
│ │ │ │ └── service.js
│ │ ├── profile
│ │ │ ├── advanced
│ │ │ │ ├── _mock.js
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── index copy.jsx
│ │ │ │ ├── index.jsx
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ └── style.less
│ │ │ └── basic
│ │ │ │ ├── _mock.js
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── index.jsx
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ └── style.less
│ │ ├── prometheus
│ │ │ ├── _mock.js
│ │ │ ├── components
│ │ │ │ ├── MonitorCodeMirror
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ │ └── MonitorEdit
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ └── index.less
│ │ │ ├── data.d.ts
│ │ │ ├── index.jsx
│ │ │ ├── model.js
│ │ │ ├── service.js
│ │ │ └── style.less
│ │ ├── result
│ │ │ ├── fail
│ │ │ │ ├── index.jsx
│ │ │ │ ├── index.less
│ │ │ │ └── locales
│ │ │ │ │ ├── en-US.js
│ │ │ │ │ ├── zh-CN.js
│ │ │ │ │ └── zh-TW.js
│ │ │ └── success
│ │ │ │ ├── index.jsx
│ │ │ │ ├── index.less
│ │ │ │ └── locales
│ │ │ │ ├── en-US.js
│ │ │ │ ├── zh-CN.js
│ │ │ │ └── zh-TW.js
│ │ └── user
│ │ │ ├── login
│ │ │ ├── _mock.js
│ │ │ ├── components
│ │ │ │ └── Login
│ │ │ │ │ ├── LoginContext.jsx
│ │ │ │ │ ├── LoginItem.jsx
│ │ │ │ │ ├── LoginSubmit.jsx
│ │ │ │ │ ├── LoginTab.jsx
│ │ │ │ │ ├── index.jsx
│ │ │ │ │ ├── index.less
│ │ │ │ │ └── map.jsx
│ │ │ ├── index.jsx
│ │ │ ├── locales
│ │ │ │ ├── en-US.js
│ │ │ │ ├── zh-CN.js
│ │ │ │ └── zh-TW.js
│ │ │ ├── model.js
│ │ │ ├── service.js
│ │ │ ├── style.less
│ │ │ └── utils
│ │ │ │ └── utils.js
│ │ │ ├── register-result
│ │ │ ├── index.jsx
│ │ │ ├── locales
│ │ │ │ ├── en-US.js
│ │ │ │ ├── zh-CN.js
│ │ │ │ └── zh-TW.js
│ │ │ └── style.less
│ │ │ └── register
│ │ │ ├── _mock.js
│ │ │ ├── index.jsx
│ │ │ ├── locales
│ │ │ ├── en-US.js
│ │ │ ├── zh-CN.js
│ │ │ └── zh-TW.js
│ │ │ ├── model.js
│ │ │ ├── service.js
│ │ │ └── style.less
│ ├── service-worker.js
│ ├── services
│ │ ├── login.js
│ │ ├── prometheus.js
│ │ └── user.js
│ └── utils
│ │ ├── Authorized.js
│ │ ├── authority.js
│ │ ├── authority.test.js
│ │ ├── request.js
│ │ ├── utils.js
│ │ ├── utils.less
│ │ └── utils.test.js
└── tests
│ ├── run-tests.js
│ └── setupTests.js
├── go.mod
├── go.sum
├── main.go
├── models
└── prometheus.go
├── routers
├── commentsRouter_controllers.go
└── router.go
├── services
├── base.go
└── prometheus.go
└── utils
├── const.go
├── date.go
├── date_test.go
├── errorcode.go
├── errorcode_test.go
├── helper.go
└── helper_test.go
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lastupdate.tmp
3 | .idea
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 vermouth
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # prometheusPro
2 | prometheus配置管理、alertmanager规则、报警事件管理。使用 beego + ant-design
3 |
--------------------------------------------------------------------------------
/conf/app.conf:
--------------------------------------------------------------------------------
1 | appname = proBackend
2 | httpport = 8080
3 | runmode = dev
4 | configFilePath = /Users/xuyasong/go/src/prometheusPro/proBackend/conf/prometheus.yml
--------------------------------------------------------------------------------
/frontend/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [Makefile]
16 | indent_style = tab
17 |
--------------------------------------------------------------------------------
/frontend/.eslintignore:
--------------------------------------------------------------------------------
1 | /lambda/
2 | /scripts
3 | /config
4 | .history
--------------------------------------------------------------------------------
/frontend/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [require.resolve('@umijs/fabric/dist/eslint')],
3 | globals: {
4 | ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
5 | page: true,
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | **/node_modules
5 | # roadhog-api-doc ignore
6 | /src/utils/request-temp.js
7 | _roadhog-api-doc
8 |
9 | # production
10 | /dist
11 | /.vscode
12 |
13 | # misc
14 | .DS_Store
15 | npm-debug.log*
16 | yarn-error.log
17 |
18 | /coverage
19 | .idea
20 | yarn.lock
21 | package-lock.json
22 | *bak
23 | .vscode
24 |
25 | # visual studio code
26 | .history
27 | *.log
28 | functions/*
29 | .temp/**
30 |
31 | # umi
32 | .umi
33 | .umi-production
34 |
35 | # screenshot
36 | screenshot
37 | .firebase
38 | .eslintcache
39 |
40 | build
41 |
--------------------------------------------------------------------------------
/frontend/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.svg
2 | package.json
3 | .umi
4 | .umi-production
5 | /dist
6 | .dockerignore
7 | .DS_Store
8 | .eslintignore
9 | *.png
10 | *.toml
11 | docker
12 | .editorconfig
13 | Dockerfile*
14 | .gitignore
15 | .prettierignore
16 | LICENSE
17 | .eslintcache
18 | *.lock
19 | yarn-error.log
20 | .history
--------------------------------------------------------------------------------
/frontend/.prettierrc.js:
--------------------------------------------------------------------------------
1 | const fabric = require('@umijs/fabric');
2 |
3 | module.exports = {
4 | ...fabric.prettier,
5 | };
6 |
--------------------------------------------------------------------------------
/frontend/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | const fabric = require('@umijs/fabric');
2 |
3 | module.exports = {
4 | ...fabric.stylelint,
5 | };
6 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Ant Design Pro
2 |
3 | This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use.
4 |
5 | ## Environment Prepare
6 |
7 | Install `node_modules`:
8 |
9 | ```bash
10 | npm install
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn
17 | ```
18 |
19 | ## Provided Scripts
20 |
21 | Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test.
22 |
23 | Scripts provided in `package.json`. It's safe to modify or add additional script:
24 |
25 | ### Start project
26 |
27 | ```bash
28 | npm start
29 | ```
30 |
31 | ### Build project
32 |
33 | ```bash
34 | npm run build
35 | ```
36 |
37 | ### Check code style
38 |
39 | ```bash
40 | npm run lint
41 | ```
42 |
43 | You can also use script to auto fix some lint error:
44 |
45 | ```bash
46 | npm run lint:fix
47 | ```
48 |
49 | ### Test code
50 |
51 | ```bash
52 | npm test
53 | ```
54 |
55 | ## More
56 |
57 | You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro).
58 |
--------------------------------------------------------------------------------
/frontend/config/defaultSettings.js:
--------------------------------------------------------------------------------
1 | export default {
2 | "navTheme": "light",
3 | "primaryColor": "daybreak",
4 | "layout": "sidemenu",
5 | "contentWidth": "Fluid",
6 | "fixedHeader": true,
7 | "autoHideHeader": false,
8 | "fixSiderbar": true,
9 | "menu": {
10 | "locale": true
11 | },
12 | "title": "监控中心",
13 | "pwa": false,
14 | "iconfontUrl": ""
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/jest-puppeteer.config.js:
--------------------------------------------------------------------------------
1 | // ps https://github.com/GoogleChrome/puppeteer/issues/3120
2 | module.exports = {
3 | launch: {
4 | args: [
5 | '--disable-gpu',
6 | '--disable-dev-shm-usage',
7 | '--no-first-run',
8 | '--no-zygote',
9 | '--no-sandbox',
10 | ],
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/frontend/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testURL: 'http://localhost:8000',
3 | preset: 'jest-puppeteer',
4 | extraSetupFiles: ['./tests/setupTests.js'],
5 | globals: {
6 | ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,
7 | localStorage: null,
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "emitDecoratorMetadata": true,
4 | "experimentalDecorators": true,
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": ["./src/*"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/mock/route.js:
--------------------------------------------------------------------------------
1 | export default {
2 | '/api/auth_routes': {
3 | '/form/advanced-form': {
4 | authority: ['admin', 'user'],
5 | },
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/frontend/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yasongxu/prometheusPro/528cf8d7000423a2ca355da7d729f0c375892700/frontend/public/favicon.png
--------------------------------------------------------------------------------
/frontend/public/icons/icon-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yasongxu/prometheusPro/528cf8d7000423a2ca355da7d729f0c375892700/frontend/public/icons/icon-128x128.png
--------------------------------------------------------------------------------
/frontend/public/icons/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yasongxu/prometheusPro/528cf8d7000423a2ca355da7d729f0c375892700/frontend/public/icons/icon-192x192.png
--------------------------------------------------------------------------------
/frontend/public/icons/icon-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yasongxu/prometheusPro/528cf8d7000423a2ca355da7d729f0c375892700/frontend/public/icons/icon-512x512.png
--------------------------------------------------------------------------------
/frontend/src/components/Authorized/Authorized.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Result } from 'antd';
3 | import check from './CheckPermissions';
4 |
5 | const Authorized = ({
6 | children,
7 | authority,
8 | noMatch = (
9 |
14 | ),
15 | }) => {
16 | const childrenRender = typeof children === 'undefined' ? null : children;
17 | const dom = check(authority, childrenRender, noMatch);
18 | return <>{dom}>;
19 | };
20 |
21 | export default Authorized;
22 |
--------------------------------------------------------------------------------
/frontend/src/components/Authorized/AuthorizedRoute.jsx:
--------------------------------------------------------------------------------
1 | import { Redirect, Route } from 'umi';
2 | import React from 'react';
3 | import Authorized from './Authorized';
4 |
5 | const AuthorizedRoute = ({ component: Component, render, authority, redirectPath, ...rest }) => (
6 | (
12 |
17 | )}
18 | />
19 | }
20 | >
21 | (Component ? : render(props))} />
22 |
23 | );
24 |
25 | export default AuthorizedRoute;
26 |
--------------------------------------------------------------------------------
/frontend/src/components/Authorized/index.jsx:
--------------------------------------------------------------------------------
1 | import Authorized from './Authorized';
2 | import Secured from './Secured';
3 | import check from './CheckPermissions';
4 | import renderAuthorize from './renderAuthorize';
5 | Authorized.Secured = Secured;
6 | Authorized.check = check;
7 | const RenderAuthorize = renderAuthorize(Authorized);
8 | export default RenderAuthorize;
9 |
--------------------------------------------------------------------------------
/frontend/src/components/Authorized/renderAuthorize.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable eslint-comments/disable-enable-pair */
2 |
3 | /* eslint-disable import/no-mutable-exports */
4 | let CURRENT = 'NULL';
5 |
6 | /**
7 | * use authority or getAuthority
8 | * @param {string|()=>String} currentAuthority
9 | */
10 | const renderAuthorize = Authorized => currentAuthority => {
11 | if (currentAuthority) {
12 | if (typeof currentAuthority === 'function') {
13 | CURRENT = currentAuthority();
14 | }
15 |
16 | if (
17 | Object.prototype.toString.call(currentAuthority) === '[object String]' ||
18 | Array.isArray(currentAuthority)
19 | ) {
20 | CURRENT = currentAuthority;
21 | }
22 | } else {
23 | CURRENT = 'NULL';
24 | }
25 |
26 | return Authorized;
27 | };
28 |
29 | export { CURRENT };
30 | export default Authorized => renderAuthorize(Authorized);
31 |
--------------------------------------------------------------------------------
/frontend/src/components/GlobalHeader/RightContent.jsx:
--------------------------------------------------------------------------------
1 | import { QuestionCircleOutlined } from '@ant-design/icons';
2 | import { Tooltip } from 'antd';
3 | import React from 'react';
4 | import { connect } from 'dva';
5 | import Avatar from './AvatarDropdown';
6 | import HeaderSearch from '../HeaderSearch';
7 | import SelectLang from '../SelectLang';
8 | import styles from './index.less';
9 | import NoticeIconView from './NoticeIconView';
10 |
11 | const GlobalHeaderRight = props => {
12 | const { theme, layout } = props;
13 | let className = styles.right;
14 |
15 | if (theme === 'dark' && layout === 'topmenu') {
16 | className = `${styles.right} ${styles.dark}`;
17 | }
18 |
19 | return (
20 |
24 | );
25 | };
26 |
27 | export default connect(({ settings }) => ({
28 | theme: settings.navTheme,
29 | layout: settings.layout,
30 | }))(GlobalHeaderRight);
31 |
--------------------------------------------------------------------------------
/frontend/src/components/HeaderDropdown/index.jsx:
--------------------------------------------------------------------------------
1 | import { Dropdown } from 'antd';
2 | import React from 'react';
3 | import classNames from 'classnames';
4 | import styles from './index.less';
5 |
6 | const HeaderDropdown = ({ overlayClassName: cls, ...restProps }) => (
7 |
8 | );
9 |
10 | export default HeaderDropdown;
11 |
--------------------------------------------------------------------------------
/frontend/src/components/HeaderDropdown/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .container > * {
4 | background-color: @popover-bg;
5 | border-radius: 4px;
6 | box-shadow: @shadow-1-down;
7 | }
8 |
9 | @media screen and (max-width: @screen-xs) {
10 | .container {
11 | width: 100% !important;
12 | }
13 | .container > * {
14 | border-radius: 0 !important;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/frontend/src/components/HeaderSearch/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .headerSearch {
4 | .input {
5 | width: 0;
6 | min-width: 0;
7 | overflow: hidden;
8 | background: transparent;
9 | border-radius: 0;
10 | transition: width 0.3s, margin-left 0.3s;
11 | :global(.ant-select-selection) {
12 | background: transparent;
13 | }
14 | input {
15 | padding-right: 0;
16 | padding-left: 0;
17 | border: 0;
18 | box-shadow: none !important;
19 | }
20 | &,
21 | &:hover,
22 | &:focus {
23 | border-bottom: 1px solid @border-color-base;
24 | }
25 | &.show {
26 | width: 210px;
27 | margin-left: 8px;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/frontend/src/components/NoticeIcon/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .popover {
4 | position: relative;
5 | width: 336px;
6 | }
7 |
8 | .noticeButton {
9 | display: inline-block;
10 | cursor: pointer;
11 | transition: all 0.3s;
12 | }
13 | .icon {
14 | padding: 4px;
15 | vertical-align: middle;
16 | }
17 |
18 | .badge {
19 | font-size: 16px;
20 | }
21 |
22 | .tabs {
23 | :global {
24 | .ant-tabs-nav-scroll {
25 | text-align: center;
26 | }
27 | .ant-tabs-bar {
28 | margin-bottom: 0;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/frontend/src/components/PageLoading/index.jsx:
--------------------------------------------------------------------------------
1 | import { PageLoading } from '@ant-design/pro-layout'; // loading components from code split
2 | // https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
3 |
4 | export default PageLoading;
5 |
--------------------------------------------------------------------------------
/frontend/src/components/SelectLang/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .menu {
4 | :global(.anticon) {
5 | margin-right: 8px;
6 | }
7 | :global(.ant-dropdown-menu-item) {
8 | min-width: 160px;
9 | }
10 | }
11 |
12 | .dropDown {
13 | line-height: @layout-header-height;
14 | vertical-align: top;
15 | cursor: pointer;
16 | > span {
17 | font-size: 16px !important;
18 | transform: none !important;
19 | svg {
20 | position: relative;
21 | top: -1px;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/frontend/src/e2e/__mocks__/antd-pro-merge-less.js:
--------------------------------------------------------------------------------
1 | export default undefined;
2 |
--------------------------------------------------------------------------------
/frontend/src/e2e/topMenu.e2e.js:
--------------------------------------------------------------------------------
1 | const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
2 |
3 | describe('Homepage', () => {
4 | it('topmenu should have footer', async () => {
5 | const params = '?navTheme=light&layout=topmenu';
6 | await page.goto(`${BASE_URL}${params}`);
7 | await page.waitForSelector('footer', {
8 | timeout: 2000,
9 | });
10 | const haveFooter = await page.evaluate(
11 | () => document.getElementsByTagName('footer').length > 0,
12 | );
13 | expect(haveFooter).toBeTruthy();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/frontend/src/global.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | html,
4 | body,
5 | #root {
6 | height: 100%;
7 | }
8 |
9 | .colorWeak {
10 | filter: invert(80%);
11 | }
12 |
13 | .ant-layout {
14 | min-height: 100vh;
15 | }
16 |
17 | canvas {
18 | display: block;
19 | }
20 |
21 | body {
22 | text-rendering: optimizeLegibility;
23 | -webkit-font-smoothing: antialiased;
24 | -moz-osx-font-smoothing: grayscale;
25 | }
26 |
27 | ul,
28 | ol {
29 | list-style: none;
30 | }
31 |
32 | @media (max-width: @screen-xs) {
33 | .ant-table {
34 | width: 100%;
35 | overflow-x: auto;
36 | &-thead > tr,
37 | &-tbody > tr {
38 | > th,
39 | > td {
40 | white-space: pre;
41 | > span {
42 | display: block;
43 | }
44 | }
45 | }
46 | }
47 | }
48 |
49 | // 兼容IE11
50 | @media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) {
51 | body .ant-design-pro > .ant-layout {
52 | min-height: 100vh;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/frontend/src/layouts/BlankLayout.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Layout = ({ children }) => <>{children}>;
4 |
5 | export default Layout;
6 |
--------------------------------------------------------------------------------
/frontend/src/layouts/SecurityLayout.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'dva';
3 | import { PageLoading } from '@ant-design/pro-layout';
4 | import { Redirect } from 'umi';
5 | import { stringify } from 'querystring';
6 |
7 | class SecurityLayout extends React.Component {
8 | state = {
9 | isReady: false,
10 | };
11 |
12 | componentDidMount() {
13 | this.setState({
14 | isReady: true,
15 | });
16 | const { dispatch } = this.props;
17 |
18 | if (dispatch) {
19 | dispatch({
20 | type: 'user/fetchCurrent',
21 | });
22 | }
23 | }
24 |
25 | render() {
26 | const { isReady } = this.state;
27 | const { children, loading, currentUser } = this.props; // You can replace it to your authentication rule (such as check token exists)
28 | // 你可以把它替换成你自己的登录认证规则(比如判断 token 是否存在)
29 |
30 | const isLogin = currentUser && currentUser.userid;
31 | const queryString = stringify({
32 | redirect: window.location.href,
33 | });
34 |
35 | if ((!isLogin && loading) || !isReady) {
36 | return ;
37 | }
38 |
39 | if (!isLogin) {
40 | return ;
41 | }
42 |
43 | return children;
44 | }
45 | }
46 |
47 | export default connect(({ user, loading }) => ({
48 | currentUser: user.currentUser,
49 | loading: loading.models.user,
50 | }))(SecurityLayout);
51 |
--------------------------------------------------------------------------------
/frontend/src/locales/en-US.js:
--------------------------------------------------------------------------------
1 | import component from './en-US/component';
2 | import globalHeader from './en-US/globalHeader';
3 | import menu from './en-US/menu';
4 | import pwa from './en-US/pwa';
5 | import settingDrawer from './en-US/settingDrawer';
6 | import settings from './en-US/settings';
7 | export default {
8 | 'navBar.lang': 'Languages',
9 | 'layout.user.link.help': 'Help',
10 | 'layout.user.link.privacy': 'Privacy',
11 | 'layout.user.link.terms': 'Terms',
12 | 'app.preview.down.block': 'Download this page to your local project',
13 | 'app.welcome.link.fetch-blocks': 'Get all block',
14 | 'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
15 | ...globalHeader,
16 | ...menu,
17 | ...settingDrawer,
18 | ...settings,
19 | ...pwa,
20 | ...component,
21 | };
22 |
--------------------------------------------------------------------------------
/frontend/src/locales/en-US/component.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': 'Expand',
3 | 'component.tagSelect.collapse': 'Collapse',
4 | 'component.tagSelect.all': 'All',
5 | };
6 |
--------------------------------------------------------------------------------
/frontend/src/locales/en-US/globalHeader.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.globalHeader.search': 'Search',
3 | 'component.globalHeader.search.example1': 'Search example 1',
4 | 'component.globalHeader.search.example2': 'Search example 2',
5 | 'component.globalHeader.search.example3': 'Search example 3',
6 | 'component.globalHeader.help': 'Help',
7 | 'component.globalHeader.notification': 'Notification',
8 | 'component.globalHeader.notification.empty': 'You have viewed all notifications.',
9 | 'component.globalHeader.message': 'Message',
10 | 'component.globalHeader.message.empty': 'You have viewed all messsages.',
11 | 'component.globalHeader.event': 'Event',
12 | 'component.globalHeader.event.empty': 'You have viewed all events.',
13 | 'component.noticeIcon.clear': 'Clear',
14 | 'component.noticeIcon.cleared': 'Cleared',
15 | 'component.noticeIcon.empty': 'No notifications',
16 | 'component.noticeIcon.view-more': 'View more',
17 | };
18 |
--------------------------------------------------------------------------------
/frontend/src/locales/en-US/pwa.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.pwa.offline': 'You are offline now',
3 | 'app.pwa.serviceworker.updated': 'New content is available',
4 | 'app.pwa.serviceworker.updated.hint': 'Please press the "Refresh" button to reload current page',
5 | 'app.pwa.serviceworker.updated.ok': 'Refresh',
6 | };
7 |
--------------------------------------------------------------------------------
/frontend/src/locales/pt-BR.js:
--------------------------------------------------------------------------------
1 | import component from './pt-BR/component';
2 | import globalHeader from './pt-BR/globalHeader';
3 | import menu from './pt-BR/menu';
4 | import pwa from './pt-BR/pwa';
5 | import settingDrawer from './pt-BR/settingDrawer';
6 | import settings from './pt-BR/settings';
7 | export default {
8 | 'navBar.lang': 'Idiomas',
9 | 'layout.user.link.help': 'ajuda',
10 | 'layout.user.link.privacy': 'política de privacidade',
11 | 'layout.user.link.terms': 'termos de serviços',
12 | 'app.preview.down.block': 'Download this page to your local project',
13 | ...globalHeader,
14 | ...menu,
15 | ...settingDrawer,
16 | ...settings,
17 | ...pwa,
18 | ...component,
19 | };
20 |
--------------------------------------------------------------------------------
/frontend/src/locales/pt-BR/component.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': 'Expandir',
3 | 'component.tagSelect.collapse': 'Diminuir',
4 | 'component.tagSelect.all': 'Todas',
5 | };
6 |
--------------------------------------------------------------------------------
/frontend/src/locales/pt-BR/globalHeader.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.globalHeader.search': 'Busca',
3 | 'component.globalHeader.search.example1': 'Exemplo de busca 1',
4 | 'component.globalHeader.search.example2': 'Exemplo de busca 2',
5 | 'component.globalHeader.search.example3': 'Exemplo de busca 3',
6 | 'component.globalHeader.help': 'Ajuda',
7 | 'component.globalHeader.notification': 'Notificação',
8 | 'component.globalHeader.notification.empty': 'Você visualizou todas as notificações.',
9 | 'component.globalHeader.message': 'Mensagem',
10 | 'component.globalHeader.message.empty': 'Você visualizou todas as mensagens.',
11 | 'component.globalHeader.event': 'Evento',
12 | 'component.globalHeader.event.empty': 'Você visualizou todos os eventos.',
13 | 'component.noticeIcon.clear': 'Limpar',
14 | 'component.noticeIcon.cleared': 'Limpo',
15 | 'component.noticeIcon.empty': 'Sem notificações',
16 | 'component.noticeIcon.loaded': 'Carregado',
17 | 'component.noticeIcon.view-more': 'Veja mais',
18 | };
19 |
--------------------------------------------------------------------------------
/frontend/src/locales/pt-BR/pwa.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.pwa.offline': 'Você está offline agora',
3 | 'app.pwa.serviceworker.updated': 'Novo conteúdo está disponível',
4 | 'app.pwa.serviceworker.updated.hint':
5 | 'Por favor, pressione o botão "Atualizar" para recarregar a página atual',
6 | 'app.pwa.serviceworker.updated.ok': 'Atualizar',
7 | };
8 |
--------------------------------------------------------------------------------
/frontend/src/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | import component from './zh-CN/component';
2 | import globalHeader from './zh-CN/globalHeader';
3 | import menu from './zh-CN/menu';
4 | import pwa from './zh-CN/pwa';
5 | import settingDrawer from './zh-CN/settingDrawer';
6 | import settings from './zh-CN/settings';
7 | export default {
8 | 'navBar.lang': '语言',
9 | 'layout.user.link.help': '帮助',
10 | 'layout.user.link.privacy': '隐私',
11 | 'layout.user.link.terms': '条款',
12 | 'app.preview.down.block': '下载此页面到本地项目',
13 | 'app.welcome.link.fetch-blocks': '获取全部区块',
14 | 'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
15 | ...globalHeader,
16 | ...menu,
17 | ...settingDrawer,
18 | ...settings,
19 | ...pwa,
20 | ...component,
21 | };
22 |
--------------------------------------------------------------------------------
/frontend/src/locales/zh-CN/component.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': '展开',
3 | 'component.tagSelect.collapse': '收起',
4 | 'component.tagSelect.all': '全部',
5 | };
6 |
--------------------------------------------------------------------------------
/frontend/src/locales/zh-CN/globalHeader.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.globalHeader.search': '站内搜索',
3 | 'component.globalHeader.search.example1': '搜索提示一',
4 | 'component.globalHeader.search.example2': '搜索提示二',
5 | 'component.globalHeader.search.example3': '搜索提示三',
6 | 'component.globalHeader.help': '使用文档',
7 | 'component.globalHeader.notification': '通知',
8 | 'component.globalHeader.notification.empty': '你已查看所有通知',
9 | 'component.globalHeader.message': '消息',
10 | 'component.globalHeader.message.empty': '您已读完所有消息',
11 | 'component.globalHeader.event': '待办',
12 | 'component.globalHeader.event.empty': '你已完成所有待办',
13 | 'component.noticeIcon.clear': '清空',
14 | 'component.noticeIcon.cleared': '清空了',
15 | 'component.noticeIcon.empty': '暂无数据',
16 | 'component.noticeIcon.view-more': '查看更多',
17 | };
18 |
--------------------------------------------------------------------------------
/frontend/src/locales/zh-CN/pwa.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.pwa.offline': '当前处于离线状态',
3 | 'app.pwa.serviceworker.updated': '有新内容',
4 | 'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面',
5 | 'app.pwa.serviceworker.updated.ok': '刷新',
6 | };
7 |
--------------------------------------------------------------------------------
/frontend/src/locales/zh-CN/settingDrawer.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.setting.pagestyle': '整体风格设置',
3 | 'app.setting.pagestyle.dark': '暗色菜单风格',
4 | 'app.setting.pagestyle.light': '亮色菜单风格',
5 | 'app.setting.content-width': '内容区域宽度',
6 | 'app.setting.content-width.fixed': '定宽',
7 | 'app.setting.content-width.fluid': '流式',
8 | 'app.setting.themecolor': '主题色',
9 | 'app.setting.themecolor.dust': '薄暮',
10 | 'app.setting.themecolor.volcano': '火山',
11 | 'app.setting.themecolor.sunset': '日暮',
12 | 'app.setting.themecolor.cyan': '明青',
13 | 'app.setting.themecolor.green': '极光绿',
14 | 'app.setting.themecolor.daybreak': '拂晓蓝(默认)',
15 | 'app.setting.themecolor.geekblue': '极客蓝',
16 | 'app.setting.themecolor.purple': '酱紫',
17 | 'app.setting.navigationmode': '导航模式',
18 | 'app.setting.sidemenu': '侧边菜单布局',
19 | 'app.setting.topmenu': '顶部菜单布局',
20 | 'app.setting.fixedheader': '固定 Header',
21 | 'app.setting.fixedsidebar': '固定侧边菜单',
22 | 'app.setting.fixedsidebar.hint': '侧边菜单布局时可配置',
23 | 'app.setting.hideheader': '下滑时隐藏 Header',
24 | 'app.setting.hideheader.hint': '固定 Header 时可配置',
25 | 'app.setting.othersettings': '其他设置',
26 | 'app.setting.weakmode': '色弱模式',
27 | 'app.setting.copy': '拷贝设置',
28 | 'app.setting.copyinfo': '拷贝成功,请到 src/defaultSettings.js 中替换默认配置',
29 | 'app.setting.production.hint':
30 | '配置栏只在开发环境用于预览,生产环境不会展现,请拷贝后手动修改配置文件',
31 | };
32 |
--------------------------------------------------------------------------------
/frontend/src/locales/zh-TW.js:
--------------------------------------------------------------------------------
1 | import component from './zh-TW/component';
2 | import globalHeader from './zh-TW/globalHeader';
3 | import menu from './zh-TW/menu';
4 | import pwa from './zh-TW/pwa';
5 | import settingDrawer from './zh-TW/settingDrawer';
6 | import settings from './zh-TW/settings';
7 | export default {
8 | 'navBar.lang': '語言',
9 | 'layout.user.link.help': '幫助',
10 | 'layout.user.link.privacy': '隱私',
11 | 'layout.user.link.terms': '條款',
12 | 'app.preview.down.block': '下載此頁面到本地項目',
13 | ...globalHeader,
14 | ...menu,
15 | ...settingDrawer,
16 | ...settings,
17 | ...pwa,
18 | ...component,
19 | };
20 |
--------------------------------------------------------------------------------
/frontend/src/locales/zh-TW/component.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': '展開',
3 | 'component.tagSelect.collapse': '收起',
4 | 'component.tagSelect.all': '全部',
5 | };
6 |
--------------------------------------------------------------------------------
/frontend/src/locales/zh-TW/globalHeader.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.globalHeader.search': '站內搜索',
3 | 'component.globalHeader.search.example1': '搜索提示壹',
4 | 'component.globalHeader.search.example2': '搜索提示二',
5 | 'component.globalHeader.search.example3': '搜索提示三',
6 | 'component.globalHeader.help': '使用手冊',
7 | 'component.globalHeader.notification': '通知',
8 | 'component.globalHeader.notification.empty': '妳已查看所有通知',
9 | 'component.globalHeader.message': '消息',
10 | 'component.globalHeader.message.empty': '您已讀完所有消息',
11 | 'component.globalHeader.event': '待辦',
12 | 'component.globalHeader.event.empty': '妳已完成所有待辦',
13 | 'component.noticeIcon.clear': '清空',
14 | 'component.noticeIcon.cleared': '清空了',
15 | 'component.noticeIcon.empty': '暫無資料',
16 | 'component.noticeIcon.view-more': '查看更多',
17 | };
18 |
--------------------------------------------------------------------------------
/frontend/src/locales/zh-TW/pwa.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.pwa.offline': '當前處於離線狀態',
3 | 'app.pwa.serviceworker.updated': '有新內容',
4 | 'app.pwa.serviceworker.updated.hint': '請點擊“刷新”按鈕或者手動刷新頁面',
5 | 'app.pwa.serviceworker.updated.ok': '刷新',
6 | };
7 |
--------------------------------------------------------------------------------
/frontend/src/locales/zh-TW/settingDrawer.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.setting.pagestyle': '整體風格設置',
3 | 'app.setting.pagestyle.dark': '暗色菜單風格',
4 | 'app.setting.pagestyle.light': '亮色菜單風格',
5 | 'app.setting.content-width': '內容區域寬度',
6 | 'app.setting.content-width.fixed': '定寬',
7 | 'app.setting.content-width.fluid': '流式',
8 | 'app.setting.themecolor': '主題色',
9 | 'app.setting.themecolor.dust': '薄暮',
10 | 'app.setting.themecolor.volcano': '火山',
11 | 'app.setting.themecolor.sunset': '日暮',
12 | 'app.setting.themecolor.cyan': '明青',
13 | 'app.setting.themecolor.green': '極光綠',
14 | 'app.setting.themecolor.daybreak': '拂曉藍(默認)',
15 | 'app.setting.themecolor.geekblue': '極客藍',
16 | 'app.setting.themecolor.purple': '醬紫',
17 | 'app.setting.navigationmode': '導航模式',
18 | 'app.setting.sidemenu': '側邊菜單布局',
19 | 'app.setting.topmenu': '頂部菜單布局',
20 | 'app.setting.fixedheader': '固定 Header',
21 | 'app.setting.fixedsidebar': '固定側邊菜單',
22 | 'app.setting.fixedsidebar.hint': '側邊菜單布局時可配置',
23 | 'app.setting.hideheader': '下滑時隱藏 Header',
24 | 'app.setting.hideheader.hint': '固定 Header 時可配置',
25 | 'app.setting.othersettings': '其他設置',
26 | 'app.setting.weakmode': '色弱模式',
27 | 'app.setting.copy': '拷貝設置',
28 | 'app.setting.copyinfo': '拷貝成功,請到 src/defaultSettings.js 中替換默認配置',
29 | 'app.setting.production.hint':
30 | '配置欄只在開發環境用於預覽,生產環境不會展現,請拷貝後手動修改配置文件',
31 | };
32 |
--------------------------------------------------------------------------------
/frontend/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ant Design Pro",
3 | "short_name": "Ant Design Pro",
4 | "display": "standalone",
5 | "start_url": "./?utm_source=homescreen",
6 | "theme_color": "#002140",
7 | "background_color": "#001529",
8 | "icons": [
9 | {
10 | "src": "icons/icon-192x192.png",
11 | "sizes": "192x192"
12 | },
13 | {
14 | "src": "icons/icon-128x128.png",
15 | "sizes": "128x128"
16 | },
17 | {
18 | "src": "icons/icon-512x512.png",
19 | "sizes": "512x512"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/frontend/src/models/setting.js:
--------------------------------------------------------------------------------
1 | import defaultSettings from '../../config/defaultSettings';
2 |
3 | const updateColorWeak = colorWeak => {
4 | const root = document.getElementById('root');
5 |
6 | if (root) {
7 | root.className = colorWeak ? 'colorWeak' : '';
8 | }
9 | };
10 |
11 | const SettingModel = {
12 | namespace: 'settings',
13 | state: defaultSettings,
14 | reducers: {
15 | changeSetting(state = defaultSettings, { payload }) {
16 | const { colorWeak, contentWidth } = payload;
17 |
18 | if (state.contentWidth !== contentWidth && window.dispatchEvent) {
19 | window.dispatchEvent(new Event('resize'));
20 | }
21 |
22 | updateColorWeak(!!colorWeak);
23 | return { ...state, ...payload };
24 | },
25 | },
26 | };
27 | export default SettingModel;
28 |
--------------------------------------------------------------------------------
/frontend/src/models/user.js:
--------------------------------------------------------------------------------
1 | import { queryCurrent, query as queryUsers } from '@/services/user';
2 | const UserModel = {
3 | namespace: 'user',
4 | state: {
5 | currentUser: {},
6 | },
7 | effects: {
8 | *fetch(_, { call, put }) {
9 | const response = yield call(queryUsers);
10 | yield put({
11 | type: 'save',
12 | payload: response,
13 | });
14 | },
15 |
16 | *fetchCurrent(_, { call, put }) {
17 | const response = yield call(queryCurrent);
18 | yield put({
19 | type: 'saveCurrentUser',
20 | payload: response,
21 | });
22 | },
23 | },
24 | reducers: {
25 | saveCurrentUser(state, action) {
26 | return { ...state, currentUser: action.payload || {} };
27 | },
28 |
29 | changeNotifyCount(
30 | state = {
31 | currentUser: {},
32 | },
33 | action,
34 | ) {
35 | return {
36 | ...state,
37 | currentUser: {
38 | ...state.currentUser,
39 | notifyCount: action.payload.totalCount,
40 | unreadCount: action.payload.unreadCount,
41 | },
42 | };
43 | },
44 | },
45 | };
46 | export default UserModel;
47 |
--------------------------------------------------------------------------------
/frontend/src/pages/404.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Result } from 'antd';
2 | import React from 'react';
3 | import router from 'umi/router';
4 |
5 | const NoFoundPage = () => (
6 | router.push('/')}>
12 | Back Home
13 |
14 | }
15 | >
16 | );
17 |
18 | export default NoFoundPage;
19 |
--------------------------------------------------------------------------------
/frontend/src/pages/Admin.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons';
3 | import { Card, Typography, Alert } from 'antd';
4 | import { PageHeaderWrapper } from '@ant-design/pro-layout';
5 | export default () => (
6 |
7 |
8 |
18 |
24 | Ant Design Pro You
25 |
26 |
27 |
33 | Want to add more pages? Please refer to{' '}
34 |
35 | use block
36 |
37 | 。
38 |
39 |
40 | );
41 |
--------------------------------------------------------------------------------
/frontend/src/pages/Authorized.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Redirect from 'umi/redirect';
3 | import { connect } from 'dva';
4 | import Authorized from '@/utils/Authorized';
5 | import { getRouteAuthority } from '@/utils/utils';
6 |
7 | const AuthComponent = ({
8 | children,
9 | route = {
10 | routes: [],
11 | },
12 | location = {
13 | pathname: '',
14 | },
15 | user,
16 | }) => {
17 | const { currentUser } = user;
18 | const { routes = [] } = route;
19 | const isLogin = currentUser && currentUser.name;
20 | return (
21 | : }
24 | >
25 | {children}
26 |
27 | );
28 | };
29 |
30 | export default connect(({ user }) => ({
31 | user,
32 | }))(AuthComponent);
33 |
--------------------------------------------------------------------------------
/frontend/src/pages/Welcome.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .pre {
4 | margin: 12px 0;
5 | padding: 12px 20px;
6 | background: @input-bg;
7 | box-shadow: @card-shadow;
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/center/components/Applications/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .filterCardList {
4 | margin-bottom: -24px;
5 | :global {
6 | .ant-card-meta-content {
7 | margin-top: 0;
8 | }
9 | // disabled white space
10 | .ant-card-meta-avatar {
11 | font-size: 0;
12 | }
13 |
14 | .ant-list .ant-list-item-content-single {
15 | max-width: 100%;
16 | }
17 | }
18 | .cardInfo {
19 | margin-top: 16px;
20 | margin-left: 40px;
21 | zoom: 1;
22 | &::before,
23 | &::after {
24 | display: table;
25 | content: ' ';
26 | }
27 | &::after {
28 | clear: both;
29 | height: 0;
30 | font-size: 0;
31 | visibility: hidden;
32 | }
33 | & > div {
34 | position: relative;
35 | float: left;
36 | width: 50%;
37 | text-align: left;
38 | p {
39 | margin: 0;
40 | font-size: 24px;
41 | line-height: 32px;
42 | }
43 | p:first-child {
44 | margin-bottom: 4px;
45 | color: @text-color-secondary;
46 | font-size: 12px;
47 | line-height: 20px;
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/center/components/ArticleListContent/index.jsx:
--------------------------------------------------------------------------------
1 | import { Avatar } from 'antd';
2 | import React from 'react';
3 | import moment from 'moment';
4 | import styles from './index.less';
5 |
6 | const ArticleListContent = ({ data: { content, updatedAt, avatar, owner, href } }) => (
7 |
8 |
{content}
9 |
10 |
11 |
{owner} 发布在
{href}
12 |
{moment(updatedAt).format('YYYY-MM-DD HH:mm')}
13 |
14 |
15 | );
16 |
17 | export default ArticleListContent;
18 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/center/components/ArticleListContent/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .listContent {
4 | .description {
5 | max-width: 720px;
6 | line-height: 22px;
7 | }
8 | .extra {
9 | margin-top: 16px;
10 | color: @text-color-secondary;
11 | line-height: 22px;
12 | & > :global(.ant-avatar) {
13 | position: relative;
14 | top: 1px;
15 | width: 20px;
16 | height: 20px;
17 | margin-right: 8px;
18 | vertical-align: top;
19 | }
20 | & > em {
21 | margin-left: 16px;
22 | color: @disabled-color;
23 | font-style: normal;
24 | }
25 | }
26 | }
27 |
28 | @media screen and (max-width: @screen-xs) {
29 | .listContent {
30 | .extra {
31 | & > em {
32 | display: block;
33 | margin-top: 8px;
34 | margin-left: 0;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/center/components/Articles/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .articleList {
4 | :global {
5 | .ant-list-item:first-child {
6 | padding-top: 0;
7 | }
8 | }
9 | }
10 | a.listItemMetaTitle {
11 | color: @heading-color;
12 | }
13 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/center/components/AvatarList/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .avatarList {
4 | display: inline-block;
5 | ul {
6 | display: inline-block;
7 | margin-left: 8px;
8 | font-size: 0;
9 | }
10 | }
11 |
12 | .avatarItem {
13 | display: inline-block;
14 | width: @avatar-size-base;
15 | height: @avatar-size-base;
16 | margin-left: -8px;
17 | font-size: @font-size-base;
18 | :global {
19 | .ant-avatar {
20 | border: 1px solid #fff;
21 | }
22 | }
23 | }
24 |
25 | .avatarItemLarge {
26 | width: @avatar-size-lg;
27 | height: @avatar-size-lg;
28 | }
29 |
30 | .avatarItemSmall {
31 | width: @avatar-size-sm;
32 | height: @avatar-size-sm;
33 | }
34 |
35 | .avatarItemMini {
36 | width: 20px;
37 | height: 20px;
38 | :global {
39 | .ant-avatar {
40 | width: 20px;
41 | height: 20px;
42 | line-height: 20px;
43 |
44 | .ant-avatar-string {
45 | font-size: 12px;
46 | line-height: 18px;
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/center/components/Projects/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .coverCardList {
4 | margin-bottom: -24px;
5 |
6 | .card {
7 | :global {
8 | .ant-card-meta-title {
9 | margin-bottom: 4px;
10 | & > a {
11 | display: inline-block;
12 | max-width: 100%;
13 | color: @heading-color;
14 | }
15 | }
16 | .ant-card-meta-description {
17 | height: 44px;
18 | overflow: hidden;
19 | line-height: 22px;
20 | }
21 | }
22 |
23 | &:hover {
24 | :global {
25 | .ant-card-meta-title > a {
26 | color: @primary-color;
27 | }
28 | }
29 | }
30 | }
31 |
32 | .cardItemContent {
33 | display: flex;
34 | height: 20px;
35 | margin-top: 16px;
36 | margin-bottom: -4px;
37 | line-height: 20px;
38 | & > span {
39 | flex: 1;
40 | color: @text-color-secondary;
41 | font-size: 12px;
42 | }
43 | .avatarList {
44 | flex: 0 1 auto;
45 | }
46 | }
47 | .cardList {
48 | margin-top: 24px;
49 | }
50 |
51 | :global {
52 | .ant-list .ant-list-item-content-single {
53 | max-width: 100%;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/center/model.js:
--------------------------------------------------------------------------------
1 | import { queryCurrent, queryFakeList } from './service';
2 |
3 | const Model = {
4 | namespace: 'accountAndcenter',
5 | state: {
6 | currentUser: {},
7 | list: [],
8 | },
9 | effects: {
10 | *fetchCurrent(_, { call, put }) {
11 | const response = yield call(queryCurrent);
12 | yield put({
13 | type: 'saveCurrentUser',
14 | payload: response,
15 | });
16 | },
17 |
18 | *fetch({ payload }, { call, put }) {
19 | const response = yield call(queryFakeList, payload);
20 | yield put({
21 | type: 'queryList',
22 | payload: Array.isArray(response) ? response : [],
23 | });
24 | },
25 | },
26 | reducers: {
27 | saveCurrentUser(state, action) {
28 | return { ...state, currentUser: action.payload || {} };
29 | },
30 |
31 | queryList(state, action) {
32 | return { ...state, list: action.payload };
33 | },
34 | },
35 | };
36 | export default Model;
37 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/center/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryCurrent() {
4 | return request('/api/currentUser');
5 | }
6 | export async function queryFakeList(params) {
7 | return request('/api/fake_list', {
8 | params,
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/settings/components/BaseView.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .baseView {
4 | display: flex;
5 | padding-top: 12px;
6 |
7 | :global {
8 | .ant-legacy-form-item .ant-legacy-form-item-control-wrapper {
9 | width: 100%;
10 | }
11 | }
12 |
13 | .left {
14 | min-width: 224px;
15 | max-width: 448px;
16 | }
17 | .right {
18 | flex: 1;
19 | padding-left: 104px;
20 | .avatar_title {
21 | height: 22px;
22 | margin-bottom: 8px;
23 | color: @heading-color;
24 | font-size: @font-size-base;
25 | line-height: 22px;
26 | }
27 | .avatar {
28 | width: 144px;
29 | height: 144px;
30 | margin-bottom: 12px;
31 | overflow: hidden;
32 | img {
33 | width: 100%;
34 | }
35 | }
36 | .button_view {
37 | width: 144px;
38 | text-align: center;
39 | }
40 | }
41 | }
42 |
43 | @media screen and (max-width: @screen-xl) {
44 | .baseView {
45 | flex-direction: column-reverse;
46 |
47 | .right {
48 | display: flex;
49 | flex-direction: column;
50 | align-items: center;
51 | max-width: 448px;
52 | padding: 20px;
53 | .avatar_title {
54 | display: none;
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/settings/components/GeographicView.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .row {
4 | .item {
5 | width: 50%;
6 | max-width: 220px;
7 | }
8 | .item:first-child {
9 | width: ~'calc(50% - 8px)';
10 | margin-right: 8px;
11 | }
12 | }
13 |
14 | @media screen and (max-width: @screen-sm) {
15 | .item:first-child {
16 | margin: 0;
17 | margin-bottom: 8px;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/settings/components/PhoneView.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Input } from 'antd';
3 | import styles from './PhoneView.less';
4 |
5 | const PhoneView = props => {
6 | const { value, onChange } = props;
7 | let values = ['', ''];
8 |
9 | if (value) {
10 | values = value.split('-');
11 | }
12 |
13 | return (
14 | <>
15 | {
19 | if (onChange) {
20 | onChange(`${e.target.value}-${values[1]}`);
21 | }
22 | }}
23 | />
24 | {
27 | if (onChange) {
28 | onChange(`${values[0]}-${e.target.value}`);
29 | }
30 | }}
31 | value={values[1]}
32 | />
33 | >
34 | );
35 | };
36 |
37 | export default PhoneView;
38 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/settings/components/PhoneView.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .area_code {
4 | width: 30%;
5 | max-width: 128px;
6 | margin-right: 8px;
7 | }
8 | .phone_number {
9 | width: ~'calc(70% - 8px)';
10 | max-width: 312px;
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/settings/components/notification.jsx:
--------------------------------------------------------------------------------
1 | import { List, Switch } from 'antd';
2 | import React, { Component, Fragment } from 'react';
3 |
4 | class NotificationView extends Component {
5 | getData = () => {
6 | const Action = ;
7 | return [
8 | {
9 | title: '账户密码',
10 | description: '其他用户的消息将以站内信的形式通知',
11 | actions: [Action],
12 | },
13 | {
14 | title: '系统消息',
15 | description: '系统消息将以站内信的形式通知',
16 | actions: [Action],
17 | },
18 | {
19 | title: '待办任务',
20 | description: '待办任务将以站内信的形式通知',
21 | actions: [Action],
22 | },
23 | ];
24 | };
25 |
26 | render() {
27 | const data = this.getData();
28 | return (
29 |
30 | (
34 |
35 |
36 |
37 | )}
38 | />
39 |
40 | );
41 | }
42 | }
43 |
44 | export default NotificationView;
45 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/settings/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TagType {
2 | key: string;
3 | label: string;
4 | }
5 |
6 | export interface GeographicItemType {
7 | name: string;
8 | id: string;
9 | }
10 |
11 | export interface GeographicType {
12 | province: GeographicItemType;
13 | city: GeographicItemType;
14 | }
15 |
16 | export interface NoticeType {
17 | id: string;
18 | title: string;
19 | logo: string;
20 | description: string;
21 | updatedAt: string;
22 | member: string;
23 | href: string;
24 | memberLink: string;
25 | }
26 |
27 | export interface CurrentUser {
28 | name: string;
29 | avatar: string;
30 | userid: string;
31 | notice: NoticeType[];
32 | email: string;
33 | signature: string;
34 | title: string;
35 | group: string;
36 | tags: TagType[];
37 | notifyCount: number;
38 | unreadCount: number;
39 | country: string;
40 | geographic: GeographicType;
41 | address: string;
42 | phone: string;
43 | }
44 |
--------------------------------------------------------------------------------
/frontend/src/pages/account/settings/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryCurrent() {
4 | return request('/api/currentUser');
5 | }
6 | export async function queryProvince() {
7 | return request('/api/geographic/province');
8 | }
9 | export async function queryCity(province) {
10 | return request(`/api/geographic/city/${province}`);
11 | }
12 | export async function query() {
13 | return request('/api/users');
14 | }
15 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/Field/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from './index.less';
3 |
4 | const Field = ({ label, value, ...rest }) => (
5 |
6 | {label}
7 | {value}
8 |
9 | );
10 |
11 | export default Field;
12 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/Field/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .field {
4 | margin: 0;
5 | overflow: hidden;
6 | white-space: nowrap;
7 | text-overflow: ellipsis;
8 | .label,
9 | .number {
10 | font-size: @font-size-base;
11 | line-height: 22px;
12 | }
13 | .number {
14 | margin-left: 8px;
15 | color: @heading-color;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/MiniBar/index.jsx:
--------------------------------------------------------------------------------
1 | import { Chart, Geom, Tooltip } from 'bizcharts';
2 | import React from 'react';
3 | import autoHeight from '../autoHeight';
4 | import styles from '../index.less';
5 |
6 | const MiniBar = props => {
7 | const { height = 0, forceFit = true, color = '#1890FF', data = [] } = props;
8 | const scale = {
9 | x: {
10 | type: 'cat',
11 | },
12 | y: {
13 | min: 0,
14 | },
15 | };
16 | const padding = [36, 5, 30, 5];
17 | const tooltip = [
18 | 'x*y',
19 | (x, y) => ({
20 | name: x,
21 | value: y,
22 | }),
23 | ]; // for tooltip not to be hide
24 |
25 | const chartHeight = height + 54;
26 | return (
27 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default autoHeight()(MiniBar);
44 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/MiniProgress/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Tooltip } from 'antd';
3 | import styles from './index.less';
4 |
5 | const MiniProgress = ({
6 | targetLabel,
7 | target,
8 | color = 'rgb(19, 194, 194)',
9 | strokeWidth,
10 | percent,
11 | }) => (
12 |
13 |
14 |
20 |
25 |
30 |
31 |
32 |
42 |
43 | );
44 |
45 | export default MiniProgress;
46 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/MiniProgress/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .miniProgress {
4 | position: relative;
5 | width: 100%;
6 | padding: 5px 0;
7 | .progressWrap {
8 | position: relative;
9 | background-color: @background-color-base;
10 | }
11 | .progress {
12 | width: 0;
13 | height: 100%;
14 | background-color: @primary-color;
15 | border-radius: 1px 0 0 1px;
16 | transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s;
17 | }
18 | .target {
19 | position: absolute;
20 | top: 0;
21 | bottom: 0;
22 | z-index: 9;
23 | width: 20px;
24 | span {
25 | position: absolute;
26 | top: 0;
27 | left: 0;
28 | width: 2px;
29 | height: 4px;
30 | border-radius: 100px;
31 | }
32 | span:last-child {
33 | top: auto;
34 | bottom: 0;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/TagCloud/index.less:
--------------------------------------------------------------------------------
1 | .tagCloud {
2 | overflow: hidden;
3 | canvas {
4 | transform-origin: 0 0;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/TimelineChart/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .timelineChart {
4 | background: @component-background;
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/WaterWave/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .waterWave {
4 | position: relative;
5 | display: inline-block;
6 | transform-origin: left;
7 | .text {
8 | position: absolute;
9 | top: 32px;
10 | left: 0;
11 | width: 100%;
12 | text-align: center;
13 | span {
14 | color: @text-color-secondary;
15 | font-size: 14px;
16 | line-height: 22px;
17 | }
18 | h4 {
19 | color: @heading-color;
20 | font-size: 24px;
21 | line-height: 32px;
22 | }
23 | }
24 | .waterWaveCanvasWrapper {
25 | transform: scale(0.5);
26 | transform-origin: 0 0;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/bizcharts.d.ts:
--------------------------------------------------------------------------------
1 | import * as BizChart from 'bizcharts';
2 |
3 | export = BizChart;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/bizcharts.jsx:
--------------------------------------------------------------------------------
1 | import * as BizChart from 'bizcharts';
2 |
3 | export default BizChart;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/index.jsx:
--------------------------------------------------------------------------------
1 | import numeral from 'numeral';
2 | import Bar from './Bar';
3 | import ChartCard from './ChartCard';
4 | import Field from './Field';
5 | import Gauge from './Gauge';
6 | import MiniArea from './MiniArea';
7 | import MiniBar from './MiniBar';
8 | import MiniProgress from './MiniProgress';
9 | import Pie from './Pie';
10 | import TagCloud from './TagCloud';
11 | import TimelineChart from './TimelineChart';
12 | import WaterWave from './WaterWave';
13 |
14 | const yuan = val => `¥ ${numeral(val).format('0,0')}`;
15 |
16 | const Charts = {
17 | yuan,
18 | Bar,
19 | Pie,
20 | Gauge,
21 | MiniBar,
22 | MiniArea,
23 | MiniProgress,
24 | ChartCard,
25 | Field,
26 | WaterWave,
27 | TagCloud,
28 | TimelineChart,
29 | };
30 | export {
31 | Charts as default,
32 | yuan,
33 | Bar,
34 | Pie,
35 | Gauge,
36 | MiniBar,
37 | MiniArea,
38 | MiniProgress,
39 | ChartCard,
40 | Field,
41 | WaterWave,
42 | TagCloud,
43 | TimelineChart,
44 | };
45 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Charts/index.less:
--------------------------------------------------------------------------------
1 | .miniChart {
2 | position: relative;
3 | width: 100%;
4 | .chartContent {
5 | position: absolute;
6 | bottom: -28px;
7 | width: 100%;
8 | > div {
9 | margin: 0 -5px;
10 | overflow: hidden;
11 | }
12 | }
13 | .chartLoading {
14 | position: absolute;
15 | top: 16px;
16 | left: 50%;
17 | margin-left: -7px;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/PageLoading/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Spin } from 'antd'; // loading components from code split
3 | // https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
4 |
5 | export default () => (
6 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Trend/index.jsx:
--------------------------------------------------------------------------------
1 | import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons';
2 | import React from 'react';
3 | import classNames from 'classnames';
4 | import styles from './index.less';
5 |
6 | const Trend = ({ colorful = true, reverseColor = false, flag, children, className, ...rest }) => {
7 | const classString = classNames(
8 | styles.trendItem,
9 | {
10 | [styles.trendItemGrey]: !colorful,
11 | [styles.reverseColor]: reverseColor && colorful,
12 | },
13 | className,
14 | );
15 | return (
16 |
17 | {children}
18 | {flag && (
19 |
20 | {flag === 'up' ? : }
21 |
22 | )}
23 |
24 | );
25 | };
26 |
27 | export default Trend;
28 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/components/Trend/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .trendItem {
4 | display: inline-block;
5 | font-size: @font-size-base;
6 | line-height: 22px;
7 |
8 | .up,
9 | .down {
10 | position: relative;
11 | top: 1px;
12 | margin-left: 4px;
13 | span {
14 | font-size: 12px;
15 | transform: scale(0.83);
16 | }
17 | }
18 | .up {
19 | color: @red-6;
20 | }
21 | .down {
22 | top: -1px;
23 | color: @green-6;
24 | }
25 |
26 | &.trendItemGrey .up,
27 | &.trendItemGrey .down {
28 | color: @text-color;
29 | }
30 |
31 | &.reverseColor .up {
32 | color: @green-6;
33 | }
34 | &.reverseColor .down {
35 | color: @red-6;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface VisitDataType {
2 | x: string;
3 | y: number;
4 | }
5 |
6 | export interface SearchDataType {
7 | index: number;
8 | keyword: string;
9 | count: number;
10 | range: number;
11 | status: number;
12 | }
13 |
14 | export interface OfflineDataType {
15 | name: string;
16 | cvr: number;
17 | }
18 |
19 | export interface OfflineChartData {
20 | x: any;
21 | y1: number;
22 | y2: number;
23 | }
24 |
25 | export interface RadarData {
26 | name: string;
27 | label: string;
28 | value: number;
29 | }
30 |
31 | export interface AnalysisData {
32 | visitData: VisitDataType[];
33 | visitData2: VisitDataType[];
34 | salesData: VisitDataType[];
35 | searchData: SearchDataType[];
36 | offlineData: OfflineDataType[];
37 | offlineChartData: OfflineChartData[];
38 | salesTypeData: VisitDataType[];
39 | salesTypeDataOnline: VisitDataType[];
40 | salesTypeDataOffline: VisitDataType[];
41 | radarData: RadarData[];
42 | }
43 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/model.jsx:
--------------------------------------------------------------------------------
1 | import { fakeChartData } from './service';
2 |
3 | const initState = {
4 | visitData: [],
5 | visitData2: [],
6 | salesData: [],
7 | searchData: [],
8 | offlineData: [],
9 | offlineChartData: [],
10 | salesTypeData: [],
11 | salesTypeDataOnline: [],
12 | salesTypeDataOffline: [],
13 | radarData: [],
14 | };
15 | const Model = {
16 | namespace: 'dashboardAndanalysis',
17 | state: initState,
18 | effects: {
19 | *fetch(_, { call, put }) {
20 | const response = yield call(fakeChartData);
21 | yield put({
22 | type: 'save',
23 | payload: response,
24 | });
25 | },
26 |
27 | *fetchSalesData(_, { call, put }) {
28 | const response = yield call(fakeChartData);
29 | yield put({
30 | type: 'save',
31 | payload: {
32 | salesData: response.salesData,
33 | },
34 | });
35 | },
36 | },
37 | reducers: {
38 | save(state, { payload }) {
39 | return { ...state, ...payload };
40 | },
41 |
42 | clear() {
43 | return initState;
44 | },
45 | },
46 | };
47 | export default Model;
48 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/service.jsx:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function fakeChartData() {
4 | return request('/api/fake_chart_data');
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/utils/Yuan.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { yuan } from '../components/Charts';
3 | /**
4 | * 减少使用 dangerouslySetInnerHTML
5 | */
6 |
7 | export default class Yuan extends React.Component {
8 | main = null;
9 |
10 | componentDidMount() {
11 | this.renderToHtml();
12 | }
13 |
14 | componentDidUpdate() {
15 | this.renderToHtml();
16 | }
17 |
18 | renderToHtml = () => {
19 | const { children } = this.props;
20 |
21 | if (this.main) {
22 | this.main.innerHTML = yuan(children);
23 | }
24 | };
25 |
26 | render() {
27 | return (
28 | {
30 | this.main = ref;
31 | }}
32 | />
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/utils/utils.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 |
3 | export function fixedZero(val) {
4 | return val * 1 < 10 ? `0${val}` : val;
5 | }
6 | export function getTimeDistance(type) {
7 | const now = new Date();
8 | const oneDay = 1000 * 60 * 60 * 24;
9 |
10 | if (type === 'today') {
11 | now.setHours(0);
12 | now.setMinutes(0);
13 | now.setSeconds(0);
14 | return [moment(now), moment(now.getTime() + (oneDay - 1000))];
15 | }
16 |
17 | if (type === 'week') {
18 | let day = now.getDay();
19 | now.setHours(0);
20 | now.setMinutes(0);
21 | now.setSeconds(0);
22 |
23 | if (day === 0) {
24 | day = 6;
25 | } else {
26 | day -= 1;
27 | }
28 |
29 | const beginTime = now.getTime() - day * oneDay;
30 | return [moment(beginTime), moment(beginTime + (7 * oneDay - 1000))];
31 | }
32 |
33 | const year = now.getFullYear();
34 |
35 | if (type === 'month') {
36 | const month = now.getMonth();
37 | const nextDate = moment(now).add(1, 'months');
38 | const nextYear = nextDate.year();
39 | const nextMonth = nextDate.month();
40 | return [
41 | moment(`${year}-${fixedZero(month + 1)}-01 00:00:00`),
42 | moment(moment(`${nextYear}-${fixedZero(nextMonth + 1)}-01 00:00:00`).valueOf() - 1000),
43 | ];
44 | }
45 |
46 | return [moment(`${year}-01-01 00:00:00`), moment(`${year}-12-31 23:59:59`)];
47 | }
48 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/analysis/utils/utils.less:
--------------------------------------------------------------------------------
1 | .textOverflow() {
2 | overflow: hidden;
3 | white-space: nowrap;
4 | text-overflow: ellipsis;
5 | word-break: break-all;
6 | }
7 |
8 | .textOverflowMulti(@line: 3, @bg: #fff) {
9 | position: relative;
10 | max-height: @line * 1.5em;
11 | margin-right: -1em;
12 | padding-right: 1em;
13 | overflow: hidden;
14 | line-height: 1.5em;
15 | text-align: justify;
16 | &::before {
17 | position: absolute;
18 | right: 14px;
19 | bottom: 0;
20 | padding: 0 1px;
21 | background: @bg;
22 | content: '...';
23 | }
24 | &::after {
25 | position: absolute;
26 | right: 14px;
27 | width: 1em;
28 | height: 1em;
29 | margin-top: 0.2em;
30 | background: white;
31 | content: '';
32 | }
33 | }
34 |
35 | // mixins for clearfix
36 | // ------------------------
37 | .clearfix() {
38 | zoom: 1;
39 | &::before,
40 | &::after {
41 | display: table;
42 | content: ' ';
43 | }
44 | &::after {
45 | clear: both;
46 | height: 0;
47 | font-size: 0;
48 | visibility: hidden;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/_mock.js:
--------------------------------------------------------------------------------
1 | import mockjs from 'mockjs';
2 |
3 | export default {
4 | 'GET /api/tags': mockjs.mock({
5 | 'list|100': [
6 | {
7 | name: '@city',
8 | 'value|1-100': 150,
9 | 'type|0-2': 1,
10 | },
11 | ],
12 | }),
13 | };
14 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/components/ActiveChart/index.less:
--------------------------------------------------------------------------------
1 | .activeChart {
2 | position: relative;
3 | }
4 | .activeChartGrid {
5 | p {
6 | position: absolute;
7 | top: 80px;
8 | }
9 | p:last-child {
10 | top: 115px;
11 | }
12 | }
13 | .activeChartLegend {
14 | position: relative;
15 | height: 20px;
16 | margin-top: 8px;
17 | font-size: 0;
18 | line-height: 20px;
19 | span {
20 | display: inline-block;
21 | width: 33.33%;
22 | font-size: 12px;
23 | text-align: center;
24 | }
25 | span:first-child {
26 | text-align: left;
27 | }
28 | span:last-child {
29 | text-align: right;
30 | }
31 | }
32 | .dashedLine {
33 | position: relative;
34 | top: -70px;
35 | left: -3px;
36 | height: 1px;
37 |
38 | .line {
39 | position: absolute;
40 | top: 0;
41 | left: 0;
42 | width: 100%;
43 | height: 100%;
44 | background-image: linear-gradient(to right, transparent 50%, #e9e9e9 50%);
45 | background-size: 6px;
46 | }
47 | }
48 |
49 | .dashedLine:last-child {
50 | top: -36px;
51 | }
52 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/components/Charts/TagCloud/index.less:
--------------------------------------------------------------------------------
1 | .tagCloud {
2 | overflow: hidden;
3 | canvas {
4 | transform-origin: 0 0;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/components/Charts/WaterWave/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .waterWave {
4 | position: relative;
5 | display: inline-block;
6 | transform-origin: left;
7 | .text {
8 | position: absolute;
9 | top: 32px;
10 | left: 0;
11 | width: 100%;
12 | text-align: center;
13 | span {
14 | color: @text-color-secondary;
15 | font-size: 14px;
16 | line-height: 22px;
17 | }
18 | h4 {
19 | color: @heading-color;
20 | font-size: 24px;
21 | line-height: 32px;
22 | }
23 | }
24 | .waterWaveCanvasWrapper {
25 | transform: scale(0.5);
26 | transform-origin: 0 0;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/components/Charts/index.jsx:
--------------------------------------------------------------------------------
1 | import Gauge from './Gauge';
2 | import MiniArea from './MiniArea';
3 | import Pie from './Pie';
4 | import TagCloud from './TagCloud';
5 | import WaterWave from './WaterWave';
6 | import Map from './Map';
7 |
8 | const Charts = {
9 | Pie,
10 | WaterWave,
11 | Gauge,
12 | MiniArea,
13 | TagCloud,
14 | Map,
15 | };
16 | export { Charts as default, Pie, WaterWave, Gauge, TagCloud, MiniArea, Map };
17 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/components/Charts/index.less:
--------------------------------------------------------------------------------
1 | .miniChart {
2 | position: relative;
3 | width: 100%;
4 | .chartContent {
5 | position: absolute;
6 | bottom: -28px;
7 | width: 100%;
8 | > div {
9 | margin: 0 -5px;
10 | overflow: hidden;
11 | }
12 | }
13 | .chartLoading {
14 | position: absolute;
15 | top: 16px;
16 | left: 50%;
17 | margin-left: -7px;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TagType {
2 | name: string;
3 | value: string;
4 | type: string;
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/locales/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'dashboardandmonitor.monitor.trading-activity': 'Real-Time Trading Activity',
3 | 'dashboardandmonitor.monitor.total-transactions': 'Total transactions today',
4 | 'dashboardandmonitor.monitor.sales-target': 'Sales target completion rate',
5 | 'dashboardandmonitor.monitor.remaining-time': 'Remaining time of activity',
6 | 'dashboardandmonitor.monitor.total-transactions-per-second': 'Total transactions per second',
7 | 'dashboardandmonitor.monitor.activity-forecast': 'Activity forecast',
8 | 'dashboardandmonitor.monitor.efficiency': 'Efficiency',
9 | 'dashboardandmonitor.monitor.ratio': 'Ratio',
10 | 'dashboardandmonitor.monitor.proportion-per-category': 'Proportion Per Category',
11 | 'dashboardandmonitor.monitor.fast-food': 'Fast food',
12 | 'dashboardandmonitor.monitor.western-food': 'Western food',
13 | 'dashboardandmonitor.monitor.hot-pot': 'Hot pot',
14 | 'dashboardandmonitor.monitor.waiting-for-implementation': 'Waiting for implementation',
15 | 'dashboardandmonitor.monitor.popular-searches': 'Popular Searches',
16 | 'dashboardandmonitor.monitor.resource-surplus': 'Resource Surplus',
17 | 'dashboardandmonitor.monitor.fund-surplus': 'Fund Surplus',
18 | };
19 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/locales/pt-BR.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'dashboardandmonitor.monitor.trading-activity': 'Real-Time Trading Activity',
3 | 'dashboardandmonitor.monitor.total-transactions': 'Total transactions today',
4 | 'dashboardandmonitor.monitor.sales-target': 'Sales target completion rate',
5 | 'dashboardandmonitor.monitor.remaining-time': 'Remaining time of activity',
6 | 'dashboardandmonitor.monitor.total-transactions-per-second': 'Total transactions per second',
7 | 'dashboardandmonitor.monitor.activity-forecast': 'Activity forecast',
8 | 'dashboardandmonitor.monitor.efficiency': 'Efficiency',
9 | 'dashboardandmonitor.monitor.ratio': 'Ratio',
10 | 'dashboardandmonitor.monitor.proportion-per-category': 'Proportion Per Category',
11 | 'dashboardandmonitor.monitor.fast-food': 'Fast food',
12 | 'dashboardandmonitor.monitor.western-food': 'Western food',
13 | 'dashboardandmonitor.monitor.hot-pot': 'Hot pot',
14 | 'dashboardandmonitor.monitor.waiting-for-implementation': 'Waiting for implementation',
15 | 'dashboardandmonitor.monitor.popular-searches': 'Popular Searches',
16 | 'dashboardandmonitor.monitor.resource-surplus': 'Resource Surplus',
17 | 'dashboardandmonitor.monitor.fund-surplus': 'Fund Surplus',
18 | 'dashboardandmonitor.exception.back': 'Back to home',
19 | };
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'dashboardandmonitor.monitor.trading-activity': '活动实时交易情况',
3 | 'dashboardandmonitor.monitor.total-transactions': '今日交易总额',
4 | 'dashboardandmonitor.monitor.sales-target': '销售目标完成率',
5 | 'dashboardandmonitor.monitor.remaining-time': '活动剩余时间',
6 | 'dashboardandmonitor.monitor.total-transactions-per-second': '每秒交易总额',
7 | 'dashboardandmonitor.monitor.activity-forecast': '活动情况预测',
8 | 'dashboardandmonitor.monitor.efficiency': '券核效率',
9 | 'dashboardandmonitor.monitor.ratio': '跳出率',
10 | 'dashboardandmonitor.monitor.proportion-per-category': '各品类占比',
11 | 'dashboardandmonitor.monitor.fast-food': '中式快餐',
12 | 'dashboardandmonitor.monitor.western-food': '西餐',
13 | 'dashboardandmonitor.monitor.hot-pot': '火锅',
14 | 'dashboardandmonitor.monitor.waiting-for-implementation': 'Waiting for implementation',
15 | 'dashboardandmonitor.monitor.popular-searches': '热门搜索',
16 | 'dashboardandmonitor.monitor.resource-surplus': '资源剩余',
17 | 'dashboardandmonitor.monitor.fund-surplus': '补贴资金剩余',
18 | };
19 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/locales/zh-TW.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'dashboardandmonitor.monitor.trading-activity': '活動實時交易情況',
3 | 'dashboardandmonitor.monitor.total-transactions': '今日交易總額',
4 | 'dashboardandmonitor.monitor.sales-target': '銷售目標完成率',
5 | 'dashboardandmonitor.monitor.remaining-time': '活動剩余時間',
6 | 'dashboardandmonitor.monitor.total-transactions-per-second': '每秒交易總額',
7 | 'dashboardandmonitor.monitor.activity-forecast': '活動情況預測',
8 | 'dashboardandmonitor.monitor.efficiency': '券核效率',
9 | 'dashboardandmonitor.monitor.ratio': '跳出率',
10 | 'dashboardandmonitor.monitor.proportion-per-category': '各品類占比',
11 | 'dashboardandmonitor.monitor.fast-food': '中式快餐',
12 | 'dashboardandmonitor.monitor.western-food': '西餐',
13 | 'dashboardandmonitor.monitor.hot-pot': '火鍋',
14 | 'dashboardandmonitor.monitor.waiting-for-implementation': 'Waiting for implementation',
15 | 'dashboardandmonitor.monitor.popular-searches': '熱門搜索',
16 | 'dashboardandmonitor.monitor.resource-surplus': '資源剩余',
17 | 'dashboardandmonitor.monitor.fund-surplus': '補貼資金剩余',
18 | };
19 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/model.js:
--------------------------------------------------------------------------------
1 | import { queryTags } from './service';
2 |
3 | const Model = {
4 | namespace: 'dashboardAndmonitor',
5 | state: {
6 | tags: [],
7 | },
8 | effects: {
9 | *fetchTags(_, { call, put }) {
10 | const response = yield call(queryTags);
11 | yield put({
12 | type: 'saveTags',
13 | payload: response.list,
14 | });
15 | },
16 | },
17 | reducers: {
18 | saveTags(state, action) {
19 | return { ...state, tags: action.payload };
20 | },
21 | },
22 | };
23 | export default Model;
24 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryTags() {
4 | return request('/api/tags');
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/monitor/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .mapChart {
4 | height: 452px;
5 | padding-top: 24px;
6 | img {
7 | display: inline-block;
8 | max-width: 100%;
9 | max-height: 437px;
10 | }
11 | }
12 |
13 | .pieCard :global(.pie-stat) {
14 | font-size: 24px !important;
15 | }
16 |
17 | @media screen and (max-width: @screen-lg) {
18 | .mapChart {
19 | height: auto;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/workplace/components/EditableLinkGroup/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { createElement } from 'react';
2 | import { PlusOutlined } from '@ant-design/icons';
3 | import { Button } from 'antd';
4 | import styles from './index.less';
5 |
6 | const EditableLinkGroup = props => {
7 | const { links, linkElement, onAdd } = props;
8 | return (
9 |
10 | {links.map(link =>
11 | createElement(
12 | linkElement,
13 | {
14 | key: `linkGroup-item-${link.id || link.title}`,
15 | to: link.href,
16 | href: link.href,
17 | },
18 | link.title,
19 | ),
20 | )}
21 |
24 |
25 | );
26 | };
27 |
28 | EditableLinkGroup.defaultProps = {
29 | links: [],
30 | onAdd: () => {},
31 | linkElement: 'a',
32 | };
33 | export default EditableLinkGroup;
34 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/workplace/components/EditableLinkGroup/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .linkGroup {
4 | padding: 20px 0 8px 24px;
5 | font-size: 0;
6 | & > a {
7 | display: inline-block;
8 | width: 25%;
9 | margin-bottom: 13px;
10 | color: @text-color;
11 | font-size: @font-size-base;
12 | &:hover {
13 | color: @primary-color;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/workplace/components/Radar/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .radar {
4 | .legend {
5 | margin-top: 16px;
6 | .legendItem {
7 | position: relative;
8 | color: @text-color-secondary;
9 | line-height: 22px;
10 | text-align: center;
11 | cursor: pointer;
12 | p {
13 | margin: 0;
14 | }
15 | h6 {
16 | margin-top: 4px;
17 | margin-bottom: 0;
18 | padding-left: 16px;
19 | color: @heading-color;
20 | font-size: 24px;
21 | line-height: 32px;
22 | }
23 | &::after {
24 | position: absolute;
25 | top: 8px;
26 | right: 0;
27 | width: 1px;
28 | height: 40px;
29 | background-color: @border-color-split;
30 | content: '';
31 | }
32 | }
33 | > :last-child .legendItem::after {
34 | display: none;
35 | }
36 | .dot {
37 | position: relative;
38 | top: -1px;
39 | display: inline-block;
40 | width: 6px;
41 | height: 6px;
42 | margin-right: 6px;
43 | border-radius: 6px;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/frontend/src/pages/dashboard/workplace/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryProjectNotice() {
4 | return request('/api/project/notice');
5 | }
6 | export async function queryActivities() {
7 | return request('/api/activities');
8 | }
9 | export async function fakeChartData() {
10 | return request('/api/fake_chart_data');
11 | }
12 | export async function queryCurrent() {
13 | return request('/api/currentUser');
14 | }
15 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/common/IconFont/index.js:
--------------------------------------------------------------------------------
1 | import { createFromIconfontCN } from '@ant-design/icons';
2 |
3 | const IconFont = createFromIconfontCN({
4 | scriptUrl: 'https://at.alicdn.com/t/font_1101588_01zniftxm9yp.js',
5 | });
6 | export default IconFont;
7 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorContextMenu/FlowContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasMenu, ContextMenu, EdgeMenu, GroupMenu, MultiMenu, NodeMenu } from 'gg-editor';
2 | import React from 'react';
3 | import MenuItem from './MenuItem';
4 | import styles from './index.less';
5 |
6 | const FlowContextMenu = () => (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 |
34 | export default FlowContextMenu;
35 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorContextMenu/KoniContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import FlowContextMenu from './FlowContextMenu';
2 |
3 | export default FlowContextMenu;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorContextMenu/MenuItem.jsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'gg-editor';
2 | import React from 'react';
3 | import IconFont from '../../common/IconFont';
4 | import styles from './index.less';
5 |
6 | const upperFirst = str => str.toLowerCase().replace(/( |^)[a-z]/g, l => l.toUpperCase());
7 |
8 | const MenuItem = props => {
9 | const { command, icon, text } = props;
10 | return (
11 |
12 |
13 |
14 | {text || upperFirst(command)}
15 |
16 |
17 | );
18 | };
19 |
20 | export default MenuItem;
21 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorContextMenu/MindContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasMenu, ContextMenu, NodeMenu } from 'gg-editor';
2 | import React from 'react';
3 | import MenuItem from './MenuItem';
4 | import styles from './index.less';
5 |
6 | const MindContextMenu = () => (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 |
22 | export default MindContextMenu;
23 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorContextMenu/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowContextMenu from './FlowContextMenu';
2 | import KoniContextMenu from './KoniContextMenu';
3 | import MindContextMenu from './MindContextMenu';
4 |
5 | export { FlowContextMenu, MindContextMenu, KoniContextMenu };
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorContextMenu/index.less:
--------------------------------------------------------------------------------
1 | .contextMenu {
2 | display: none;
3 | overflow: hidden;
4 | background: #fff;
5 | border-radius: 4px;
6 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
7 |
8 | .item {
9 | display: flex;
10 | align-items: center;
11 | padding: 5px 12px;
12 | cursor: pointer;
13 | transition: all 0.3s;
14 | user-select: none;
15 |
16 | &:hover {
17 | background: #e6f7ff;
18 | }
19 |
20 | span.anticon {
21 | margin-right: 8px;
22 | }
23 | }
24 |
25 | :global {
26 | .disable {
27 | :local {
28 | .item {
29 | color: rgba(0, 0, 0, 0.25);
30 | cursor: auto;
31 |
32 | &:hover {
33 | background: #fff;
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorDetailPanel/FlowDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasPanel, DetailPanel, EdgePanel, GroupPanel, MultiPanel, NodePanel } from 'gg-editor';
2 | import { Card } from 'antd';
3 | import React from 'react';
4 | import DetailForm from './DetailForm';
5 | import styles from './index.less';
6 |
7 | const FlowDetailPanel = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 |
27 | export default FlowDetailPanel;
28 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorDetailPanel/KoniDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import FlowDetailPanel from './FlowDetailPanel';
2 |
3 | export default FlowDetailPanel;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorDetailPanel/MindDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasPanel, DetailPanel, NodePanel } from 'gg-editor';
2 | import { Card } from 'antd';
3 | import React from 'react';
4 | import DetailForm from './DetailForm';
5 | import styles from './index.less';
6 |
7 | const MindDetailPanel = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | );
17 |
18 | export default MindDetailPanel;
19 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorDetailPanel/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowDetailPanel from './FlowDetailPanel';
2 | import KoniDetailPanel from './KoniDetailPanel';
3 | import MindDetailPanel from './MindDetailPanel';
4 |
5 | export { FlowDetailPanel, MindDetailPanel, KoniDetailPanel };
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorDetailPanel/index.less:
--------------------------------------------------------------------------------
1 | .detailPanel {
2 | flex: 1;
3 | background: #fafafa;
4 |
5 | :global {
6 | .ant-card {
7 | background: #fafafa;
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorItemPanel/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowItemPanel from './FlowItemPanel';
2 | import KoniItemPanel from './KoniItemPanel';
3 |
4 | export { FlowItemPanel, KoniItemPanel };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorItemPanel/index.less:
--------------------------------------------------------------------------------
1 | .itemPanel {
2 | flex: 1;
3 | background: #fafafa;
4 |
5 | :global {
6 | .ant-card {
7 | background: #fafafa;
8 | }
9 |
10 | .ant-card-body {
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 |
15 | > div {
16 | margin-bottom: 16px;
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorMinimap/index.jsx:
--------------------------------------------------------------------------------
1 | import { Card } from 'antd';
2 | import { Minimap } from 'gg-editor';
3 | import React from 'react';
4 |
5 | const EditorMinimap = () => (
6 |
7 |
8 |
9 | );
10 |
11 | export default EditorMinimap;
12 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorToolbar/FlowToolbar.jsx:
--------------------------------------------------------------------------------
1 | import { Divider } from 'antd';
2 | import React from 'react';
3 | import { Toolbar } from 'gg-editor';
4 | import ToolbarButton from './ToolbarButton';
5 | import styles from './index.less';
6 |
7 | const FlowToolbar = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 |
30 | export default FlowToolbar;
31 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorToolbar/KoniToolbar.jsx:
--------------------------------------------------------------------------------
1 | import FlowToolbar from './FlowToolbar';
2 |
3 | export default FlowToolbar;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorToolbar/MindToolbar.jsx:
--------------------------------------------------------------------------------
1 | import { Divider } from 'antd';
2 | import React from 'react';
3 | import { Toolbar } from 'gg-editor';
4 | import ToolbarButton from './ToolbarButton';
5 | import styles from './index.less';
6 |
7 | const FlowToolbar = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 |
25 | export default FlowToolbar;
26 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorToolbar/ToolbarButton.jsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'gg-editor';
2 | import React from 'react';
3 | import { Tooltip } from 'antd';
4 | import IconFont from '../../common/IconFont';
5 | import styles from './index.less';
6 |
7 | const upperFirst = str => str.toLowerCase().replace(/( |^)[a-z]/g, l => l.toUpperCase());
8 |
9 | const ToolbarButton = props => {
10 | const { command, icon, text } = props;
11 | return (
12 |
13 |
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default ToolbarButton;
25 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorToolbar/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowToolbar from './FlowToolbar';
2 | import KoniToolbar from './KoniToolbar';
3 | import MindToolbar from './MindToolbar';
4 |
5 | export { FlowToolbar, MindToolbar, KoniToolbar };
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/components/EditorToolbar/index.less:
--------------------------------------------------------------------------------
1 | .toolbar {
2 | display: flex;
3 | align-items: center;
4 |
5 | :global {
6 | .command .anticon {
7 | display: inline-block;
8 | width: 27px;
9 | height: 27px;
10 | margin: 0 6px;
11 | padding-top: 6px;
12 | text-align: center;
13 | border: 1px solid #fff;
14 | cursor: pointer;
15 |
16 | &:hover {
17 | border: 1px solid #e6e9ed;
18 | }
19 | }
20 |
21 | .disable .anticon {
22 | color: rgba(0, 0, 0, 0.25);
23 | cursor: auto;
24 |
25 | &:hover {
26 | border: 1px solid #fff;
27 | }
28 | }
29 | }
30 | }
31 |
32 | .tooltip {
33 | :global {
34 | .ant-tooltip-inner {
35 | font-size: 12px;
36 | border-radius: 0;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/index.jsx:
--------------------------------------------------------------------------------
1 | import { Col, Row } from 'antd';
2 | import GGEditor, { Flow } from 'gg-editor';
3 | import { PageHeaderWrapper } from '@ant-design/pro-layout';
4 | import React from 'react';
5 | import EditorMinimap from './components/EditorMinimap';
6 | import { FlowContextMenu } from './components/EditorContextMenu';
7 | import { FlowDetailPanel } from './components/EditorDetailPanel';
8 | import { FlowItemPanel } from './components/EditorItemPanel';
9 | import { FlowToolbar } from './components/EditorToolbar';
10 | import styles from './index.less';
11 |
12 | GGEditor.setTrackable(false);
13 | export default () => (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | );
37 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/index.less:
--------------------------------------------------------------------------------
1 | .editor {
2 | display: flex;
3 | flex: 1;
4 | flex-direction: column;
5 | width: 100%;
6 | height: calc(100vh - 250px);
7 | background: #fff;
8 | }
9 |
10 | .editorHd {
11 | padding: 8px;
12 | border: 1px solid #e6e9ed;
13 | }
14 |
15 | .editorBd {
16 | flex: 1;
17 | }
18 |
19 | .editorSidebar,
20 | .editorContent {
21 | display: flex;
22 | flex-direction: column;
23 | }
24 |
25 | .editorSidebar {
26 | background: #fafafa;
27 |
28 | &:first-child {
29 | border-right: 1px solid #e6e9ed;
30 | }
31 |
32 | &:last-child {
33 | border-left: 1px solid #e6e9ed;
34 | }
35 | }
36 |
37 | .flow,
38 | .mind,
39 | .koni {
40 | flex: 1;
41 | }
42 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/locales/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'editorandflow.description':
3 | 'The flow chart is an excellent way to represent the idea of the algorithm',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/flow/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'editorandflow.description': '千言万语不如一张图,流程图是表示算法思路的好方法',
3 | };
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/common/IconFont/index.js:
--------------------------------------------------------------------------------
1 | import { createFromIconfontCN } from '@ant-design/icons';
2 |
3 | const IconFont = createFromIconfontCN({
4 | scriptUrl: 'https://at.alicdn.com/t/font_1101588_01zniftxm9yp.js',
5 | });
6 | export default IconFont;
7 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorContextMenu/FlowContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasMenu, ContextMenu, EdgeMenu, GroupMenu, MultiMenu, NodeMenu } from 'gg-editor';
2 | import React from 'react';
3 | import MenuItem from './MenuItem';
4 | import styles from './index.less';
5 |
6 | const FlowContextMenu = () => (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 |
34 | export default FlowContextMenu;
35 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorContextMenu/KoniContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import FlowContextMenu from './FlowContextMenu';
2 |
3 | export default FlowContextMenu;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorContextMenu/MenuItem.jsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'gg-editor';
2 | import React from 'react';
3 | import IconFont from '../../common/IconFont';
4 | import styles from './index.less';
5 |
6 | const upperFirst = str => str.toLowerCase().replace(/( |^)[a-z]/g, l => l.toUpperCase());
7 |
8 | const MenuItem = props => {
9 | const { command, icon, text } = props;
10 | return (
11 |
12 |
13 |
14 | {text || upperFirst(command)}
15 |
16 |
17 | );
18 | };
19 |
20 | export default MenuItem;
21 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorContextMenu/MindContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasMenu, ContextMenu, NodeMenu } from 'gg-editor';
2 | import React from 'react';
3 | import MenuItem from './MenuItem';
4 | import styles from './index.less';
5 |
6 | const MindContextMenu = () => (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 |
22 | export default MindContextMenu;
23 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorContextMenu/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowContextMenu from './FlowContextMenu';
2 | import KoniContextMenu from './KoniContextMenu';
3 | import MindContextMenu from './MindContextMenu';
4 |
5 | export { FlowContextMenu, MindContextMenu, KoniContextMenu };
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorContextMenu/index.less:
--------------------------------------------------------------------------------
1 | .contextMenu {
2 | display: none;
3 | overflow: hidden;
4 | background: #fff;
5 | border-radius: 4px;
6 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
7 |
8 | .item {
9 | display: flex;
10 | align-items: center;
11 | padding: 5px 12px;
12 | cursor: pointer;
13 | transition: all 0.3s;
14 | user-select: none;
15 |
16 | &:hover {
17 | background: #e6f7ff;
18 | }
19 |
20 | .anticon {
21 | margin-right: 8px;
22 | }
23 | }
24 |
25 | :global {
26 | .disable {
27 | :local {
28 | .item {
29 | color: rgba(0, 0, 0, 0.25);
30 | cursor: auto;
31 |
32 | &:hover {
33 | background: #fff;
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorDetailPanel/FlowDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasPanel, DetailPanel, EdgePanel, GroupPanel, MultiPanel, NodePanel } from 'gg-editor';
2 | import { Card } from 'antd';
3 | import React from 'react';
4 | import DetailForm from './DetailForm';
5 | import styles from './index.less';
6 |
7 | const FlowDetailPanel = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 |
27 | export default FlowDetailPanel;
28 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorDetailPanel/KoniDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import FlowDetailPanel from './FlowDetailPanel';
2 |
3 | export default FlowDetailPanel;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorDetailPanel/MindDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasPanel, DetailPanel, NodePanel } from 'gg-editor';
2 | import { Card } from 'antd';
3 | import React from 'react';
4 | import DetailForm from './DetailForm';
5 | import styles from './index.less';
6 |
7 | const MindDetailPanel = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | );
17 |
18 | export default MindDetailPanel;
19 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorDetailPanel/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowDetailPanel from './FlowDetailPanel';
2 | import KoniDetailPanel from './KoniDetailPanel';
3 | import MindDetailPanel from './MindDetailPanel';
4 |
5 | export { FlowDetailPanel, MindDetailPanel, KoniDetailPanel };
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorDetailPanel/index.less:
--------------------------------------------------------------------------------
1 | .detailPanel {
2 | flex: 1;
3 | background: #fafafa;
4 |
5 | :global {
6 | .ant-card {
7 | background: #fafafa;
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorItemPanel/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowItemPanel from './FlowItemPanel';
2 | import KoniItemPanel from './KoniItemPanel';
3 |
4 | export { FlowItemPanel, KoniItemPanel };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorItemPanel/index.less:
--------------------------------------------------------------------------------
1 | .itemPanel {
2 | flex: 1;
3 | background: #fafafa;
4 |
5 | :global {
6 | .ant-card {
7 | background: #fafafa;
8 | }
9 |
10 | .ant-card-body {
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 |
15 | > div {
16 | margin-bottom: 16px;
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorMinimap/index.jsx:
--------------------------------------------------------------------------------
1 | import { Card } from 'antd';
2 | import { Minimap } from 'gg-editor';
3 | import React from 'react';
4 |
5 | const EditorMinimap = () => (
6 |
7 |
8 |
9 | );
10 |
11 | export default EditorMinimap;
12 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorToolbar/KoniToolbar.jsx:
--------------------------------------------------------------------------------
1 | import FlowToolbar from './FlowToolbar';
2 |
3 | export default FlowToolbar;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorToolbar/MindToolbar.jsx:
--------------------------------------------------------------------------------
1 | import { Divider } from 'antd';
2 | import React from 'react';
3 | import { Toolbar } from 'gg-editor';
4 | import ToolbarButton from './ToolbarButton';
5 | import styles from './index.less';
6 |
7 | const FlowToolbar = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 |
25 | export default FlowToolbar;
26 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorToolbar/ToolbarButton.jsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'gg-editor';
2 | import React from 'react';
3 | import { Tooltip } from 'antd';
4 | import IconFont from '../../common/IconFont';
5 | import styles from './index.less';
6 |
7 | const upperFirst = str => str.toLowerCase().replace(/( |^)[a-z]/g, l => l.toUpperCase());
8 |
9 | const ToolbarButton = props => {
10 | const { command, icon, text } = props;
11 | return (
12 |
13 |
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default ToolbarButton;
25 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorToolbar/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowToolbar from './FlowToolbar';
2 | import KoniToolbar from './KoniToolbar';
3 | import MindToolbar from './MindToolbar';
4 |
5 | export { FlowToolbar, MindToolbar, KoniToolbar };
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/components/EditorToolbar/index.less:
--------------------------------------------------------------------------------
1 | .toolbar {
2 | display: flex;
3 | align-items: center;
4 |
5 | :global {
6 | .command .anticon {
7 | display: inline-block;
8 | width: 27px;
9 | height: 27px;
10 | margin: 0 6px;
11 | padding-top: 6px;
12 | text-align: center;
13 | border: 1px solid #fff;
14 | cursor: pointer;
15 |
16 | &:hover {
17 | border: 1px solid #e6e9ed;
18 | }
19 | }
20 |
21 | .disable .anticon {
22 | color: rgba(0, 0, 0, 0.25);
23 | cursor: auto;
24 |
25 | &:hover {
26 | border: 1px solid #fff;
27 | }
28 | }
29 | }
30 | }
31 |
32 | .tooltip {
33 | :global {
34 | .ant-tooltip-inner {
35 | font-size: 12px;
36 | border-radius: 0;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/index.less:
--------------------------------------------------------------------------------
1 | .editor {
2 | display: flex;
3 | flex: 1;
4 | flex-direction: column;
5 | width: 100%;
6 | height: calc(100vh - 250px);
7 | background: #fff;
8 | }
9 |
10 | .editorHd {
11 | padding: 8px;
12 | border: 1px solid #e6e9ed;
13 | }
14 |
15 | .editorBd {
16 | flex: 1;
17 | }
18 |
19 | .editorSidebar,
20 | .editorContent {
21 | display: flex;
22 | flex-direction: column;
23 | }
24 |
25 | .editorContent {
26 | :global {
27 | .graph-container canvas {
28 | vertical-align: middle;
29 | }
30 | }
31 | }
32 |
33 | .editorSidebar {
34 | background: #fafafa;
35 |
36 | &:first-child {
37 | border-right: 1px solid #e6e9ed;
38 | }
39 |
40 | &:last-child {
41 | border-left: 1px solid #e6e9ed;
42 | }
43 | }
44 |
45 | .flow,
46 | .mind,
47 | .koni {
48 | flex: 1;
49 | }
50 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/locales/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'editorandkoni.description':
3 | 'The topology diagram refers to the network structure diagram composed of network node devices and communication media',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/koni/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'editorandkoni.description': '拓扑结构图是指由网络节点设备和通信介质构成的网络结构图',
3 | };
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/common/IconFont/index.js:
--------------------------------------------------------------------------------
1 | import { createFromIconfontCN } from '@ant-design/icons';
2 |
3 | const IconFont = createFromIconfontCN({
4 | scriptUrl: 'https://at.alicdn.com/t/font_1101588_01zniftxm9yp.js',
5 | });
6 | export default IconFont;
7 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorContextMenu/FlowContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasMenu, ContextMenu, EdgeMenu, GroupMenu, MultiMenu, NodeMenu } from 'gg-editor';
2 | import React from 'react';
3 | import MenuItem from './MenuItem';
4 | import styles from './index.less';
5 |
6 | const FlowContextMenu = () => (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 |
34 | export default FlowContextMenu;
35 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorContextMenu/KoniContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import FlowContextMenu from './FlowContextMenu';
2 |
3 | export default FlowContextMenu;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorContextMenu/MenuItem.jsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'gg-editor';
2 | import React from 'react';
3 | import IconFont from '../../common/IconFont';
4 | import styles from './index.less';
5 |
6 | const upperFirst = str => str.toLowerCase().replace(/( |^)[a-z]/g, l => l.toUpperCase());
7 |
8 | const MenuItem = props => {
9 | const { command, icon, text } = props;
10 | return (
11 |
12 |
13 |
14 | {text || upperFirst(command)}
15 |
16 |
17 | );
18 | };
19 |
20 | export default MenuItem;
21 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorContextMenu/MindContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasMenu, ContextMenu, NodeMenu } from 'gg-editor';
2 | import React from 'react';
3 | import MenuItem from './MenuItem';
4 | import styles from './index.less';
5 |
6 | const MindContextMenu = () => (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 |
22 | export default MindContextMenu;
23 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorContextMenu/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowContextMenu from './FlowContextMenu';
2 | import KoniContextMenu from './KoniContextMenu';
3 | import MindContextMenu from './MindContextMenu';
4 |
5 | export { FlowContextMenu, MindContextMenu, KoniContextMenu };
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorContextMenu/index.less:
--------------------------------------------------------------------------------
1 | .contextMenu {
2 | display: none;
3 | overflow: hidden;
4 | background: #fff;
5 | border-radius: 4px;
6 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
7 |
8 | .item {
9 | display: flex;
10 | align-items: center;
11 | padding: 5px 12px;
12 | cursor: pointer;
13 | transition: all 0.3s;
14 | user-select: none;
15 |
16 | &:hover {
17 | background: #e6f7ff;
18 | }
19 |
20 | .anticon {
21 | margin-right: 8px;
22 | }
23 | }
24 |
25 | :global {
26 | .disable {
27 | :local {
28 | .item {
29 | color: rgba(0, 0, 0, 0.25);
30 | cursor: auto;
31 |
32 | &:hover {
33 | background: #fff;
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorDetailPanel/FlowDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasPanel, DetailPanel, EdgePanel, GroupPanel, MultiPanel, NodePanel } from 'gg-editor';
2 | import { Card } from 'antd';
3 | import React from 'react';
4 | import DetailForm from './DetailForm';
5 | import styles from './index.less';
6 |
7 | const FlowDetailPanel = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 |
27 | export default FlowDetailPanel;
28 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorDetailPanel/KoniDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import FlowDetailPanel from './FlowDetailPanel';
2 |
3 | export default FlowDetailPanel;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorDetailPanel/MindDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import { CanvasPanel, DetailPanel, NodePanel } from 'gg-editor';
2 | import { Card } from 'antd';
3 | import React from 'react';
4 | import DetailForm from './DetailForm';
5 | import styles from './index.less';
6 |
7 | const MindDetailPanel = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | );
17 |
18 | export default MindDetailPanel;
19 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorDetailPanel/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowDetailPanel from './FlowDetailPanel';
2 | import KoniDetailPanel from './KoniDetailPanel';
3 | import MindDetailPanel from './MindDetailPanel';
4 |
5 | export { FlowDetailPanel, MindDetailPanel, KoniDetailPanel };
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorDetailPanel/index.less:
--------------------------------------------------------------------------------
1 | .detailPanel {
2 | flex: 1;
3 | background: #fafafa;
4 |
5 | :global {
6 | .ant-card {
7 | background: #fafafa;
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorItemPanel/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowItemPanel from './FlowItemPanel';
2 | import KoniItemPanel from './KoniItemPanel';
3 |
4 | export { FlowItemPanel, KoniItemPanel };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorItemPanel/index.less:
--------------------------------------------------------------------------------
1 | .itemPanel {
2 | flex: 1;
3 | background: #fafafa;
4 |
5 | :global {
6 | .ant-card {
7 | background: #fafafa;
8 | }
9 |
10 | .ant-card-body {
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 |
15 | > div {
16 | margin-bottom: 16px;
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorMinimap/index.jsx:
--------------------------------------------------------------------------------
1 | import { Card } from 'antd';
2 | import { Minimap } from 'gg-editor';
3 | import React from 'react';
4 |
5 | const EditorMinimap = () => (
6 |
7 |
8 |
9 | );
10 |
11 | export default EditorMinimap;
12 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorToolbar/KoniToolbar.jsx:
--------------------------------------------------------------------------------
1 | import FlowToolbar from './FlowToolbar';
2 |
3 | export default FlowToolbar;
4 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorToolbar/MindToolbar.jsx:
--------------------------------------------------------------------------------
1 | import { Divider } from 'antd';
2 | import React from 'react';
3 | import { Toolbar } from 'gg-editor';
4 | import ToolbarButton from './ToolbarButton';
5 | import styles from './index.less';
6 |
7 | const FlowToolbar = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 |
25 | export default FlowToolbar;
26 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorToolbar/ToolbarButton.jsx:
--------------------------------------------------------------------------------
1 | import { Command } from 'gg-editor';
2 | import React from 'react';
3 | import { Tooltip } from 'antd';
4 | import IconFont from '../../common/IconFont';
5 | import styles from './index.less';
6 |
7 | const upperFirst = str => str.toLowerCase().replace(/( |^)[a-z]/g, l => l.toUpperCase());
8 |
9 | const ToolbarButton = props => {
10 | const { command, icon, text } = props;
11 | return (
12 |
13 |
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default ToolbarButton;
25 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorToolbar/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowToolbar from './FlowToolbar';
2 | import KoniToolbar from './KoniToolbar';
3 | import MindToolbar from './MindToolbar';
4 |
5 | export { FlowToolbar, MindToolbar, KoniToolbar };
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/components/EditorToolbar/index.less:
--------------------------------------------------------------------------------
1 | .toolbar {
2 | display: flex;
3 | align-items: center;
4 |
5 | :global {
6 | .command .anticon {
7 | display: inline-block;
8 | width: 27px;
9 | height: 27px;
10 | margin: 0 6px;
11 | padding-top: 6px;
12 | text-align: center;
13 | border: 1px solid #fff;
14 | cursor: pointer;
15 |
16 | &:hover {
17 | border: 1px solid #e6e9ed;
18 | }
19 | }
20 |
21 | .disable .anticon {
22 | color: rgba(0, 0, 0, 0.25);
23 | cursor: auto;
24 |
25 | &:hover {
26 | border: 1px solid #fff;
27 | }
28 | }
29 | }
30 | }
31 |
32 | .tooltip {
33 | :global {
34 | .ant-tooltip-inner {
35 | font-size: 12px;
36 | border-radius: 0;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/index.jsx:
--------------------------------------------------------------------------------
1 | import { Col, Row } from 'antd';
2 | import GGEditor, { Mind } from 'gg-editor';
3 | import { PageHeaderWrapper } from '@ant-design/pro-layout';
4 | import React from 'react';
5 | import EditorMinimap from './components/EditorMinimap';
6 | import { MindContextMenu } from './components/EditorContextMenu';
7 | import { MindDetailPanel } from './components/EditorDetailPanel';
8 | import { MindToolbar } from './components/EditorToolbar';
9 | import data from './worldCup2018.json';
10 | import styles from './index.less';
11 |
12 | GGEditor.setTrackable(false);
13 | export default () => (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/index.less:
--------------------------------------------------------------------------------
1 | .editor {
2 | display: flex;
3 | flex: 1;
4 | flex-direction: column;
5 | width: 100%;
6 | height: calc(100vh - 250px);
7 | background: #fff;
8 | }
9 |
10 | .editorHd {
11 | padding: 8px;
12 | border: 1px solid #e6e9ed;
13 | }
14 |
15 | .editorBd {
16 | flex: 1;
17 | }
18 |
19 | .editorSidebar,
20 | .editorContent {
21 | display: flex;
22 | flex-direction: column;
23 | }
24 |
25 | .editorContent {
26 | :global {
27 | .graph-container canvas {
28 | vertical-align: middle;
29 | }
30 | }
31 | }
32 |
33 | .editorSidebar {
34 | background: #fafafa;
35 |
36 | &:first-child {
37 | border-right: 1px solid #e6e9ed;
38 | }
39 |
40 | &:last-child {
41 | border-left: 1px solid #e6e9ed;
42 | }
43 | }
44 |
45 | .flow,
46 | .mind,
47 | .koni {
48 | flex: 1;
49 | }
50 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/locales/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'editorandmind.description':
3 | 'The brain map is an effective graphical thinking tool for expressing divergent thinking. It is simple but effective and is a practical thinking tool',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/editor/mind/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'editorandmind.description':
3 | '脑图是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/403/index.jsx:
--------------------------------------------------------------------------------
1 | import Link from 'umi/link';
2 | import { Result, Button } from 'antd';
3 | import React from 'react';
4 |
5 | export default () => (
6 |
15 |
16 |
17 | }
18 | />
19 | );
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/403/locales/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand403.exception.back': 'Back to home',
3 | 'exceptionand403.description.403': "Sorry, you don't have access to this page.",
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/403/locales/pt-BR.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand403.exception.back': 'Voltar para Início',
3 | 'exceptionand403.description.403': 'Desculpe, você não tem acesso a esta página.',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/403/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand403.exception.back': '返回首页',
3 | 'exceptionand403.description.403': '抱歉,你无权访问该页面。',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/403/locales/zh-TW.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand403.exception.back': '返回首頁',
3 | 'exceptionand403.description.403': '抱歉,妳無權訪問該頁面。',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/404/index.jsx:
--------------------------------------------------------------------------------
1 | import Link from 'umi/link';
2 | import { Result, Button } from 'antd';
3 | import React from 'react';
4 |
5 | export default () => (
6 |
15 |
16 |
17 | }
18 | />
19 | );
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/404/locales/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand404.exception.back': 'Back to home',
3 | 'exceptionand404.description.404': 'Sorry, the page you visited does not exist.',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/404/locales/pt-BR.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand404.exception.back': 'Voltar para Início',
3 | 'exceptionand404.description.404': 'Desculpe, a página que você visitou não existe.',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/404/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand404.exception.back': '返回首页',
3 | 'exceptionand404.description.404': '抱歉,你访问的页面不存在。',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/404/locales/zh-TW.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand404.exception.back': '返回首頁',
3 | 'exceptionand404.description.404': '抱歉,妳訪問的頁面不存在。',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/500/index.jsx:
--------------------------------------------------------------------------------
1 | import Link from 'umi/link';
2 | import { Result, Button } from 'antd';
3 | import React from 'react';
4 |
5 | export default () => (
6 |
15 |
16 |
17 | }
18 | />
19 | );
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/500/locales/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand500.exception.back': 'Back to home',
3 | 'exceptionand500.description.500': 'Sorry, the server is reporting an error.',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/500/locales/pt-BR.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand500.exception.back': 'Voltar para Início',
3 | 'exceptionand500.description.500': 'Desculpe, o servidor está reportando um erro.',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/500/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand500.exception.back': '返回首页',
3 | 'exceptionand500.description.500': '抱歉,服务器出错了。',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/exception/500/locales/zh-TW.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand500.exception.back': '返回首頁',
3 | 'exceptionand500.description.500': '抱歉,服務器出錯了。',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/advanced-form/_mock.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'POST /api/forms': (_, res) => {
3 | res.send({
4 | message: 'Ok',
5 | });
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/advanced-form/components/FooterToolbar/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { RouteContext } from '@ant-design/pro-layout';
3 | import classNames from 'classnames';
4 | import styles from './index.less';
5 |
6 | export default class FooterToolbar extends Component {
7 | getWidth = ({ collapsed, isMobile, siderWidth }) => {
8 | const sider = document.querySelector('.ant-layout-sider');
9 |
10 | if (!sider) {
11 | return undefined;
12 | }
13 |
14 | return isMobile ? undefined : `calc(100% - ${collapsed ? 80 : siderWidth || 256}px)`;
15 | };
16 |
17 | render() {
18 | const { children, className, extra, ...restProps } = this.props;
19 | return (
20 |
21 | {value => (
22 |
30 |
{extra}
31 |
{children}
32 |
33 | )}
34 |
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/advanced-form/components/FooterToolbar/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .toolbar {
4 | position: fixed;
5 | right: 0;
6 | bottom: 0;
7 | z-index: 99;
8 | width: 100%;
9 | height: 56px;
10 | padding: 0 24px;
11 | line-height: 56px;
12 | background: @component-background;
13 | border-top: 1px solid @border-color-split;
14 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03);
15 |
16 | &::after {
17 | display: block;
18 | clear: both;
19 | content: '';
20 | }
21 |
22 | .left {
23 | float: left;
24 | }
25 |
26 | .right {
27 | float: right;
28 | }
29 |
30 | button + button {
31 | margin-left: 8px;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/advanced-form/model.js:
--------------------------------------------------------------------------------
1 | import { message } from 'antd';
2 | import { fakeSubmitForm } from './service';
3 |
4 | const Model = {
5 | namespace: 'formAndadvancedForm',
6 | state: {},
7 | effects: {
8 | *submitAdvancedForm({ payload }, { call }) {
9 | yield call(fakeSubmitForm, payload);
10 | message.success('提交成功');
11 | },
12 | },
13 | };
14 | export default Model;
15 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/advanced-form/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function fakeSubmitForm(params) {
4 | return request('/api/forms', {
5 | method: 'POST',
6 | data: params,
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/basic-form/_mock.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'POST /api/forms': (_, res) => {
3 | res.send({
4 | message: 'Ok',
5 | });
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/basic-form/model.js:
--------------------------------------------------------------------------------
1 | import { message } from 'antd';
2 | import { fakeSubmitForm } from './service';
3 |
4 | const Model = {
5 | namespace: 'formAndbasicForm',
6 | state: {},
7 | effects: {
8 | *submitRegularForm({ payload }, { call }) {
9 | yield call(fakeSubmitForm, payload);
10 | message.success('提交成功');
11 | },
12 | },
13 | };
14 | export default Model;
15 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/basic-form/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function fakeSubmitForm(params) {
4 | return request('/api/forms', {
5 | method: 'POST',
6 | data: params,
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/step-form/_mock.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'POST /api/forms': (_, res) => {
3 | res.send({
4 | message: 'Ok',
5 | });
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/step-form/model.js:
--------------------------------------------------------------------------------
1 | import { fakeSubmitForm } from './service';
2 |
3 | const Model = {
4 | namespace: 'formAndstepForm',
5 | state: {
6 | current: 'info',
7 | step: {
8 | payAccount: 'ant-design@alipay.com',
9 | receiverAccount: 'test@example.com',
10 | receiverName: 'Alex',
11 | amount: '500',
12 | },
13 | },
14 | effects: {
15 | *submitStepForm({ payload }, { call, put }) {
16 | yield call(fakeSubmitForm, payload);
17 | yield put({
18 | type: 'saveStepFormData',
19 | payload,
20 | });
21 | yield put({
22 | type: 'saveCurrentStep',
23 | payload: 'result',
24 | });
25 | },
26 | },
27 | reducers: {
28 | saveCurrentStep(state, { payload }) {
29 | return { ...state, current: payload };
30 | },
31 |
32 | saveStepFormData(state, { payload }) {
33 | return { ...state, step: { ...state.step, ...payload } };
34 | },
35 | },
36 | };
37 | export default Model;
38 |
--------------------------------------------------------------------------------
/frontend/src/pages/form/step-form/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function fakeSubmitForm(params) {
4 | return request('/api/forms', {
5 | method: 'POST',
6 | data: params,
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/basic-list/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface Member {
2 | avatar: string;
3 | name: string;
4 | id: string;
5 | }
6 |
7 | export interface BasicListItemDataType {
8 | id: string;
9 | owner: string;
10 | title: string;
11 | avatar: string;
12 | cover: string;
13 | status: 'normal' | 'exception' | 'active' | 'success';
14 | percent: number;
15 | logo: string;
16 | href: string;
17 | body?: any;
18 | updatedAt: number;
19 | createdAt: number;
20 | subDescription: string;
21 | description: string;
22 | activeUser: number;
23 | newUser: number;
24 | star: number;
25 | like: number;
26 | message: number;
27 | content: string;
28 | members: Member[];
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/basic-list/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryFakeList(params) {
4 | return request('/api/fake_list', {
5 | params,
6 | });
7 | }
8 | export async function removeFakeList(params) {
9 | const { count = 5, ...restParams } = params;
10 | return request('/api/fake_list', {
11 | method: 'POST',
12 | params: {
13 | count,
14 | },
15 | data: { ...restParams, method: 'delete' },
16 | });
17 | }
18 | export async function addFakeList(params) {
19 | const { count = 5, ...restParams } = params;
20 | return request('/api/fake_list', {
21 | method: 'POST',
22 | params: {
23 | count,
24 | },
25 | data: { ...restParams, method: 'post' },
26 | });
27 | }
28 | export async function updateFakeList(params) {
29 | const { count = 5, ...restParams } = params;
30 | return request('/api/fake_list', {
31 | method: 'POST',
32 | params: {
33 | count,
34 | },
35 | data: { ...restParams, method: 'update' },
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/basic-list/utils/utils.less:
--------------------------------------------------------------------------------
1 | .textOverflow() {
2 | overflow: hidden;
3 | white-space: nowrap;
4 | text-overflow: ellipsis;
5 | word-break: break-all;
6 | }
7 |
8 | .textOverflowMulti(@line: 3, @bg: #fff) {
9 | position: relative;
10 | max-height: @line * 1.5em;
11 | margin-right: -1em;
12 | padding-right: 1em;
13 | overflow: hidden;
14 | line-height: 1.5em;
15 | text-align: justify;
16 | &::before {
17 | position: absolute;
18 | right: 14px;
19 | bottom: 0;
20 | padding: 0 1px;
21 | background: @bg;
22 | content: '...';
23 | }
24 | &::after {
25 | position: absolute;
26 | right: 14px;
27 | width: 1em;
28 | height: 1em;
29 | margin-top: 0.2em;
30 | background: white;
31 | content: '';
32 | }
33 | }
34 |
35 | // mixins for clearfix
36 | // ------------------------
37 | .clearfix() {
38 | zoom: 1;
39 | &::before,
40 | &::after {
41 | display: table;
42 | content: ' ';
43 | }
44 | &::after {
45 | clear: both;
46 | height: 0;
47 | font-size: 0;
48 | visibility: hidden;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/card-list/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface Member {
2 | avatar: string;
3 | name: string;
4 | id: string;
5 | }
6 |
7 | export interface CardListItemDataType {
8 | id: string;
9 | owner: string;
10 | title: string;
11 | avatar: string;
12 | cover: string;
13 | status: 'normal' | 'exception' | 'active' | 'success';
14 | percent: number;
15 | logo: string;
16 | href: string;
17 | body?: any;
18 | updatedAt: number;
19 | createdAt: number;
20 | subDescription: string;
21 | description: string;
22 | activeUser: number;
23 | newUser: number;
24 | star: number;
25 | like: number;
26 | message: number;
27 | content: string;
28 | members: Member[];
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/card-list/model.js:
--------------------------------------------------------------------------------
1 | import { queryFakeList } from './service';
2 |
3 | const Model = {
4 | namespace: 'listAndcardList',
5 | state: {
6 | list: [],
7 | },
8 | effects: {
9 | *fetch({ payload }, { call, put }) {
10 | const response = yield call(queryFakeList, payload);
11 | yield put({
12 | type: 'queryList',
13 | payload: Array.isArray(response) ? response : [],
14 | });
15 | },
16 | },
17 | reducers: {
18 | queryList(state, action) {
19 | return { ...state, list: action.payload };
20 | },
21 | },
22 | };
23 | export default Model;
24 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/card-list/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryFakeList(params) {
4 | return request('/api/fake_list', {
5 | params,
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/card-list/utils/utils.less:
--------------------------------------------------------------------------------
1 | .textOverflow() {
2 | overflow: hidden;
3 | white-space: nowrap;
4 | text-overflow: ellipsis;
5 | word-break: break-all;
6 | }
7 |
8 | .textOverflowMulti(@line: 3, @bg: #fff) {
9 | position: relative;
10 | max-height: @line * 1.5em;
11 | margin-right: -1em;
12 | padding-right: 1em;
13 | overflow: hidden;
14 | line-height: 1.5em;
15 | text-align: justify;
16 | &::before {
17 | position: absolute;
18 | right: 14px;
19 | bottom: 0;
20 | padding: 0 1px;
21 | background: @bg;
22 | content: '...';
23 | }
24 | &::after {
25 | position: absolute;
26 | right: 14px;
27 | width: 1em;
28 | height: 1em;
29 | margin-top: 0.2em;
30 | background: white;
31 | content: '';
32 | }
33 | }
34 |
35 | // mixins for clearfix
36 | // ------------------------
37 | .clearfix() {
38 | zoom: 1;
39 | &::before,
40 | &::after {
41 | display: table;
42 | content: ' ';
43 | }
44 | &::after {
45 | clear: both;
46 | height: 0;
47 | font-size: 0;
48 | visibility: hidden;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/applications/components/StandardFormRow/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import styles from './index.less';
4 |
5 | const StandardFormRow = ({ title, children, last, block, grid, ...rest }) => {
6 | const cls = classNames(styles.standardFormRow, {
7 | [styles.standardFormRowBlock]: block,
8 | [styles.standardFormRowLast]: last,
9 | [styles.standardFormRowGrid]: grid,
10 | });
11 | return (
12 |
13 | {title && (
14 |
15 | {title}
16 |
17 | )}
18 |
{children}
19 |
20 | );
21 | };
22 |
23 | export default StandardFormRow;
24 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/applications/components/TagSelect/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .tagSelect {
4 | position: relative;
5 | max-height: 32px;
6 | margin-left: -8px;
7 | overflow: hidden;
8 | line-height: 32px;
9 | transition: all 0.3s;
10 | user-select: none;
11 | :global {
12 | .ant-tag {
13 | margin-right: 24px;
14 | padding: 0 8px;
15 | font-size: @font-size-base;
16 | }
17 | }
18 | &.expanded {
19 | max-height: 200px;
20 | transition: all 0.3s;
21 | }
22 | .trigger {
23 | position: absolute;
24 | top: 0;
25 | right: 0;
26 |
27 | span.anticon {
28 | font-size: 12px;
29 | }
30 | }
31 | &.hasExpandTag {
32 | padding-right: 50px;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/applications/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface Member {
2 | avatar: string;
3 | name: string;
4 | id: string;
5 | }
6 |
7 | export interface ListItemDataType {
8 | id: string;
9 | owner: string;
10 | title: string;
11 | avatar: string;
12 | cover: string;
13 | status: 'normal' | 'exception' | 'active' | 'success';
14 | percent: number;
15 | logo: string;
16 | href: string;
17 | body?: any;
18 | updatedAt: number;
19 | createdAt: number;
20 | subDescription: string;
21 | description: string;
22 | activeUser: number;
23 | newUser: number;
24 | star: number;
25 | like: number;
26 | message: number;
27 | content: string;
28 | members: Member[];
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/applications/model.js:
--------------------------------------------------------------------------------
1 | import { queryFakeList } from './service';
2 |
3 | const Model = {
4 | namespace: 'listAndsearchAndapplications',
5 | state: {
6 | list: [],
7 | },
8 | effects: {
9 | *fetch({ payload }, { call, put }) {
10 | const response = yield call(queryFakeList, payload);
11 | yield put({
12 | type: 'queryList',
13 | payload: Array.isArray(response) ? response : [],
14 | });
15 | },
16 | },
17 | reducers: {
18 | queryList(state, action) {
19 | return { ...state, list: action.payload };
20 | },
21 | },
22 | };
23 | export default Model;
24 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/applications/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryFakeList(params) {
4 | return request('/api/fake_list', {
5 | params,
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/applications/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .filterCardList {
4 | margin-bottom: -24px;
5 | :global {
6 | .ant-card-meta-content {
7 | margin-top: 0;
8 | }
9 | // disabled white space
10 | .ant-card-meta-avatar {
11 | font-size: 0;
12 | }
13 |
14 | .ant-list .ant-list-item-content-single {
15 | max-width: 100%;
16 | }
17 | }
18 | .cardInfo {
19 | margin-top: 16px;
20 | margin-left: 40px;
21 | zoom: 1;
22 | &::before,
23 | &::after {
24 | display: table;
25 | content: ' ';
26 | }
27 | &::after {
28 | clear: both;
29 | height: 0;
30 | font-size: 0;
31 | visibility: hidden;
32 | }
33 | & > div {
34 | position: relative;
35 | float: left;
36 | width: 50%;
37 | text-align: left;
38 | p {
39 | margin: 0;
40 | font-size: 24px;
41 | line-height: 32px;
42 | }
43 | p:first-child {
44 | margin-bottom: 4px;
45 | color: @text-color-secondary;
46 | font-size: 12px;
47 | line-height: 20px;
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/applications/utils/utils.less:
--------------------------------------------------------------------------------
1 | .textOverflow() {
2 | overflow: hidden;
3 | white-space: nowrap;
4 | text-overflow: ellipsis;
5 | word-break: break-all;
6 | }
7 |
8 | .textOverflowMulti(@line: 3, @bg: #fff) {
9 | position: relative;
10 | max-height: @line * 1.5em;
11 | margin-right: -1em;
12 | padding-right: 1em;
13 | overflow: hidden;
14 | line-height: 1.5em;
15 | text-align: justify;
16 | &::before {
17 | position: absolute;
18 | right: 14px;
19 | bottom: 0;
20 | padding: 0 1px;
21 | background: @bg;
22 | content: '...';
23 | }
24 | &::after {
25 | position: absolute;
26 | right: 14px;
27 | width: 1em;
28 | height: 1em;
29 | margin-top: 0.2em;
30 | background: white;
31 | content: '';
32 | }
33 | }
34 |
35 | // mixins for clearfix
36 | // ------------------------
37 | .clearfix() {
38 | zoom: 1;
39 | &::before,
40 | &::after {
41 | display: table;
42 | content: ' ';
43 | }
44 | &::after {
45 | clear: both;
46 | height: 0;
47 | font-size: 0;
48 | visibility: hidden;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/articles/components/ArticleListContent/index.jsx:
--------------------------------------------------------------------------------
1 | import { Avatar } from 'antd';
2 | import React from 'react';
3 | import moment from 'moment';
4 | import styles from './index.less';
5 |
6 | const ArticleListContent = ({ data: { content, updatedAt, avatar, owner, href } }) => (
7 |
8 |
{content}
9 |
10 |
11 |
{owner} 发布在
{href}
12 |
{moment(updatedAt).format('YYYY-MM-DD HH:mm')}
13 |
14 |
15 | );
16 |
17 | export default ArticleListContent;
18 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/articles/components/ArticleListContent/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .listContent {
4 | .description {
5 | max-width: 720px;
6 | line-height: 22px;
7 | }
8 | .extra {
9 | margin-top: 16px;
10 | color: @text-color-secondary;
11 | line-height: 22px;
12 | & > :global(.ant-avatar) {
13 | position: relative;
14 | top: 1px;
15 | width: 20px;
16 | height: 20px;
17 | margin-right: 8px;
18 | vertical-align: top;
19 | }
20 | & > em {
21 | margin-left: 16px;
22 | color: @disabled-color;
23 | font-style: normal;
24 | }
25 | }
26 | }
27 |
28 | @media screen and (max-width: @screen-xs) {
29 | .listContent {
30 | .extra {
31 | & > em {
32 | display: block;
33 | margin-top: 8px;
34 | margin-left: 0;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/articles/components/StandardFormRow/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import styles from './index.less';
4 |
5 | const StandardFormRow = ({ title, children, last, block, grid, ...rest }) => {
6 | const cls = classNames(styles.standardFormRow, {
7 | [styles.standardFormRowBlock]: block,
8 | [styles.standardFormRowLast]: last,
9 | [styles.standardFormRowGrid]: grid,
10 | });
11 | return (
12 |
13 | {title && (
14 |
15 | {title}
16 |
17 | )}
18 |
{children}
19 |
20 | );
21 | };
22 |
23 | export default StandardFormRow;
24 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/articles/components/TagSelect/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .tagSelect {
4 | position: relative;
5 | max-height: 32px;
6 | margin-left: -8px;
7 | overflow: hidden;
8 | line-height: 32px;
9 | transition: all 0.3s;
10 | user-select: none;
11 | :global {
12 | .ant-tag {
13 | margin-right: 24px;
14 | padding: 0 8px;
15 | font-size: @font-size-base;
16 | }
17 | }
18 | &.expanded {
19 | max-height: 200px;
20 | transition: all 0.3s;
21 | }
22 | .trigger {
23 | position: absolute;
24 | top: 0;
25 | right: 0;
26 | span.anticon {
27 | font-size: 12px;
28 | }
29 | }
30 | &.hasExpandTag {
31 | padding-right: 50px;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/articles/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface Member {
2 | avatar: string;
3 | name: string;
4 | id: string;
5 | }
6 |
7 | export interface ListItemDataType {
8 | id: string;
9 | owner: string;
10 | title: string;
11 | avatar: string;
12 | cover: string;
13 | status: 'normal' | 'exception' | 'active' | 'success';
14 | percent: number;
15 | logo: string;
16 | href: string;
17 | body?: any;
18 | updatedAt: number;
19 | createdAt: number;
20 | subDescription: string;
21 | description: string;
22 | activeUser: number;
23 | newUser: number;
24 | star: number;
25 | like: number;
26 | message: number;
27 | content: string;
28 | members: Member[];
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/articles/model.js:
--------------------------------------------------------------------------------
1 | import { queryFakeList } from './service';
2 |
3 | const Model = {
4 | namespace: 'listAndsearchAndarticles',
5 | state: {
6 | list: [],
7 | },
8 | effects: {
9 | *fetch({ payload }, { call, put }) {
10 | const response = yield call(queryFakeList, payload);
11 | yield put({
12 | type: 'queryList',
13 | payload: Array.isArray(response) ? response : [],
14 | });
15 | },
16 |
17 | *appendFetch({ payload }, { call, put }) {
18 | const response = yield call(queryFakeList, payload);
19 | yield put({
20 | type: 'appendList',
21 | payload: Array.isArray(response) ? response : [],
22 | });
23 | },
24 | },
25 | reducers: {
26 | queryList(state, action) {
27 | return { ...state, list: action.payload };
28 | },
29 |
30 | appendList(state, action) {
31 | return { ...state, list: state.list.concat(action.payload) };
32 | },
33 | },
34 | };
35 | export default Model;
36 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/articles/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryFakeList(params) {
4 | return request('/api/fake_list', {
5 | params,
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/articles/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | a.listItemMetaTitle {
4 | color: @heading-color;
5 | }
6 | .listItemExtra {
7 | width: 272px;
8 | height: 1px;
9 | }
10 | .selfTrigger {
11 | margin-left: 12px;
12 | }
13 |
14 | @media screen and (max-width: @screen-xs) {
15 | .selfTrigger {
16 | display: block;
17 | margin-left: 0;
18 | }
19 | }
20 | @media screen and (max-width: @screen-md) {
21 | .selfTrigger {
22 | display: block;
23 | margin-left: 0;
24 | }
25 | }
26 | @media screen and (max-width: @screen-lg) {
27 | .listItemExtra {
28 | width: 0;
29 | height: 1px;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/projects/components/AvatarList/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .avatarList {
4 | display: inline-block;
5 | ul {
6 | display: inline-block;
7 | margin-left: 8px;
8 | font-size: 0;
9 | }
10 | }
11 |
12 | .avatarItem {
13 | display: inline-block;
14 | width: @avatar-size-base;
15 | height: @avatar-size-base;
16 | margin-left: -8px;
17 | font-size: @font-size-base;
18 | :global {
19 | .ant-avatar {
20 | border: 1px solid #fff;
21 | }
22 | }
23 | }
24 |
25 | .avatarItemLarge {
26 | width: @avatar-size-lg;
27 | height: @avatar-size-lg;
28 | }
29 |
30 | .avatarItemSmall {
31 | width: @avatar-size-sm;
32 | height: @avatar-size-sm;
33 | }
34 |
35 | .avatarItemMini {
36 | width: 20px;
37 | height: 20px;
38 | :global {
39 | .ant-avatar {
40 | width: 20px;
41 | height: 20px;
42 | line-height: 20px;
43 |
44 | .ant-avatar-string {
45 | font-size: 12px;
46 | line-height: 18px;
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/projects/components/StandardFormRow/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import styles from './index.less';
4 |
5 | const StandardFormRow = ({ title, children, last, block, grid, ...rest }) => {
6 | const cls = classNames(styles.standardFormRow, {
7 | [styles.standardFormRowBlock]: block,
8 | [styles.standardFormRowLast]: last,
9 | [styles.standardFormRowGrid]: grid,
10 | });
11 | return (
12 |
13 | {title && (
14 |
15 | {title}
16 |
17 | )}
18 |
{children}
19 |
20 | );
21 | };
22 |
23 | export default StandardFormRow;
24 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/projects/components/TagSelect/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .tagSelect {
4 | position: relative;
5 | max-height: 32px;
6 | margin-left: -8px;
7 | overflow: hidden;
8 | line-height: 32px;
9 | transition: all 0.3s;
10 | user-select: none;
11 | :global {
12 | .ant-tag {
13 | margin-right: 24px;
14 | padding: 0 8px;
15 | font-size: @font-size-base;
16 | }
17 | }
18 | &.expanded {
19 | max-height: 200px;
20 | transition: all 0.3s;
21 | }
22 | .trigger {
23 | position: absolute;
24 | top: 0;
25 | right: 0;
26 | span.anticon {
27 | font-size: 12px;
28 | }
29 | }
30 | &.hasExpandTag {
31 | padding-right: 50px;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/projects/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface Member {
2 | avatar: string;
3 | name: string;
4 | id: string;
5 | }
6 |
7 | export interface ListItemDataType {
8 | id: string;
9 | owner: string;
10 | title: string;
11 | avatar: string;
12 | cover: string;
13 | status: 'normal' | 'exception' | 'active' | 'success';
14 | percent: number;
15 | logo: string;
16 | href: string;
17 | body?: any;
18 | updatedAt: number;
19 | createdAt: number;
20 | subDescription: string;
21 | description: string;
22 | activeUser: number;
23 | newUser: number;
24 | star: number;
25 | like: number;
26 | message: number;
27 | content: string;
28 | members: Member[];
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/projects/model.js:
--------------------------------------------------------------------------------
1 | import { queryFakeList } from './service';
2 |
3 | const Model = {
4 | namespace: 'listAndsearchAndprojects',
5 | state: {
6 | list: [],
7 | },
8 | effects: {
9 | *fetch({ payload }, { call, put }) {
10 | const response = yield call(queryFakeList, payload);
11 | yield put({
12 | type: 'queryList',
13 | payload: Array.isArray(response) ? response : [],
14 | });
15 | },
16 | },
17 | reducers: {
18 | queryList(state, action) {
19 | return { ...state, list: action.payload };
20 | },
21 | },
22 | };
23 | export default Model;
24 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/projects/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryFakeList(params) {
4 | return request('/api/fake_list', {
5 | params,
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/projects/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 | @import './utils/utils.less';
3 |
4 | .coverCardList {
5 | margin-bottom: -24px;
6 |
7 | .card {
8 | :global {
9 | .ant-card-meta-title {
10 | margin-bottom: 4px;
11 | & > a {
12 | display: inline-block;
13 | max-width: 100%;
14 | color: @heading-color;
15 | }
16 | }
17 | .ant-card-meta-description {
18 | height: 44px;
19 | overflow: hidden;
20 | line-height: 22px;
21 | }
22 | }
23 |
24 | &:hover {
25 | :global {
26 | .ant-card-meta-title > a {
27 | color: @primary-color;
28 | }
29 | }
30 | }
31 | }
32 |
33 | .cardItemContent {
34 | display: flex;
35 | height: 20px;
36 | margin-top: 16px;
37 | margin-bottom: -4px;
38 | line-height: 20px;
39 | & > span {
40 | flex: 1;
41 | color: @text-color-secondary;
42 | font-size: 12px;
43 | }
44 | .avatarList {
45 | flex: 0 1 auto;
46 | }
47 | }
48 | .cardList {
49 | margin-top: 24px;
50 | }
51 |
52 | :global {
53 | .ant-list .ant-list-item-content-single {
54 | max-width: 100%;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/projects/utils/utils.less:
--------------------------------------------------------------------------------
1 | .textOverflow() {
2 | overflow: hidden;
3 | white-space: nowrap;
4 | text-overflow: ellipsis;
5 | word-break: break-all;
6 | }
7 |
8 | .textOverflowMulti(@line: 3, @bg: #fff) {
9 | position: relative;
10 | max-height: @line * 1.5em;
11 | margin-right: -1em;
12 | padding-right: 1em;
13 | overflow: hidden;
14 | line-height: 1.5em;
15 | text-align: justify;
16 | &::before {
17 | position: absolute;
18 | right: 14px;
19 | bottom: 0;
20 | padding: 0 1px;
21 | background: @bg;
22 | content: '...';
23 | }
24 | &::after {
25 | position: absolute;
26 | right: 14px;
27 | width: 1em;
28 | height: 1em;
29 | margin-top: 0.2em;
30 | background: white;
31 | content: '';
32 | }
33 | }
34 |
35 | // mixins for clearfix
36 | // ------------------------
37 | .clearfix() {
38 | zoom: 1;
39 | &::before,
40 | &::after {
41 | display: table;
42 | content: ' ';
43 | }
44 | &::after {
45 | clear: both;
46 | height: 0;
47 | font-size: 0;
48 | visibility: hidden;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/search/utils/utils.less:
--------------------------------------------------------------------------------
1 | .textOverflow() {
2 | overflow: hidden;
3 | white-space: nowrap;
4 | text-overflow: ellipsis;
5 | word-break: break-all;
6 | }
7 |
8 | .textOverflowMulti(@line: 3, @bg: #fff) {
9 | position: relative;
10 | max-height: @line * 1.5em;
11 | margin-right: -1em;
12 | padding-right: 1em;
13 | overflow: hidden;
14 | line-height: 1.5em;
15 | text-align: justify;
16 | &::before {
17 | position: absolute;
18 | right: 14px;
19 | bottom: 0;
20 | padding: 0 1px;
21 | background: @bg;
22 | content: '...';
23 | }
24 | &::after {
25 | position: absolute;
26 | right: 14px;
27 | width: 1em;
28 | height: 1em;
29 | margin-top: 0.2em;
30 | background: white;
31 | content: '';
32 | }
33 | }
34 |
35 | // mixins for clearfix
36 | // ------------------------
37 | .clearfix() {
38 | zoom: 1;
39 | &::before,
40 | &::after {
41 | display: table;
42 | content: ' ';
43 | }
44 | &::after {
45 | clear: both;
46 | height: 0;
47 | font-size: 0;
48 | visibility: hidden;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/table-list/components/CreateForm.jsx:
--------------------------------------------------------------------------------
1 | import { Form } from '@ant-design/compatible';
2 | import '@ant-design/compatible/assets/index.css';
3 | import { Input, Modal } from 'antd';
4 | import React from 'react';
5 |
6 | const FormItem = Form.Item;
7 |
8 | const CreateForm = props => {
9 | const { modalVisible, form, onSubmit: handleAdd, onCancel } = props;
10 |
11 | const okHandle = () => {
12 | form.validateFields((err, fieldsValue) => {
13 | if (err) return;
14 | form.resetFields();
15 | handleAdd(fieldsValue);
16 | });
17 | };
18 |
19 | return (
20 | onCancel()}
26 | >
27 |
36 | {form.getFieldDecorator('desc', {
37 | rules: [
38 | {
39 | required: true,
40 | message: '请输入至少五个字符的规则描述!',
41 | min: 5,
42 | },
43 | ],
44 | })()}
45 |
46 |
47 | );
48 | };
49 |
50 | export default Form.create()(CreateForm);
51 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/table-list/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableListItem {
2 | key: number;
3 | disabled?: boolean;
4 | href: string;
5 | avatar: string;
6 | name: string;
7 | title: string;
8 | owner: string;
9 | desc: string;
10 | callNo: number;
11 | status: number;
12 | updatedAt: Date;
13 | createdAt: Date;
14 | progress: number;
15 | }
16 |
17 | export interface TableListPagination {
18 | total: number;
19 | pageSize: number;
20 | current: number;
21 | }
22 |
23 | export interface TableListData {
24 | list: TableListItem[];
25 | pagination: Partial;
26 | }
27 |
28 | export interface TableListParams {
29 | sorter?: string;
30 | status?: string;
31 | name?: string;
32 | desc?: string;
33 | key?: number;
34 | pageSize?: number;
35 | currentPage?: number;
36 | }
37 |
--------------------------------------------------------------------------------
/frontend/src/pages/list/table-list/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryRule(params) {
4 | return request('/api/rule', {
5 | params,
6 | });
7 | }
8 | export async function removeRule(params) {
9 | return request('/api/rule', {
10 | method: 'POST',
11 | data: { ...params, method: 'delete' },
12 | });
13 | }
14 | export async function addRule(params) {
15 | return request('/api/rule', {
16 | method: 'POST',
17 | data: { ...params, method: 'post' },
18 | });
19 | }
20 | export async function updateRule(params) {
21 | return request('/api/rule', {
22 | method: 'POST',
23 | data: { ...params, method: 'update' },
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/frontend/src/pages/profile/advanced/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface AdvancedOperation1 {
2 | key: string;
3 | type: string;
4 | name: string;
5 | status: string;
6 | updatedAt: string;
7 | memo: string;
8 | }
9 |
10 | export interface AdvancedOperation2 {
11 | key: string;
12 | type: string;
13 | name: string;
14 | status: string;
15 | updatedAt: string;
16 | memo: string;
17 | }
18 |
19 | export interface AdvancedOperation3 {
20 | key: string;
21 | type: string;
22 | name: string;
23 | status: string;
24 | updatedAt: string;
25 | memo: string;
26 | }
27 |
28 | export interface AdvancedProfileData {
29 | advancedOperation1: AdvancedOperation1[];
30 | advancedOperation2: AdvancedOperation2[];
31 | advancedOperation3: AdvancedOperation3[];
32 | }
33 |
--------------------------------------------------------------------------------
/frontend/src/pages/profile/advanced/model.js:
--------------------------------------------------------------------------------
1 | import { queryAdvancedProfile } from './service';
2 |
3 | const Model = {
4 | namespace: 'profileAndadvanced',
5 | state: {
6 | advancedOperation1: [],
7 | advancedOperation2: [],
8 | advancedOperation3: [],
9 | },
10 | effects: {
11 | *fetchAdvanced(_, { call, put }) {
12 | const response = yield call(queryAdvancedProfile);
13 | yield put({
14 | type: 'show',
15 | payload: response,
16 | });
17 | },
18 | },
19 | reducers: {
20 | show(state, { payload }) {
21 | return { ...state, ...payload };
22 | },
23 | },
24 | };
25 | export default Model;
26 |
--------------------------------------------------------------------------------
/frontend/src/pages/profile/advanced/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryAdvancedProfile() {
4 | return request('/api/profile/advanced');
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/profile/advanced/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 | @import '~codemirror/lib/codemirror.css';
3 | @import '~codemirror/theme/material.css';
4 |
5 | .main {
6 | :global {
7 | .ant-descriptions-row > td {
8 | padding-bottom: 8px;
9 | }
10 | .ant-page-header-heading-extra {
11 | flex-direction: column;
12 | }
13 | }
14 | }
15 |
16 | .headerList {
17 | margin-bottom: 4px;
18 | :global {
19 | .ant-descriptions-row > td {
20 | padding-bottom: 8px;
21 | }
22 | }
23 |
24 | .stepDescription {
25 | position: relative;
26 | left: 38px;
27 | padding-top: 8px;
28 | font-size: 14px;
29 | text-align: left;
30 |
31 | > div {
32 | margin-top: 8px;
33 | margin-bottom: 4px;
34 | }
35 | }
36 | }
37 |
38 | .pageHeader {
39 | :global {
40 | .ant-page-header-heading-extra > * + * {
41 | margin-left: 8px;
42 | }
43 | }
44 | .moreInfo {
45 | display: flex;
46 | justify-content: space-between;
47 | width: 200px;
48 | }
49 | }
50 |
51 | @media screen and (max-width: @screen-sm) {
52 | .stepDescription {
53 | left: 8px;
54 | }
55 | .pageHeader {
56 | :global {
57 | .ant-pro-page-header-wrap-row {
58 | flex-direction: column;
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/frontend/src/pages/profile/basic/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface BasicGood {
2 | id: string;
3 | name?: string;
4 | barcode?: string;
5 | price?: string;
6 | num?: string | number;
7 | amount?: string | number;
8 | }
9 |
10 | export interface BasicProgress {
11 | key: string;
12 | time: string;
13 | rate: string;
14 | status: string;
15 | operator: string;
16 | cost: string;
17 | }
18 |
19 | export interface BasicProfileDataType {
20 | basicGoods: BasicGood[];
21 | basicProgress: BasicProgress[];
22 | }
23 |
--------------------------------------------------------------------------------
/frontend/src/pages/profile/basic/model.js:
--------------------------------------------------------------------------------
1 | import { queryBasicProfile } from './service';
2 |
3 | const Model = {
4 | namespace: 'profileAndbasic',
5 | state: {
6 | basicGoods: [],
7 | },
8 | effects: {
9 | *fetchBasic(_, { call, put }) {
10 | const response = yield call(queryBasicProfile);
11 | yield put({
12 | type: 'show',
13 | payload: response,
14 | });
15 | },
16 | },
17 | reducers: {
18 | show(state, { payload }) {
19 | return { ...state, ...payload };
20 | },
21 | },
22 | };
23 | export default Model;
24 |
--------------------------------------------------------------------------------
/frontend/src/pages/profile/basic/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryBasicProfile() {
4 | return request('/api/profile/basic');
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/profile/basic/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .title {
4 | margin-bottom: 16px;
5 | color: @heading-color;
6 | font-weight: 500;
7 | font-size: 16px;
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/pages/prometheus/components/MonitorCodeMirror/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, Fragment } from 'react';
2 | import { UnControlled as CodeMirror } from 'react-codemirror2'
3 | import styles from './index.less';
4 |
5 | require('codemirror/mode/yaml/yaml');
6 |
7 | class MonitorCodeMirror extends Component {
8 | render() {
9 | const {value, onChange} = this.props;
10 |
11 | return (
12 |
22 | );
23 | }
24 | }
25 |
26 | export default MonitorCodeMirror;
--------------------------------------------------------------------------------
/frontend/src/pages/prometheus/components/MonitorCodeMirror/index.less:
--------------------------------------------------------------------------------
1 | @import '~codemirror/lib/codemirror.css';
2 | @import '~codemirror/theme/mdn-like.css';
3 |
4 | .code {
5 | height: 550px;
6 | :global {
7 | .CodeMirror {
8 | height: 100%;
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/frontend/src/pages/prometheus/components/MonitorEdit/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Descriptions } from 'antd';
3 |
4 | import styles from './index.less';
5 |
6 | const MonitorEdit = (props) => {
7 | const { global, scrape_configs } = props.value
8 | const globalKeys = Object.keys(global)
9 | const globalMap = {
10 | 'scrape_interval': '采集周期',
11 | 'scrape_timeout': '采集超时',
12 | 'evaluation_interval': '评估周期',
13 | 'external_labels': '自定义标签',
14 | }
15 | console.log(globalKeys)
16 | return (
17 | <>
18 |
24 | {
25 | globalKeys.map((key, index) => {
26 | // return key
27 | console.log(globalMap[key], global[key])
28 | if('external_labels' !== key) return {global[key]}
29 | })
30 | }
31 |
32 |
33 | 表格配置
34 | >
35 | )
36 | }
37 |
38 | export default MonitorEdit
--------------------------------------------------------------------------------
/frontend/src/pages/prometheus/components/MonitorEdit/index.less:
--------------------------------------------------------------------------------
1 | .main {
2 |
3 | }
--------------------------------------------------------------------------------
/frontend/src/pages/prometheus/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface AdvancedOperation1 {
2 | key: string;
3 | type: string;
4 | name: string;
5 | status: string;
6 | updatedAt: string;
7 | memo: string;
8 | }
9 |
10 | export interface AdvancedOperation2 {
11 | key: string;
12 | type: string;
13 | name: string;
14 | status: string;
15 | updatedAt: string;
16 | memo: string;
17 | }
18 |
19 | export interface AdvancedOperation3 {
20 | key: string;
21 | type: string;
22 | name: string;
23 | status: string;
24 | updatedAt: string;
25 | memo: string;
26 | }
27 |
28 | export interface AdvancedProfileData {
29 | advancedOperation1: AdvancedOperation1[];
30 | advancedOperation2: AdvancedOperation2[];
31 | advancedOperation3: AdvancedOperation3[];
32 | }
33 |
--------------------------------------------------------------------------------
/frontend/src/pages/prometheus/model.js:
--------------------------------------------------------------------------------
1 | import { queryAdvancedProfile } from './service';
2 |
3 | const Model = {
4 | namespace: 'profileAndadvanced',
5 | state: {
6 | advancedOperation1: [],
7 | advancedOperation2: [],
8 | advancedOperation3: [],
9 | },
10 | effects: {
11 | *fetchAdvanced(_, { call, put }) {
12 | const response = yield call(queryAdvancedProfile);
13 | yield put({
14 | type: 'show',
15 | payload: response,
16 | });
17 | },
18 | },
19 | reducers: {
20 | show(state, { payload }) {
21 | return { ...state, ...payload };
22 | },
23 | },
24 | };
25 | export default Model;
26 |
--------------------------------------------------------------------------------
/frontend/src/pages/prometheus/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryAdvancedProfile() {
4 | return request('/api/profile/advanced');
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/prometheus/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .headerList {
4 | margin-bottom: 4px;
5 | :global {
6 | .ant-descriptions-row > td {
7 | padding-bottom: 8px;
8 | }
9 | }
10 |
11 | .stepDescription {
12 | position: relative;
13 | left: 38px;
14 | padding-top: 8px;
15 | font-size: 14px;
16 | text-align: left;
17 |
18 | > div {
19 | margin-top: 8px;
20 | margin-bottom: 4px;
21 | }
22 | }
23 | }
24 |
25 | .pageHeader {
26 | :global {
27 | .ant-page-header-heading-extra > * + * {
28 | margin-left: 8px;
29 | }
30 | .ant-descriptions-row > td {
31 | padding-bottom: 8px;
32 | }
33 | .ant-page-header-heading-extra {
34 | flex-direction: column;
35 | }
36 | }
37 |
38 | .card {
39 | margin-bottom: 24px;
40 |
41 | :global {
42 | .ant-legacy-form-item .ant-legacy-form-item-control-wrapper {
43 | width: 100%;
44 | }
45 | }
46 | }
47 | .btn {
48 | margin-top: 24px;
49 | }
50 | .moreInfo {
51 | display: flex;
52 | justify-content: space-between;
53 | width: 200px;
54 | }
55 | }
56 |
57 | @media screen and (max-width: @screen-sm) {
58 | .pageHeader {
59 | :global {
60 | .ant-pro-page-header-wrap-row {
61 | flex-direction: column;
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/frontend/src/pages/result/fail/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .error_icon {
4 | color: @highlight-color;
5 | }
6 | .title {
7 | margin-bottom: 16px;
8 | color: @heading-color;
9 | font-weight: 500;
10 | font-size: 16px;
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/src/pages/result/fail/locales/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'resultandfail.error.title': 'Submission Failed',
3 | 'resultandfail.error.description':
4 | 'Please check and modify the following information before resubmitting.',
5 | 'resultandfail.error.hint-title': 'The content you submitted has the following error:',
6 | 'resultandfail.error.hint-text1': 'Your account has been frozen',
7 | 'resultandfail.error.hint-btn1': 'Thaw immediately',
8 | 'resultandfail.error.hint-text2': 'Your account is not yet eligible to apply',
9 | 'resultandfail.error.hint-btn2': 'Upgrade immediately',
10 | 'resultandfail.error.btn-text': 'Return to modify',
11 | };
12 |
--------------------------------------------------------------------------------
/frontend/src/pages/result/fail/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'resultandfail.error.title': '提交失败',
3 | 'resultandfail.error.description': '请核对并修改以下信息后,再重新提交。',
4 | 'resultandfail.error.hint-title': '您提交的内容有如下错误:',
5 | 'resultandfail.error.hint-text1': '您的账户已被冻结',
6 | 'resultandfail.error.hint-btn1': '立即解冻',
7 | 'resultandfail.error.hint-text2': '您的账户还不具备申请资格',
8 | 'resultandfail.error.hint-btn2': '立即升级',
9 | 'resultandfail.error.btn-text': '返回修改',
10 | };
11 |
--------------------------------------------------------------------------------
/frontend/src/pages/result/fail/locales/zh-TW.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'resultandfail.error.title': '提交失敗',
3 | 'resultandfail.error.description': '請核對並修改以下信息後,再重新提交。',
4 | 'resultandfail.error.hint-title': '您提交的內容有如下錯誤:',
5 | 'resultandfail.error.hint-text1': '您的賬戶已被凍結',
6 | 'resultandfail.error.hint-btn1': '立即解凍',
7 | 'resultandfail.error.hint-text2': '您的賬戶還不具備申請資格',
8 | 'resultandfail.error.hint-btn2': '立即升級',
9 | 'resultandfail.error.btn-text': '返回修改',
10 | };
11 |
--------------------------------------------------------------------------------
/frontend/src/pages/result/success/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .title {
4 | position: relative;
5 | color: @text-color;
6 | font-size: 12px;
7 | text-align: center;
8 | }
9 |
10 | .head-title {
11 | margin-bottom: 20px;
12 | color: @heading-color;
13 | font-weight: 500px;
14 | font-size: 16px;
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/src/pages/result/success/locales/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'resultandsuccess.success.title': 'Submission Success',
3 | 'resultandsuccess.success.description':
4 | 'The submission results page is used to feed back the results of a series of operational tasks. If it is a simple operation, use the Message global prompt feedback. This text area can show a simple supplementary explanation. If there is a similar requirement for displaying “documents”, the following gray area can present more complicated content.',
5 | 'resultandsuccess.success.operate-title': 'Project Name',
6 | 'resultandsuccess.success.operate-id': 'Project ID',
7 | 'resultandsuccess.success.principal': 'Principal',
8 | 'resultandsuccess.success.operate-time': 'Effective time',
9 | 'resultandsuccess.success.step1-title': 'Create project',
10 | 'resultandsuccess.success.step1-operator': 'Qu Lili',
11 | 'resultandsuccess.success.step2-title': 'Departmental preliminary review',
12 | 'resultandsuccess.success.step2-operator': 'Zhou Maomao',
13 | 'resultandsuccess.success.step2-extra': 'Urge',
14 | 'resultandsuccess.success.step3-title': 'Financial review',
15 | 'resultandsuccess.success.step4-title': 'Finish',
16 | 'resultandsuccess.success.btn-return': 'Back List',
17 | 'resultandsuccess.success.btn-project': 'View Project',
18 | 'resultandsuccess.success.btn-print': 'Print',
19 | };
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/result/success/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'resultandsuccess.success.title': '提交成功',
3 | 'resultandsuccess.success.description':
4 | '提交结果页用于反馈一系列操作任务的处理结果, 如果仅是简单操作,使用 Message 全局提示反馈即可。 本文字区域可以展示简单的补充说明,如果有类似展示 “单据”的需求,下面这个灰色区域可以呈现比较复杂的内容。',
5 | 'resultandsuccess.success.operate-title': '项目名称',
6 | 'resultandsuccess.success.operate-id': '项目 ID',
7 | 'resultandsuccess.success.principal': '负责人',
8 | 'resultandsuccess.success.operate-time': '生效时间',
9 | 'resultandsuccess.success.step1-title': '创建项目',
10 | 'resultandsuccess.success.step1-operator': '曲丽丽',
11 | 'resultandsuccess.success.step2-title': '部门初审',
12 | 'resultandsuccess.success.step2-operator': '周毛毛',
13 | 'resultandsuccess.success.step2-extra': '催一下',
14 | 'resultandsuccess.success.step3-title': '财务复核',
15 | 'resultandsuccess.success.step4-title': '完成',
16 | 'resultandsuccess.success.btn-return': '返回列表',
17 | 'resultandsuccess.success.btn-project': '查看项目',
18 | 'resultandsuccess.success.btn-print': '打印',
19 | };
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/result/success/locales/zh-TW.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'resultandsuccess.success.title': '提交成功',
3 | 'resultandsuccess.success.description':
4 | '提交結果頁用於反饋壹系列操作任務的處理結果, 如果僅是簡單操作,使用 Message 全局提示反饋即可。 本文字區域可以展示簡單的補充說明,如果有類似展示 “單據”的需求,下面這個灰色區域可以呈現比較復雜的內容。',
5 | 'resultandsuccess.success.operate-title': '項目名稱',
6 | 'resultandsuccess.success.operate-id': '項目 ID',
7 | 'resultandsuccess.success.principal': '負責人',
8 | 'resultandsuccess.success.operate-time': '生效時間',
9 | 'resultandsuccess.success.step1-title': '創建項目',
10 | 'resultandsuccess.success.step1-operator': '曲麗麗',
11 | 'resultandsuccess.success.step2-title': '部門初審',
12 | 'resultandsuccess.success.step2-operator': '周毛毛',
13 | 'resultandsuccess.success.step2-extra': '催壹下',
14 | 'resultandsuccess.success.step3-title': '財務復核',
15 | 'resultandsuccess.success.step4-title': '完成',
16 | 'resultandsuccess.success.btn-return': '返回列表',
17 | 'resultandsuccess.success.btn-project': '查看項目',
18 | 'resultandsuccess.success.btn-print': '打印',
19 | };
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/login/_mock.js:
--------------------------------------------------------------------------------
1 | function getFakeCaptcha(req, res) {
2 | return res.json('captcha-xxx');
3 | }
4 |
5 | export default {
6 | 'POST /api/login/account': (req, res) => {
7 | const { password, userName, type } = req.body;
8 |
9 | if (password === 'ant.design' && userName === 'admin') {
10 | res.send({
11 | status: 'ok',
12 | type,
13 | currentAuthority: 'admin',
14 | });
15 | return;
16 | }
17 |
18 | if (password === 'ant.design' && userName === 'user') {
19 | res.send({
20 | status: 'ok',
21 | type,
22 | currentAuthority: 'user',
23 | });
24 | return;
25 | }
26 |
27 | res.send({
28 | status: 'error',
29 | type,
30 | currentAuthority: 'guest',
31 | });
32 | },
33 | 'GET /api/login/captcha': getFakeCaptcha,
34 | };
35 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/login/components/Login/LoginContext.jsx:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react';
2 |
3 | const LoginContext = createContext({});
4 | export default LoginContext;
5 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/login/components/Login/LoginSubmit.jsx:
--------------------------------------------------------------------------------
1 | import { Form } from '@ant-design/compatible';
2 | import '@ant-design/compatible/assets/index.css';
3 | import { Button } from 'antd';
4 | import React from 'react';
5 | import classNames from 'classnames';
6 | import styles from './index.less';
7 |
8 | const FormItem = Form.Item;
9 |
10 | const LoginSubmit = ({ className, ...rest }) => {
11 | const clsString = classNames(styles.submit, className);
12 | return (
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default LoginSubmit;
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/login/components/Login/LoginTab.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Tabs } from 'antd';
3 | import LoginContext from './LoginContext';
4 |
5 | const { TabPane } = Tabs;
6 |
7 | const generateId = (() => {
8 | let i = 0;
9 | return (prefix = '') => {
10 | i += 1;
11 | return `${prefix}${i}`;
12 | };
13 | })();
14 |
15 | class LoginTab extends Component {
16 | uniqueId = '';
17 |
18 | constructor(props) {
19 | super(props);
20 | this.uniqueId = generateId('login-tab-');
21 | }
22 |
23 | componentDidMount() {
24 | const { tabUtil } = this.props;
25 |
26 | if (tabUtil) {
27 | tabUtil.addTab(this.uniqueId);
28 | }
29 | }
30 |
31 | render() {
32 | const { children } = this.props;
33 | return {children};
34 | }
35 | }
36 |
37 | const WrapContext = props => (
38 |
39 | {value => }
40 |
41 | ); // 标志位 用来判断是不是自定义组件
42 |
43 | WrapContext.typeName = 'LoginTab';
44 | export default WrapContext;
45 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/login/components/Login/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .login {
4 | :global {
5 | .ant-tabs .ant-tabs-bar {
6 | margin-bottom: 24px;
7 | text-align: center;
8 | border-bottom: 0;
9 | }
10 |
11 | .ant-form-item {
12 | margin: 0 2px 24px;
13 | }
14 | }
15 |
16 | .getCaptcha {
17 | display: block;
18 | width: 100%;
19 | }
20 |
21 | .icon {
22 | margin-left: 16px;
23 | color: rgba(0, 0, 0, 0.2);
24 | font-size: 24px;
25 | vertical-align: middle;
26 | cursor: pointer;
27 | transition: color 0.3s;
28 |
29 | &:hover {
30 | color: @primary-color;
31 | }
32 | }
33 |
34 | .other {
35 | margin-top: 24px;
36 | line-height: 22px;
37 | text-align: left;
38 |
39 | .register {
40 | float: right;
41 | }
42 | }
43 |
44 | .prefixIcon {
45 | color: @disabled-color;
46 | font-size: @font-size-base;
47 | }
48 |
49 | .submit {
50 | width: 100%;
51 | margin-top: 24px;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/login/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function fakeAccountLogin(params) {
4 | return request('/api/login/account', {
5 | method: 'POST',
6 | data: params,
7 | });
8 | }
9 | export async function getFakeCaptcha(mobile) {
10 | return request(`/api/login/captcha?mobile=${mobile}`);
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/login/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .main {
4 | width: 368px;
5 | margin: 0 auto;
6 | @media screen and (max-width: @screen-sm) {
7 | width: 95%;
8 | }
9 |
10 | .icon {
11 | margin-left: 16px;
12 | color: rgba(0, 0, 0, 0.2);
13 | font-size: 24px;
14 | vertical-align: middle;
15 | cursor: pointer;
16 | transition: color 0.3s;
17 |
18 | &:hover {
19 | color: @primary-color;
20 | }
21 | }
22 |
23 | .other {
24 | margin-top: 24px;
25 | line-height: 22px;
26 | text-align: left;
27 |
28 | .register {
29 | float: right;
30 | }
31 | }
32 |
33 | :global {
34 | .antd-pro-login-submit {
35 | width: 100%;
36 | margin-top: 24px;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/login/utils/utils.js:
--------------------------------------------------------------------------------
1 | import { parse } from 'qs';
2 |
3 | export function getPageQuery() {
4 | return parse(window.location.href.split('?')[1]);
5 | }
6 | export function setAuthority(authority) {
7 | const proAuthority = typeof authority === 'string' ? [authority] : authority;
8 | localStorage.setItem('antd-pro-authority', JSON.stringify(proAuthority)); // hard code
9 | // reload Authorized component
10 |
11 | try {
12 | if (window.reloadAuthorized) {
13 | window.reloadAuthorized();
14 | }
15 | } catch (error) {
16 | // do not need do anything
17 | }
18 |
19 | return authority;
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/register-result/index.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Result } from 'antd';
2 | import Link from 'umi/link';
3 | import React from 'react';
4 | import styles from './style.less';
5 |
6 | const actions = (
7 |
17 | );
18 |
19 | const RegisterResult = ({ location }) => (
20 | userandregister-result.register-result.msg}
24 | subTitle="激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。"
25 | extra={actions}
26 | />
27 | );
28 |
29 | export default RegisterResult;
30 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/register-result/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'userandregister-result.login.userName': '用户名',
3 | 'userandregister-result.login.password': '密码',
4 | 'userandregister-result.login.message-invalid-credentials': '账户或密码错误(admin/ant.design)',
5 | 'userandregister-result.login.message-invalid-verification-code': '验证码错误',
6 | 'userandregister-result.login.tab-login-credentials': '账户密码登录',
7 | 'userandregister-result.login.tab-login-mobile': '手机号登录',
8 | 'userandregister-result.login.remember-me': '自动登录',
9 | 'userandregister-result.login.forgot-password': '忘记密码',
10 | 'userandregister-result.login.sign-in-with': '其他登录方式',
11 | 'userandregister-result.login.signup': '注册账户',
12 | 'userandregister-result.login.login': '登录',
13 | 'userandregister-result.register.register': '注册',
14 | 'userandregister-result.register.get-verification-code': '获取验证码',
15 | 'userandregister-result.register.sign-in': '使用已有账户登录',
16 | 'userandregister-result.register-result.msg': '你的账户:{email} 注册成功',
17 | 'userandregister-result.register-result.activation-email':
18 | '激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。',
19 | 'userandregister-result.register-result.back-home': '返回首页',
20 | 'userandregister-result.register-result.view-mailbox': '查看邮箱',
21 | 'userandregister-result.navBar.lang': '语言',
22 | };
23 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/register-result/locales/zh-TW.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'userandregister-result.login.userName': '賬戶',
3 | 'userandregister-result.login.password': '密碼',
4 | 'userandregister-result.login.message-invalid-credentials': '賬戶或密碼錯誤(admin/ant.design)',
5 | 'userandregister-result.login.message-invalid-verification-code': '驗證碼錯誤',
6 | 'userandregister-result.login.tab-login-credentials': '賬戶密碼登錄',
7 | 'userandregister-result.login.tab-login-mobile': '手機號登錄',
8 | 'userandregister-result.login.remember-me': '自動登錄',
9 | 'userandregister-result.login.forgot-password': '忘記密碼',
10 | 'userandregister-result.login.sign-in-with': '其他登錄方式',
11 | 'userandregister-result.login.signup': '註冊賬戶',
12 | 'userandregister-result.login.login': '登錄',
13 | 'userandregister-result.register.register': '註冊',
14 | 'userandregister-result.register.get-verification-code': '獲取驗證碼',
15 | 'userandregister-result.register.sign-in': '使用已有賬戶登錄',
16 | 'userandregister-result.register-result.msg': '妳的賬戶:{email} 註冊成功',
17 | 'userandregister-result.register-result.activation-email':
18 | '激活郵件已發送到妳的郵箱中,郵件有效期為24小時。請及時登錄郵箱,點擊郵件中的鏈接激活帳戶。',
19 | 'userandregister-result.register-result.back-home': '返回首頁',
20 | 'userandregister-result.register-result.view-mailbox': '查看郵箱',
21 | 'userandregister-result.navBar.lang': '語言',
22 | };
23 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/register-result/style.less:
--------------------------------------------------------------------------------
1 | .registerResult {
2 | width: 800px;
3 | min-height: 400px;
4 | margin: auto;
5 | padding: 80px;
6 | background: none;
7 | :global {
8 | .anticon {
9 | font-size: 64px;
10 | }
11 | }
12 | .title {
13 | margin-top: 32px;
14 | font-size: 20px;
15 | line-height: 28px;
16 | }
17 | .actions {
18 | margin-top: 40px;
19 | a + a {
20 | margin-left: 8px;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/register/_mock.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'POST /api/register': (_, res) => {
3 | res.send({
4 | status: 'ok',
5 | currentAuthority: 'user',
6 | });
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/register/model.js:
--------------------------------------------------------------------------------
1 | import { fakeRegister } from './service';
2 |
3 | const Model = {
4 | namespace: 'userAndregister',
5 | state: {
6 | status: undefined,
7 | },
8 | effects: {
9 | *submit({ payload }, { call, put }) {
10 | const response = yield call(fakeRegister, payload);
11 | yield put({
12 | type: 'registerHandle',
13 | payload: response,
14 | });
15 | },
16 | },
17 | reducers: {
18 | registerHandle(state, { payload }) {
19 | return { ...state, status: payload.status };
20 | },
21 | },
22 | };
23 | export default Model;
24 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/register/service.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function fakeRegister(params) {
4 | return request('/api/register', {
5 | method: 'POST',
6 | data: params,
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/pages/user/register/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .main {
4 | width: 368px;
5 | margin: 0 auto;
6 |
7 | :global {
8 | .ant-form-item {
9 | margin-bottom: 24px;
10 | }
11 | }
12 |
13 | h3 {
14 | margin-bottom: 20px;
15 | font-size: 16px;
16 | }
17 |
18 | .getCaptcha {
19 | display: block;
20 | width: 100%;
21 | }
22 |
23 | .submit {
24 | width: 50%;
25 | }
26 |
27 | .login {
28 | float: right;
29 | line-height: @btn-height-lg;
30 | }
31 | }
32 |
33 | .success,
34 | .warning,
35 | .error {
36 | transition: color 0.3s;
37 | }
38 |
39 | .success {
40 | color: @success-color;
41 | }
42 |
43 | .warning {
44 | color: @warning-color;
45 | }
46 |
47 | .error {
48 | color: @error-color;
49 | }
50 |
51 | .progress-pass > .progress {
52 | :global {
53 | .ant-progress-bg {
54 | background-color: @warning-color;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/frontend/src/services/login.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | export async function fakeAccountLogin(params) {
3 | return request('/api/login/account', {
4 | method: 'POST',
5 | data: params,
6 | });
7 | }
8 | export async function getFakeCaptcha(mobile) {
9 | return request(`/api/login/captcha?mobile=${mobile}`);
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/services/prometheus.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function query() {
4 | return request('/api/prometheus/yaml');
5 | }
6 |
7 | export async function save(params) {
8 | return request('/api/prometheus/yaml/save', {
9 | method: 'POST',
10 | data: params,
11 | });
12 | }
--------------------------------------------------------------------------------
/frontend/src/services/user.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | export async function query() {
3 | return request('/api/users');
4 | }
5 | export async function queryCurrent() {
6 | return request('/api/currentUser');
7 | }
8 | export async function queryNotices() {
9 | return request('/api/notices');
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/utils/Authorized.js:
--------------------------------------------------------------------------------
1 | import RenderAuthorize from '@/components/Authorized';
2 | import { getAuthority } from './authority';
3 | /* eslint-disable eslint-comments/disable-enable-pair */
4 |
5 | /* eslint-disable import/no-mutable-exports */
6 |
7 | let Authorized = RenderAuthorize(getAuthority()); // Reload the rights component
8 |
9 | const reloadAuthorized = () => {
10 | Authorized = RenderAuthorize(getAuthority());
11 | };
12 | /**
13 | * hard code
14 | * block need it。
15 | */
16 |
17 | window.reloadAuthorized = reloadAuthorized;
18 | export { reloadAuthorized };
19 | export default Authorized;
20 |
--------------------------------------------------------------------------------
/frontend/src/utils/authority.js:
--------------------------------------------------------------------------------
1 | import { reloadAuthorized } from './Authorized'; // use localStorage to store the authority info, which might be sent from server in actual project.
2 |
3 | export function getAuthority(str) {
4 | const authorityString =
5 | typeof str === 'undefined' && localStorage ? localStorage.getItem('antd-pro-authority') : str; // authorityString could be admin, "admin", ["admin"]
6 |
7 | let authority;
8 |
9 | try {
10 | if (authorityString) {
11 | authority = JSON.parse(authorityString);
12 | }
13 | } catch (e) {
14 | authority = authorityString;
15 | }
16 |
17 | if (typeof authority === 'string') {
18 | return [authority];
19 | } // preview.pro.ant.design only do not use in your production.
20 | // preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
21 |
22 | if (!authority && ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site') {
23 | return ['admin'];
24 | }
25 |
26 | return authority;
27 | }
28 | export function setAuthority(authority) {
29 | const proAuthority = typeof authority === 'string' ? [authority] : authority;
30 | localStorage.setItem('antd-pro-authority', JSON.stringify(proAuthority)); // auto reload
31 |
32 | reloadAuthorized();
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/src/utils/authority.test.js:
--------------------------------------------------------------------------------
1 | import { getAuthority } from './authority';
2 | describe('getAuthority should be strong', () => {
3 | it('string', () => {
4 | expect(getAuthority('admin')).toEqual(['admin']);
5 | });
6 | it('array with double quotes', () => {
7 | expect(getAuthority('"admin"')).toEqual(['admin']);
8 | });
9 | it('array with single item', () => {
10 | expect(getAuthority('["admin"]')).toEqual(['admin']);
11 | });
12 | it('array with multiple items', () => {
13 | expect(getAuthority('["admin", "guest"]')).toEqual(['admin', 'guest']);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/frontend/src/utils/utils.less:
--------------------------------------------------------------------------------
1 | .textOverflow() {
2 | overflow: hidden;
3 | white-space: nowrap;
4 | text-overflow: ellipsis;
5 | word-break: break-all;
6 | }
7 |
8 | .textOverflowMulti(@line: 3, @bg: #fff) {
9 | position: relative;
10 | max-height: @line * 1.5em;
11 | margin-right: -1em;
12 | padding-right: 1em;
13 | overflow: hidden;
14 | line-height: 1.5em;
15 | text-align: justify;
16 | &::before {
17 | position: absolute;
18 | right: 14px;
19 | bottom: 0;
20 | padding: 0 1px;
21 | background: @bg;
22 | content: '...';
23 | }
24 | &::after {
25 | position: absolute;
26 | right: 14px;
27 | width: 1em;
28 | height: 1em;
29 | margin-top: 0.2em;
30 | background: white;
31 | content: '';
32 | }
33 | }
34 |
35 | // mixins for clearfix
36 | // ------------------------
37 | .clearfix() {
38 | zoom: 1;
39 | &::before,
40 | &::after {
41 | display: table;
42 | content: ' ';
43 | }
44 | &::after {
45 | clear: both;
46 | height: 0;
47 | font-size: 0;
48 | visibility: hidden;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/frontend/tests/setupTests.js:
--------------------------------------------------------------------------------
1 | import 'jsdom-global/register';
2 |
3 | // browserMocks.js
4 | const localStorageMock = (() => {
5 | let store = {};
6 |
7 | return {
8 | getItem(key) {
9 | return store[key] || null;
10 | },
11 | setItem(key, value) {
12 | store[key] = value.toString();
13 | },
14 | clear() {
15 | store = {};
16 | },
17 | };
18 | })();
19 |
20 | Object.defineProperty(window, 'localStorage', {
21 | value: localStorageMock,
22 | });
23 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module prometheusPro/proBackend
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195
7 | github.com/astaxie/beego v1.12.0
8 | github.com/ghodss/yaml v1.0.0
9 | github.com/prometheus/prometheus v1.8.2-0.20191017095924-6f92ce560538
10 | github.com/satori/go.uuid v0.0.0-20160603004225-b111a074d5ef
11 | github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
12 | )
13 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | _ "prometheusPro/proBackend/routers"
5 | "github.com/astaxie/beego"
6 | )
7 |
8 | func main() {
9 | beego.Run()
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/models/prometheus.go:
--------------------------------------------------------------------------------
1 | /**
2 | * @Author: xuyasong
3 | * @Description:
4 | * @File: prometheus
5 | * @Version: 1.0.0
6 | * @Date: 2020-01-21 14:36
7 | */
8 |
9 | package models
10 |
11 | // ListOption definition
12 | type ListOption struct {
13 | Keyword string
14 | KeywordType string
15 | }
16 |
--------------------------------------------------------------------------------
/routers/commentsRouter_controllers.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "github.com/astaxie/beego"
5 | "github.com/astaxie/beego/context/param"
6 | )
7 |
8 | func init() {
9 |
10 | beego.GlobalControllerRouter["prometheusPro/proBackend/controllers:PrometheusController"] = append(beego.GlobalControllerRouter["prometheusPro/proBackend/controllers:PrometheusController"],
11 | beego.ControllerComments{
12 | Method: "GetConfig",
13 | Router: `/config`,
14 | AllowHTTPMethods: []string{"get"},
15 | MethodParams: param.Make(),
16 | Filters: nil,
17 | Params: nil})
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/routers/router.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "github.com/astaxie/beego"
5 |
6 | "prometheusPro/proBackend/controllers"
7 | )
8 |
9 | func init() {
10 | ns := beego.NewNamespace("/api/v1",
11 | beego.NSNamespace("/prometheus",
12 | beego.NSInclude(
13 | &controllers.PrometheusController{},
14 | ),
15 | ),
16 | beego.NSNamespace("/alertmanager",
17 | beego.NSInclude(
18 | &controllers.PrometheusController{},
19 | ),
20 | ),
21 | )
22 | beego.AddNamespace(ns)
23 | }
24 |
--------------------------------------------------------------------------------
/services/base.go:
--------------------------------------------------------------------------------
1 | /**
2 | * @Author: xuyasong
3 | * @Description:
4 | * @File: base
5 | * @Version: 1.0.0
6 | * @Date: 2020-01-21 14:40
7 | */
8 |
9 | package services
10 |
11 | // BaseService definition
12 | type BaseService struct {
13 | }
14 |
--------------------------------------------------------------------------------
/utils/const.go:
--------------------------------------------------------------------------------
1 | // Package utils definition
2 | package utils
3 |
4 | import "time"
5 |
6 | // RetryCount
7 | const RetryCount = 3
8 |
9 | // RetryTimes
10 | const RetryTimes = 5
11 |
12 | // DateFormat
13 | const DateFormat = "2006-01-02"
14 |
15 | // DateTimeFormat
16 | const DateTimeFormat = "2006-01-02 15:04:05"
17 |
18 | // RequestID
19 | const RequestID ContextKeyType = "RequestID"
20 |
21 | // ControllerTimeout
22 | const ControllerTimeout = 30 * time.Second
23 |
24 | // K8sClientTimeout
25 | const K8sClientTimeout = 3 * time.Second
26 |
--------------------------------------------------------------------------------
/utils/date.go:
--------------------------------------------------------------------------------
1 | // Package utils definition
2 | package utils
3 |
4 | import (
5 | "time"
6 |
7 | "github.com/araddon/dateparse"
8 | )
9 |
10 | // GetDateFromNow 获取距离现在n天前的日期
11 | func GetDateFromNow(days int) string {
12 | // 获取距离现在n天前的日期
13 | nTime := time.Now()
14 | yesTime := nTime.AddDate(0, 0, days)
15 | fromDate := yesTime.Format(DateFormat)
16 | return fromDate
17 | }
18 |
19 | // GetDateTimeFromNow 获取距离现在n天前的时间
20 | func GetDateTimeFromNow(days int) string {
21 | nTime := time.Now()
22 | yesTime := nTime.AddDate(0, 0, days)
23 | fromDateTime := yesTime.Format(DateTimeFormat)
24 | return fromDateTime
25 | }
26 |
27 | // FormatToDateTime 将时间格式化为 时间字符串
28 | func FormatToDateTime(srcTime time.Time) (string, error) {
29 | //将时间格式化为 时间字符串
30 | return srcTime.Format(DateTimeFormat), nil
31 | }
32 |
33 | // FormatToDate 将时间格式化为 日期字符串
34 | func FormatToDate(srcTime time.Time) (string, error) {
35 | return srcTime.Format(DateFormat), nil
36 | }
37 |
38 | // ConvertToDateTime 将时间字符串格式化为 时间格式
39 | func ConvertToDateTime(srcString string) (time.Time, error) {
40 | retTime, err := dateparse.ParseAny(srcString)
41 | if err != nil {
42 | return time.Time{}, err
43 | }
44 | return retTime, nil
45 | }
46 |
--------------------------------------------------------------------------------
/utils/date_test.go:
--------------------------------------------------------------------------------
1 | // Package utils definition
2 | package utils
3 |
4 | import (
5 | "testing"
6 | "time"
7 | )
8 |
9 | func TestGetDateFromNow(t *testing.T) {
10 | s := GetDateFromNow(10)
11 | if s == "" {
12 | t.Errorf("GetDateFromNow error")
13 | }
14 | }
15 |
16 | func TestGetDateTimeFromNow(t *testing.T) {
17 | s := GetDateTimeFromNow(10)
18 | if s == "" {
19 | t.Errorf("GetDateTimeFromNow error")
20 | }
21 | }
22 |
23 | func TestFormatToDateTime(t *testing.T) {
24 | _, err := FormatToDateTime(time.Now())
25 | if err != nil {
26 | t.Errorf(err.Error())
27 | }
28 | }
29 |
30 | func TestFormatToDate(t *testing.T) {
31 | _, err := FormatToDate(time.Now())
32 | if err != nil {
33 | t.Errorf(err.Error())
34 | }
35 | }
36 |
37 | func TestConvertToDateTime(t *testing.T) {
38 | _, err := ConvertToDateTime("2006-01-02 15:04:05")
39 | if err != nil {
40 | t.Errorf(err.Error())
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/utils/errorcode_test.go:
--------------------------------------------------------------------------------
1 | // Package utils definition
2 | package utils
3 |
4 | import (
5 | "testing"
6 | )
7 |
8 | func TestNewInvalidParam(t *testing.T) {
9 | NewInvalidParam("test")
10 | }
11 |
12 | func TestNewNoSuchObject(t *testing.T) {
13 | NewNoSuchObject("test")
14 | }
15 |
16 | func TestNewInternalServerError(t *testing.T) {
17 | NewInternalServerError("test")
18 | }
19 |
20 | func TestNewMalformedJSON(t *testing.T) {
21 | NewMalformedJSON("test")
22 | }
23 |
--------------------------------------------------------------------------------
/utils/helper.go:
--------------------------------------------------------------------------------
1 | // Package utils definition
2 | package utils
3 |
4 | import (
5 | "context"
6 | "fmt"
7 |
8 | "github.com/satori/go.uuid"
9 | )
10 |
11 | // ContextKeyType for context.WithValue(
12 | type ContextKeyType string
13 |
14 | // GetRandom 返回 64 位随机字符
15 | func GetRandom() string {
16 | newUUID := uuid.NewV4()
17 | return newUUID.String()
18 | }
19 |
20 | // Message 返回打标的信息
21 | func Message(ctx context.Context, msg string) string {
22 | if ctx != nil {
23 | return fmt.Sprintf("[ReqID:%s] %s", ctx.Value(RequestID).(string), msg)
24 | } else {
25 | return fmt.Sprintf(msg)
26 | }
27 | }
28 |
29 | // OffsetLimit2MinMax function
30 | func OffsetLimit2MinMax(offset, limit, length int) (int, int) {
31 | if offset > length {
32 | return length, length
33 | }
34 | var max = 0
35 | if (offset + limit) > length {
36 | max = length
37 | } else {
38 | max = offset + limit
39 | }
40 | return offset, max
41 | }
--------------------------------------------------------------------------------
/utils/helper_test.go:
--------------------------------------------------------------------------------
1 | // Package utils definition
2 | package utils
3 |
4 | import (
5 | "context"
6 | "testing"
7 | )
8 |
9 | // TestGetRandom function
10 | func TestGetRandom(t *testing.T) {
11 | if GetRandom() == "" {
12 | t.Errorf("GetRandom error")
13 | }
14 | }
15 |
16 | // TestMessage function
17 | func TestMessage(t *testing.T) {
18 | var ctx context.Context
19 | if Message(ctx, "test") != "test" {
20 | t.Errorf("Message error")
21 | }
22 | }
23 |
24 | // TestOffsetLimit2MinMax function
25 | func TestOffsetLimit2MinMax(t *testing.T) {
26 | OffsetLimit2MinMax(10, 5, 5)
27 | OffsetLimit2MinMax(10, 5, 11)
28 | OffsetLimit2MinMax(10, 5, 15)
29 | }
30 |
--------------------------------------------------------------------------------