├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── .stylelintrc.js
├── README.md
├── config
├── config.js
├── defaultSettings.js
├── plugin.config.js
└── proxy.js
├── jest.config.js
├── jsconfig.json
├── mock
├── listTableList.js
├── notices.js
├── route.js
└── user.js
├── money.png
├── package.json
├── public
├── CNAME
├── favicon.png
├── home_bg.png
├── icons
│ ├── icon-128x128.png
│ ├── icon-192x192.png
│ └── icon-512x512.png
└── pro_icon.svg
├── 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
│ └── TabPages
│ │ ├── index.jsx
│ │ ├── old.jsx
│ │ └── page.less
├── e2e
│ ├── __mocks__
│ │ └── antd-pro-merge-less.js
│ └── baseLayout.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
│ ├── setting.js
│ └── user.js
├── pages
│ ├── 404.jsx
│ ├── Account
│ │ └── Center
│ │ │ └── Center.less
│ ├── Admin.jsx
│ ├── Authorized.jsx
│ ├── ListTableList
│ │ ├── components
│ │ │ ├── CreateForm.jsx
│ │ │ └── UpdateForm.jsx
│ │ ├── index.jsx
│ │ └── service.js
│ ├── Welcome.jsx
│ ├── Welcome.less
│ ├── account
│ │ ├── center
│ │ │ ├── _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
│ │ │ ├── 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
│ │ │ ├── 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.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
│ │ │ ├── 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
│ │ │ ├── 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
│ │ │ ├── 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
│ │ ├── 404
│ │ │ └── index.jsx
│ │ └── 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
│ │ │ ├── components
│ │ │ │ └── OperationModal.jsx
│ │ │ ├── index.jsx
│ │ │ ├── model.js
│ │ │ ├── service.js
│ │ │ ├── style.less
│ │ │ └── utils
│ │ │ │ └── utils.less
│ │ ├── card-list
│ │ │ ├── _mock.js
│ │ │ ├── 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
│ │ │ │ ├── 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
│ │ │ │ ├── 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
│ │ │ │ ├── index.jsx
│ │ │ │ ├── model.js
│ │ │ │ ├── service.js
│ │ │ │ ├── style.less
│ │ │ │ └── utils
│ │ │ │ │ └── utils.less
│ │ │ └── utils
│ │ │ │ └── utils.less
│ │ └── table-list
│ │ │ ├── _mock.js
│ │ │ ├── components
│ │ │ ├── CreateForm.jsx
│ │ │ └── UpdateForm.jsx
│ │ │ ├── index.jsx
│ │ │ └── service.js
│ ├── profile
│ │ ├── advanced
│ │ │ ├── _mock.js
│ │ │ ├── index.jsx
│ │ │ ├── model.js
│ │ │ ├── service.js
│ │ │ └── style.less
│ │ └── basic
│ │ │ ├── _mock.js
│ │ │ ├── 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
│ └── user.js
└── utils
│ ├── Authorized.js
│ ├── authority.js
│ ├── request.js
│ ├── utils.js
│ ├── utils.less
│ └── utils.test.js
└── tests
├── PuppeteerEnvironment.js
├── beforeTest.js
├── getBrowser.js
└── run-tests.js
/.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 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /lambda/
2 | /scripts
3 | /config
4 | .history
--------------------------------------------------------------------------------
/.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 | REACT_APP_ENV: true,
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.css linguist-language=React
2 | *.html linguist-language=React
3 | *.js linguist-language=React
4 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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
21 | CNAME
22 | /build
23 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | const fabric = require('@umijs/fabric');
2 |
3 | module.exports = {
4 | ...fabric.prettier,
5 | };
6 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | const fabric = require('@umijs/fabric');
2 |
3 | module.exports = {
4 | ...fabric.stylelint,
5 | };
6 |
--------------------------------------------------------------------------------
/config/defaultSettings.js:
--------------------------------------------------------------------------------
1 | export default {
2 | navTheme: 'dark',
3 | // 拂晓蓝
4 | primaryColor: '#1890ff',
5 | layout: 'sidemenu',
6 | contentWidth: 'Fluid',
7 | fixedHeader: false,
8 | autoHideHeader: false,
9 | fixSiderbar: false,
10 | colorWeak: false,
11 | menu: {
12 | locale: true,
13 | },
14 | title: 'Ant Design Pro',
15 | pwa: false,
16 | iconfontUrl: '',
17 | };
18 |
--------------------------------------------------------------------------------
/config/proxy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
3 | * The agent cannot take effect in the production environment
4 | * so there is no configuration of the production environment
5 | * For details, please see
6 | * https://pro.ant.design/docs/deploy
7 | */
8 | export default {
9 | dev: {
10 | '/api/': {
11 | target: 'https://preview.pro.ant.design',
12 | changeOrigin: true,
13 | pathRewrite: {
14 | '^': '',
15 | },
16 | },
17 | },
18 | test: {
19 | '/api/': {
20 | target: 'https://preview.pro.ant.design',
21 | changeOrigin: true,
22 | pathRewrite: {
23 | '^': '',
24 | },
25 | },
26 | },
27 | pre: {
28 | '/api/': {
29 | target: 'your pre url',
30 | changeOrigin: true,
31 | pathRewrite: {
32 | '^': '',
33 | },
34 | },
35 | },
36 | };
37 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testURL: 'http://localhost:8000',
3 | testEnvironment: './tests/PuppeteerEnvironment',
4 | verbose: false,
5 | globals: {
6 | ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,
7 | localStorage: null,
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "emitDecoratorMetadata": true,
4 | "experimentalDecorators": true,
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": ["./src/*"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/mock/route.js:
--------------------------------------------------------------------------------
1 | export default {
2 | '/api/auth_routes': {
3 | '/form/advanced-form': {
4 | authority: ['admin', 'user'],
5 | },
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/money.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailihuiyue/ant-design-pro-tabs/df9db8a495d97aeed297c3a10be88ae3b8ec6d80/money.png
--------------------------------------------------------------------------------
/public/CNAME:
--------------------------------------------------------------------------------
1 | preview.pro.ant.design
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailihuiyue/ant-design-pro-tabs/df9db8a495d97aeed297c3a10be88ae3b8ec6d80/public/favicon.png
--------------------------------------------------------------------------------
/public/home_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailihuiyue/ant-design-pro-tabs/df9db8a495d97aeed297c3a10be88ae3b8ec6d80/public/home_bg.png
--------------------------------------------------------------------------------
/public/icons/icon-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailihuiyue/ant-design-pro-tabs/df9db8a495d97aeed297c3a10be88ae3b8ec6d80/public/icons/icon-128x128.png
--------------------------------------------------------------------------------
/public/icons/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailihuiyue/ant-design-pro-tabs/df9db8a495d97aeed297c3a10be88ae3b8ec6d80/public/icons/icon-192x192.png
--------------------------------------------------------------------------------
/public/icons/icon-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailihuiyue/ant-design-pro-tabs/df9db8a495d97aeed297c3a10be88ae3b8ec6d80/public/icons/icon-512x512.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
6 | Authorized.Secured = Secured;
7 | Authorized.check = check;
8 | const RenderAuthorize = renderAuthorize(Authorized);
9 | export default RenderAuthorize;
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/SelectLang/index.jsx:
--------------------------------------------------------------------------------
1 | import { GlobalOutlined } from '@ant-design/icons';
2 | import { Menu } from 'antd';
3 | import { getLocale, setLocale } from 'umi';
4 | import React from 'react';
5 | import classNames from 'classnames';
6 | import HeaderDropdown from '../HeaderDropdown';
7 | import styles from './index.less';
8 |
9 | const SelectLang = props => {
10 | const { className } = props;
11 | const selectedLang = getLocale();
12 |
13 | const changeLang = ({ key }) => setLocale(key);
14 |
15 | const locales = ['zh-CN', 'zh-TW', 'en-US', 'pt-BR'];
16 | const languageLabels = {
17 | 'zh-CN': '简体中文',
18 | 'zh-TW': '繁体中文',
19 | 'en-US': 'English',
20 | 'pt-BR': 'Português',
21 | };
22 | const languageIcons = {
23 | 'zh-CN': '🇨🇳',
24 | 'zh-TW': '🇭🇰',
25 | 'en-US': '🇺🇸',
26 | 'pt-BR': '🇧🇷',
27 | };
28 | const langMenu = (
29 |
39 | );
40 | return (
41 |
42 |
43 |
44 |
45 |
46 | );
47 | };
48 |
49 | export default SelectLang;
50 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/TabPages/page.less:
--------------------------------------------------------------------------------
1 | .tab_style() {
2 | .ant-tabs-card-bar {
3 | .ant-tabs-nav-container {
4 | font-size: 13px;
5 | height: 39px;
6 | }
7 | .ant-tabs-tab {
8 | border-radius: 0px;
9 | border-top: 0px;
10 | line-height: 40px;
11 | padding-top: 0;
12 | padding-bottom: 0;
13 | .anticon-close {
14 | display: none;
15 | }
16 | }
17 | #tab-\/{
18 | display: none;
19 | }
20 | .ant-tabs-tab-active {
21 | padding-bottom: 0px;
22 | background-color: rgba(0, 0, 0, 0.1);
23 | .anticon-close {
24 | display: inline-block;
25 | }
26 | }
27 | .ant-tabs-tab:hover {
28 | padding-left: 20px;
29 | padding-right: 20px;
30 | .anticon-close {
31 | color: #333;
32 | display: inline-block;
33 | }
34 | }
35 | }
36 | }
37 |
38 | .content_tab {
39 | :global {
40 | .tab_style() !important;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/e2e/__mocks__/antd-pro-merge-less.js:
--------------------------------------------------------------------------------
1 | export default undefined;
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/layouts/BlankLayout.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Layout = ({ children }) => <>{children}>;
4 |
5 | export default Layout;
6 |
--------------------------------------------------------------------------------
/src/layouts/SecurityLayout.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { PageLoading } from '@ant-design/pro-layout';
3 | import { Redirect, connect } from 'umi';
4 | import { stringify } from 'querystring';
5 |
6 | class SecurityLayout extends React.Component {
7 | state = {
8 | isReady: false,
9 | };
10 |
11 | componentDidMount() {
12 | this.setState({
13 | isReady: true,
14 | });
15 | const { dispatch } = this.props;
16 |
17 | if (dispatch) {
18 | dispatch({
19 | type: 'user/fetchCurrent',
20 | });
21 | }
22 | }
23 |
24 | render() {
25 | const { isReady } = this.state;
26 | const { children, loading, currentUser } = this.props; // You can replace it to your authentication rule (such as check token exists)
27 | // 你可以把它替换成你自己的登录认证规则(比如判断 token 是否存在)
28 |
29 | const isLogin = currentUser && currentUser.userid;
30 | const queryString = stringify({
31 | redirect: window.location.href,
32 | });
33 |
34 | if ((!isLogin && loading) || !isReady) {
35 | return ;
36 | }
37 |
38 | if (!isLogin && window.location.pathname !== '/user/login') {
39 | return ;
40 | }
41 |
42 | return children;
43 | }
44 | }
45 |
46 | export default connect(({ user, loading }) => ({
47 | currentUser: user.currentUser,
48 | loading: loading.models.user,
49 | }))(SecurityLayout);
50 |
--------------------------------------------------------------------------------
/src/layouts/UserLayout.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .container {
4 | display: flex;
5 | flex-direction: column;
6 | height: 100vh;
7 | overflow: auto;
8 | background: @layout-body-background;
9 | }
10 |
11 | .lang {
12 | width: 100%;
13 | height: 40px;
14 | line-height: 44px;
15 | text-align: right;
16 | :global(.ant-dropdown-trigger) {
17 | margin-right: 24px;
18 | }
19 | }
20 |
21 | .content {
22 | flex: 1;
23 | padding: 32px 0;
24 | }
25 |
26 | @media (min-width: @screen-md-min) {
27 | .container {
28 | background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
29 | background-repeat: no-repeat;
30 | background-position: center 110px;
31 | background-size: 100%;
32 | }
33 |
34 | .content {
35 | padding: 32px 0 24px;
36 | }
37 | }
38 |
39 | .top {
40 | text-align: center;
41 | }
42 |
43 | .header {
44 | height: 44px;
45 | line-height: 44px;
46 | a {
47 | text-decoration: none;
48 | }
49 | }
50 |
51 | .logo {
52 | height: 44px;
53 | margin-right: 16px;
54 | vertical-align: top;
55 | }
56 |
57 | .title {
58 | position: relative;
59 | top: 2px;
60 | color: @heading-color;
61 | font-weight: 600;
62 | font-size: 33px;
63 | font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
64 | }
65 |
66 | .desc {
67 | margin-top: 12px;
68 | margin-bottom: 40px;
69 | color: @text-color-secondary;
70 | font-size: @font-size-base;
71 | }
72 |
--------------------------------------------------------------------------------
/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 |
8 | export default {
9 | 'navBar.lang': 'Languages',
10 | 'layout.user.link.help': 'Help',
11 | 'layout.user.link.privacy': 'Privacy',
12 | 'layout.user.link.terms': 'Terms',
13 | 'app.preview.down.block': 'Download this page to your local project',
14 | 'app.welcome.link.fetch-blocks': 'Get all block',
15 | 'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
16 | ...globalHeader,
17 | ...menu,
18 | ...settingDrawer,
19 | ...settings,
20 | ...pwa,
21 | ...component,
22 | };
23 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/locales/en-US/settingDrawer.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.setting.pagestyle': 'Page style setting',
3 | 'app.setting.pagestyle.dark': 'Dark style',
4 | 'app.setting.pagestyle.light': 'Light style',
5 | 'app.setting.content-width': 'Content Width',
6 | 'app.setting.content-width.fixed': 'Fixed',
7 | 'app.setting.content-width.fluid': 'Fluid',
8 | 'app.setting.themecolor': 'Theme Color',
9 | 'app.setting.themecolor.dust': 'Dust Red',
10 | 'app.setting.themecolor.volcano': 'Volcano',
11 | 'app.setting.themecolor.sunset': 'Sunset Orange',
12 | 'app.setting.themecolor.cyan': 'Cyan',
13 | 'app.setting.themecolor.green': 'Polar Green',
14 | 'app.setting.themecolor.daybreak': 'Daybreak Blue (default)',
15 | 'app.setting.themecolor.geekblue': 'Geek Glue',
16 | 'app.setting.themecolor.purple': 'Golden Purple',
17 | 'app.setting.navigationmode': 'Navigation Mode',
18 | 'app.setting.sidemenu': 'Side Menu Layout',
19 | 'app.setting.topmenu': 'Top Menu Layout',
20 | 'app.setting.fixedheader': 'Fixed Header',
21 | 'app.setting.fixedsidebar': 'Fixed Sidebar',
22 | 'app.setting.fixedsidebar.hint': 'Works on Side Menu Layout',
23 | 'app.setting.hideheader': 'Hidden Header when scrolling',
24 | 'app.setting.hideheader.hint': 'Works when Hidden Header is enabled',
25 | 'app.setting.othersettings': 'Other Settings',
26 | 'app.setting.weakmode': 'Weak Mode',
27 | 'app.setting.copy': 'Copy Setting',
28 | 'app.setting.copyinfo': 'copy success,please replace defaultSettings in src/models/setting.js',
29 | 'app.setting.production.hint':
30 | 'Setting panel shows in development environment only, please manually modify',
31 | };
32 |
--------------------------------------------------------------------------------
/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 |
8 | export default {
9 | 'navBar.lang': 'Idiomas',
10 | 'layout.user.link.help': 'ajuda',
11 | 'layout.user.link.privacy': 'política de privacidade',
12 | 'layout.user.link.terms': 'termos de serviços',
13 | 'app.preview.down.block': 'Download this page to your local project',
14 | ...globalHeader,
15 | ...menu,
16 | ...settingDrawer,
17 | ...settings,
18 | ...pwa,
19 | ...component,
20 | };
21 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
8 | export default {
9 | 'navBar.lang': '语言',
10 | 'layout.user.link.help': '帮助',
11 | 'layout.user.link.privacy': '隐私',
12 | 'layout.user.link.terms': '条款',
13 | 'app.preview.down.block': '下载此页面到本地项目',
14 | 'app.welcome.link.fetch-blocks': '获取全部区块',
15 | 'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
16 | ...globalHeader,
17 | ...menu,
18 | ...settingDrawer,
19 | ...settings,
20 | ...pwa,
21 | ...component,
22 | };
23 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/component.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': '展开',
3 | 'component.tagSelect.collapse': '收起',
4 | 'component.tagSelect.all': '全部',
5 | };
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
8 | export default {
9 | 'navBar.lang': '語言',
10 | 'layout.user.link.help': '幫助',
11 | 'layout.user.link.privacy': '隱私',
12 | 'layout.user.link.terms': '條款',
13 | 'app.preview.down.block': '下載此頁面到本地項目',
14 | ...globalHeader,
15 | ...menu,
16 | ...settingDrawer,
17 | ...settings,
18 | ...pwa,
19 | ...component,
20 | };
21 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/component.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': '展開',
3 | 'component.tagSelect.collapse': '收起',
4 | 'component.tagSelect.all': '全部',
5 | };
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/models/user.js:
--------------------------------------------------------------------------------
1 | import { queryCurrent, query as queryUsers } from '@/services/user';
2 |
3 | const UserModel = {
4 | namespace: 'user',
5 | state: {
6 | currentUser: {},
7 | },
8 | effects: {
9 | *fetch(_, { call, put }) {
10 | const response = yield call(queryUsers);
11 | yield put({
12 | type: 'save',
13 | payload: response,
14 | });
15 | },
16 |
17 | *fetchCurrent(_, { call, put }) {
18 | const response = yield call(queryCurrent);
19 | yield put({
20 | type: 'saveCurrentUser',
21 | payload: response,
22 | });
23 | },
24 | },
25 | reducers: {
26 | saveCurrentUser(state, action) {
27 | return { ...state, currentUser: action.payload || {} };
28 | },
29 |
30 | changeNotifyCount(
31 | state = {
32 | currentUser: {},
33 | },
34 | action,
35 | ) {
36 | return {
37 | ...state,
38 | currentUser: {
39 | ...state.currentUser,
40 | notifyCount: action.payload.totalCount,
41 | unreadCount: action.payload.unreadCount,
42 | },
43 | };
44 | },
45 | },
46 | };
47 | export default UserModel;
48 |
--------------------------------------------------------------------------------
/src/pages/404.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Result } from 'antd';
2 | import React from 'react';
3 | import { history } from 'umi';
4 |
5 | const NoFoundPage = () => (
6 | history.push('/')}>
12 | Back Home
13 |
14 | }
15 | />
16 | );
17 |
18 | export default NoFoundPage;
19 |
--------------------------------------------------------------------------------
/src/pages/Account/Center/Center.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .avatarHolder {
4 | margin-bottom: 24px;
5 | text-align: center;
6 |
7 | & > img {
8 | width: 104px;
9 | height: 104px;
10 | margin-bottom: 20px;
11 | }
12 |
13 | .name {
14 | margin-bottom: 4px;
15 | color: @heading-color;
16 | font-weight: 500;
17 | font-size: 20px;
18 | line-height: 28px;
19 | }
20 | }
21 |
22 | .detail {
23 | p {
24 | position: relative;
25 | margin-bottom: 8px;
26 | padding-left: 26px;
27 |
28 | &:last-child {
29 | margin-bottom: 0;
30 | }
31 | }
32 |
33 | i {
34 | position: absolute;
35 | top: 4px;
36 | left: 0;
37 | width: 14px;
38 | height: 14px;
39 | }
40 | }
41 |
42 | .tagsTitle,
43 | .teamTitle {
44 | margin-bottom: 12px;
45 | color: @heading-color;
46 | font-weight: 500;
47 | }
48 |
49 | .tags {
50 | :global {
51 | .ant-tag {
52 | margin-bottom: 8px;
53 | }
54 | }
55 | }
56 |
57 | .team {
58 | :global {
59 | .ant-avatar {
60 | margin-right: 12px;
61 | }
62 | }
63 |
64 | a {
65 | display: block;
66 | margin-bottom: 24px;
67 | overflow: hidden;
68 | color: @text-color;
69 | white-space: nowrap;
70 | text-overflow: ellipsis;
71 | word-break: break-all;
72 | transition: color 0.3s;
73 |
74 | &:hover {
75 | color: @primary-color;
76 | }
77 | }
78 | }
79 |
80 | .tabsCard {
81 | :global {
82 | .ant-card-head {
83 | padding: 0 16px;
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/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 |
6 | export default () => (
7 |
8 |
9 |
19 |
25 | Ant Design Pro You
26 |
27 |
28 |
34 | Want to add more pages? Please refer to{' '}
35 |
36 | use block
37 |
38 | 。
39 |
40 |
41 | );
42 |
--------------------------------------------------------------------------------
/src/pages/Authorized.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Redirect, connect } from 'umi';
3 | import Authorized from '@/utils/Authorized';
4 | import { getRouteAuthority } from '@/utils/utils';
5 |
6 | const AuthComponent = ({
7 | children,
8 | route = {
9 | routes: [],
10 | },
11 | location = {
12 | pathname: '',
13 | },
14 | user,
15 | }) => {
16 | const { currentUser } = user;
17 | const { routes = [] } = route;
18 | const isLogin = currentUser && currentUser.name;
19 | return (
20 | : }
23 | >
24 | {children}
25 |
26 | );
27 | };
28 |
29 | export default connect(({ user }) => ({
30 | user,
31 | }))(AuthComponent);
32 |
--------------------------------------------------------------------------------
/src/pages/ListTableList/components/CreateForm.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Modal } from 'antd';
3 |
4 | const CreateForm = props => {
5 | const { modalVisible, onCancel } = props;
6 | return (
7 | onCancel()}
12 | footer={null}
13 | >
14 | {props.children}
15 |
16 | );
17 | };
18 |
19 | export default CreateForm;
20 |
--------------------------------------------------------------------------------
/src/pages/ListTableList/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 @border-color-base;
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/account/center/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/account/settings/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-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 |
--------------------------------------------------------------------------------
/src/pages/dashboard/analysis/components/Charts/ChartCard/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .chartCard {
4 | position: relative;
5 | .chartTop {
6 | position: relative;
7 | width: 100%;
8 | overflow: hidden;
9 | }
10 | .chartTopMargin {
11 | margin-bottom: 12px;
12 | }
13 | .chartTopHasMargin {
14 | margin-bottom: 20px;
15 | }
16 | .metaWrap {
17 | float: left;
18 | }
19 | .avatar {
20 | position: relative;
21 | top: 4px;
22 | float: left;
23 | margin-right: 20px;
24 | img {
25 | border-radius: 100%;
26 | }
27 | }
28 | .meta {
29 | height: 22px;
30 | color: @text-color-secondary;
31 | font-size: @font-size-base;
32 | line-height: 22px;
33 | }
34 | .action {
35 | position: absolute;
36 | top: 4px;
37 | right: 0;
38 | line-height: 1;
39 | cursor: pointer;
40 | }
41 | .total {
42 | height: 38px;
43 | margin-top: 4px;
44 | margin-bottom: 0;
45 | overflow: hidden;
46 | color: @heading-color;
47 | font-size: 30px;
48 | line-height: 38px;
49 | white-space: nowrap;
50 | text-overflow: ellipsis;
51 | word-break: break-all;
52 | }
53 | .content {
54 | position: relative;
55 | width: 100%;
56 | margin-bottom: 12px;
57 | }
58 | .contentFixed {
59 | position: absolute;
60 | bottom: 0;
61 | left: 0;
62 | width: 100%;
63 | }
64 | .footer {
65 | margin-top: 8px;
66 | padding-top: 9px;
67 | border-top: 1px solid @border-color-split;
68 | & > * {
69 | position: relative;
70 | }
71 | }
72 | .footerMargin {
73 | margin-top: 20px;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/dashboard/analysis/components/Charts/TagCloud/index.less:
--------------------------------------------------------------------------------
1 | .tagCloud {
2 | overflow: hidden;
3 | canvas {
4 | transform-origin: 0 0;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/dashboard/analysis/components/Charts/bizcharts.jsx:
--------------------------------------------------------------------------------
1 | import * as BizChart from 'bizcharts';
2 |
3 | export default BizChart;
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/dashboard/analysis/components/NumberInfo/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 NumberInfo = ({ theme, title, subTitle, total, subTotal, status, suffix, gap, ...rest }) => (
7 |
13 | {title && (
14 |
15 | {title}
16 |
17 | )}
18 | {subTitle && (
19 |
23 | {subTitle}
24 |
25 | )}
26 |
36 |
37 | {total}
38 | {suffix && {suffix}}
39 |
40 | {(status || subTotal) && (
41 |
42 | {subTotal}
43 | {status && status === 'up' ? : }
44 |
45 | )}
46 |
47 |
48 | );
49 |
50 | export default NumberInfo;
51 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/dashboard/analysis/service.jsx:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function fakeChartData() {
4 | return request('/api/fake_chart_data');
5 | }
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/dashboard/monitor/components/Charts/TagCloud/index.less:
--------------------------------------------------------------------------------
1 | .tagCloud {
2 | overflow: hidden;
3 | canvas {
4 | transform-origin: 0 0;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/dashboard/monitor/components/Charts/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .miniChart {
4 | position: relative;
5 | width: 100%;
6 | .chartContent {
7 | position: absolute;
8 | bottom: -28px;
9 | width: 100%;
10 | > div {
11 | margin: 0 -5px;
12 | overflow: hidden;
13 | }
14 | }
15 | .chartLoading {
16 | position: absolute;
17 | top: 16px;
18 | left: 50%;
19 | margin-left: -7px;
20 | }
21 | }
22 |
23 | :global {
24 | body .l7-popup-content {
25 | background: @component-background;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/dashboard/monitor/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function queryTags() {
4 | return request('/api/tags');
5 | }
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/dashboard/workplace/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/flow/components/EditorContextMenu/KoniContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import FlowContextMenu from './FlowContextMenu';
2 |
3 | export default FlowContextMenu;
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/flow/components/EditorContextMenu/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .contextMenu {
4 | display: none;
5 | overflow: hidden;
6 | background: @component-background;
7 | border-radius: 4px;
8 | box-shadow: @box-shadow-base;
9 |
10 | .item {
11 | display: flex;
12 | align-items: center;
13 | padding: 5px 12px;
14 | cursor: pointer;
15 | transition: all 0.3s;
16 | user-select: none;
17 |
18 | &:hover {
19 | background: @select-item-selected-bg;
20 | }
21 |
22 | span.anticon {
23 | margin-right: 8px;
24 | }
25 | }
26 |
27 | :global {
28 | .disable {
29 | :local {
30 | .item {
31 | color: @disabled-color;
32 | cursor: auto;
33 |
34 | &:hover {
35 | background: @item-hover-bg;
36 | }
37 | }
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/flow/components/EditorDetailPanel/KoniDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import FlowDetailPanel from './FlowDetailPanel';
2 |
3 | export default FlowDetailPanel;
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/flow/components/EditorDetailPanel/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .detailPanel {
4 | flex: 1;
5 | background-color: @component-background;
6 | }
7 |
--------------------------------------------------------------------------------
/src/pages/editor/flow/components/EditorItemPanel/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowItemPanel from './FlowItemPanel';
2 | import KoniItemPanel from './KoniItemPanel';
3 |
4 | export { FlowItemPanel, KoniItemPanel };
5 |
--------------------------------------------------------------------------------
/src/pages/editor/flow/components/EditorItemPanel/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .itemPanel {
4 | flex: 1;
5 |
6 | :global {
7 | .ant-card {
8 | height: 100%;
9 | }
10 | .ant-card-body {
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 | > div {
15 | margin-bottom: 16px;
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/flow/components/EditorToolbar/KoniToolbar.jsx:
--------------------------------------------------------------------------------
1 | import FlowToolbar from './FlowToolbar';
2 |
3 | export default FlowToolbar;
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/flow/components/EditorToolbar/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .toolbar {
4 | display: flex;
5 | align-items: center;
6 |
7 | :global {
8 | .command .anticon {
9 | display: inline-block;
10 | width: 27px;
11 | height: 27px;
12 | margin: 0 6px;
13 | padding-top: 6px;
14 | text-align: center;
15 | cursor: pointer;
16 |
17 | &:hover {
18 | border: 1px solid @item-active-bg;
19 | }
20 | }
21 |
22 | .disable .anticon {
23 | color: @text-color-secondary;
24 | cursor: auto;
25 |
26 | &:hover {
27 | border: 1px solid @border-color-base;
28 | }
29 | }
30 | }
31 | }
32 |
33 | .tooltip {
34 | :global {
35 | .ant-tooltip-inner {
36 | font-size: 12px;
37 | border-radius: 0;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/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 { formatMessage } from 'umi';
6 | import EditorMinimap from './components/EditorMinimap';
7 | import { FlowContextMenu } from './components/EditorContextMenu';
8 | import { FlowDetailPanel } from './components/EditorDetailPanel';
9 | import { FlowItemPanel } from './components/EditorItemPanel';
10 | import { FlowToolbar } from './components/EditorToolbar';
11 | import styles from './index.less';
12 |
13 | GGEditor.setTrackable(false);
14 | export default () => (
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 |
--------------------------------------------------------------------------------
/src/pages/editor/flow/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .editor {
4 | display: flex;
5 | flex: 1;
6 | flex-direction: column;
7 | width: 100%;
8 | height: calc(100vh - 250px);
9 | background: @component-background;
10 | }
11 |
12 | .editorHd {
13 | padding: 8px;
14 | background: @descriptions-bg;
15 | border: 1px solid @item-active-bg;
16 | }
17 |
18 | .editorBd {
19 | flex: 1;
20 | }
21 |
22 | .editorSidebar,
23 | .editorContent {
24 | display: flex;
25 | flex-direction: column;
26 | }
27 |
28 | .editorSidebar {
29 | background: @descriptions-bg;
30 | :global {
31 | .g6-editor-minimap-container {
32 | background: none !important ;
33 | }
34 | }
35 | &:first-child {
36 | border-right: 1px solid @item-active-bg;
37 | }
38 |
39 | &:last-child {
40 | border-left: 1px solid @item-active-bg;
41 | }
42 | }
43 |
44 | .flow,
45 | .mind,
46 | .koni {
47 | flex: 1;
48 | }
49 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/flow/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'editorandflow.description': '千言万语不如一张图,流程图是表示算法思路的好方法',
3 | };
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/components/EditorContextMenu/KoniContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import FlowContextMenu from './FlowContextMenu';
2 |
3 | export default FlowContextMenu;
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/components/EditorContextMenu/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .contextMenu {
4 | display: none;
5 | overflow: hidden;
6 | background: @component-background;
7 | border-radius: 4px;
8 | box-shadow: @box-shadow-base;
9 | .item {
10 | display: flex;
11 | align-items: center;
12 | padding: 5px 12px;
13 | cursor: pointer;
14 | transition: all 0.3s;
15 | user-select: none;
16 |
17 | &:hover {
18 | background: @select-item-selected-bg;
19 | }
20 |
21 | .anticon {
22 | margin-right: 8px;
23 | }
24 | }
25 |
26 | :global {
27 | .disable {
28 | :local {
29 | .item {
30 | color: @disabled-color;
31 | cursor: auto;
32 |
33 | &:hover {
34 | background: @item-hover-bg;
35 | }
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/components/EditorDetailPanel/KoniDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import FlowDetailPanel from './FlowDetailPanel';
2 |
3 | export default FlowDetailPanel;
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/components/EditorDetailPanel/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .detailPanel {
4 | flex: 1;
5 | background-color: @component-background;
6 | }
7 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/components/EditorItemPanel/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowItemPanel from './FlowItemPanel';
2 | import KoniItemPanel from './KoniItemPanel';
3 |
4 | export { FlowItemPanel, KoniItemPanel };
5 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/components/EditorItemPanel/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .itemPanel {
4 | flex: 1;
5 |
6 | :global {
7 | .ant-card {
8 | height: 100%;
9 | }
10 | .ant-card-body {
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 | > div {
15 | margin-bottom: 16px;
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/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 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/components/EditorToolbar/KoniToolbar.jsx:
--------------------------------------------------------------------------------
1 | import FlowToolbar from './FlowToolbar';
2 |
3 | export default FlowToolbar;
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/components/EditorToolbar/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .toolbar {
4 | display: flex;
5 | align-items: center;
6 | :global {
7 | .command .anticon {
8 | display: inline-block;
9 | width: 27px;
10 | height: 27px;
11 | margin: 0 6px;
12 | padding-top: 6px;
13 | text-align: center;
14 | cursor: pointer;
15 |
16 | &:hover {
17 | border: 1px solid @item-active-bg;
18 | }
19 | }
20 |
21 | .disable .anticon {
22 | color: @text-color-secondary;
23 | cursor: auto;
24 |
25 | &:hover {
26 | border: 1px solid @border-color-base;
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 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/index.jsx:
--------------------------------------------------------------------------------
1 | import { Col, Row } from 'antd';
2 | import GGEditor, { Koni } from 'gg-editor';
3 | import { PageHeaderWrapper } from '@ant-design/pro-layout';
4 | import React from 'react';
5 | import { formatMessage } from 'umi';
6 | import EditorMinimap from './components/EditorMinimap';
7 | import { KoniContextMenu } from './components/EditorContextMenu';
8 | import { KoniDetailPanel } from './components/EditorDetailPanel';
9 | import { KoniItemPanel } from './components/EditorItemPanel';
10 | import { KoniToolbar } from './components/EditorToolbar';
11 | import styles from './index.less';
12 |
13 | GGEditor.setTrackable(false);
14 | export default () => (
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .editor {
4 | display: flex;
5 | flex: 1;
6 | flex-direction: column;
7 | width: 100%;
8 | height: calc(100vh - 250px);
9 | background: @descriptions-bg;
10 | }
11 |
12 | .editorHd {
13 | padding: 8px;
14 | background: @descriptions-bg;
15 | border: 1px solid @item-active-bg;
16 | }
17 |
18 | .editorBd {
19 | flex: 1;
20 | }
21 |
22 | .editorSidebar,
23 | .editorContent {
24 | display: flex;
25 | flex-direction: column;
26 | }
27 |
28 | .editorContent {
29 | :global {
30 | .graph-container canvas {
31 | vertical-align: middle;
32 | }
33 | }
34 | }
35 |
36 | .editorSidebar {
37 | background: @descriptions-bg;
38 | :global {
39 | .g6-editor-minimap-container {
40 | background: none !important ;
41 | }
42 | }
43 | &:first-child {
44 | border-right: 1px solid @item-active-bg;
45 | }
46 |
47 | &:last-child {
48 | border-left: 1px solid @item-active-bg;
49 | }
50 | }
51 |
52 | .flow,
53 | .mind,
54 | .koni {
55 | flex: 1;
56 | }
57 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/koni/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'editorandkoni.description': '拓扑结构图是指由网络节点设备和通信介质构成的网络结构图',
3 | };
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/components/EditorContextMenu/KoniContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import FlowContextMenu from './FlowContextMenu';
2 |
3 | export default FlowContextMenu;
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/components/EditorContextMenu/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .contextMenu {
4 | display: none;
5 | overflow: hidden;
6 | background: @component-background;
7 | border-radius: 4px;
8 | box-shadow: @box-shadow-base;
9 | .item {
10 | display: flex;
11 | align-items: center;
12 | padding: 5px 12px;
13 | cursor: pointer;
14 | transition: all 0.3s;
15 | user-select: none;
16 |
17 | &:hover {
18 | background: @select-item-selected-bg;
19 | }
20 |
21 | .anticon {
22 | margin-right: 8px;
23 | }
24 | }
25 |
26 | :global {
27 | .disable {
28 | :local {
29 | .item {
30 | color: @disabled-color;
31 | cursor: auto;
32 |
33 | &:hover {
34 | background: @item-hover-bg;
35 | }
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/components/EditorDetailPanel/KoniDetailPanel.jsx:
--------------------------------------------------------------------------------
1 | import FlowDetailPanel from './FlowDetailPanel';
2 |
3 | export default FlowDetailPanel;
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/components/EditorDetailPanel/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .detailPanel {
4 | flex: 1;
5 | background-color: @component-background;
6 | }
7 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/components/EditorItemPanel/index.jsx:
--------------------------------------------------------------------------------
1 | import FlowItemPanel from './FlowItemPanel';
2 | import KoniItemPanel from './KoniItemPanel';
3 |
4 | export { FlowItemPanel, KoniItemPanel };
5 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/components/EditorItemPanel/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .itemPanel {
4 | flex: 1;
5 |
6 | :global {
7 | .ant-card {
8 | height: 100%;
9 | }
10 | .ant-card-body {
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 | > div {
15 | margin-bottom: 16px;
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/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 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/components/EditorToolbar/KoniToolbar.jsx:
--------------------------------------------------------------------------------
1 | import FlowToolbar from './FlowToolbar';
2 |
3 | export default FlowToolbar;
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/components/EditorToolbar/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .toolbar {
4 | display: flex;
5 | align-items: center;
6 |
7 | :global {
8 | .command .anticon {
9 | display: inline-block;
10 | width: 27px;
11 | height: 27px;
12 | margin: 0 6px;
13 | padding-top: 6px;
14 | text-align: center;
15 | cursor: pointer;
16 |
17 | &:hover {
18 | border: 1px solid @item-active-bg;
19 | }
20 | }
21 |
22 | .disable .anticon {
23 | color: @text-color-secondary;
24 | cursor: auto;
25 |
26 | &:hover {
27 | border: 1px solid @border-color-base;
28 | }
29 | }
30 | }
31 | }
32 |
33 | .tooltip {
34 | :global {
35 | .ant-tooltip-inner {
36 | font-size: 12px;
37 | border-radius: 0;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/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 { formatMessage } from 'umi';
6 | import EditorMinimap from './components/EditorMinimap';
7 | import { MindContextMenu } from './components/EditorContextMenu';
8 | import { MindDetailPanel } from './components/EditorDetailPanel';
9 | import { MindToolbar } from './components/EditorToolbar';
10 | import data from './worldCup2018.json';
11 | import styles from './index.less';
12 |
13 | GGEditor.setTrackable(false);
14 | export default () => (
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | );
40 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .editor {
4 | display: flex;
5 | flex: 1;
6 | flex-direction: column;
7 | width: 100%;
8 | height: calc(100vh - 250px);
9 | background: @descriptions-bg;
10 | }
11 |
12 | .editorHd {
13 | padding: 8px;
14 | background: @descriptions-bg;
15 | border: 1px solid @item-active-bg;
16 | }
17 |
18 | .editorBd {
19 | flex: 1;
20 | }
21 |
22 | .editorSidebar,
23 | .editorContent {
24 | display: flex;
25 | flex-direction: column;
26 | }
27 |
28 | .editorContent {
29 | :global {
30 | .graph-container canvas {
31 | vertical-align: middle;
32 | }
33 | }
34 | }
35 |
36 | .editorSidebar {
37 | background: @descriptions-bg;
38 | :global {
39 | .g6-editor-minimap-container {
40 | background: none !important ;
41 | }
42 | }
43 | &:first-child {
44 | border-right: 1px solid @item-active-bg;
45 | }
46 |
47 | &:last-child {
48 | border-left: 1px solid @item-active-bg;
49 | }
50 | }
51 |
52 | .flow,
53 | .mind,
54 | .koni {
55 | flex: 1;
56 | }
57 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/editor/mind/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'editorandmind.description':
3 | '脑图是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具',
4 | };
5 |
--------------------------------------------------------------------------------
/src/pages/exception/403/index.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'umi';
2 | import { Result, Button } from 'antd';
3 | import React from 'react';
4 |
5 | export default () => (
6 |
15 |
16 |
17 | }
18 | />
19 | );
20 |
--------------------------------------------------------------------------------
/src/pages/exception/404/index.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'umi';
2 | import { Result, Button } from 'antd';
3 | import React from 'react';
4 |
5 | export default () => (
6 |
15 |
16 |
17 | }
18 | />
19 | );
20 |
--------------------------------------------------------------------------------
/src/pages/exception/500/index.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'umi';
2 | import { Result, Button } from 'antd';
3 | import React from 'react';
4 |
5 | export default () => (
6 |
15 |
16 |
17 | }
18 | />
19 | );
20 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/exception/500/locales/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand500.exception.back': '返回首页',
3 | 'exceptionand500.description.500': '抱歉,服务器出错了。',
4 | };
5 |
--------------------------------------------------------------------------------
/src/pages/exception/500/locales/zh-TW.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'exceptionand500.exception.back': '返回首頁',
3 | 'exceptionand500.description.500': '抱歉,服務器出錯了。',
4 | };
5 |
--------------------------------------------------------------------------------
/src/pages/form/advanced-form/_mock.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-extraneous-dependencies
2 | export default {
3 | 'POST /api/forms': (_, res) => {
4 | res.send({
5 | message: 'Ok',
6 | });
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | border-top: 1px solid @border-color-split;
13 | box-shadow: @box-shadow-base;
14 |
15 | &::after {
16 | display: block;
17 | clear: both;
18 | content: '';
19 | }
20 |
21 | .left {
22 | float: left;
23 | }
24 |
25 | .right {
26 | float: right;
27 | }
28 |
29 | button + button {
30 | margin-left: 8px;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/form/advanced-form/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function fakeSubmitForm(params) {
4 | return request('/api/forms', {
5 | method: 'POST',
6 | data: params,
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/src/pages/form/advanced-form/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .card {
4 | margin-bottom: 24px;
5 |
6 | :global {
7 | .ant-legacy-form-item .ant-legacy-form-item-control-wrapper {
8 | width: 100%;
9 | }
10 | }
11 | }
12 |
13 | .errorIcon {
14 | margin-right: 24px;
15 | color: @error-color;
16 | cursor: pointer;
17 |
18 | span.anticon {
19 | margin-right: 4px;
20 | }
21 | }
22 |
23 | .errorPopover {
24 | :global {
25 | .ant-popover-inner-content {
26 | min-width: 256px;
27 | max-height: 290px;
28 | padding: 0;
29 | overflow: auto;
30 | }
31 | }
32 | }
33 |
34 | .errorListItem {
35 | padding: 8px 16px;
36 | list-style: none;
37 | border-bottom: 1px solid @border-color-split;
38 | cursor: pointer;
39 | transition: all 0.3s;
40 | &:hover {
41 | background: @item-active-bg;
42 | }
43 | &:last-child {
44 | border: 0;
45 | }
46 | .errorIcon {
47 | float: left;
48 | margin-top: 4px;
49 | margin-right: 12px;
50 | padding-bottom: 22px;
51 | color: @error-color;
52 | }
53 | .errorField {
54 | margin-top: 2px;
55 | color: @text-color-secondary;
56 | font-size: 12px;
57 | }
58 | }
59 |
60 | .editable {
61 | td {
62 | padding-top: 13px !important;
63 | padding-bottom: 12.5px !important;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/pages/form/basic-form/_mock.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-extraneous-dependencies
2 | export default {
3 | 'POST /api/forms': (_, res) => {
4 | res.send({
5 | message: 'Ok',
6 | });
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/form/basic-form/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function fakeSubmitForm(params) {
4 | return request('/api/forms', {
5 | method: 'POST',
6 | data: params,
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/src/pages/form/step-form/_mock.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-extraneous-dependencies
2 | export default {
3 | 'POST /api/forms': (_, res) => {
4 | res.send({
5 | message: 'Ok',
6 | });
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/src/pages/form/step-form/components/Step1/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .stepForm {
4 | max-width: 500px;
5 | margin: 40px auto 0;
6 | }
7 |
8 | .stepFormText {
9 | margin-bottom: 24px;
10 | :global {
11 | .ant-form-item-label,
12 | .ant-form-item-control {
13 | line-height: 22px;
14 | }
15 | }
16 | }
17 |
18 | .result {
19 | max-width: 560px;
20 | margin: 0 auto;
21 | padding: 24px 0 8px;
22 | }
23 |
24 | .desc {
25 | padding: 0 56px;
26 | color: @text-color-secondary;
27 | h3 {
28 | margin: 0 0 12px 0;
29 | color: @text-color-secondary;
30 | font-size: 16px;
31 | line-height: 32px;
32 | }
33 | h4 {
34 | margin: 0 0 4px 0;
35 | color: @text-color-secondary;
36 | font-size: 14px;
37 | line-height: 22px;
38 | }
39 | p {
40 | margin-top: 0;
41 | margin-bottom: 12px;
42 | line-height: 22px;
43 | }
44 | }
45 |
46 | @media screen and (max-width: @screen-md) {
47 | .desc {
48 | padding: 0;
49 | }
50 | }
51 |
52 | .information {
53 | line-height: 22px;
54 | :global {
55 | .ant-row:not(:last-child) {
56 | margin-bottom: 24px;
57 | }
58 | }
59 | .label {
60 | padding-right: 8px;
61 | color: @heading-color;
62 | text-align: right;
63 | @media screen and (max-width: @screen-sm) {
64 | text-align: left;
65 | }
66 | }
67 | }
68 |
69 | .money {
70 | font-weight: 500;
71 | font-size: 20px;
72 | font-family: 'Helvetica Neue', sans-serif;
73 | line-height: 14px;
74 | }
75 |
76 | .uppercase {
77 | font-size: 12px;
78 | }
79 |
--------------------------------------------------------------------------------
/src/pages/form/step-form/components/Step2/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .stepForm {
4 | max-width: 500px;
5 | margin: 40px auto 0;
6 | }
7 |
8 | .stepFormText {
9 | margin-bottom: 24px;
10 | :global {
11 | .ant-form-item-label,
12 | .ant-form-item-control {
13 | line-height: 22px;
14 | }
15 | }
16 | }
17 |
18 | .result {
19 | max-width: 560px;
20 | margin: 0 auto;
21 | padding: 24px 0 8px;
22 | }
23 |
24 | .desc {
25 | padding: 0 56px;
26 | color: @text-color-secondary;
27 | h3 {
28 | margin: 0 0 12px 0;
29 | color: @text-color-secondary;
30 | font-size: 16px;
31 | line-height: 32px;
32 | }
33 | h4 {
34 | margin: 0 0 4px 0;
35 | color: @text-color-secondary;
36 | font-size: 14px;
37 | line-height: 22px;
38 | }
39 | p {
40 | margin-top: 0;
41 | margin-bottom: 12px;
42 | line-height: 22px;
43 | }
44 | }
45 |
46 | @media screen and (max-width: @screen-md) {
47 | .desc {
48 | padding: 0;
49 | }
50 | }
51 |
52 | .information {
53 | line-height: 22px;
54 | :global {
55 | .ant-row:not(:last-child) {
56 | margin-bottom: 24px;
57 | }
58 | }
59 | .label {
60 | padding-right: 8px;
61 | color: @heading-color;
62 | text-align: right;
63 | @media screen and (max-width: @screen-sm) {
64 | text-align: left;
65 | }
66 | }
67 | }
68 |
69 | .money {
70 | font-weight: 500;
71 | font-size: 20px;
72 | font-family: 'Helvetica Neue', sans-serif;
73 | line-height: 14px;
74 | }
75 |
76 | .uppercase {
77 | font-size: 12px;
78 | }
79 |
--------------------------------------------------------------------------------
/src/pages/form/step-form/components/Step3/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .stepForm {
4 | max-width: 500px;
5 | margin: 40px auto 0;
6 | }
7 |
8 | .stepFormText {
9 | margin-bottom: 24px;
10 | :global {
11 | .ant-form-item-label,
12 | .ant-form-item-control {
13 | line-height: 22px;
14 | }
15 | }
16 | }
17 |
18 | .result {
19 | max-width: 560px;
20 | margin: 0 auto;
21 | padding: 24px 0 8px;
22 | }
23 |
24 | .desc {
25 | padding: 0 56px;
26 | color: @text-color-secondary;
27 | h3 {
28 | margin: 0 0 12px 0;
29 | color: @text-color-secondary;
30 | font-size: 16px;
31 | line-height: 32px;
32 | }
33 | h4 {
34 | margin: 0 0 4px 0;
35 | color: @text-color-secondary;
36 | font-size: 14px;
37 | line-height: 22px;
38 | }
39 | p {
40 | margin-top: 0;
41 | margin-bottom: 12px;
42 | line-height: 22px;
43 | }
44 | }
45 |
46 | @media screen and (max-width: @screen-md) {
47 | .desc {
48 | padding: 0;
49 | }
50 | }
51 |
52 | .information {
53 | line-height: 22px;
54 | :global {
55 | .ant-row:not(:last-child) {
56 | margin-bottom: 24px;
57 | }
58 | }
59 | .label {
60 | padding-right: 8px;
61 | color: @heading-color;
62 | text-align: right;
63 | @media screen and (max-width: @screen-sm) {
64 | text-align: left;
65 | }
66 | }
67 | }
68 |
69 | .uppercase {
70 | font-size: 12px;
71 | }
72 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/form/step-form/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function fakeSubmitForm(params) {
4 | return request('/api/forms', {
5 | method: 'POST',
6 | data: params,
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/src/pages/list/basic-list/model.js:
--------------------------------------------------------------------------------
1 | import { addFakeList, queryFakeList, removeFakeList, updateFakeList } from './service';
2 |
3 | const Model = {
4 | namespace: 'listAndbasicList',
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 | *submit({ payload }, { call, put }) {
26 | let callback;
27 |
28 | if (payload.id) {
29 | callback = Object.keys(payload).length === 1 ? removeFakeList : updateFakeList;
30 | } else {
31 | callback = addFakeList;
32 | }
33 |
34 | const response = yield call(callback, payload); // post
35 |
36 | yield put({
37 | type: 'queryList',
38 | payload: response,
39 | });
40 | },
41 | },
42 | reducers: {
43 | queryList(state, action) {
44 | return { ...state, list: action.payload };
45 | },
46 |
47 | appendList(
48 | state = {
49 | list: [],
50 | },
51 | action,
52 | ) {
53 | return { ...state, list: state.list.concat(action.payload) };
54 | },
55 | },
56 | };
57 | export default Model;
58 |
--------------------------------------------------------------------------------
/src/pages/list/basic-list/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/list/card-list/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function queryFakeList(params) {
4 | return request('/api/fake_list', {
5 | params,
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/list/search/applications/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function queryFakeList(params) {
4 | return request('/api/fake_list', {
5 | params,
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/list/search/articles/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function queryFakeList(params) {
4 | return request('/api/fake_list', {
5 | params,
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 @border-color-base;
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/list/search/projects/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function queryFakeList(params) {
4 | return request('/api/fake_list', {
5 | params,
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/list/table-list/components/CreateForm.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Form, Input, Modal } from 'antd';
3 |
4 | const FormItem = Form.Item;
5 |
6 | const CreateForm = props => {
7 | const [form] = Form.useForm();
8 | const { modalVisible, onSubmit: handleAdd, onCancel } = props;
9 |
10 | const okHandle = async () => {
11 | const fieldsValue = await form.validateFields();
12 | form.resetFields();
13 | handleAdd(fieldsValue);
14 | };
15 |
16 | return (
17 | onCancel()}
23 | >
24 |
45 |
46 | );
47 | };
48 |
49 | export default CreateForm;
50 |
--------------------------------------------------------------------------------
/src/pages/list/table-list/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-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 |
--------------------------------------------------------------------------------
/src/pages/profile/advanced/_mock.js:
--------------------------------------------------------------------------------
1 | const advancedOperation1 = [
2 | {
3 | key: 'op1',
4 | type: '订购关系生效',
5 | name: '曲丽丽',
6 | status: 'agree',
7 | updatedAt: '2017-10-03 19:23:12',
8 | memo: '-',
9 | },
10 | {
11 | key: 'op2',
12 | type: '财务复审',
13 | name: '付小小',
14 | status: 'reject',
15 | updatedAt: '2017-10-03 19:23:12',
16 | memo: '不通过原因',
17 | },
18 | {
19 | key: 'op3',
20 | type: '部门初审',
21 | name: '周毛毛',
22 | status: 'agree',
23 | updatedAt: '2017-10-03 19:23:12',
24 | memo: '-',
25 | },
26 | {
27 | key: 'op4',
28 | type: '提交订单',
29 | name: '林东东',
30 | status: 'agree',
31 | updatedAt: '2017-10-03 19:23:12',
32 | memo: '很棒',
33 | },
34 | {
35 | key: 'op5',
36 | type: '创建订单',
37 | name: '汗牙牙',
38 | status: 'agree',
39 | updatedAt: '2017-10-03 19:23:12',
40 | memo: '-',
41 | },
42 | ];
43 | const advancedOperation2 = [
44 | {
45 | key: 'op1',
46 | type: '订购关系生效',
47 | name: '曲丽丽',
48 | status: 'agree',
49 | updatedAt: '2017-10-03 19:23:12',
50 | memo: '-',
51 | },
52 | ];
53 | const advancedOperation3 = [
54 | {
55 | key: 'op1',
56 | type: '创建订单',
57 | name: '汗牙牙',
58 | status: 'agree',
59 | updatedAt: '2017-10-03 19:23:12',
60 | memo: '-',
61 | },
62 | ];
63 | const getProfileAdvancedData = {
64 | advancedOperation1,
65 | advancedOperation2,
66 | advancedOperation3,
67 | };
68 | export default {
69 | 'GET /api/profile/advanced': getProfileAdvancedData,
70 | };
71 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/profile/advanced/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function queryAdvancedProfile() {
4 | return request('/api/profile/advanced');
5 | }
6 |
--------------------------------------------------------------------------------
/src/pages/profile/advanced/style.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .main {
4 | :global {
5 | .ant-descriptions-row > td {
6 | padding-bottom: 8px;
7 | }
8 | .ant-page-header-heading-extra {
9 | flex-direction: column;
10 | }
11 | }
12 | }
13 |
14 | .headerList {
15 | margin-bottom: 4px;
16 | :global {
17 | .ant-descriptions-row > td {
18 | padding-bottom: 8px;
19 | }
20 | }
21 |
22 | .stepDescription {
23 | position: relative;
24 | left: 38px;
25 | padding-top: 8px;
26 | font-size: 14px;
27 | text-align: left;
28 |
29 | > div {
30 | margin-top: 8px;
31 | margin-bottom: 4px;
32 | }
33 | }
34 | }
35 |
36 | .pageHeader {
37 | :global {
38 | .ant-page-header-heading-extra > * + * {
39 | margin-left: 8px;
40 | }
41 | }
42 | .moreInfo {
43 | display: flex;
44 | justify-content: space-between;
45 | width: 200px;
46 | }
47 | }
48 |
49 | @media screen and (max-width: @screen-sm) {
50 | .stepDescription {
51 | left: 8px;
52 | }
53 | .pageHeader {
54 | :global {
55 | .ant-pro-page-header-wrap-row {
56 | flex-direction: column;
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/profile/basic/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function queryBasicProfile() {
4 | return request('/api/profile/basic');
5 | }
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/user/login/_mock.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-extraneous-dependencies
2 | function getFakeCaptcha(req, res) {
3 | return res.json('captcha-xxx');
4 | }
5 |
6 | export default {
7 | 'POST /api/login/account': (req, res) => {
8 | const { password, userName, type } = req.body;
9 |
10 | if (password === 'ant.design' && userName === 'admin') {
11 | res.send({
12 | status: 'ok',
13 | type,
14 | currentAuthority: 'admin',
15 | });
16 | return;
17 | }
18 |
19 | if (password === 'ant.design' && userName === 'user') {
20 | res.send({
21 | status: 'ok',
22 | type,
23 | currentAuthority: 'user',
24 | });
25 | return;
26 | }
27 |
28 | if (type === 'mobile') {
29 | res.send({
30 | status: 'ok',
31 | type,
32 | currentAuthority: 'admin',
33 | });
34 | return;
35 | }
36 |
37 | res.send({
38 | status: 'error',
39 | type,
40 | currentAuthority: 'guest',
41 | });
42 | },
43 | 'GET /api/login/captcha': getFakeCaptcha,
44 | };
45 |
--------------------------------------------------------------------------------
/src/pages/user/login/components/Login/LoginContext.jsx:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react';
2 |
3 | const LoginContext = createContext({});
4 | export default LoginContext;
5 |
--------------------------------------------------------------------------------
/src/pages/user/login/components/Login/LoginSubmit.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Form } from 'antd';
2 | import React from 'react';
3 | import classNames from 'classnames';
4 | import styles from './index.less';
5 |
6 | const FormItem = Form.Item;
7 |
8 | const LoginSubmit = ({ className, ...rest }) => {
9 | const clsString = classNames(styles.submit, className);
10 | return (
11 |
12 |
13 |
14 | );
15 | };
16 |
17 | export default LoginSubmit;
18 |
--------------------------------------------------------------------------------
/src/pages/user/login/components/Login/LoginTab.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } 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 | const LoginTab = props => {
16 | useEffect(() => {
17 | const uniqueId = generateId('login-tab-');
18 | const { tabUtil } = props;
19 |
20 | if (tabUtil) {
21 | tabUtil.addTab(uniqueId);
22 | }
23 | }, []);
24 | const { children } = props;
25 | return {props.active && children};
26 | };
27 |
28 | const WrapContext = props => (
29 |
30 | {value => }
31 |
32 | ); // 标志位 用来判断是不是自定义组件
33 |
34 | WrapContext.typeName = 'LoginTab';
35 | export default WrapContext;
36 |
--------------------------------------------------------------------------------
/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 |
12 | .getCaptcha {
13 | display: block;
14 | width: 100%;
15 | }
16 |
17 | .icon {
18 | margin-left: 16px;
19 | color: @text-color-secondary;
20 | font-size: 24px;
21 | vertical-align: middle;
22 | cursor: pointer;
23 | transition: color 0.3s;
24 |
25 | &:hover {
26 | color: @primary-color;
27 | }
28 | }
29 |
30 | .other {
31 | margin-top: 24px;
32 | line-height: 22px;
33 | text-align: left;
34 |
35 | .register {
36 | float: right;
37 | }
38 | }
39 |
40 | .prefixIcon {
41 | color: @disabled-color;
42 | font-size: @font-size-base;
43 | }
44 |
45 | .submit {
46 | width: 100%;
47 | margin-top: 24px;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/pages/user/login/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-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 |
--------------------------------------------------------------------------------
/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: @text-color-secondary;
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/user/register-result/index.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Result } from 'antd';
2 | import { FormattedMessage, formatMessage, Link } from 'umi';
3 | import React from 'react';
4 | import styles from './style.less';
5 |
6 | const actions = (
7 |
19 | );
20 |
21 | const RegisterResult = ({ location }) => (
22 |
27 |
33 |
34 | }
35 | subTitle={formatMessage({
36 | id: 'userandregister-result.register-result.activation-email',
37 | })}
38 | extra={actions}
39 | />
40 | );
41 |
42 | export default RegisterResult;
43 |
--------------------------------------------------------------------------------
/src/pages/user/register-result/locales/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'userandregister-result.login.userName': 'userName',
3 | 'userandregister-result.login.password': 'password',
4 | 'userandregister-result.login.message-invalid-credentials':
5 | 'Invalid username or password(admin/ant.design)',
6 | 'userandregister-result.login.message-invalid-verification-code': 'Invalid verification code',
7 | 'userandregister-result.login.tab-login-credentials': 'Credentials',
8 | 'userandregister-result.login.tab-login-mobile': 'Mobile number',
9 | 'userandregister-result.login.remember-me': 'Remember me',
10 | 'userandregister-result.login.forgot-password': 'Forgot your password?',
11 | 'userandregister-result.login.sign-in-with': 'Sign in with',
12 | 'userandregister-result.login.signup': 'Sign up',
13 | 'userandregister-result.login.login': 'Login',
14 | 'userandregister-result.register.register': 'Register',
15 | 'userandregister-result.register.get-verification-code': 'Get code',
16 | 'userandregister-result.register.sign-in': 'Already have an account?',
17 | 'userandregister-result.register-result.msg': 'Account:registered at {email}',
18 | 'userandregister-result.register-result.activation-email':
19 | 'The activation email has been sent to your email address and is valid for 24 hours. Please log in to the email in time and click on the link in the email to activate the account.',
20 | 'userandregister-result.register-result.back-home': 'Back to home',
21 | 'userandregister-result.register-result.view-mailbox': 'View mailbox',
22 | 'userandregister-result.navBar.lang': 'Languages',
23 | };
24 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/user/register/_mock.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-extraneous-dependencies
2 | export default {
3 | 'POST /api/register': (_, res) => {
4 | res.send({
5 | status: 'ok',
6 | currentAuthority: 'user',
7 | });
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/user/register/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export async function fakeRegister(params) {
4 | return request('/api/register', {
5 | method: 'POST',
6 | data: params,
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/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 | h3 {
8 | margin-bottom: 20px;
9 | font-size: 16px;
10 | }
11 |
12 | .password {
13 | margin-bottom: 24px;
14 | :global {
15 | .ant-form-item-explain {
16 | display: none;
17 | }
18 | }
19 | }
20 |
21 | .getCaptcha {
22 | display: block;
23 | width: 100%;
24 | }
25 |
26 | .submit {
27 | width: 50%;
28 | }
29 |
30 | .login {
31 | float: right;
32 | line-height: @btn-height-lg;
33 | }
34 | }
35 |
36 | .success,
37 | .warning,
38 | .error {
39 | transition: color 0.3s;
40 | }
41 |
42 | .success {
43 | color: @success-color;
44 | }
45 |
46 | .warning {
47 | color: @warning-color;
48 | }
49 |
50 | .error {
51 | color: @error-color;
52 | }
53 |
54 | .progress-pass > .progress {
55 | :global {
56 | .ant-progress-bg {
57 | background-color: @warning-color;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/services/login.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 |
--------------------------------------------------------------------------------
/src/services/user.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function query() {
4 | return request('/api/users');
5 | }
6 | export async function queryCurrent() {
7 | return request('/api/currentUser');
8 | }
9 | export async function queryNotices() {
10 | return request('/api/notices');
11 | }
12 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | /**
2 | * request 网络请求工具
3 | * 更详细的 api 文档: https://github.com/umijs/umi-request
4 | */
5 | import { extend } from 'umi-request';
6 | import { notification } from 'antd';
7 |
8 | const codeMessage = {
9 | 200: '服务器成功返回请求的数据。',
10 | 201: '新建或修改数据成功。',
11 | 202: '一个请求已经进入后台排队(异步任务)。',
12 | 204: '删除数据成功。',
13 | 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
14 | 401: '用户没有权限(令牌、用户名、密码错误)。',
15 | 403: '用户得到授权,但是访问是被禁止的。',
16 | 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
17 | 406: '请求的格式不可得。',
18 | 410: '请求的资源被永久删除,且不会再得到的。',
19 | 422: '当创建一个对象时,发生一个验证错误。',
20 | 500: '服务器发生错误,请检查服务器。',
21 | 502: '网关错误。',
22 | 503: '服务不可用,服务器暂时过载或维护。',
23 | 504: '网关超时。',
24 | };
25 | /**
26 | * 异常处理程序
27 | */
28 |
29 | const errorHandler = error => {
30 | const { response } = error;
31 |
32 | if (response && response.status) {
33 | const errorText = codeMessage[response.status] || response.statusText;
34 | const { status, url } = response;
35 | notification.error({
36 | message: `请求错误 ${status}: ${url}`,
37 | description: errorText,
38 | });
39 | } else if (!response) {
40 | notification.error({
41 | description: '您的网络发生异常,无法连接服务器',
42 | message: '网络异常',
43 | });
44 | }
45 |
46 | return response;
47 | };
48 | /**
49 | * 配置request请求时的默认参数
50 | */
51 |
52 | const request = extend({
53 | errorHandler,
54 | // 默认错误处理
55 | credentials: 'include', // 默认请求是否带上cookie
56 | });
57 | export default request;
58 |
--------------------------------------------------------------------------------
/src/utils/utils.less:
--------------------------------------------------------------------------------
1 | // mixins for clearfix
2 | // ------------------------
3 | .clearfix() {
4 | zoom: 1;
5 | &::before,
6 | &::after {
7 | display: table;
8 | content: ' ';
9 | }
10 | &::after {
11 | clear: both;
12 | height: 0;
13 | font-size: 0;
14 | visibility: hidden;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tests/PuppeteerEnvironment.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | const NodeEnvironment = require('jest-environment-node');
3 | const getBrowser = require('./getBrowser');
4 |
5 | class PuppeteerEnvironment extends NodeEnvironment {
6 | // Jest is not available here, so we have to reverse engineer
7 | // the setTimeout function, see https://github.com/facebook/jest/blob/v23.1.0/packages/jest-runtime/src/index.js#L823
8 | setTimeout(timeout) {
9 | if (this.global.jasmine) {
10 | // eslint-disable-next-line no-underscore-dangle
11 | this.global.jasmine.DEFAULT_TIMEOUT_INTERVAL = timeout;
12 | } else {
13 | this.global[Symbol.for('TEST_TIMEOUT_SYMBOL')] = timeout;
14 | }
15 | }
16 |
17 | async setup() {
18 | const browser = await getBrowser();
19 | const page = await browser.newPage();
20 | this.global.browser = browser;
21 | this.global.page = page;
22 | }
23 |
24 | async teardown() {
25 | const { page, browser } = this.global;
26 |
27 | if (page) {
28 | await page.close();
29 | }
30 |
31 | if (browser) {
32 | await browser.disconnect();
33 | }
34 |
35 | if (browser) {
36 | await browser.close();
37 | }
38 | }
39 | }
40 |
41 | module.exports = PuppeteerEnvironment;
42 |
--------------------------------------------------------------------------------
/tests/beforeTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | /* eslint-disable import/no-extraneous-dependencies */
3 | const { execSync } = require('child_process');
4 | const { join } = require('path');
5 | const findChrome = require('carlo/lib/find_chrome');
6 | const detectInstaller = require('detect-installer');
7 |
8 | const installPuppeteer = () => {
9 | // find can use package manger
10 | const packages = detectInstaller(join(__dirname, '../../'));
11 | // get installed package manger
12 | const packageName = packages.find(detectInstaller.hasPackageCommand) || 'npm';
13 | console.log(`🤖 will use ${packageName} install puppeteer`);
14 | const command = `${packageName} ${packageName.includes('yarn') ? 'add' : 'i'} puppeteer`;
15 | execSync(command, {
16 | stdio: 'inherit',
17 | });
18 | };
19 |
20 | const initPuppeteer = async () => {
21 | try {
22 | // eslint-disable-next-line import/no-unresolved
23 | const findChromePath = await findChrome({});
24 | const { executablePath } = findChromePath;
25 | console.log(`🧲 find you browser in ${executablePath}`);
26 | return;
27 | } catch (error) {
28 | console.log('🧲 no find chrome');
29 | }
30 |
31 | try {
32 | require.resolve('puppeteer');
33 | } catch (error) {
34 | // need install puppeteer
35 | await installPuppeteer();
36 | }
37 | };
38 |
39 | initPuppeteer();
40 |
--------------------------------------------------------------------------------
/tests/getBrowser.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | /* eslint-disable import/no-extraneous-dependencies */
3 | const findChrome = require('carlo/lib/find_chrome');
4 |
5 | const getBrowser = async () => {
6 | try {
7 | // eslint-disable-next-line import/no-unresolved
8 | const puppeteer = require('puppeteer');
9 | const browser = await puppeteer.launch({
10 | args: [
11 | '--disable-gpu',
12 | '--disable-dev-shm-usage',
13 | '--no-first-run',
14 | '--no-zygote',
15 | '--no-sandbox',
16 | ],
17 | });
18 | return browser;
19 | } catch (error) {
20 | // console.log(error)
21 | }
22 |
23 | try {
24 | // eslint-disable-next-line import/no-unresolved
25 | const puppeteer = require('puppeteer-core');
26 | const findChromePath = await findChrome({});
27 | const { executablePath } = findChromePath;
28 | const browser = await puppeteer.launch({
29 | executablePath,
30 | headless: false,
31 | args: [
32 | '--disable-gpu',
33 | '--disable-dev-shm-usage',
34 | '--no-first-run',
35 | '--no-zygote',
36 | '--no-sandbox',
37 | ],
38 | });
39 | return browser;
40 | } catch (error) {
41 | console.log('🧲 no find chrome');
42 | }
43 | throw new Error('no find puppeteer');
44 | };
45 |
46 | module.exports = getBrowser;
47 |
--------------------------------------------------------------------------------