├── src ├── components │ ├── tools │ │ ├── index.js │ │ ├── DetailList.vue │ │ ├── Logo.vue │ │ ├── Breadcrumb.vue │ │ ├── HeadInfo.vue │ │ └── TwoStepCaptcha.vue │ ├── Menu │ │ ├── index.js │ │ └── SideMenu.vue │ ├── Result │ │ ├── index.js │ │ └── Result.vue │ ├── Trend │ │ ├── index.js │ │ ├── index.less │ │ ├── index.md │ │ └── Trend.vue │ ├── Ellipsis │ │ ├── index.js │ │ ├── index.md │ │ └── Ellipsis.vue │ ├── CountDown │ │ ├── index.js │ │ ├── index.md │ │ └── CountDown.vue │ ├── NoticeIcon │ │ └── index.js │ ├── PageHeader │ │ └── index.js │ ├── NumberInfo │ │ ├── index.js │ │ ├── index.md │ │ ├── NumberInfo.vue │ │ └── index.less │ ├── Exception │ │ ├── index.js │ │ └── type.js │ ├── GlobalFooter │ │ ├── index.js │ │ └── GlobalFooter.vue │ ├── GlobalHeader │ │ └── index.js │ ├── IconSelector │ │ ├── index.js │ │ ├── README.md │ │ └── IconSelector.vue │ ├── SettingDrawer │ │ ├── index.js │ │ ├── SettingItem.vue │ │ └── themeColor.js │ ├── DescriptionList │ │ └── index.js │ ├── AvatarList │ │ ├── index.js │ │ ├── Item.vue │ │ ├── index.less │ │ └── index.md │ ├── MultiTab │ │ ├── index.js │ │ └── index.less │ ├── StandardFormRow │ │ └── index.js │ ├── ArticleListContent │ │ ├── index.js │ │ └── ArticleListContent.vue │ ├── FooterToolbar │ │ ├── index.js │ │ ├── index.less │ │ ├── FooterToolBar.vue │ │ └── index.md │ ├── index.less │ ├── Charts │ │ ├── chart.less │ │ ├── smooth.area.less │ │ ├── MiniSmoothArea.vue │ │ ├── MiniBar.vue │ │ ├── MiniArea.vue │ │ ├── TransferBar.vue │ │ ├── Bar.vue │ │ ├── Liquid.vue │ │ ├── Radar.vue │ │ ├── MiniProgress.vue │ │ ├── RankList.vue │ │ └── Trend.vue │ ├── PageLoading │ │ └── index.jsx │ ├── _util │ │ └── util.js │ ├── TagSelect │ │ └── TagSelectOption.jsx │ ├── Editor │ │ ├── WangEditor.vue │ │ └── QuillEditor.vue │ └── index.js ├── views │ ├── app │ │ ├── README.md │ │ ├── admin │ │ │ ├── Test1.vue │ │ │ └── Test.vue │ │ └── layout │ │ │ └── chat │ │ │ ├── ChatFriendList.vue │ │ │ ├── ChatFriendListItem.vue │ │ │ └── AddChatFriends.vue │ ├── account │ │ ├── README.md │ │ └── components │ │ │ └── tenantChange.vue │ ├── shared │ │ ├── README.md │ │ └── exception │ │ │ ├── 403.vue │ │ │ ├── 404.vue │ │ │ └── 500.vue │ └── demo │ │ ├── account │ │ ├── center │ │ │ └── page │ │ │ │ ├── index.js │ │ │ │ └── Article.vue │ │ └── settings │ │ │ ├── Binding.vue │ │ │ ├── Notification.vue │ │ │ ├── Security.vue │ │ │ └── Custom.vue │ │ ├── dashboard │ │ └── Monitor.vue │ │ ├── list │ │ ├── search │ │ │ ├── components │ │ │ │ ├── IconText.vue │ │ │ │ └── CardInfo.vue │ │ │ └── SearchLayout.vue │ │ ├── QueryList.vue │ │ └── modules │ │ │ ├── CreateForm.vue │ │ │ └── TaskForm.vue │ │ ├── other │ │ ├── IconSelectorView.vue │ │ └── modules │ │ │ └── OrgModal.vue │ │ ├── user │ │ └── RegisterResult.vue │ │ ├── result │ │ └── Error.vue │ │ └── form │ │ └── stepForm │ │ ├── StepForm.vue │ │ └── Step3.vue ├── assets │ ├── logo.png │ ├── icons │ │ └── bx-analyse.svg │ └── logo.svg ├── abpZero │ ├── abp-vue-module │ │ ├── index.js │ │ ├── multi-tenancy │ │ │ └── abp-multi-tenancy.service.js │ │ ├── settings │ │ │ └── setting.service.js │ │ ├── utils │ │ │ └── utils.service.js │ │ ├── features │ │ │ └── feature-checker.service.js │ │ ├── log │ │ │ └── log.service.js │ │ ├── localization │ │ │ └── localization.service.js │ │ ├── notify │ │ │ └── notify.service.js │ │ ├── session │ │ │ └── abp-session.service.js │ │ ├── auth │ │ │ ├── permission-checker.service.js │ │ │ └── token.service.js │ │ └── message │ │ │ └── message.service.js │ ├── shared │ │ ├── service-proxies │ │ │ ├── ThemeSettingServiceProxy.js │ │ │ ├── SessionServiceProxy.js │ │ │ ├── AppNavigationServiceProxy.js │ │ │ ├── ChatServiceProxy.js │ │ │ ├── TokenAuthServiceProxy.js │ │ │ ├── UserServiceProxy.js │ │ │ └── FriendshipServiceProxy.js │ │ ├── AppConsts.js │ │ ├── helpers │ │ │ ├── HtmlHelper.js │ │ │ ├── SubdomainTenancyNameFinder.js │ │ │ ├── XmlHttpRequestHelper.js │ │ │ ├── SignalRHelper.js │ │ │ ├── UrlHelper.js │ │ │ └── DomHelper.js │ │ ├── common │ │ │ ├── localStorage.Service.js │ │ │ └── session │ │ │ │ └── app-session.service.js │ │ └── abpMixin.js │ ├── app │ │ └── shared │ │ │ └── common │ │ │ ├── chat │ │ │ └── ChatState.js │ │ │ └── auth │ │ │ └── app-auth.service.js │ ├── utils │ │ ├── file-download.service.js │ │ └── array-to-tree-converter.service.js │ └── appconfig.js ├── utils │ ├── permissions.js │ ├── dom │ │ └── index.js │ ├── filter.js │ ├── domUtil.js │ ├── axios.js │ ├── device.js │ ├── utils.less │ ├── helper │ │ └── permission.js │ ├── util.js │ ├── request.js │ └── mixin.js ├── layouts │ ├── BlankLayout.vue │ ├── index.js │ └── RouteView.vue ├── api │ ├── index.js │ ├── login.js │ └── manage.js ├── core │ ├── icons.js │ ├── lazy_use.js │ ├── use.js │ ├── directives │ │ └── action.js │ ├── bootstrap.js │ └── lazy_lib │ │ └── components_use.js ├── eleComponents │ ├── index.js │ └── lookup.vue ├── App.vue ├── router │ └── index.js ├── mock │ ├── index.js │ ├── util.js │ └── services │ │ └── auth.js ├── store │ ├── index.js │ ├── mutation-types.js │ ├── getters.js │ └── modules │ │ ├── appSession.js │ │ ├── chat.js │ │ └── tagsView.js ├── config │ └── defaultSettings.js ├── permission.js └── main.js ├── .gitattributes ├── .env ├── .env.preview ├── public ├── logo.png ├── user.png ├── static │ ├── element-ui │ │ └── theme-chalk │ │ │ └── fonts │ │ │ └── element-icons.woff │ ├── abp-web-resources │ │ ├── abp.freeze-ui.js │ │ ├── abp.notify.js │ │ └── abp.sweet-alert.js │ └── FreezeUI │ │ ├── freeze-ui.min.css │ │ └── freeze-ui.js └── index.html ├── tests └── unit │ └── .eslintrc.js ├── .prettierrc ├── .travis.yml ├── jsconfig.json ├── .gitignore ├── webstorm.config.js ├── babel.config.js ├── jest.config.js ├── docs ├── multi-tabs.md ├── add-page-loading-animate.md ├── webpack-bundle-analyzer.md └── load-on-demand.md ├── .editorconfig └── LICENSE /src/components/tools/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/app/README.md: -------------------------------------------------------------------------------- 1 | 存放项目各模块业务页面 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | public/* linguist-vendored -------------------------------------------------------------------------------- /src/views/account/README.md: -------------------------------------------------------------------------------- 1 | 存放程序登陆注册等鉴权相关页面 -------------------------------------------------------------------------------- /src/views/shared/README.md: -------------------------------------------------------------------------------- 1 | 存放全局通用业务组件及页面 2 | 例如 异常页 -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_PREVIEW=false 3 | VUE_APP_API_BASE_URL=/api -------------------------------------------------------------------------------- /.env.preview: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_PREVIEW=true 3 | VUE_APP_API_BASE_URL=/api -------------------------------------------------------------------------------- /src/components/Menu/index.js: -------------------------------------------------------------------------------- 1 | import SMenu from './menu' 2 | export default SMenu 3 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeiMeng/vue-ant-abpzero/HEAD/public/logo.png -------------------------------------------------------------------------------- /public/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeiMeng/vue-ant-abpzero/HEAD/public/user.png -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "semi": false, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeiMeng/vue-ant-abpzero/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/Result/index.js: -------------------------------------------------------------------------------- 1 | import Result from './Result.vue' 2 | export default Result 3 | -------------------------------------------------------------------------------- /src/components/Trend/index.js: -------------------------------------------------------------------------------- 1 | import Trend from './Trend.vue' 2 | 3 | export default Trend 4 | -------------------------------------------------------------------------------- /src/components/Ellipsis/index.js: -------------------------------------------------------------------------------- 1 | import Ellipsis from './Ellipsis' 2 | 3 | export default Ellipsis 4 | -------------------------------------------------------------------------------- /src/components/CountDown/index.js: -------------------------------------------------------------------------------- 1 | import CountDown from './CountDown' 2 | 3 | export default CountDown 4 | -------------------------------------------------------------------------------- /src/components/NoticeIcon/index.js: -------------------------------------------------------------------------------- 1 | import NoticeIcon from './NoticeIcon' 2 | export default NoticeIcon 3 | -------------------------------------------------------------------------------- /src/components/PageHeader/index.js: -------------------------------------------------------------------------------- 1 | import PageHeader from './PageHeader' 2 | export default PageHeader 3 | -------------------------------------------------------------------------------- /src/components/NumberInfo/index.js: -------------------------------------------------------------------------------- 1 | import NumberInfo from './NumberInfo' 2 | 3 | export default NumberInfo 4 | -------------------------------------------------------------------------------- /src/components/Exception/index.js: -------------------------------------------------------------------------------- 1 | import ExceptionPage from './ExceptionPage.vue' 2 | export default ExceptionPage 3 | -------------------------------------------------------------------------------- /src/components/GlobalFooter/index.js: -------------------------------------------------------------------------------- 1 | import GlobalFooter from './GlobalFooter' 2 | export default GlobalFooter 3 | -------------------------------------------------------------------------------- /src/components/GlobalHeader/index.js: -------------------------------------------------------------------------------- 1 | import GlobalHeader from './GlobalHeader' 2 | export default GlobalHeader 3 | -------------------------------------------------------------------------------- /src/components/IconSelector/index.js: -------------------------------------------------------------------------------- 1 | import IconSelector from './IconSelector' 2 | export default IconSelector 3 | -------------------------------------------------------------------------------- /src/components/SettingDrawer/index.js: -------------------------------------------------------------------------------- 1 | import SettingDrawer from './SettingDrawer' 2 | export default SettingDrawer 3 | -------------------------------------------------------------------------------- /src/components/DescriptionList/index.js: -------------------------------------------------------------------------------- 1 | import DescriptionList from './DescriptionList' 2 | export default DescriptionList 3 | -------------------------------------------------------------------------------- /src/components/AvatarList/index.js: -------------------------------------------------------------------------------- 1 | import AvatarList from './List' 2 | import './index.less' 3 | 4 | export default AvatarList 5 | -------------------------------------------------------------------------------- /src/components/MultiTab/index.js: -------------------------------------------------------------------------------- 1 | import MultiTab from './MultiTab' 2 | import './index.less' 3 | 4 | export default MultiTab 5 | -------------------------------------------------------------------------------- /src/components/StandardFormRow/index.js: -------------------------------------------------------------------------------- 1 | import StandardFormRow from './StandardFormRow' 2 | 3 | export default StandardFormRow 4 | -------------------------------------------------------------------------------- /src/components/ArticleListContent/index.js: -------------------------------------------------------------------------------- 1 | import ArticleListContent from './ArticleListContent' 2 | 3 | export default ArticleListContent 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10.15.0 4 | cache: yarn 5 | script: 6 | - yarn 7 | - yarn run lint --no-fix && yarn run build 8 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/index.js: -------------------------------------------------------------------------------- 1 | import FooterToolBar from './FooterToolBar' 2 | import './index.less' 3 | 4 | export default FooterToolBar 5 | -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/index.js: -------------------------------------------------------------------------------- 1 | export { AbpModule } from './abp.module'; 2 | export { AbpUserConfigurationService } from './abp-user-configuration.service'; 3 | -------------------------------------------------------------------------------- /public/static/element-ui/theme-chalk/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeiMeng/vue-ant-abpzero/HEAD/public/static/element-ui/theme-chalk/fonts/element-icons.woff -------------------------------------------------------------------------------- /src/components/tools/DetailList.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/utils/permissions.js: -------------------------------------------------------------------------------- 1 | export function actionToObject (json) { 2 | try { 3 | return JSON.parse(json) 4 | } catch (e) { 5 | console.log('err', e.message) 6 | } 7 | return [] 8 | } 9 | -------------------------------------------------------------------------------- /src/components/index.less: -------------------------------------------------------------------------------- 1 | @import "~ant-design-vue/lib/style/index"; 2 | 3 | // The prefix to use on all css classes from ant-pro. 4 | @ant-pro-prefix : ant-pro; 5 | @ant-global-header-zindex : 105; -------------------------------------------------------------------------------- /src/views/demo/account/center/page/index.js: -------------------------------------------------------------------------------- 1 | import AppPage from './App' 2 | import ArticlePage from './Article' 3 | import ProjectPage from './Project' 4 | 5 | export { AppPage, ArticlePage, ProjectPage } 6 | -------------------------------------------------------------------------------- /src/utils/dom/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author 刘鹏飞 3 | * @email creatworld2013@live.com 4 | * @create date 2018-02-27 05:29:23 5 | * @modify date 2018-02-27 05:29:23 6 | * @desc [description] 7 | */ 8 | import './dom' -------------------------------------------------------------------------------- /src/views/demo/dashboard/Monitor.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": ["src/*"] 7 | } 8 | }, 9 | "exclude": ["node_modules", "dist"], 10 | "include": ["src/**/*"] 11 | } 12 | -------------------------------------------------------------------------------- /src/layouts/BlankLayout.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 17 | -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/multi-tenancy/abp-multi-tenancy.service.js: -------------------------------------------------------------------------------- 1 | 2 | let AbpMultiTenancyService = class AbpMultiTenancyService { 3 | get isEnabled() { 4 | return abp.multiTenancy.isEnabled; 5 | } 6 | }; 7 | export { AbpMultiTenancyService }; 8 | -------------------------------------------------------------------------------- /src/components/Charts/chart.less: -------------------------------------------------------------------------------- 1 | .antv-chart-mini { 2 | position: relative; 3 | width: 100%; 4 | 5 | .chart-wrapper { 6 | position: absolute; 7 | bottom: -28px; 8 | width: 100%; 9 | 10 | /* margin: 0 -5px; 11 | overflow: hidden;*/ 12 | } 13 | } -------------------------------------------------------------------------------- /src/components/PageLoading/index.jsx: -------------------------------------------------------------------------------- 1 | import { Spin } from 'ant-design-vue' 2 | 3 | export default { 4 | name: 'PageLoading', 5 | render () { 6 | return (
7 | 8 |
) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/layouts/index.js: -------------------------------------------------------------------------------- 1 | import UserLayout from './UserLayout' 2 | import BlankLayout from './BlankLayout' 3 | import BasicLayout from './BasicLayout' 4 | import RouteView from './RouteView' 5 | import PageView from './PageView' 6 | 7 | export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView } 8 | -------------------------------------------------------------------------------- /src/views/shared/exception/403.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /src/views/shared/exception/404.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /src/views/shared/exception/500.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /src/abpZero/shared/service-proxies/ThemeSettingServiceProxy.js: -------------------------------------------------------------------------------- 1 | let ThemeSettingServiceProxy = class ThemeSettingServiceProxy { 2 | constructor() { 3 | } 4 | saveThemeSetting(theme){ 5 | return httpClient.post("/api/services/app/ThemeSetting/SettingTheme",theme) 6 | } 7 | }; 8 | export { ThemeSettingServiceProxy }; -------------------------------------------------------------------------------- /src/components/Charts/smooth.area.less: -------------------------------------------------------------------------------- 1 | @import "../index"; 2 | 3 | @smoothArea-prefix-cls: ~"@{ant-pro-prefix}-smooth-area"; 4 | 5 | .@{smoothArea-prefix-cls} { 6 | position: relative; 7 | width: 100%; 8 | 9 | .chart-wrapper { 10 | position: absolute; 11 | bottom: -28px; 12 | width: 100%; 13 | } 14 | } -------------------------------------------------------------------------------- /public/static/abp-web-resources/abp.freeze-ui.js: -------------------------------------------------------------------------------- 1 | var abp = abp || {}; 2 | (function () { 3 | 4 | abp.ui.setBusy = function (element, text) { 5 | FreezeUI({ element: element, text: text ? text : ' ' }); 6 | }; 7 | 8 | abp.ui.clearBusy = function (element) { 9 | UnFreezeUI({ element: element }); 10 | }; 11 | 12 | })(); 13 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | const api = { 2 | Login: '/auth/login', 3 | Logout: '/auth/logout', 4 | ForgePassword: '/auth/forge-password', 5 | Register: '/auth/register', 6 | twoStepCode: '/auth/2step-code', 7 | SendSms: '/account/sms', 8 | SendSmsErr: '/account/sms_err', 9 | // get my info 10 | UserInfo: '/user/info' 11 | } 12 | export default api 13 | -------------------------------------------------------------------------------- /src/core/icons.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom icon list 3 | * All icons are loaded here for easy management 4 | * @see https://vue.ant.design/components/icon/#Custom-Font-Icon 5 | * 6 | * 自定义图标加载表 7 | * 所有图标均从这里加载,方便管理 8 | */ 9 | import bxAnaalyse from '@/assets/icons/bx-analyse.svg?inline' // path to your '*.svg?inline' file. 10 | 11 | export { bxAnaalyse } 12 | -------------------------------------------------------------------------------- /webstorm.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | 4 | function resolve (dir) { 5 | return path.join(__dirname, '.', dir) 6 | } 7 | 8 | module.exports = { 9 | context: path.resolve(__dirname, './'), 10 | resolve: { 11 | extensions: ['.js', '.vue', '.json'], 12 | alias: { 13 | '@': resolve('src') 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/abpZero/app/shared/common/chat/ChatState.js: -------------------------------------------------------------------------------- 1 | class ChatSide {} 2 | ChatSide.Sender = 1 3 | ChatSide.Receiver = 2 4 | class ChatMessageReadState {} 5 | ChatMessageReadState.Unread = 1 6 | ChatMessageReadState.Read = 2 7 | class FriendshipState {} 8 | FriendshipState.Accepted = 1 9 | FriendshipState.Blocked = 2 10 | 11 | export {ChatSide, ChatMessageReadState,FriendshipState} -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/settings/setting.service.js: -------------------------------------------------------------------------------- 1 | let SettingService = class SettingService { 2 | get(name) { 3 | return abp.setting.get(name); 4 | } 5 | getBoolean(name) { 6 | return abp.setting.getBoolean(name); 7 | } 8 | getInt(name) { 9 | return abp.setting.getInt(name); 10 | } 11 | }; 12 | export { SettingService }; 13 | -------------------------------------------------------------------------------- /src/views/demo/account/settings/Binding.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /src/views/demo/account/settings/Notification.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/utils/utils.service.js: -------------------------------------------------------------------------------- 1 | let UtilsService = class UtilsService { 2 | getCookieValue(key) { 3 | return abp.utils.getCookieValue(key); 4 | } 5 | setCookieValue(key, value, expireDate, path) { 6 | abp.utils.setCookieValue(key, value, expireDate, path); 7 | } 8 | deleteCookie(key, path) { 9 | abp.utils.deleteCookie(key, path); 10 | } 11 | }; 12 | 13 | export { UtilsService }; 14 | -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/features/feature-checker.service.js: -------------------------------------------------------------------------------- 1 | 2 | let FeatureCheckerService = class FeatureCheckerService { 3 | get(featureName) { 4 | return abp.features.get(featureName); 5 | } 6 | getValue(featureName) { 7 | return abp.features.getValue(featureName); 8 | } 9 | isEnabled(featureName) { 10 | return abp.features.isEnabled(featureName); 11 | } 12 | }; 13 | 14 | export { FeatureCheckerService }; 15 | -------------------------------------------------------------------------------- /src/views/demo/list/search/components/IconText.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app', 4 | [ 5 | '@babel/preset-env', 6 | { 7 | 'useBuiltIns': 'entry' 8 | } 9 | ] 10 | ] 11 | // if your use import on Demand, Use this code 12 | // , 13 | // plugins: [ 14 | // [ 'import', { 15 | // 'libraryName': 'ant-design-vue', 16 | // 'libraryDirectory': 'es', 17 | // 'style': true // `style: true` 会加载 less 文件 18 | // } ] 19 | // ] 20 | } 21 | -------------------------------------------------------------------------------- /src/eleComponents/index.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | import sPagination from './sPagination' 7 | 8 | import crud from './crud' 9 | 10 | let items=[ 11 | sPagination, 12 | crud 13 | ] 14 | 15 | let alias=""; 16 | 17 | let components = { 18 | install: function(instance) { 19 | Object.keys(items).forEach(key => { 20 | let name = `${alias}${items[key].name}`; 21 | instance.component(name, items[key]); 22 | }); 23 | } 24 | }; 25 | Vue.use(components); -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/log/log.service.js: -------------------------------------------------------------------------------- 1 | 2 | let LogService = class LogService { 3 | debug(logObject) { 4 | abp.log.debug(logObject); 5 | } 6 | info(logObject) { 7 | abp.log.info(logObject); 8 | } 9 | warn(logObject) { 10 | abp.log.warn(logObject); 11 | } 12 | error(logObject) { 13 | abp.log.error(logObject); 14 | } 15 | fatal(logObject) { 16 | abp.log.fatal(logObject); 17 | } 18 | }; 19 | 20 | export { LogService }; 21 | -------------------------------------------------------------------------------- /src/abpZero/shared/AppConsts.js: -------------------------------------------------------------------------------- 1 | export class AppConsts {} 2 | AppConsts.tenancyNamePlaceHolderInUrl = '{TENANCY_NAME}' 3 | AppConsts.appBaseUrl=''; 4 | AppConsts.appBaseUrlFormat=''; 5 | AppConsts.localeMappings = [] 6 | AppConsts.userManagement = { 7 | defaultAdminUserName: 'admin' 8 | } 9 | AppConsts.localization = { 10 | defaultLocalizationSourceName: 'WuAi' 11 | } 12 | AppConsts.authorization = { 13 | encrptedAuthTokenName: 'enc_auth_token' 14 | } 15 | AppConsts.grid = { 16 | defaultPageSize: 10 17 | } 18 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/index.less: -------------------------------------------------------------------------------- 1 | @import "../index"; 2 | 3 | @footer-toolbar-prefix-cls: ~"@{ant-pro-prefix}-footer-toolbar"; 4 | 5 | .@{footer-toolbar-prefix-cls} { 6 | position: fixed; 7 | width: 100%; 8 | bottom: 0; 9 | right: 0; 10 | height: 56px; 11 | line-height: 56px; 12 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03); 13 | background: #fff; 14 | border-top: 1px solid #e8e8e8; 15 | padding: 0 24px; 16 | z-index: 9; 17 | 18 | &:after { 19 | content: ""; 20 | display: block; 21 | clear: both; 22 | } 23 | } -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/localization/localization.service.js: -------------------------------------------------------------------------------- 1 | 2 | let LocalizationService = class LocalizationService { 3 | get languages() { 4 | return abp.localization.languages; 5 | } 6 | get currentLanguage() { 7 | return abp.localization.currentLanguage; 8 | } 9 | localize(key, sourceName) { 10 | return abp.localization.localize(key, sourceName); 11 | } 12 | getSource(sourceName) { 13 | return abp.localization.getSource(sourceName); 14 | } 15 | }; 16 | 17 | export { LocalizationService }; 18 | -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/notify/notify.service.js: -------------------------------------------------------------------------------- 1 | 2 | let NotifyService = class NotifyService { 3 | info(message, title, options) { 4 | abp.notify.info(message, title, options); 5 | } 6 | success(message, title, options) { 7 | abp.notify.success(message, title, options); 8 | } 9 | warn(message, title, options) { 10 | abp.notify.warn(message, title, options); 11 | } 12 | error(message, title, options) { 13 | abp.notify.error(message, title, options); 14 | } 15 | }; 16 | export { NotifyService }; 17 | -------------------------------------------------------------------------------- /src/components/Exception/type.js: -------------------------------------------------------------------------------- 1 | const types = { 2 | 403: { 3 | img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg', 4 | title: '403', 5 | desc: '抱歉,你无权访问该页面' 6 | }, 7 | 404: { 8 | img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg', 9 | title: '404', 10 | desc: '抱歉,你访问的页面不存在或仍在开发中' 11 | }, 12 | 500: { 13 | img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg', 14 | title: '500', 15 | desc: '抱歉,服务器出错了' 16 | } 17 | } 18 | 19 | export default types 20 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 25 | 30 | -------------------------------------------------------------------------------- /src/core/lazy_use.js: -------------------------------------------------------------------------------- 1 | //import Vue from 'vue' 2 | import VueStorage from 'vue-ls' 3 | import config from '@/config/defaultSettings' 4 | 5 | // base library 6 | import '@/core/lazy_lib/components_use' 7 | import Viser from 'viser-vue' 8 | 9 | // ext library 10 | import VueClipboard from 'vue-clipboard2' 11 | import PermissionHelper from '@/utils/helper/permission' 12 | import './directives/action' 13 | 14 | VueClipboard.config.autoSetContainer = true 15 | 16 | Vue.use(Viser) 17 | 18 | Vue.use(VueStorage, config.storageOptions) 19 | Vue.use(VueClipboard) 20 | Vue.use(PermissionHelper) 21 | -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/session/abp-session.service.js: -------------------------------------------------------------------------------- 1 | 2 | let AbpSessionService = class AbpSessionService { 3 | get userId() { 4 | return abp.session.userId; 5 | } 6 | get tenantId() { 7 | return abp.session.tenantId; 8 | } 9 | get impersonatorUserId() { 10 | return abp.session.impersonatorUserId; 11 | } 12 | get impersonatorTenantId() { 13 | return abp.session.impersonatorTenantId; 14 | } 15 | get multiTenancySide() { 16 | return abp.session.multiTenancySide; 17 | } 18 | }; 19 | export { AbpSessionService }; 20 | -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/auth/permission-checker.service.js: -------------------------------------------------------------------------------- 1 | 2 | let PermissionCheckerService = class PermissionCheckerService { 3 | isGranted(permissionName) { 4 | return abp.auth.isGranted(permissionName); 5 | } 6 | isGrantedAny(...permissions) { 7 | if (!permissions) { 8 | return false; 9 | } 10 | for (const permission of permissions) { 11 | if (this.isGranted(permission)) { 12 | return true; 13 | } 14 | } 15 | return false; 16 | } 17 | }; 18 | 19 | export { PermissionCheckerService }; 20 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | //import Vue from 'vue' 2 | //import Router from 'vue-router' 3 | import { constantRouterMap } from '@/config/router.config' 4 | 5 | //Vue.use(Router) 6 | 7 | 8 | //解决message: "Navigating to current location ("/homePage") is not allowed",警告的问题 9 | const VueRouterPush = VueRouter.prototype.push 10 | VueRouter.prototype.push = function push (to) { 11 | return VueRouterPush.call(this, to).catch(err => err) 12 | } 13 | 14 | export default new VueRouter({ 15 | mode: 'history', 16 | base: process.env.BASE_URL, 17 | scrollBehavior: () => ({ y: 0 }), 18 | routes: constantRouterMap 19 | }) 20 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: [ 3 | 'js', 4 | 'jsx', 5 | 'json', 6 | 'vue' 7 | ], 8 | transform: { 9 | '^.+\\.vue$': 'vue-jest', 10 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', 11 | '^.+\\.jsx?$': 'babel-jest' 12 | }, 13 | moduleNameMapper: { 14 | '^@/(.*)$': '/src/$1' 15 | }, 16 | snapshotSerializers: [ 17 | 'jest-serializer-vue' 18 | ], 19 | testMatch: [ 20 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 21 | ], 22 | testURL: 'http://localhost/' 23 | } 24 | -------------------------------------------------------------------------------- /src/mock/index.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs2' 2 | 3 | // 判断环境不是 prod 或者 preview 是 true 时,加载 mock 服务 4 | if (process.env.NODE_ENV !== 'production' || process.env.VUE_APP_PREVIEW === 'true') { 5 | // 使用同步加载依赖 6 | // 防止 vuex 中的 GetInfo 早于 mock 运行,导致无法 mock 请求返回结果 7 | console.log('mock mounting') 8 | require('./services/auth') 9 | require('./services/user') 10 | require('./services/manage') 11 | require('./services/other') 12 | require('./services/tagCloud') 13 | require('./services/article') 14 | 15 | Mock.setup({ 16 | timeout: 800 // setter delay time 17 | }) 18 | console.log('mock mounted') 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/filter.js: -------------------------------------------------------------------------------- 1 | //import Vue from 'vue' 2 | import moment from 'moment' 3 | import 'moment/locale/zh-cn' 4 | moment.locale('zh-cn') 5 | 6 | Vue.filter('NumberFormat', function (value) { 7 | if (!value) { 8 | return '0' 9 | } 10 | const intPartFormat = value.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') // 将整数部分逢三一断 11 | return intPartFormat 12 | }) 13 | 14 | Vue.filter('dayjs', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') { 15 | return moment(dataStr).format(pattern) 16 | }) 17 | 18 | Vue.filter('moment', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') { 19 | return moment(dataStr).format(pattern) 20 | }) 21 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/FooterToolBar.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /docs/multi-tabs.md: -------------------------------------------------------------------------------- 1 | 多(页签)标签 模式 2 | ==== 3 | 4 | 5 | ## 让框架支持打开的页面增加多标签,可随时切换 6 | 7 | ### 关于如何移除该功能 组件 8 | 1. 移除 `/src/layouts/BasicLayout.vue` L44, L69, L80 9 | ```vue 10 | // L44 11 | 12 | 13 | // L69 14 | import MultiTab from '@/components/MultiTab' 15 | 16 | // L80 17 | MultiTab, 18 | ``` 19 | 2. 移除 `/src/config/defaultSettings.js` L25 20 | 21 | 3. 移除 `src/store/modules/app.js` L27, L76-L79, L118-L120 22 | 23 | 4. 移除 `src/utils/mixin.js` L21 24 | 25 | 5. 删除组件目录 `src/components/MultiTab` 26 | 27 | > 以上 `L x` 均代表行N ,如 L3 = 行3 28 | 29 | -------------------------------------------------------------------------------- /src/components/MultiTab/index.less: -------------------------------------------------------------------------------- 1 | @import '../index'; 2 | 3 | @multi-tab-prefix-cls: ~"@{ant-pro-prefix}-multi-tab"; 4 | @multi-tab-wrapper-prefix-cls: ~"@{ant-pro-prefix}-multi-tab-wrapper"; 5 | 6 | /* 7 | .topmenu .@{multi-tab-prefix-cls} { 8 | max-width: 1200px; 9 | margin: -23px auto 24px auto; 10 | } 11 | */ 12 | .@{multi-tab-prefix-cls} { 13 | margin: -23px -24px 24px -24px; 14 | background: #fff; 15 | } 16 | 17 | .topmenu .@{multi-tab-wrapper-prefix-cls} { 18 | max-width: 1200px; 19 | margin: 0 auto; 20 | } 21 | 22 | .topmenu.content-width-Fluid .@{multi-tab-wrapper-prefix-cls} { 23 | max-width: 100%; 24 | margin: 0 auto; 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/domUtil.js: -------------------------------------------------------------------------------- 1 | export const setDocumentTitle = function (title) { 2 | document.title = title 3 | const ua = navigator.userAgent 4 | // eslint-disable-next-line 5 | const regex = /\bMicroMessenger\/([\d\.]+)/ 6 | if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) { 7 | const i = document.createElement('iframe') 8 | i.src = '/favicon.ico' 9 | i.style.display = 'none' 10 | i.onload = function () { 11 | setTimeout(function () { 12 | i.remove() 13 | }, 9) 14 | } 15 | document.body.appendChild(i) 16 | } 17 | } 18 | import config from '@/config/defaultSettings' 19 | export const domTitle = config.appTitle 20 | -------------------------------------------------------------------------------- /src/components/tools/Logo.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 32 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | //import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import app from './modules/app' 5 | import user from './modules/user' 6 | import permission from './modules/permission' 7 | import appSession from './modules/appSession' 8 | import tabView from './modules/tagsView' 9 | import chat from './modules/chat' 10 | import getters from './getters' 11 | 12 | Vue.use(Vuex) 13 | 14 | export default new Vuex.Store({ 15 | modules: { 16 | app, 17 | user, 18 | permission, 19 | appSession, 20 | tabView, 21 | chat 22 | }, 23 | state: { 24 | 25 | }, 26 | mutations: { 27 | 28 | }, 29 | actions: { 30 | 31 | }, 32 | getters 33 | }) 34 | -------------------------------------------------------------------------------- /src/abpZero/utils/file-download.service.js: -------------------------------------------------------------------------------- 1 | 2 | import appconfig from '@/abpZero/appconfig' 3 | let FileDownloadService = class FileDownloadService { 4 | downloadTempFile(file) { 5 | const url = appconfig.remoteServiceBaseUrl + '/File/DownloadTempFile?fileType=' + file.fileType + '&fileToken=' + file.fileToken + '&fileName=' + file.fileName; 6 | location.href = url; //TODO: This causes reloading of same page in Firefox 7 | } 8 | downloadUploadFile(fileId) { 9 | const url = appconfig.remoteServiceBaseUrl + '/File/DownLoad?id=' + fileId; 10 | location.href = url; //TODO: This causes reloading of same page in Firefox 11 | } 12 | }; 13 | export { FileDownloadService }; 14 | -------------------------------------------------------------------------------- /src/components/CountDown/index.md: -------------------------------------------------------------------------------- 1 | # CountDown 倒计时 2 | 3 | 倒计时组件。 4 | 5 | 6 | 7 | 引用方式: 8 | 9 | ```javascript 10 | import CountDown from '@/components/CountDown/CountDown' 11 | 12 | export default { 13 | components: { 14 | CountDown 15 | } 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## 代码演示 [demo](https://pro.loacg.com/test/home) 22 | 23 | ```html 24 | 25 | ``` 26 | 27 | 28 | 29 | ## API 30 | 31 | | 参数 | 说明 | 类型 | 默认值 | 32 | |----------|------------------------------------------|-------------|-------| 33 | | target | 目标时间 | Date | - | 34 | | onEnd | 倒计时结束回调 | funtion | -| 35 | -------------------------------------------------------------------------------- /src/abpZero/shared/helpers/HtmlHelper.js: -------------------------------------------------------------------------------- 1 | export class HtmlHelper { 2 | static encodeText(value) { 3 | let div = document.createElement('div'); 4 | div[('textContent' in div) ? 'textContent' : 'innerText'] = value; 5 | return div.innerHTML; 6 | } 7 | static decodeText(value) { 8 | let div = document.createElement('div'); 9 | div.innerHTML = value; 10 | return ('textContent' in div) ? div.textContent : div.innerText; 11 | } 12 | static encodeJson(jsonObject) { 13 | return JSON.parse(this.encodeText(JSON.stringify(jsonObject))); 14 | } 15 | static decodeJson(jsonObject) { 16 | return JSON.parse(this.decodeText(JSON.stringify(jsonObject))); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/abpZero/shared/service-proxies/SessionServiceProxy.js: -------------------------------------------------------------------------------- 1 | let SessionServiceProxy = class SessionServiceProxy { 2 | constructor() { 3 | } 4 | /** 5 | * @return Success 6 | */ 7 | getCurrentLoginInformations() { 8 | let url_ ="/api/services/app/Session/GetCurrentLoginInformations"; 9 | url_ = url_.replace(/[?&]$/, ""); 10 | //return httpClient.post(url_) //6.3 11 | return httpClient.get(url_) //6.4+ 12 | } 13 | 14 | /** 15 | * @return Success 16 | */ 17 | updateUserSignInToken() { 18 | let url_ = "/api/services/app/Session/UpdateUserSignInToken"; 19 | url_ = url_.replace(/[?&]$/, ""); 20 | 21 | } 22 | 23 | }; 24 | export { SessionServiceProxy }; -------------------------------------------------------------------------------- /docs/add-page-loading-animate.md: -------------------------------------------------------------------------------- 1 | 为首屏增加 加载动画 2 | ==== 3 | 4 | 5 | 6 | ## 需求 7 | 8 | > 为了缓解用户第一次访问时,加载 JS 过大所导致用户等待白屏时间过长导致的用户体验不好,进行的一个优化动效。 9 | 10 | 11 | 12 | ## 实现方案 13 | 14 | 1. 将 动画加载 dom 元素放在 #app 内,Vue 生命周期开始时,会自动清掉 #app 下的所有元素。 15 | 2. 将 动画加载 dom 元素放在 body 下,Vue 生命周期开始时 App.vue (created, mounted) 调用 `@/utils/utll` 下的 removeLoadingAnimate(#id, timeout) 则会移除加载动画 16 | 17 | 最后一步: 18 | ​ 将样式插入到 `public/index.html` 文件的 `` 最好写成内联 `` 19 | 20 | 21 | 22 | ---- 23 | 24 | 目前提供有两个样式,均在 `public/loading` 文件夹内。且 pro 已经默认使用了一套 loading 动画方案,可以直接参考 `public/index.html` 25 | 26 | 27 | ## 写在最后 28 | 29 | 目前 pro 有页面 overflow 显示出浏览器滚动条时,页面会抖动一下的问题。 30 | 31 | 欢迎各位提供能解决的方案和实现 demo。如果在条件允许的情况下,建议请直接使用 pro 进行改造,也欢迎直接 PR 到 pro 的仓库 32 | -------------------------------------------------------------------------------- /src/abpZero/shared/service-proxies/AppNavigationServiceProxy.js: -------------------------------------------------------------------------------- 1 | let AppNavigationServiceProxy = class AppNavigationServiceProxy { 2 | constructor() { 3 | } 4 | getMenuTree() { 5 | return httpClient.get("/api/services/app/Menu/GetMenuTree") 6 | } 7 | getMenuById(id){ 8 | return httpClient.get("/api/services/app/Menu/GetMenuForEdit",{ 9 | params:{id:id} 10 | }) 11 | } 12 | saveMenu(menu){ 13 | return httpClient.post("/api/services/app/Menu/CreateOrUpdateMenuForOutput",menu) 14 | } 15 | delMenu(id){ 16 | return httpClient.delete("/api/services/app/Menu/DeleteMenu",{ 17 | params: {id:id} 18 | }) 19 | } 20 | }; 21 | export { AppNavigationServiceProxy }; -------------------------------------------------------------------------------- /src/store/mutation-types.js: -------------------------------------------------------------------------------- 1 | export const ACCESS_TOKEN = 'Access-Token' 2 | export const SIDEBAR_TYPE = 'SIDEBAR_TYPE' 3 | export const DEFAULT_THEME = 'DEFAULT_THEME' 4 | export const DEFAULT_LAYOUT_MODE = 'DEFAULT_LAYOUT_MODE' 5 | export const DEFAULT_COLOR = 'DEFAULT_COLOR' 6 | export const DEFAULT_COLOR_WEAK = 'DEFAULT_COLOR_WEAK' 7 | export const DEFAULT_FIXED_HEADER = 'DEFAULT_FIXED_HEADER' 8 | export const DEFAULT_FIXED_SIDEMENU = 'DEFAULT_FIXED_SIDEMENU' 9 | export const DEFAULT_FIXED_HEADER_HIDDEN = 'DEFAULT_FIXED_HEADER_HIDDEN' 10 | export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE' 11 | export const DEFAULT_MULTI_TAB = 'DEFAULT_MULTI_TAB' 12 | 13 | export const CONTENT_WIDTH_TYPE = { 14 | Fluid: 'Fluid', 15 | Fixed: 'Fixed' 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/axios.js: -------------------------------------------------------------------------------- 1 | const VueAxios = { 2 | vm: {}, 3 | // eslint-disable-next-line no-unused-vars 4 | install (Vue, instance) { 5 | if (this.installed) { 6 | return 7 | } 8 | this.installed = true 9 | 10 | if (!instance) { 11 | // eslint-disable-next-line no-console 12 | console.error('You have to install axios') 13 | return 14 | } 15 | 16 | Vue.axios = instance 17 | 18 | Object.defineProperties(Vue.prototype, { 19 | axios: { 20 | get: function get () { 21 | return instance 22 | } 23 | }, 24 | $http: { 25 | get: function get () { 26 | return instance 27 | } 28 | } 29 | }) 30 | } 31 | } 32 | 33 | export { 34 | VueAxios 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Trend/index.less: -------------------------------------------------------------------------------- 1 | @import "../index"; 2 | 3 | @trend-prefix-cls: ~"@{ant-pro-prefix}-trend"; 4 | 5 | .@{trend-prefix-cls} { 6 | display: inline-block; 7 | font-size: @font-size-base; 8 | line-height: 22px; 9 | 10 | .up, 11 | .down { 12 | margin-left: 4px; 13 | position: relative; 14 | top: 1px; 15 | 16 | i { 17 | font-size: 12px; 18 | transform: scale(0.83); 19 | } 20 | } 21 | 22 | .item-text { 23 | display: inline-block; 24 | margin-left: 8px; 25 | color: rgba(0,0,0,.85); 26 | } 27 | 28 | .up { 29 | color: @red-6; 30 | } 31 | .down { 32 | color: @green-6; 33 | top: -1px; 34 | } 35 | 36 | &.reverse-color .up { 37 | color: @green-6; 38 | } 39 | &.reverse-color .down { 40 | color: @red-6; 41 | } 42 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset=utf-8 3 | end_of_line=lf 4 | insert_final_newline=false 5 | indent_style=space 6 | indent_size=2 7 | 8 | [{*.ng,*.sht,*.html,*.shtm,*.shtml,*.htm}] 9 | indent_style=space 10 | indent_size=2 11 | 12 | [{*.jhm,*.xslt,*.xul,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.jrxml,*.xml,*.jnlp,*.wsdl}] 13 | indent_style=space 14 | indent_size=2 15 | 16 | [{.babelrc,.stylelintrc,jest.config,.eslintrc,.prettierrc,*.json,*.jsb3,*.jsb2,*.bowerrc}] 17 | indent_style=space 18 | indent_size=2 19 | 20 | [*.svg] 21 | indent_style=space 22 | indent_size=2 23 | 24 | [*.js.map] 25 | indent_style=space 26 | indent_size=2 27 | 28 | [*.less] 29 | indent_style=space 30 | indent_size=2 31 | 32 | [*.vue] 33 | indent_style=space 34 | indent_size=2 35 | 36 | [{.analysis_options,*.yml,*.yaml}] 37 | indent_style=space 38 | indent_size=2 39 | 40 | -------------------------------------------------------------------------------- /src/components/SettingDrawer/SettingItem.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | 25 | 39 | -------------------------------------------------------------------------------- /src/layouts/RouteView.vue: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /src/components/Ellipsis/index.md: -------------------------------------------------------------------------------- 1 | # Ellipsis 文本自动省略号 2 | 3 | 文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。 4 | 5 | 6 | 7 | 引用方式: 8 | 9 | ```javascript 10 | import Ellipsis from '@/components/Ellipsis' 11 | 12 | export default { 13 | components: { 14 | Ellipsis 15 | } 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## 代码演示 [demo](https://pro.loacg.com/test/home) 22 | 23 | ```html 24 | 25 | There were injuries alleged in three cases in 2015, and a 26 | fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall. 27 | 28 | ``` 29 | 30 | 31 | 32 | ## API 33 | 34 | 35 | 参数 | 说明 | 类型 | 默认值 36 | ----|------|-----|------ 37 | tooltip | 移动到文本展示完整内容的提示 | boolean | - 38 | length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | - -------------------------------------------------------------------------------- /src/abpZero/shared/helpers/SubdomainTenancyNameFinder.js: -------------------------------------------------------------------------------- 1 | import { AppConsts } from '@/abpZero/shared/AppConsts'; 2 | import { FormattedStringValueExtracter } from '@/abpZero/shared/helpers/FormattedStringValueExtracter'; 3 | export class SubdomainTenancyNameFinder { 4 | getCurrentTenancyNameOrNull(rootAddress) { 5 | if (rootAddress.indexOf(AppConsts.tenancyNamePlaceHolderInUrl) < 0) { 6 | // Web site does not support subdomain tenant name 7 | return null; 8 | } 9 | const currentRootAddress = document.location.href; 10 | const formattedStringValueExtracter = new FormattedStringValueExtracter(); 11 | const values = formattedStringValueExtracter.IsMatch(currentRootAddress, rootAddress); 12 | if (!values.length) { 13 | return null; 14 | } 15 | return values[0]; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Trend/index.md: -------------------------------------------------------------------------------- 1 | # Trend 趋势标记 2 | 3 | 趋势符号,标记上升和下降趋势。通常用绿色代表“好”,红色代表“不好”,股票涨跌场景除外。 4 | 5 | 6 | 7 | 引用方式: 8 | 9 | ```javascript 10 | import Trend from '@/components/Trend' 11 | 12 | export default { 13 | components: { 14 | Trend 15 | } 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## 代码演示 [demo](https://pro.loacg.com/test/home) 22 | 23 | ```html 24 | 5% 25 | ``` 26 | 或 27 | ```html 28 | 29 | 工资 30 | 5% 31 | 32 | ``` 33 | 或 34 | ```html 35 | 5% 36 | ``` 37 | 38 | 39 | ## API 40 | 41 | | 参数 | 说明 | 类型 | 默认值 | 42 | |----------|------------------------------------------|-------------|-------| 43 | | flag | 上升下降标识:`up|down` | string | - | 44 | | reverseColor | 颜色反转 | Boolean | false | 45 | 46 | -------------------------------------------------------------------------------- /src/components/SettingDrawer/themeColor.js: -------------------------------------------------------------------------------- 1 | import client from 'webpack-theme-color-replacer/client' 2 | import generate from '@ant-design/colors/lib/generate' 3 | 4 | export default { 5 | getAntdSerials (color) { 6 | // 淡化(即less的tint) 7 | const lightens = new Array(9).fill().map((t, i) => { 8 | return client.varyColor.lighten(color, i / 10) 9 | }) 10 | // colorPalette变换得到颜色值 11 | const colorPalettes = generate(color) 12 | return lightens.concat(colorPalettes) 13 | }, 14 | changeColor (newColor) { 15 | var options = { 16 | newColors: this.getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors` 17 | changeUrl (cssUrl) { 18 | return `/${cssUrl}` // while router is not `hash` mode, it needs absolute path 19 | } 20 | } 21 | return client.changer.changeColor(options, Promise) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/auth/token.service.js: -------------------------------------------------------------------------------- 1 | 2 | let TokenService = class TokenService { 3 | getToken() { 4 | return abp.auth.getToken(); 5 | } 6 | getTokenCookieName() { 7 | return abp.auth.tokenCookieName; 8 | } 9 | clearToken() { 10 | abp.auth.clearToken(); 11 | } 12 | setToken(authToken, expireDate) { 13 | abp.auth.setToken(authToken, expireDate); 14 | } 15 | //refresh token 16 | getRefreshToken() { 17 | return abp.auth.getRefreshToken(); 18 | }; 19 | getRefreshTokenCookieName() { 20 | return abp.auth.refreshTokenCookieName; 21 | }; 22 | clearRefreshToken() { 23 | abp.auth.clearRefreshToken(); 24 | }; 25 | setRefreshToken(refreshToken, expireDate) { 26 | abp.auth.setRefreshToken(refreshToken, expireDate); 27 | }; 28 | }; 29 | 30 | export { TokenService }; 31 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/index.md: -------------------------------------------------------------------------------- 1 | # FooterToolbar 底部工具栏 2 | 3 | 固定在底部的工具栏。 4 | 5 | 6 | 7 | ## 何时使用 8 | 9 | 固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。 10 | 11 | 12 | 13 | 引用方式: 14 | 15 | ```javascript 16 | import FooterToolBar from '@/components/FooterToolbar' 17 | 18 | export default { 19 | components: { 20 | FooterToolBar 21 | } 22 | } 23 | ``` 24 | 25 | 26 | 27 | ## 代码演示 28 | 29 | ```html 30 | 31 | 提交 32 | 33 | ``` 34 | 或 35 | ```html 36 | 37 | 提交 38 | 39 | ``` 40 | 41 | 42 | ## API 43 | 44 | 参数 | 说明 | 类型 | 默认值 45 | ----|------|-----|------ 46 | children (slot) | 工具栏内容,向右对齐 | - | - 47 | extra | 额外信息,向左对齐 | String, Object | - 48 | 49 | -------------------------------------------------------------------------------- /src/components/Trend/Trend.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 38 | 39 | 42 | -------------------------------------------------------------------------------- /src/views/app/admin/Test1.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 35 | 36 | -------------------------------------------------------------------------------- /src/utils/device.js: -------------------------------------------------------------------------------- 1 | import enquireJs from 'enquire.js' 2 | 3 | export const DEVICE_TYPE = { 4 | DESKTOP: 'desktop', 5 | TABLET: 'tablet', 6 | MOBILE: 'mobile' 7 | } 8 | 9 | export const deviceEnquire = function (callback) { 10 | const matchDesktop = { 11 | match: () => { 12 | callback && callback(DEVICE_TYPE.DESKTOP) 13 | } 14 | } 15 | 16 | const matchLablet = { 17 | match: () => { 18 | callback && callback(DEVICE_TYPE.TABLET) 19 | } 20 | } 21 | 22 | const matchMobile = { 23 | match: () => { 24 | callback && callback(DEVICE_TYPE.MOBILE) 25 | } 26 | } 27 | 28 | // screen and (max-width: 1087.99px) 29 | enquireJs 30 | .register('screen and (max-width: 576px)', matchMobile) 31 | .register('screen and (min-width: 576px) and (max-width: 1199px)', matchLablet) 32 | .register('screen and (min-width: 1200px)', matchDesktop) 33 | } 34 | -------------------------------------------------------------------------------- /src/components/NumberInfo/index.md: -------------------------------------------------------------------------------- 1 | # NumberInfo 数据文本 2 | 3 | 常用在数据卡片中,用于突出展示某个业务数据。 4 | 5 | 6 | 7 | 引用方式: 8 | 9 | ```javascript 10 | import NumberInfo from '@/components/NumberInfo' 11 | 12 | export default { 13 | components: { 14 | NumberInfo 15 | } 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## 代码演示 [demo](https://pro.loacg.com/test/home) 22 | 23 | ```html 24 | 29 | ``` 30 | 31 | 32 | 33 | ## API 34 | 35 | 参数 | 说明 | 类型 | 默认值 36 | ----|------|-----|------ 37 | title | 标题 | ReactNode\|string | - 38 | subTitle | 子标题 | ReactNode\|string | - 39 | total | 总量 | ReactNode\|string | - 40 | subTotal | 子总量 | ReactNode\|string | - 41 | status | 增加状态 | 'up \| down' | - 42 | theme | 状态样式 | string | 'light' 43 | gap | 设置数字和描述之间的间距(像素)| number | 8 44 | -------------------------------------------------------------------------------- /docs/webpack-bundle-analyzer.md: -------------------------------------------------------------------------------- 1 | 先增加依赖 2 | 3 | ```bash 4 | // npm 5 | $ npm install --save-dev webpack-bundle-analyzer 6 | 7 | // or yarn 8 | $ yarn add webpack-bundle-analyzer -D 9 | ``` 10 | 11 | 配置文件 `vue.config.js` 增加 `configureWebpack.plugins` 参数 12 | 13 | ``` 14 | const path = require('path') 15 | const webpack = require('webpack') 16 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 17 | 18 | function resolve (dir) { 19 | return path.join(__dirname, dir) 20 | } 21 | 22 | // vue.config.js 23 | module.exports = { 24 | configureWebpack: { 25 | plugins: [ 26 | // Ignore all locale files of moment.js 27 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), 28 | // 依赖大小分析工具 29 | new BundleAnalyzerPlugin(), 30 | ] 31 | }, 32 | 33 | 34 | ... 35 | } 36 | ``` 37 | 38 | 39 | 40 | 启动 `cli` 的 `build` 命令进行项目编译,编译完成时,会自动运行一个 http://localhost:8888 的地址,完整显示了支持库依赖 -------------------------------------------------------------------------------- /src/components/IconSelector/README.md: -------------------------------------------------------------------------------- 1 | IconSelector 2 | ==== 3 | 4 | > 图标选择组件,常用于为某一个数据设定一个图标时使用 5 | > eg: 设定菜单列表时,为每个菜单设定一个图标 6 | 7 | 该组件由 [@Saraka](https://github.com/saraka-tsukai) 封装 8 | 9 | 10 | 11 | ### 使用方式 12 | 13 | ```vue 14 | 19 | 20 | 39 | ``` 40 | 41 | 42 | 43 | ### 事件 44 | 45 | 46 | | 名称 | 说明 | 类型 | 默认值 | 47 | | ------ | -------------------------- | ------ | ------ | 48 | | change | 当改变了 `icon` 选中项触发 | String | - | 49 | -------------------------------------------------------------------------------- /src/abpZero/abp-vue-module/message/message.service.js: -------------------------------------------------------------------------------- 1 | 2 | let MessageService = class MessageService { 3 | info(message, title, isHtml) { 4 | return abp.message.info(message, title, isHtml); 5 | } 6 | success(message, title, isHtml) { 7 | return abp.message.success(message, title, isHtml); 8 | } 9 | warn(message, title, isHtml) { 10 | return abp.message.warn(message, title, isHtml); 11 | } 12 | error(message, title, isHtml) { 13 | return abp.message.error(message, title, isHtml); 14 | } 15 | confirm(message, titleOrCallBack, callback, isHtml) { 16 | if (typeof titleOrCallBack == 'string') { 17 | return abp.message.confirm(message, titleOrCallBack, callback, isHtml); 18 | } 19 | else { 20 | return abp.message.confirm(message, undefined, titleOrCallBack, isHtml); 21 | } 22 | } 23 | }; 24 | 25 | export { MessageService }; 26 | -------------------------------------------------------------------------------- /src/core/use.js: -------------------------------------------------------------------------------- 1 | //import Vue from 'vue' 2 | import VueStorage from 'vue-ls' 3 | import config from '@/config/defaultSettings' 4 | 5 | // base library 6 | import Antd from 'ant-design-vue' 7 | import Viser from 'viser-vue' 8 | import VueCropper from 'vue-cropper' 9 | import 'ant-design-vue/dist/antd.less' 10 | 11 | 12 | // import Element from 'element-ui'; 13 | // import 'element-ui/lib/theme-chalk/index.css'; 14 | Vue.use(ELEMENT, { size: 'small', zIndex: 2000 }); 15 | 16 | // ext library 17 | import VueClipboard from 'vue-clipboard2' 18 | import PermissionHelper from '@/utils/helper/permission' 19 | // import '@/components/use' 20 | import './directives/action' 21 | 22 | VueClipboard.config.autoSetContainer = true 23 | 24 | Vue.use(Antd) 25 | Vue.use(Viser) 26 | 27 | Vue.use(VueStorage, config.storageOptions) 28 | Vue.use(VueClipboard) 29 | Vue.use(PermissionHelper) 30 | Vue.use(VueCropper) 31 | 32 | import '../eleComponents/index' 33 | -------------------------------------------------------------------------------- /src/abpZero/shared/common/localStorage.Service.js: -------------------------------------------------------------------------------- 1 | import * as localForage from 'localforage'; 2 | localForage.setDriver(localForage.LOCALSTORAGE); //强制使用localStorage存储。todo 目前在发布环境,默认使用IndexedDB存储失效,开发环境OK 3 | let LocalStorageService = class LocalStorageService { 4 | constructor() { 5 | 6 | } 7 | getItem(key,callback){ 8 | if (!localForage) { 9 | return; 10 | } 11 | 12 | localForage.getItem(key, callback); 13 | } 14 | 15 | 16 | setItem(key, value, callback){ 17 | if (!localForage) { 18 | return; 19 | } 20 | 21 | if (value === null) { 22 | value = undefined; 23 | } 24 | localForage.setItem(key, value, callback); 25 | } 26 | 27 | removeItem(key, callback){ 28 | if (!localForage) { 29 | return; 30 | } 31 | 32 | localForage.removeItem(key, callback); 33 | } 34 | 35 | }; 36 | export { LocalStorageService }; 37 | -------------------------------------------------------------------------------- /src/components/Charts/MiniSmoothArea.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /src/components/AvatarList/Item.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 47 | -------------------------------------------------------------------------------- /src/utils/utils.less: -------------------------------------------------------------------------------- 1 | .textOverflow() { 2 | overflow: hidden; 3 | white-space: nowrap; 4 | text-overflow: ellipsis; 5 | word-break: break-all; 6 | } 7 | 8 | .textOverflowMulti(@line: 3, @bg: #fff) { 9 | position: relative; 10 | max-height: @line * 1.5em; 11 | margin-right: -1em; 12 | padding-right: 1em; 13 | overflow: hidden; 14 | line-height: 1.5em; 15 | text-align: justify; 16 | &::before { 17 | position: absolute; 18 | right: 14px; 19 | bottom: 0; 20 | padding: 0 1px; 21 | background: @bg; 22 | content: '...'; 23 | } 24 | &::after { 25 | position: absolute; 26 | right: 14px; 27 | width: 1em; 28 | height: 1em; 29 | margin-top: 0.2em; 30 | background: white; 31 | content: ''; 32 | } 33 | } 34 | 35 | // mixins for clearfix 36 | // ------------------------ 37 | .clearfix() { 38 | zoom: 1; 39 | &::before, 40 | &::after { 41 | display: table; 42 | content: ' '; 43 | } 44 | &::after { 45 | clear: both; 46 | height: 0; 47 | font-size: 0; 48 | visibility: hidden; 49 | } 50 | } -------------------------------------------------------------------------------- /src/views/demo/other/IconSelectorView.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 37 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | device: state => state.app.device, 3 | theme: state => state.app.theme, 4 | color: state => state.app.color, 5 | token: state => state.user.token, 6 | avatar: state => { 7 | if(state.appSession.user.profilePictureId){ 8 | return '/user.png' 9 | }else{ 10 | return '/user.png' 11 | } 12 | }, 13 | nickname: state => state.appSession.user.name, 14 | welcome: state => state.user.welcome, 15 | roles: state => state.user.roles, 16 | userInfo: state => state.appSession.user, 17 | tenant: state => state.appSession.tenant, 18 | addRouters: state => state.permission.addRouters, 19 | multiTab: state => state.app.multiTab, 20 | friends:state => state.chat.friends, 21 | userNameFilter:state => state.chat.userNameFilter, 22 | selectedFriend:state => state.chat.selectedFriend, 23 | showChatMessage:state => state.chat.showChatMessage, 24 | isOpen:state => state.chat.isOpen, 25 | serverClientTimeDifference:state => state.chat.serverClientTimeDifference, 26 | } 27 | 28 | export default getters 29 | -------------------------------------------------------------------------------- /src/abpZero/app/shared/common/auth/app-auth.service.js: -------------------------------------------------------------------------------- 1 | 2 | import { AppConsts } from '@/abpZero/shared/AppConsts'; 3 | import { XmlHttpRequestHelper } from '@/abpZero/shared/helpers/XmlHttpRequestHelper'; 4 | let AppAuthService = class AppAuthService { 5 | logout(reload, returnUrl) { 6 | let customHeaders = { 7 | 'Abp.TenantId': abp.multiTenancy.getTenantIdCookie(), 8 | 'Authorization': 'Bearer ' + abp.auth.getToken() 9 | }; 10 | XmlHttpRequestHelper.ajax('GET', AppConsts.remoteServiceBaseUrl + '/api/TokenAuth/LogOut', customHeaders, null, () => { 11 | abp.auth.clearToken(); 12 | abp.utils.setCookieValue(AppConsts.authorization.encrptedAuthTokenName, undefined, undefined, abp.appPath); 13 | if (reload !== false) { 14 | if (returnUrl) { 15 | location.href = returnUrl; 16 | } 17 | else { 18 | location.href = ''; 19 | } 20 | } 21 | }); 22 | } 23 | }; 24 | export { AppAuthService }; 25 | -------------------------------------------------------------------------------- /src/abpZero/appconfig.js: -------------------------------------------------------------------------------- 1 | const appConfig=process.env.NODE_ENV === 'production' ? 2 | { 3 | "remoteServiceBaseUrl": "http://wuaiapi.tbkmama.com", //后端部署的api接口地址 4 | "remoteSignalrBaseUrl": "http://wuaiapi.tbkmama.com", //后端部署的signalr地址,可以直连asp.net core 也可以配置反向代理地址(这样需要配置ws) 5 | "appBaseUrl": "http://{TENANCY_NAME}.tbkmama.com", //前端部署的地址 6 | "localeMappings": [ 7 | { 8 | "from": "pt-BR", 9 | "to": "pt" 10 | }, 11 | { 12 | "from": "zh-CN", 13 | "to": "zh" 14 | }, 15 | { 16 | "from": "he-IL", 17 | "to": "he" 18 | } 19 | ] 20 | } 21 | : 22 | { 23 | "remoteServiceBaseUrl": "http://localhost:44301", 24 | "remoteSignalrBaseUrl": "http://localhost:44301", 25 | "appBaseUrl": "http://localhost:4200", 26 | "localeMappings": [ 27 | { 28 | "from": "pt-BR", 29 | "to": "pt" 30 | }, 31 | { 32 | "from": "zh-CN", 33 | "to": "zh" 34 | }, 35 | { 36 | "from": "he-IL", 37 | "to": "he" 38 | } 39 | ] 40 | } 41 | export default appConfig; -------------------------------------------------------------------------------- /src/mock/util.js: -------------------------------------------------------------------------------- 1 | const responseBody = { 2 | message: '', 3 | timestamp: 0, 4 | result: null, 5 | code: 0 6 | } 7 | 8 | export const builder = (data, message, code = 0, headers = {}) => { 9 | responseBody.result = data 10 | if (message !== undefined && message !== null) { 11 | responseBody.message = message 12 | } 13 | if (code !== undefined && code !== 0) { 14 | responseBody.code = code 15 | responseBody._status = code 16 | } 17 | if (headers !== null && typeof headers === 'object' && Object.keys(headers).length > 0) { 18 | responseBody._headers = headers 19 | } 20 | responseBody.timestamp = new Date().getTime() 21 | return responseBody 22 | } 23 | 24 | export const getQueryParameters = (options) => { 25 | const url = options.url 26 | const search = url.split('?')[1] 27 | if (!search) { 28 | return {} 29 | } 30 | return JSON.parse('{"' + decodeURIComponent(search) 31 | .replace(/"/g, '\\"') 32 | .replace(/&/g, '","') 33 | .replace(/=/g, '":"') + '"}') 34 | } 35 | 36 | export const getBody = (options) => { 37 | return options.body && JSON.parse(options.body) 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Anan Yang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/components/_util/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * components util 3 | */ 4 | 5 | /** 6 | * 清理空值,对象 7 | * @param children 8 | * @returns {*[]} 9 | */ 10 | export function filterEmpty (children = []) { 11 | return children.filter(c => c.tag || (c.text && c.text.trim() !== '')) 12 | } 13 | 14 | /** 15 | * 获取字符串长度,英文字符 长度1,中文字符长度2 16 | * @param {*} str 17 | */ 18 | export const getStrFullLength = (str = '') => 19 | str.split('').reduce((pre, cur) => { 20 | const charCode = cur.charCodeAt(0) 21 | if (charCode >= 0 && charCode <= 128) { 22 | return pre + 1 23 | } 24 | return pre + 2 25 | }, 0) 26 | 27 | /** 28 | * 截取字符串,根据 maxLength 截取后返回 29 | * @param {*} str 30 | * @param {*} maxLength 31 | */ 32 | export const cutStrByFullLength = (str = '', maxLength) => { 33 | let showLength = 0 34 | return str.split('').reduce((pre, cur) => { 35 | const charCode = cur.charCodeAt(0) 36 | if (charCode >= 0 && charCode <= 128) { 37 | showLength += 1 38 | } else { 39 | showLength += 2 40 | } 41 | if (showLength <= maxLength) { 42 | return pre + cur 43 | } 44 | return pre 45 | }, '') 46 | } 47 | -------------------------------------------------------------------------------- /src/views/demo/user/RegisterResult.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 47 | 48 | 51 | -------------------------------------------------------------------------------- /src/core/directives/action.js: -------------------------------------------------------------------------------- 1 | //import Vue from 'vue' 2 | import store from '@/store' 3 | 4 | /** 5 | * Action 权限指令 6 | * 指令用法: 7 | * - 在需要控制 action 级别权限的组件上使用 v-action:[method] , 如下: 8 | * 添加用户 9 | * 删除用户 10 | * 修改 11 | * 12 | * - 当前用户没有权限时,组件上使用了该指令则会被隐藏 13 | * - 当后台权限跟 pro 提供的模式不同时,只需要针对这里的权限过滤进行修改即可 14 | * 15 | * @see https://github.com/sendya/ant-design-pro-vue/pull/53 16 | */ 17 | const action = Vue.directive('action', { 18 | inserted: function (el, binding, vnode) { 19 | const actionName = binding.arg 20 | const roles = store.getters.roles 21 | const elVal = vnode.context.$route.meta.permission 22 | const permissionId = elVal instanceof String && [elVal] || elVal 23 | roles.permissions.forEach(p => { 24 | if (!permissionId.includes(p.permissionId)) { 25 | return 26 | } 27 | if (p.actionList && !p.actionList.includes(actionName)) { 28 | el.parentNode && el.parentNode.removeChild(el) || (el.style.display = 'none') 29 | } 30 | }) 31 | } 32 | }) 33 | 34 | export default action 35 | -------------------------------------------------------------------------------- /src/components/TagSelect/TagSelectOption.jsx: -------------------------------------------------------------------------------- 1 | import { Tag } from 'ant-design-vue' 2 | const { CheckableTag } = Tag 3 | 4 | export default { 5 | name: 'TagSelectOption', 6 | props: { 7 | prefixCls: { 8 | type: String, 9 | default: 'ant-pro-tag-select-option' 10 | }, 11 | value: { 12 | type: [String, Number, Object], 13 | default: '' 14 | }, 15 | checked: { 16 | type: Boolean, 17 | default: false 18 | } 19 | }, 20 | data () { 21 | return { 22 | localChecked: this.checked || false 23 | } 24 | }, 25 | watch: { 26 | 'checked' (val) { 27 | this.localChecked = val 28 | }, 29 | '$parent.items': { 30 | handler: function (val) { 31 | this.value && val.hasOwnProperty(this.value) && (this.localChecked = val[this.value]) 32 | }, 33 | deep: true 34 | } 35 | }, 36 | render () { 37 | const { $slots, value } = this 38 | const onChange = (checked) => { 39 | this.$emit('change', { value, checked }) 40 | } 41 | return ( 42 | {$slots.default} 43 | ) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/views/demo/list/search/components/CardInfo.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 29 | 30 | 59 | -------------------------------------------------------------------------------- /src/views/demo/list/QueryList.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 51 | -------------------------------------------------------------------------------- /src/components/tools/Breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /src/store/modules/appSession.js: -------------------------------------------------------------------------------- 1 | 2 | import { SessionServiceProxy } from '@/abpZero/shared/service-proxies/SessionServiceProxy'; 3 | let _sessionService=new SessionServiceProxy(); 4 | const appSession = { 5 | state: { 6 | //application: null, 7 | user:null, 8 | tenant:null, 9 | theme:null 10 | }, 11 | mutations: { 12 | SET_APPSESSION(state, session) { 13 | //state.application = session.application 14 | state.user = session.user; 15 | state.tenant = session.tenant; 16 | state.theme = session.theme; 17 | }, 18 | }, 19 | actions: { 20 | init({commit,state}){ 21 | return new Promise((resolve, reject) => { 22 | _sessionService.getCurrentLoginInformations() 23 | .then(result=>{ 24 | commit('SET_APPSESSION', result) 25 | resolve(result); 26 | }) 27 | .catch(function (error) { 28 | reject(error); 29 | }); 30 | }); 31 | } 32 | } 33 | } 34 | export default appSession -------------------------------------------------------------------------------- /src/components/Charts/MiniBar.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 54 | 55 | 58 | -------------------------------------------------------------------------------- /src/components/Charts/MiniArea.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 53 | 54 | 57 | -------------------------------------------------------------------------------- /src/abpZero/shared/helpers/XmlHttpRequestHelper.js: -------------------------------------------------------------------------------- 1 | export class XmlHttpRequestHelper { 2 | static ajax(type, url, customHeaders, data, success) { 3 | let xhr = new XMLHttpRequest(); 4 | xhr.onreadystatechange = () => { 5 | if (xhr.readyState === XMLHttpRequest.DONE) { 6 | if (xhr.status === 200) { 7 | let result = JSON.parse(xhr.responseText); 8 | success(result); 9 | } 10 | else if (xhr.status !== 0) { 11 | alert(abp.localization.localize('InternalServerError', 'AbpWeb')); 12 | } 13 | } 14 | }; 15 | url += (url.indexOf('?') >= 0 ? '&' : '?') + 'd=' + new Date().getTime(); 16 | xhr.open(type, url, true); 17 | for (let property in customHeaders) { 18 | if (customHeaders.hasOwnProperty(property)) { 19 | xhr.setRequestHeader(property, customHeaders[property]); 20 | } 21 | } 22 | xhr.setRequestHeader('Content-type', 'application/json'); 23 | if (data) { 24 | xhr.send(data); 25 | } 26 | else { 27 | xhr.send(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/components/NumberInfo/NumberInfo.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 51 | 52 | 55 | -------------------------------------------------------------------------------- /src/abpZero/shared/service-proxies/ChatServiceProxy.js: -------------------------------------------------------------------------------- 1 | let ChatServiceProxy = class ChatServiceProxy { 2 | constructor() { 3 | } 4 | getUserChatFriendsWithSettings() { 5 | return httpClient.get("/api/services/app/Chat/GetUserChatFriendsWithSettings") 6 | } 7 | getUserChatMessages(tenantId, userId, minMessageId){ 8 | let url_ ="/api/services/app/Chat/GetUserChatMessages?"; 9 | if (tenantId !== undefined) 10 | url_ += "TenantId=" + encodeURIComponent("" + tenantId) + "&"; 11 | if (userId === null) 12 | throw new Error("The parameter 'userId' cannot be null."); 13 | else if (userId !== undefined) 14 | url_ += "UserId=" + encodeURIComponent("" + userId) + "&"; 15 | if (minMessageId !== undefined) 16 | url_ += "MinMessageId=" + encodeURIComponent("" + minMessageId) + "&"; 17 | url_ = url_.replace(/[?&]$/, ""); 18 | return httpClient.get(url_) 19 | } 20 | markAllUnreadMessagesOfUserAsRead(markAllUnreadMessagesOfUserAsReadInput){ 21 | return httpClient.post("/api/services/app/Chat/MarkAllUnreadMessagesOfUserAsRead",markAllUnreadMessagesOfUserAsReadInput) 22 | } 23 | }; 24 | export { ChatServiceProxy }; -------------------------------------------------------------------------------- /src/views/account/components/tenantChange.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 34 | 35 | -------------------------------------------------------------------------------- /src/components/Editor/WangEditor.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 50 | 51 | 58 | -------------------------------------------------------------------------------- /src/components/AvatarList/index.less: -------------------------------------------------------------------------------- 1 | @import "../index"; 2 | 3 | @avatar-list-prefix-cls: ~"@{ant-pro-prefix}-avatar-list"; 4 | @avatar-list-item-prefix-cls: ~"@{ant-pro-prefix}-avatar-list-item"; 5 | 6 | .@{avatar-list-prefix-cls} { 7 | display: inline-block; 8 | 9 | ul { 10 | list-style: none; 11 | display: inline-block; 12 | padding: 0; 13 | margin: 0 0 0 8px; 14 | font-size: 0; 15 | } 16 | } 17 | 18 | .@{avatar-list-item-prefix-cls} { 19 | display: inline-block; 20 | font-size: @font-size-base; 21 | margin-left: -8px; 22 | width: @avatar-size-base; 23 | height: @avatar-size-base; 24 | 25 | :global { 26 | .ant-avatar { 27 | border: 1px solid #fff; 28 | cursor: pointer; 29 | } 30 | } 31 | 32 | &.large { 33 | width: @avatar-size-lg; 34 | height: @avatar-size-lg; 35 | } 36 | 37 | &.small { 38 | width: @avatar-size-sm; 39 | height: @avatar-size-sm; 40 | } 41 | 42 | &.mini { 43 | width: 20px; 44 | height: 20px; 45 | 46 | :global { 47 | .ant-avatar { 48 | width: 20px; 49 | height: 20px; 50 | line-height: 20px; 51 | 52 | .ant-avatar-string { 53 | font-size: 12px; 54 | line-height: 18px; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/components/Charts/TransferBar.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 65 | -------------------------------------------------------------------------------- /src/api/login.js: -------------------------------------------------------------------------------- 1 | import api from './index' 2 | import { axios } from '@/utils/request' 3 | 4 | /** 5 | * login func 6 | * parameter: { 7 | * username: '', 8 | * password: '', 9 | * remember_me: true, 10 | * captcha: '12345' 11 | * } 12 | * @param parameter 13 | * @returns {*} 14 | */ 15 | export function login (parameter) { 16 | return axios({ 17 | url: '/auth/login', 18 | method: 'post', 19 | data: parameter 20 | }) 21 | } 22 | 23 | export function getSmsCaptcha (parameter) { 24 | return axios({ 25 | url: api.SendSms, 26 | method: 'post', 27 | data: parameter 28 | }) 29 | } 30 | 31 | export function getInfo () { 32 | return axios({ 33 | url: '/user/info', 34 | method: 'get', 35 | headers: { 36 | 'Content-Type': 'application/json;charset=UTF-8' 37 | } 38 | }) 39 | } 40 | 41 | export function logout () { 42 | return axios({ 43 | url: '/auth/logout', 44 | method: 'post', 45 | headers: { 46 | 'Content-Type': 'application/json;charset=UTF-8' 47 | } 48 | }) 49 | } 50 | 51 | /** 52 | * get user 2step code open? 53 | * @param parameter {*} 54 | */ 55 | export function get2step (parameter) { 56 | return axios({ 57 | url: api.twoStepCode, 58 | method: 'post', 59 | data: parameter 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /src/components/Charts/Bar.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 63 | -------------------------------------------------------------------------------- /src/components/GlobalFooter/GlobalFooter.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 32 | 33 | 60 | -------------------------------------------------------------------------------- /src/views/demo/result/Error.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 42 | 43 | 46 | -------------------------------------------------------------------------------- /src/components/NumberInfo/index.less: -------------------------------------------------------------------------------- 1 | @import "../index"; 2 | 3 | @numberInfo-prefix-cls: ~"@{ant-pro-prefix}-number-info"; 4 | 5 | .@{numberInfo-prefix-cls} { 6 | 7 | .ant-pro-number-info-subtitle { 8 | color: @text-color-secondary; 9 | font-size: @font-size-base; 10 | height: 22px; 11 | line-height: 22px; 12 | overflow: hidden; 13 | text-overflow: ellipsis; 14 | word-break: break-all; 15 | white-space: nowrap; 16 | } 17 | 18 | .number-info-value { 19 | margin-top: 4px; 20 | font-size: 0; 21 | overflow: hidden; 22 | text-overflow: ellipsis; 23 | word-break: break-all; 24 | white-space: nowrap; 25 | 26 | & > span { 27 | color: @heading-color; 28 | display: inline-block; 29 | line-height: 32px; 30 | height: 32px; 31 | font-size: 24px; 32 | margin-right: 32px; 33 | } 34 | 35 | .sub-total { 36 | color: @text-color-secondary; 37 | font-size: @font-size-lg; 38 | vertical-align: top; 39 | margin-right: 0; 40 | i { 41 | font-size: 12px; 42 | transform: scale(0.82); 43 | margin-left: 4px; 44 | } 45 | :global { 46 | .anticon-caret-up { 47 | color: @red-6; 48 | } 49 | .anticon-caret-down { 50 | color: @green-6; 51 | } 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/config/defaultSettings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 项目默认配置项 3 | * primaryColor - 默认主题色, 如果修改颜色不生效,请清理 localStorage 4 | * navTheme - sidebar theme ['dark', 'light'] 两种主题 5 | * colorWeak - 色盲模式 6 | * layout - 整体布局方式 ['sidemenu', 'topmenu'] 两种布局 7 | * fixedHeader - 固定 Header : boolean 8 | * fixSiderbar - 固定左侧菜单栏 : boolean 9 | * autoHideHeader - 向下滚动时,隐藏 Header : boolean 10 | * contentWidth - 内容区布局: 流式 | 固定 11 | * 12 | * storageOptions: {} - Vue-ls 插件配置项 (localStorage/sessionStorage) 13 | * 14 | */ 15 | 16 | export default { 17 | appTitle:'BeiDream', 18 | appDesc:'BeiDream后台管理系统', 19 | primaryColor: '#52C41A', // primary color of ant design 20 | navTheme: 'dark', // theme for nav menu 21 | layout: 'sidemenu', // nav menu position: sidemenu or topmenu 22 | contentWidth: 'Fixed', // layout of content: Fluid or Fixed, only works when layout is topmenu 23 | fixedHeader: true, // sticky header 24 | fixSiderbar: true, // sticky siderbar 25 | autoHideHeader: false, // auto hide header 26 | colorWeak: false, 27 | multiTab: true, 28 | production: process.env.NODE_ENV === 'production' && process.env.VUE_APP_PREVIEW !== 'true', 29 | // vue-ls options 30 | storageOptions: { 31 | namespace: 'pro__', // key prefix 32 | name: 'ls', // name variable Vue.[ls] or this.[$ls], 33 | storage: 'local' // storage name session, local, memory 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/abpZero/shared/helpers/SignalRHelper.js: -------------------------------------------------------------------------------- 1 | import { LocalStorageService } from '@/abpZero/shared/common/localStorage.Service' 2 | import { AppConsts } from '@/abpZero/shared/AppConsts'; 3 | export class SignalRHelper { 4 | static initSignalR(callback) { 5 | new LocalStorageService().getItem(AppConsts.authorization.encrptedAuthTokenName, function (err, value) { 6 | let encryptedAuthToken; 7 | if(value!=null) 8 | { 9 | encryptedAuthToken = value.token; 10 | } 11 | abp.signalr = { 12 | autoConnect: true, 13 | connect: undefined, 14 | hubs: undefined, 15 | qs: AppConsts.authorization.encrptedAuthTokenName + '=' + encodeURIComponent(encryptedAuthToken), 16 | remoteServiceBaseUrl: AppConsts.remoteSignalrBaseUrl, 17 | startConnection: undefined, 18 | url: '/signalr' 19 | }; 20 | let script = document.createElement('script'); 21 | script.onload = () => { 22 | if(callback){ 23 | callback(); 24 | } 25 | }; 26 | script.src = AppConsts.appBaseUrl + '/static/abp-web-resources/abp.signalr-client.js'; 27 | document.body.appendChild(script); 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/api/manage.js: -------------------------------------------------------------------------------- 1 | import { axios } from '@/utils/request' 2 | 3 | const api = { 4 | user: '/user', 5 | role: '/role', 6 | service: '/service', 7 | permission: '/permission', 8 | permissionNoPager: '/permission/no-pager', 9 | orgTree: '/org/tree' 10 | } 11 | 12 | export default api 13 | 14 | export function getUserList (parameter) { 15 | return axios({ 16 | url: api.user, 17 | method: 'get', 18 | params: parameter 19 | }) 20 | } 21 | 22 | export function getRoleList (parameter) { 23 | return axios({ 24 | url: api.role, 25 | method: 'get', 26 | params: parameter 27 | }) 28 | } 29 | 30 | export function getServiceList (parameter) { 31 | return axios({ 32 | url: api.service, 33 | method: 'get', 34 | params: parameter 35 | }) 36 | } 37 | 38 | export function getPermissions (parameter) { 39 | return axios({ 40 | url: api.permissionNoPager, 41 | method: 'get', 42 | params: parameter 43 | }) 44 | } 45 | 46 | export function getOrgTree (parameter) { 47 | return axios({ 48 | url: api.orgTree, 49 | method: 'get', 50 | params: parameter 51 | }) 52 | } 53 | 54 | // id == 0 add post 55 | // id != 0 update put 56 | export function saveService (parameter) { 57 | return axios({ 58 | url: api.service, 59 | method: parameter.id === 0 ? 'post' : 'put', 60 | data: parameter 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /src/abpZero/shared/service-proxies/TokenAuthServiceProxy.js: -------------------------------------------------------------------------------- 1 | let TokenAuthServiceProxy = class TokenAuthServiceProxy { 2 | constructor() { 3 | } 4 | /** 5 | * @param model (optional) 6 | * @return Success 7 | */ 8 | authenticate(model) { 9 | let url_ ="/api/TokenAuth/Authenticate"; 10 | url_ = url_.replace(/[?&]$/, ""); 11 | return httpClient.post(url_, model) 12 | } 13 | /** 14 | * @return Success 15 | */ 16 | logOut() { 17 | let url_ = "/api/TokenAuth/LogOut"; 18 | url_ = url_.replace(/[?&]$/, ""); 19 | return httpClient.get(url_) 20 | } 21 | refreshToken(refreshToken) { 22 | let url_ ="/api/TokenAuth/RefreshToken?refreshToken="+refreshToken; 23 | url_ = url_.replace(/[?&]$/, ""); 24 | return httpClient.post(url_) 25 | } 26 | /** 27 | * @param message (optional) 28 | * @param severity (optional) 29 | * @return Success 30 | */ 31 | testNotification(message, severity) { 32 | let url_ = "/api/TokenAuth/TestNotification?"; 33 | if (message !== undefined) 34 | url_ += "message=" + encodeURIComponent("" + message) + "&"; 35 | if (severity !== undefined) 36 | url_ += "severity=" + encodeURIComponent("" + severity) + "&"; 37 | url_ = url_.replace(/[?&]$/, ""); 38 | } 39 | 40 | }; 41 | 42 | export { TokenAuthServiceProxy }; -------------------------------------------------------------------------------- /src/components/tools/HeadInfo.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 32 | 33 | 68 | -------------------------------------------------------------------------------- /public/static/FreezeUI/freeze-ui.min.css: -------------------------------------------------------------------------------- 1 | @keyframes spin { 2 | 0% { 3 | transform: translateZ(0) rotate(0) 4 | } 5 | 6 | 100% { 7 | transform: translateZ(0) rotate(360deg) 8 | } 9 | } 10 | 11 | .freeze-ui { 12 | position: fixed; 13 | top: 0; 14 | left: 0; 15 | width: 100%; 16 | height: 100%; 17 | z-index: 999999999; 18 | background-color: #fff; 19 | opacity: .8; 20 | transition: opacity .25s 21 | } 22 | 23 | .freeze-ui.is-unfreezing { 24 | opacity: 0 25 | } 26 | 27 | .freeze-ui:after { 28 | content: attr(data-text); 29 | display: block; 30 | max-width: 125px; 31 | position: absolute; 32 | top: 50%; 33 | left: 50%; 34 | transform: translate(-50%,-50%); 35 | font-size: 20px; 36 | font-family: sans-serif; 37 | color: #343a40; 38 | text-align: center; 39 | text-transform: uppercase 40 | } 41 | 42 | .freeze-ui:before { 43 | content: ""; 44 | display: block; 45 | width: 150px; 46 | height: 150px; 47 | border-radius: 50%; 48 | border-width: 2px; 49 | border-style: solid; 50 | border-color: transparent #228ae6 #228ae6; 51 | position: absolute; 52 | top: calc(50% - 75px); 53 | left: calc(50% - 75px); 54 | will-change: transform; 55 | animation: spin .75s infinite ease-in-out 56 | } 57 | -------------------------------------------------------------------------------- /src/views/demo/form/stepForm/StepForm.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 56 | 57 | 63 | -------------------------------------------------------------------------------- /src/components/Charts/Liquid.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 64 | 65 | 68 | -------------------------------------------------------------------------------- /src/core/bootstrap.js: -------------------------------------------------------------------------------- 1 | //import Vue from 'vue' 2 | import store from '@/store/' 3 | import { 4 | ACCESS_TOKEN, 5 | DEFAULT_COLOR, 6 | DEFAULT_THEME, 7 | DEFAULT_LAYOUT_MODE, 8 | DEFAULT_COLOR_WEAK, 9 | SIDEBAR_TYPE, 10 | DEFAULT_FIXED_HEADER, 11 | DEFAULT_FIXED_HEADER_HIDDEN, 12 | DEFAULT_FIXED_SIDEMENU, 13 | DEFAULT_CONTENT_WIDTH_TYPE, 14 | DEFAULT_MULTI_TAB 15 | } from '@/store/mutation-types' 16 | import config from '@/config/defaultSettings' 17 | 18 | export default function Initializer () { 19 | console.log(`API_URL: ${process.env.VUE_APP_API_BASE_URL}`) 20 | 21 | store.commit('SET_SIDEBAR_TYPE', Vue.ls.get(SIDEBAR_TYPE, true)) 22 | store.commit('TOGGLE_THEME', Vue.ls.get(DEFAULT_THEME, config.navTheme)) 23 | store.commit('TOGGLE_LAYOUT_MODE', Vue.ls.get(DEFAULT_LAYOUT_MODE, config.layout)) 24 | store.commit('TOGGLE_FIXED_HEADER', Vue.ls.get(DEFAULT_FIXED_HEADER, config.fixedHeader)) 25 | store.commit('TOGGLE_FIXED_SIDERBAR', Vue.ls.get(DEFAULT_FIXED_SIDEMENU, config.fixSiderbar)) 26 | store.commit('TOGGLE_CONTENT_WIDTH', Vue.ls.get(DEFAULT_CONTENT_WIDTH_TYPE, config.contentWidth)) 27 | store.commit('TOGGLE_FIXED_HEADER_HIDDEN', Vue.ls.get(DEFAULT_FIXED_HEADER_HIDDEN, config.autoHideHeader)) 28 | store.commit('TOGGLE_WEAK', Vue.ls.get(DEFAULT_COLOR_WEAK, config.colorWeak)) 29 | store.commit('TOGGLE_COLOR', Vue.ls.get(DEFAULT_COLOR, config.primaryColor)) 30 | store.commit('TOGGLE_MULTI_TAB', Vue.ls.get(DEFAULT_MULTI_TAB, config.multiTab)) 31 | store.commit('SET_TOKEN', Vue.ls.get(ACCESS_TOKEN)) 32 | 33 | // last step 34 | } 35 | -------------------------------------------------------------------------------- /src/eleComponents/lookup.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 23 | -------------------------------------------------------------------------------- /src/utils/helper/permission.js: -------------------------------------------------------------------------------- 1 | const PERMISSION_ENUM = { 2 | 'add': { key: 'add', label: '新增' }, 3 | 'delete': { key: 'delete', label: '删除' }, 4 | 'edit': { key: 'edit', label: '修改' }, 5 | 'query': { key: 'query', label: '查询' }, 6 | 'get': { key: 'get', label: '详情' }, 7 | 'enable': { key: 'enable', label: '启用' }, 8 | 'disable': { key: 'disable', label: '禁用' }, 9 | 'import': { key: 'import', label: '导入' }, 10 | 'export': { key: 'export', label: '导出' } 11 | } 12 | 13 | function plugin (Vue) { 14 | if (plugin.installed) { 15 | return 16 | } 17 | 18 | !Vue.prototype.$auth && Object.defineProperties(Vue.prototype, { 19 | $auth: { 20 | get () { 21 | const _this = this 22 | return (permissions) => { 23 | const [permission, action] = permissions.split('.') 24 | const permissionList = _this.$store.getters.roles.permissions 25 | return permissionList.find((val) => { 26 | return val.permissionId === permission 27 | }).actionList.findIndex((val) => { 28 | return val === action 29 | }) > -1 30 | } 31 | } 32 | } 33 | }) 34 | 35 | !Vue.prototype.$enum && Object.defineProperties(Vue.prototype, { 36 | $enum: { 37 | get () { 38 | // const _this = this; 39 | return (val) => { 40 | let result = PERMISSION_ENUM 41 | val && val.split('.').forEach(v => { 42 | result = result && result[v] || null 43 | }) 44 | return result 45 | } 46 | } 47 | } 48 | }) 49 | } 50 | 51 | export default plugin 52 | -------------------------------------------------------------------------------- /src/components/Charts/Radar.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 65 | 66 | 69 | -------------------------------------------------------------------------------- /src/components/Menu/SideMenu.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 68 | -------------------------------------------------------------------------------- /src/mock/services/auth.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs2' 2 | import { builder, getBody } from '../util' 3 | 4 | const username = ['admin', 'user', 'super'] 5 | const password = ['21232f297a57a5a743894a0e4a801fc3', '8914de686ab28dc22f30d3d8e107ff6c'] // admin, ant.design 6 | 7 | const login = (options) => { 8 | const body = getBody(options) 9 | console.log('mock: body', body) 10 | if (!username.includes(body.username) || !password.includes(body.password)) { 11 | return builder({ isLogin: true }, '账户或密码错误', 401) 12 | } 13 | 14 | return builder({ 15 | 'id': Mock.mock('@guid'), 16 | 'name': Mock.mock('@name'), 17 | 'username': 'admin', 18 | 'password': '', 19 | 'avatar': 'https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png', 20 | 'status': 1, 21 | 'telephone': '', 22 | 'lastLoginIp': '27.154.74.117', 23 | 'lastLoginTime': 1534837621348, 24 | 'creatorId': 'admin', 25 | 'createTime': 1497160610259, 26 | 'deleted': 0, 27 | 'roleId': 'admin', 28 | 'lang': 'zh-CN', 29 | 'token': '4291d7da9005377ec9aec4a71ea837f' 30 | }, '', 200, { 'Custom-Header': Mock.mock('@guid') }) 31 | } 32 | 33 | const logout = () => { 34 | return builder({}, '[测试接口] 注销成功') 35 | } 36 | 37 | const smsCaptcha = () => { 38 | return builder({ captcha: Mock.mock('@integer(10000, 99999)') }) 39 | } 40 | 41 | const twofactor = () => { 42 | return builder({ stepCode: Mock.mock('@integer(0, 1)') }) 43 | } 44 | 45 | Mock.mock(/\/auth\/login/, 'post', login) 46 | Mock.mock(/\/auth\/logout/, 'post', logout) 47 | Mock.mock(/\/account\/sms/, 'post', smsCaptcha) 48 | Mock.mock(/\/auth\/2step-code/, 'post', twofactor) 49 | -------------------------------------------------------------------------------- /src/abpZero/shared/service-proxies/UserServiceProxy.js: -------------------------------------------------------------------------------- 1 | let UserServiceProxy = class UserServiceProxy { 2 | constructor() { 3 | } 4 | /** 5 | * @return Success 6 | */ 7 | /** 8 | * @param filter (optional) 9 | * @param permission (optional) 10 | * @param role (optional) 11 | * @param onlyLockedUsers (optional) 12 | * @param sorting (optional) 13 | * @param maxResultCount (optional) 14 | * @param skipCount (optional) 15 | * @return Success 16 | */ 17 | getUsers(filter, permission, role, onlyLockedUsers, sorting, maxResultCount, skipCount) { 18 | let url_ = this.baseUrl + "/api/services/app/User/GetUsers?"; 19 | if (filter !== undefined) 20 | url_ += "Filter=" + encodeURIComponent("" + filter) + "&"; 21 | if (permission !== undefined) 22 | url_ += "Permission=" + encodeURIComponent("" + permission) + "&"; 23 | if (role !== undefined) 24 | url_ += "Role=" + encodeURIComponent("" + role) + "&"; 25 | if (onlyLockedUsers !== undefined) 26 | url_ += "OnlyLockedUsers=" + encodeURIComponent("" + onlyLockedUsers) + "&"; 27 | if (sorting !== undefined) 28 | url_ += "Sorting=" + encodeURIComponent("" + sorting) + "&"; 29 | if (maxResultCount !== undefined) 30 | url_ += "MaxResultCount=" + encodeURIComponent("" + maxResultCount) + "&"; 31 | if (skipCount !== undefined) 32 | url_ += "SkipCount=" + encodeURIComponent("" + skipCount) + "&"; 33 | url_ = url_.replace(/[?&]$/, ""); 34 | return httpClient.get(url_) 35 | } 36 | 37 | }; 38 | export { UserServiceProxy }; -------------------------------------------------------------------------------- /src/utils/util.js: -------------------------------------------------------------------------------- 1 | export function timeFix () { 2 | const time = new Date() 3 | const hour = time.getHours() 4 | return hour < 9 ? '早上好' : hour <= 11 ? '上午好' : hour <= 13 ? '中午好' : hour < 20 ? '下午好' : '晚上好' 5 | } 6 | 7 | export function welcome () { 8 | const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 DOTA', '我猜你可能累了'] 9 | const index = Math.floor(Math.random() * arr.length) 10 | return arr[index] 11 | } 12 | 13 | /** 14 | * 触发 window.resize 15 | */ 16 | export function triggerWindowResizeEvent () { 17 | const event = document.createEvent('HTMLEvents') 18 | event.initEvent('resize', true, true) 19 | event.eventType = 'message' 20 | window.dispatchEvent(event) 21 | } 22 | 23 | export function handleScrollHeader (callback) { 24 | let timer = 0 25 | 26 | let beforeScrollTop = window.pageYOffset 27 | callback = callback || function () {} 28 | window.addEventListener( 29 | 'scroll', 30 | event => { 31 | clearTimeout(timer) 32 | timer = setTimeout(() => { 33 | let direction = 'up' 34 | const afterScrollTop = window.pageYOffset 35 | const delta = afterScrollTop - beforeScrollTop 36 | if (delta === 0) { 37 | return false 38 | } 39 | direction = delta > 0 ? 'down' : 'up' 40 | callback(direction) 41 | beforeScrollTop = afterScrollTop 42 | }, 50) 43 | }, 44 | false 45 | ) 46 | } 47 | 48 | /** 49 | * Remove loading animate 50 | * @param id parent element id or class 51 | * @param timeout 52 | */ 53 | export function removeLoadingAnimate (id = '', timeout = 1500) { 54 | if (id === '') { 55 | return 56 | } 57 | setTimeout(() => { 58 | document.body.removeChild(document.getElementById(id)) 59 | }, timeout) 60 | } 61 | -------------------------------------------------------------------------------- /src/views/demo/list/modules/CreateForm.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 68 | -------------------------------------------------------------------------------- /src/components/Ellipsis/Ellipsis.vue: -------------------------------------------------------------------------------- 1 | 65 | -------------------------------------------------------------------------------- /src/abpZero/shared/helpers/UrlHelper.js: -------------------------------------------------------------------------------- 1 | export class UrlHelper { 2 | static getQueryParameters() { 3 | return UrlHelper.getQueryParametersUsingParameters(document.location.search); 4 | } 5 | static getQueryParametersUsingParameters(search) { 6 | return search.replace(/(^\?)/, '').split('&').map(function (n) { return n = n.split('='), this[n[0]] = n[1], this; }.bind({}))[0]; 7 | } 8 | static getQueryParametersUsingHash() { 9 | return document.location.hash.substr(1, document.location.hash.length - 1).replace(/(^\?)/, '').split('&').map(function (n) { return n = n.split('='), this[n[0]] = n[1], this; }.bind({}))[0]; 10 | } 11 | static getInitialUrlParameters() { 12 | let questionMarkIndex = UrlHelper.initialUrl.indexOf('?'); 13 | if (questionMarkIndex >= 0) { 14 | return UrlHelper.initialUrl.substr(questionMarkIndex, UrlHelper.initialUrl.length - questionMarkIndex); 15 | } 16 | return ''; 17 | } 18 | static getReturnUrl() { 19 | const queryStringObj = UrlHelper.getQueryParametersUsingParameters(UrlHelper.getInitialUrlParameters()); 20 | if (queryStringObj.returnUrl) { 21 | return decodeURIComponent(queryStringObj.returnUrl); 22 | } 23 | return null; 24 | } 25 | static getSingleSignIn() { 26 | const queryStringObj = UrlHelper.getQueryParametersUsingParameters(UrlHelper.getInitialUrlParameters()); 27 | if (queryStringObj.ss) { 28 | return queryStringObj.ss; 29 | } 30 | return false; 31 | } 32 | static isInstallUrl(url) { 33 | return url && url.indexOf('app/admin/install') >= 0; 34 | } 35 | } 36 | /** 37 | * The URL requested, before initial routing. 38 | */ 39 | UrlHelper.initialUrl = location.href; 40 | -------------------------------------------------------------------------------- /src/components/Charts/MiniProgress.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 36 | 37 | 76 | -------------------------------------------------------------------------------- /src/components/Charts/RankList.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 30 | 31 | 78 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | //import Vue from 'vue' 2 | import axios from 'axios' 3 | import store from '@/store' 4 | import notification from 'ant-design-vue/es/notification' 5 | import { VueAxios } from './axios' 6 | import { ACCESS_TOKEN } from '@/store/mutation-types' 7 | 8 | // 创建 axios 实例 9 | const service = axios.create({ 10 | baseURL: process.env.VUE_APP_API_BASE_URL, // api base_url 11 | timeout: 6000 // 请求超时时间 12 | }) 13 | 14 | const err = (error) => { 15 | if (error.response) { 16 | const data = error.response.data 17 | const token = Vue.ls.get(ACCESS_TOKEN) 18 | if (error.response.status === 403) { 19 | notification.error({ 20 | message: 'Forbidden', 21 | description: data.message 22 | }) 23 | } 24 | if (error.response.status === 401 && !(data.result && data.result.isLogin)) { 25 | notification.error({ 26 | message: 'Unauthorized', 27 | description: 'Authorization verification failed' 28 | }) 29 | if (token) { 30 | store.dispatch('Logout').then(() => { 31 | setTimeout(() => { 32 | window.location.reload() 33 | }, 1500) 34 | }) 35 | } 36 | } 37 | } 38 | return Promise.reject(error) 39 | } 40 | 41 | // request interceptor 42 | service.interceptors.request.use(config => { 43 | const token = Vue.ls.get(ACCESS_TOKEN) 44 | if (token) { 45 | config.headers['Access-Token'] = token // 让每个请求携带自定义 token 请根据实际情况自行修改 46 | } 47 | return config 48 | }, err) 49 | 50 | // response interceptor 51 | service.interceptors.response.use((response) => { 52 | return response.data 53 | }, err) 54 | 55 | const installer = { 56 | vm: {}, 57 | install (Vue) { 58 | Vue.use(VueAxios, service) 59 | } 60 | } 61 | 62 | export { 63 | installer as VueAxios, 64 | service as axios 65 | } 66 | -------------------------------------------------------------------------------- /src/views/demo/account/settings/Security.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 38 | 39 | 42 | -------------------------------------------------------------------------------- /src/abpZero/utils/array-to-tree-converter.service.js: -------------------------------------------------------------------------------- 1 | 2 | import * as _ from 'lodash'; 3 | 4 | let ArrayToTreeConverterService = class ArrayToTreeConverterService { 5 | 6 | createTree(array, parentIdProperty, idProperty, parentIdValue, childrenProperty,warp=false,mappingList=[]){ 7 | let tree = []; 8 | 9 | let nodes = _.filter(array, [parentIdProperty, parentIdValue]); 10 | 11 | _.forEach(nodes, node => { 12 | let d=new Object(); 13 | d=_.cloneDeep(node); 14 | if(mappingList.length>0){ 15 | for (let index = 0; index < mappingList.length; index++) { 16 | const element = mappingList[index]; 17 | d[element[0]]=d[element[1]]; 18 | } 19 | } 20 | if(warp){ 21 | let newNode = { 22 | data: d 23 | }; 24 | 25 | 26 | newNode[childrenProperty] = this.createTree( 27 | array, 28 | parentIdProperty, 29 | idProperty, 30 | d[idProperty], 31 | childrenProperty, 32 | warp, 33 | mappingList 34 | ); 35 | 36 | tree.push(newNode); 37 | }else{ 38 | 39 | d[childrenProperty] = this.createTree( 40 | array, 41 | parentIdProperty, 42 | idProperty, 43 | d[idProperty], 44 | childrenProperty, 45 | warp, 46 | mappingList 47 | ); 48 | tree.push(d); 49 | } 50 | }); 51 | 52 | return tree; 53 | } 54 | } 55 | export {ArrayToTreeConverterService}; 56 | -------------------------------------------------------------------------------- /src/assets/icons/bx-analyse.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/abpZero/shared/service-proxies/FriendshipServiceProxy.js: -------------------------------------------------------------------------------- 1 | export class FriendshipServiceProxy { 2 | constructor() { 3 | } 4 | /** 5 | * @param body (optional) 6 | * @return Success 7 | */ 8 | createFriendshipRequest(createFriendshipRequestInput){ 9 | let url_ ="/api/services/app/Friendship/CreateFriendshipRequest"; 10 | url_ = url_.replace(/[?&]$/, ""); 11 | return httpClient.post(url_,createFriendshipRequestInput); 12 | } 13 | 14 | /** 15 | * @param body (optional) 16 | * @return Success 17 | */ 18 | createFriendshipRequestByUserName(createFriendshipRequestByUserNameInput){ 19 | let url_ ="/api/services/app/Friendship/CreateFriendshipRequestByUserName"; 20 | url_ = url_.replace(/[?&]$/, ""); 21 | return httpClient.post(url_,createFriendshipRequestByUserNameInput); 22 | } 23 | /** 24 | * @param body (optional) 25 | * @return Success 26 | */ 27 | blockUser(blockUserInput){ 28 | let url_ = "/api/services/app/Friendship/BlockUser"; 29 | url_ = url_.replace(/[?&]$/, ""); 30 | return httpClient.post(url_,blockUserInput); 31 | } 32 | 33 | /** 34 | * @param body (optional) 35 | * @return Success 36 | */ 37 | unblockUser(unblockUserInput){ 38 | let url_ ="/api/services/app/Friendship/UnblockUser"; 39 | url_ = url_.replace(/[?&]$/, ""); 40 | return httpClient.post(url_,unblockUserInput); 41 | } 42 | /** 43 | * @param body (optional) 44 | * @return Success 45 | */ 46 | acceptFriendshipRequest(acceptFriendshipRequestInput){ 47 | let url_ = "/api/services/app/Friendship/AcceptFriendshipRequest"; 48 | url_ = url_.replace(/[?&]$/, ""); 49 | return httpClient.post(url_,acceptFriendshipRequestInput); 50 | } 51 | } -------------------------------------------------------------------------------- /src/store/modules/chat.js: -------------------------------------------------------------------------------- 1 | import {ChatServiceProxy} from '@/abpZero/shared/service-proxies/ChatServiceProxy' 2 | let _chatService=new ChatServiceProxy() 3 | const chat = { 4 | state: { 5 | friends:[], 6 | serverClientTimeDifference:null, 7 | userNameFilter:'', 8 | selectedFriend:{}, 9 | isOpen:false, 10 | showChatMessage:false 11 | }, 12 | mutations: { 13 | GET_FRIENDS(state, result) { 14 | state.friends = result; 15 | state.serverClientTimeDifference = moment(abp.clock.now()).diff(result.serverTime, 'seconds'); 16 | }, 17 | ADD_FRIEND(state, friend) { 18 | state.friends.push(friend); 19 | }, 20 | SET_USERNAMEFILTER(state, value) { 21 | state.userNameFilter=value; 22 | }, 23 | SET_SELECTEDFRIEND(state, value) { 24 | state.selectedFriend=_.cloneDeep(value); 25 | }, 26 | SET_SHOWCHATMESSAGE(state, value) { 27 | state.showChatMessage=value; 28 | }, 29 | SET_OPEN(state, value) { 30 | state.isOpen=value; 31 | }, 32 | }, 33 | actions: { 34 | getFriendsAndSettings({commit,state},callback){ 35 | return new Promise((resolve, reject) => { 36 | _chatService.getUserChatFriendsWithSettings() 37 | .then(result => { 38 | commit('GET_FRIENDS', result.friends) 39 | resolve(result); 40 | callback(); 41 | }) 42 | .catch(function (error) { 43 | reject(error); 44 | }); 45 | }); 46 | } 47 | } 48 | } 49 | export default chat -------------------------------------------------------------------------------- /src/components/Charts/Trend.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 52 | 53 | 83 | -------------------------------------------------------------------------------- /src/views/demo/list/search/SearchLayout.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 65 | 66 | 79 | -------------------------------------------------------------------------------- /src/views/app/admin/Test.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 61 | 62 | -------------------------------------------------------------------------------- /src/views/app/layout/chat/ChatFriendList.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | -------------------------------------------------------------------------------- /src/views/demo/form/stepForm/Step3.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 55 | 70 | -------------------------------------------------------------------------------- /src/core/lazy_lib/components_use.js: -------------------------------------------------------------------------------- 1 | 2 | /* eslint-disable */ 3 | /** 4 | * 该文件是为了按需加载,剔除掉了一些不需要的框架组件。 5 | * 减少了编译支持库包大小 6 | * 7 | * 当需要更多组件依赖时,在该文件加入即可 8 | */ 9 | //import Vue from 'vue' 10 | import { 11 | LocaleProvider, 12 | Layout, 13 | Input, 14 | InputNumber, 15 | Button, 16 | Switch, 17 | Radio, 18 | Checkbox, 19 | Select, 20 | Card, 21 | Form, 22 | Row, 23 | Col, 24 | Modal, 25 | Table, 26 | Tabs, 27 | Icon, 28 | Badge, 29 | Popover, 30 | Dropdown, 31 | List, 32 | Avatar, 33 | Breadcrumb, 34 | Steps, 35 | Spin, 36 | Menu, 37 | Drawer, 38 | Tooltip, 39 | Alert, 40 | Tag, 41 | Divider, 42 | DatePicker, 43 | TimePicker, 44 | Upload, 45 | Progress, 46 | Skeleton, 47 | Popconfirm, 48 | message, 49 | notification 50 | } from 'ant-design-vue' 51 | // import VueCropper from 'vue-cropper' 52 | 53 | Vue.use(LocaleProvider) 54 | Vue.use(Layout) 55 | Vue.use(Input) 56 | Vue.use(InputNumber) 57 | Vue.use(Button) 58 | Vue.use(Switch) 59 | Vue.use(Radio) 60 | Vue.use(Checkbox) 61 | Vue.use(Select) 62 | Vue.use(Card) 63 | Vue.use(Form) 64 | Vue.use(Row) 65 | Vue.use(Col) 66 | Vue.use(Modal) 67 | Vue.use(Table) 68 | Vue.use(Tabs) 69 | Vue.use(Icon) 70 | Vue.use(Badge) 71 | Vue.use(Popover) 72 | Vue.use(Dropdown) 73 | Vue.use(List) 74 | Vue.use(Avatar) 75 | Vue.use(Breadcrumb) 76 | Vue.use(Steps) 77 | Vue.use(Spin) 78 | Vue.use(Menu) 79 | Vue.use(Drawer) 80 | Vue.use(Tooltip) 81 | Vue.use(Alert) 82 | Vue.use(Tag) 83 | Vue.use(Divider) 84 | Vue.use(DatePicker) 85 | Vue.use(TimePicker) 86 | Vue.use(Upload) 87 | Vue.use(Progress) 88 | Vue.use(Skeleton) 89 | Vue.use(Popconfirm) 90 | // Vue.use(VueCropper) 91 | Vue.use(notification) 92 | 93 | Vue.prototype.$confirm = Modal.confirm 94 | Vue.prototype.$message = message 95 | Vue.prototype.$notification = notification 96 | Vue.prototype.$info = Modal.info 97 | Vue.prototype.$success = Modal.success 98 | Vue.prototype.$error = Modal.error 99 | Vue.prototype.$warning = Modal.warning -------------------------------------------------------------------------------- /src/abpZero/shared/helpers/DomHelper.js: -------------------------------------------------------------------------------- 1 | export class DomHelper { 2 | static waitUntilElementIsReady(selector, callback, checkPeriod) { 3 | let selectors = selector.split(','); 4 | let elementCount = selectors.length; 5 | if (!checkPeriod) { 6 | checkPeriod = 100; 7 | } 8 | let checkExist = setInterval(() => { 9 | let foundElementCount = 0; 10 | for (let i = 0; i < selectors.length; i++) { 11 | let selector = selectors[i].trim(); 12 | if (selector[0] === '#') { 13 | let idSelector = selector.replace('#', ''); 14 | foundElementCount = foundElementCount + (document.getElementById(idSelector) ? 1 : 0); 15 | } 16 | else if (selector[0] === '.') { 17 | let classSelector = selector.replace('.', ''); 18 | foundElementCount = foundElementCount + (document.getElementsByClassName(classSelector) ? 1 : 0); 19 | } 20 | } 21 | if (foundElementCount >= elementCount) { 22 | clearInterval(checkExist); 23 | callback(); 24 | } 25 | }, checkPeriod); 26 | } 27 | static createElement(tag, attributes) { 28 | let el = document.createElement(tag); 29 | for (let i = 0; i < attributes.length; i++) { 30 | let attribute = attributes[i]; 31 | el.setAttribute(attribute.key, attribute.value); 32 | } 33 | return el; 34 | } 35 | static getElementByAttributeValue(tag, attribute, value) { 36 | let els = document.getElementsByTagName(tag); 37 | if (!els) { 38 | return undefined; 39 | } 40 | for (let i = 0; i < els.length; i++) { 41 | let el = els[i]; 42 | if (el.getAttribute(attribute) === value) { 43 | return el; 44 | } 45 | } 46 | return undefined; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/views/app/layout/chat/ChatFriendListItem.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | -------------------------------------------------------------------------------- /src/views/demo/account/settings/Custom.vue: -------------------------------------------------------------------------------- 1 | 72 | 73 | 76 | -------------------------------------------------------------------------------- /src/components/Editor/QuillEditor.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 69 | 70 | 83 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | // chart 2 | import Bar from '@/components/Charts/Bar' 3 | import ChartCard from '@/components/Charts/ChartCard' 4 | import Liquid from '@/components/Charts/Liquid' 5 | import MiniArea from '@/components/Charts/MiniArea' 6 | import MiniSmoothArea from '@/components/Charts/MiniSmoothArea' 7 | import MiniBar from '@/components/Charts/MiniBar' 8 | import MiniProgress from '@/components/Charts/MiniProgress' 9 | import Radar from '@/components/Charts/Radar' 10 | import RankList from '@/components/Charts/RankList' 11 | import TransferBar from '@/components/Charts/TransferBar' 12 | import TagCloud from '@/components/Charts/TagCloud' 13 | 14 | // pro components 15 | import AvatarList from '@/components/AvatarList' 16 | import CountDown from '@/components/CountDown' 17 | import Ellipsis from '@/components/Ellipsis' 18 | import FooterToolbar from '@/components/FooterToolbar' 19 | import NumberInfo from '@/components/NumberInfo' 20 | import DescriptionList from '@/components/DescriptionList' 21 | import Tree from '@/components/Tree/Tree' 22 | import Trend from '@/components/Trend' 23 | import STable from '@/components/Table' 24 | import MultiTab from '@/components/MultiTab' 25 | import Result from '@/components/Result' 26 | import IconSelector from '@/components/IconSelector' 27 | import TagSelect from '@/components/TagSelect' 28 | import ExceptionPage from '@/components/Exception' 29 | import StandardFormRow from '@/components/StandardFormRow' 30 | import ArticleListContent from '@/components/ArticleListContent' 31 | 32 | export { 33 | AvatarList, 34 | Bar, 35 | ChartCard, 36 | Liquid, 37 | MiniArea, 38 | MiniSmoothArea, 39 | MiniBar, 40 | MiniProgress, 41 | Radar, 42 | TagCloud, 43 | RankList, 44 | TransferBar, 45 | Trend, 46 | CountDown, 47 | Ellipsis, 48 | FooterToolbar, 49 | NumberInfo, 50 | DescriptionList, 51 | // 兼容写法,请勿继续使用 52 | DescriptionList as DetailList, 53 | Tree, 54 | STable, 55 | MultiTab, 56 | Result, 57 | ExceptionPage, 58 | IconSelector, 59 | TagSelect, 60 | StandardFormRow, 61 | ArticleListContent 62 | } 63 | -------------------------------------------------------------------------------- /docs/load-on-demand.md: -------------------------------------------------------------------------------- 1 | 按需加载 减小打包 2 | ==== 3 | 4 | 5 | 6 | ## 按需引入组件依赖 7 | 8 | `Ant Design Pro Vue` 默认编码工作并不支持按需引入,不过可以通过以下操作结合 [Ant Design Of Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/) 官方文档来进行按需引入。 9 | 10 | - 增加项目按需引入依赖 11 | - 修改引入组件方式 12 | 13 | 14 | 15 | 1. 增加按需引入所需依赖 `babel-plugin-import` 16 | 并且修改文件 `babel.config.js` 17 | ```ecmascript 6 18 | module.exports = { 19 | presets: [ 20 | '@vue/app' 21 | ], 22 | plugins: [ 23 | [ "import", { 24 | "libraryName": "ant-design-vue", 25 | "libraryDirectory": "es", 26 | "style": "css" 27 | } ] 28 | ] 29 | } 30 | ``` 31 | 32 | 33 | 2. 修改引入组件方式 (注意,这只是一个例子,请完整引入你所需要的组件) 34 | 35 | 文件 `@/core/lazy_lib/component_use.js` 36 | 37 | ```javascript 38 | import Vue from 'vue' 39 | import { 40 | Input, 41 | Button, 42 | Select, 43 | Card, 44 | Form, 45 | Row, 46 | Col, 47 | Modal, 48 | Table, 49 | notification 50 | } from 'ant-design-vue' 51 | 52 | Vue.use(Input) 53 | Vue.use(Button) 54 | Vue.use(Select) 55 | Vue.use(Card) 56 | Vue.use(Form) 57 | Vue.use(Row) 58 | Vue.use(Col) 59 | Vue.use(Modal) 60 | Vue.use(Table) 61 | Vue.use(notification) 62 | 63 | Vue.prototype.$notification = notification; 64 | ``` 65 | 66 | 67 | 3. 最后在 `main.js` 中引入 `@/core/lazy_use.js` 文件即可,如下 68 | 69 | ```javascript 70 | 71 | import Vue from 'vue' 72 | import App from './App' 73 | 74 | // 引入 按需组件的统一引入文件 75 | import './core/lazy_use' 76 | 77 | import './style/index.less' 78 | 79 | 80 | Vue.config.productionTip = false 81 | 82 | new Vue({ 83 | render: h => h(App), 84 | }).$mount('#app') 85 | 86 | ``` 87 | 88 | 89 | 90 | ## 其他 减少打包大小 91 | 92 | 93 | 94 | 1. Ant Design Vue 1.2.x 版本起,采用的 ant-design 官方方案 svg Icon 组件,整个项目打包会变大很多,图标进行按需加载可参考 https://github.com/HeskeyBaozi/reduce-antd-icons-bundle-demo 95 | 2. moment 按需加载 可参考 https://github.com/jmblog/how-to-optimize-momentjs-with-webpack 96 | -------------------------------------------------------------------------------- /src/components/ArticleListContent/ArticleListContent.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 47 | 48 | 90 | -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/static/abp-web-resources/abp.notify.js: -------------------------------------------------------------------------------- 1 | var abp = abp || {}; 2 | (function () { 3 | 4 | /* DEFAULTS *************************************************/ 5 | 6 | var defaultOptions = { 7 | position: 'bottom-end', 8 | showConfirmButton: false, 9 | timer: 3000, 10 | padding: 0, 11 | toast: true, 12 | animation: false 13 | }; 14 | 15 | /* NOTIFICATION *********************************************/ 16 | 17 | var showNotification = function (type, message, title, options) { 18 | var icon = options.imageClass ? '' : ''; 19 | 20 | if (title) { 21 | options.title = icon + '' + title + ''; 22 | } 23 | 24 | options.html = (title ? '' : icon) + '' + message + ''; 25 | var combinedOptions = Object.assign(defaultOptions, options); 26 | 27 | swal(combinedOptions); 28 | }; 29 | 30 | abp.notify.success = function (message, title, options) { 31 | showNotification('success', message, title, 32 | Object.assign({ 33 | background: '#34bfa3', 34 | imageClass: 'fa fa-check-circle' 35 | }, options)); 36 | }; 37 | 38 | abp.notify.info = function (message, title, options) { 39 | showNotification('info', message, title, Object.assign({ 40 | background: '#36a3f7', 41 | imageClass: 'fa fa-info-circle' 42 | }, options)); 43 | }; 44 | 45 | abp.notify.warn = function (message, title, options) { 46 | showNotification('warning', message, title, Object.assign({ 47 | background: '#ffb822', 48 | imageClass: 'fa fa-exclamation-triangle' 49 | }, options)); 50 | }; 51 | 52 | abp.notify.error = function (message, title, options) { 53 | showNotification('error', message, title, Object.assign({ 54 | background: '#f4516c', 55 | imageClass: 'fa fa-exclamation-circle' 56 | }, options)); 57 | }; 58 | 59 | })(); 60 | -------------------------------------------------------------------------------- /src/components/IconSelector/IconSelector.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 62 | 63 | 87 | -------------------------------------------------------------------------------- /src/views/demo/account/center/page/Article.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 72 | 73 | 76 | -------------------------------------------------------------------------------- /public/static/FreezeUI/freeze-ui.js: -------------------------------------------------------------------------------- 1 | // taken from https://raw.githubusercontent.com/alexradulescu/FreezeUI 2 | // and modified 3 | (function() { 4 | 5 | /** 6 | * Setup the freeze element to be appended 7 | */ 8 | var freezeHtml = document.createElement('div'); 9 | freezeHtml.classList.add('freeze-ui'); 10 | 11 | /** 12 | * Freezes the UI 13 | * options = { 14 | * element: DOM object 15 | * selector: '.class-name' -> Choose an element where to limit the freeze or leave empty to freeze the whole body. Make sure the element has position relative or absolute, 16 | * text: 'Magic is happening' -> Choose any text to show or use the default "Loading". Be careful for long text as it will break the design. 17 | * } 18 | */ 19 | window.FreezeUI = function(options) { 20 | if(!options){ 21 | options = {}; 22 | } 23 | 24 | var parent; 25 | 26 | if (options.element) { 27 | parent = options.element; 28 | } else { 29 | parent = document.querySelector(options.selector) || document.body; 30 | } 31 | 32 | freezeHtml.setAttribute('data-text', options.text || 'Loading'); 33 | if (document.querySelector(options.selector) || options.element) { 34 | freezeHtml.style.position = 'absolute'; 35 | } 36 | parent.appendChild(freezeHtml); 37 | }; 38 | 39 | /** 40 | * Unfreezes the UI. 41 | */ 42 | window.UnFreezeUI = function(options) { 43 | if(!options){ 44 | options = {}; 45 | } 46 | 47 | var freezeHtml; 48 | if(options.element){ 49 | freezeHtml = options.element.querySelector('.freeze-ui'); 50 | } else { 51 | freezeHtml = document.querySelector('.freeze-ui'); 52 | } 53 | 54 | if (freezeHtml) { 55 | freezeHtml.classList.add('is-unfreezing'); 56 | setTimeout(function() { 57 | if (freezeHtml) { 58 | freezeHtml.classList.remove('is-unfreezing'); 59 | if (freezeHtml.parentElement) { 60 | freezeHtml.parentElement.removeChild(freezeHtml); 61 | } 62 | } 63 | }, 250); 64 | } 65 | }; 66 | })(); 67 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 |
19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /public/static/abp-web-resources/abp.sweet-alert.js: -------------------------------------------------------------------------------- 1 | var abp = abp || {}; 2 | (function () { 3 | var showMessage = function (type, message, title, isHtml, opts) { 4 | 5 | if (!title) { 6 | title = message; 7 | message = undefined; 8 | } 9 | 10 | opts = opts || {}; 11 | opts.title = title; 12 | opts.type = type; 13 | opts.confirmButtonText = opts.confirmButtonText || abp.localization.abpWeb('Ok'); 14 | 15 | if (isHtml) { 16 | opts.html = message; 17 | } else { 18 | opts.text = message; 19 | } 20 | 21 | return swal(opts); 22 | }; 23 | 24 | abp.message.info = function (message, title, isHtml, opts) { 25 | return showMessage('info', message, title, isHtml, opts); 26 | }; 27 | 28 | abp.message.success = function (message, title, isHtml, opts) { 29 | return showMessage('success', message, title, isHtml, opts); 30 | }; 31 | 32 | abp.message.warn = function (message, title, isHtml, opts) { 33 | return showMessage('warning', message, title, isHtml, opts); 34 | }; 35 | 36 | abp.message.error = function (message, title, isHtml, opts) { 37 | return showMessage('error', message, title, isHtml, opts); 38 | }; 39 | 40 | abp.message.confirm = function (message, titleOrCallback, callback, isHtml, opts) { 41 | 42 | var title = undefined; 43 | 44 | if (typeof titleOrCallback === "function") { 45 | callback = titleOrCallback; 46 | } 47 | else if (titleOrCallback) { 48 | title = titleOrCallback; 49 | }; 50 | 51 | opts = opts || {}; 52 | opts.title = title ? title : abp.localization.abpWeb('AreYouSure'); 53 | opts.type = 'warning'; 54 | 55 | opts.confirmButtonText = opts.confirmButtonText || abp.localization.abpWeb('Yes'); 56 | opts.cancelButtonText = opts.cancelButtonText || abp.localization.abpWeb('Cancel'); 57 | opts.showCancelButton = true; 58 | 59 | if (isHtml) { 60 | opts.html = message; 61 | } else { 62 | opts.text = message; 63 | } 64 | 65 | return swal(opts).then(function(result) { 66 | callback && callback(result.value); 67 | }); 68 | }; 69 | })(); 70 | -------------------------------------------------------------------------------- /src/utils/mixin.js: -------------------------------------------------------------------------------- 1 | // import Vue from 'vue' 2 | import { deviceEnquire, DEVICE_TYPE } from '@/utils/device' 3 | import { mapState } from 'vuex' 4 | 5 | // const mixinsComputed = Vue.config.optionMergeStrategies.computed 6 | // const mixinsMethods = Vue.config.optionMergeStrategies.methods 7 | 8 | const mixin = { 9 | computed: { 10 | ...mapState({ 11 | layoutMode: state => state.app.layout, 12 | navTheme: state => state.app.theme, 13 | primaryColor: state => state.app.color, 14 | colorWeak: state => state.app.weak, 15 | fixedHeader: state => state.app.fixedHeader, 16 | fixSiderbar: state => state.app.fixSiderbar, 17 | fixSidebar: state => state.app.fixSiderbar, 18 | contentWidth: state => state.app.contentWidth, 19 | autoHideHeader: state => state.app.autoHideHeader, 20 | sidebarOpened: state => state.app.sidebar, 21 | multiTab: state => state.app.multiTab 22 | }) 23 | }, 24 | methods: { 25 | isTopMenu () { 26 | return this.layoutMode === 'topmenu' 27 | }, 28 | isSideMenu () { 29 | return !this.isTopMenu() 30 | } 31 | } 32 | } 33 | 34 | const mixinDevice = { 35 | computed: { 36 | ...mapState({ 37 | device: state => state.app.device 38 | }) 39 | }, 40 | methods: { 41 | isMobile () { 42 | return this.device === DEVICE_TYPE.MOBILE 43 | }, 44 | isDesktop () { 45 | return this.device === DEVICE_TYPE.DESKTOP 46 | }, 47 | isTablet () { 48 | return this.device === DEVICE_TYPE.TABLET 49 | } 50 | } 51 | } 52 | 53 | const AppDeviceEnquire = { 54 | mounted () { 55 | const { $store } = this 56 | deviceEnquire(deviceType => { 57 | switch (deviceType) { 58 | case DEVICE_TYPE.DESKTOP: 59 | $store.commit('TOGGLE_DEVICE', 'desktop') 60 | $store.dispatch('setSidebar', true) 61 | break 62 | case DEVICE_TYPE.TABLET: 63 | $store.commit('TOGGLE_DEVICE', 'tablet') 64 | $store.dispatch('setSidebar', false) 65 | break 66 | case DEVICE_TYPE.MOBILE: 67 | default: 68 | $store.commit('TOGGLE_DEVICE', 'mobile') 69 | $store.dispatch('setSidebar', true) 70 | break 71 | } 72 | }) 73 | } 74 | } 75 | 76 | export { mixin, AppDeviceEnquire, mixinDevice } 77 | -------------------------------------------------------------------------------- /src/views/demo/list/modules/TaskForm.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 80 | -------------------------------------------------------------------------------- /src/components/AvatarList/index.md: -------------------------------------------------------------------------------- 1 | # AvatarList 用户头像列表 2 | 3 | 4 | 一组用户头像,常用在项目/团队成员列表。可通过设置 `size` 属性来指定头像大小。 5 | 6 | 7 | 8 | 引用方式: 9 | 10 | ```javascript 11 | import AvatarList from '@/components/AvatarList' 12 | const AvatarListItem = AvatarList.AvatarItem 13 | 14 | export default { 15 | components: { 16 | AvatarList, 17 | AvatarListItem 18 | } 19 | } 20 | ``` 21 | 22 | 23 | 24 | ## 代码演示 [demo](https://pro.loacg.com/test/home) 25 | 26 | ```html 27 | 28 | 29 | 30 | 31 | 32 | ``` 33 | 或 34 | ```html 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ``` 45 | 46 | 47 | 48 | ## API 49 | 50 | ### AvatarList 51 | 52 | | 参数 | 说明 | 类型 | 默认值 | 53 | | ---------------- | -------- | ---------------------------------- | --------- | 54 | | size | 头像大小 | `large`、`small` 、`mini`, `default` | `default` | 55 | | maxLength | 要显示的最大项目 | number | - | 56 | | excessItemsStyle | 多余的项目风格 | CSSProperties | - | 57 | 58 | ### AvatarList.Item 59 | 60 | | 参数 | 说明 | 类型 | 默认值 | 61 | | ---- | ------ | --------- | --- | 62 | | tips | 头像展示文案 | string | - | 63 | | src | 头像图片连接 | string | - | 64 | 65 | -------------------------------------------------------------------------------- /src/permission.js: -------------------------------------------------------------------------------- 1 | //import Vue from 'vue' 2 | import router from './router' 3 | import store from './store' 4 | 5 | import NProgress from 'nprogress' // progress bar 6 | import 'nprogress/nprogress.css' // progress bar style 7 | //import notification from 'ant-design-vue/es/notification' 8 | import { setDocumentTitle, domTitle } from '@/utils/domUtil' 9 | //import { ACCESS_TOKEN } from '@/store/mutation-types' 10 | 11 | NProgress.configure({ showSpinner: false }) // NProgress Configuration 12 | 13 | const whiteList = ['login', 'register', 'registerResult'] // no redirect whitelist 14 | 15 | 16 | router.beforeEach((to, from, next) => { 17 | NProgress.start() // start progress bar 18 | to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`)) 19 | const token = store.state.appSession.user; 20 | if (token) { 21 | /* has token */ 22 | if (to.path === '/user/login') { 23 | next({ path: '/' }) 24 | NProgress.done() 25 | } else { 26 | if(store.state.permission.hasAddRouters){ 27 | if(to.meta.keepAlive){ 28 | store.dispatch('addCachedView', to); 29 | } 30 | next() 31 | }else{ 32 | const permissionList = abp.auth.grantedPermissions //暂时没用上,直接取 33 | store.dispatch('GenerateRoutes', { permissionList }).then(() => { 34 | // 根据roles权限生成可访问的路由表 35 | // 动态添加可访问路由表 36 | router.addRoutes(store.getters.addRouters) 37 | const redirect = decodeURIComponent(from.query.redirect || to.path) 38 | if(to.meta.keepAlive){ 39 | store.dispatch('addCachedView', to); 40 | } 41 | if (to.path === redirect) { 42 | // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record 43 | next({ ...to, replace: true }) 44 | } else { 45 | // 跳转到目的路由 46 | next({ path: redirect }) 47 | } 48 | }) 49 | } 50 | } 51 | } else { 52 | if (whiteList.includes(to.name)) { 53 | // 在免登录白名单,直接进入 54 | next() 55 | } else { 56 | next({ path: '/user/login', query: { redirect: to.fullPath } }) 57 | NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it 58 | } 59 | } 60 | }) 61 | 62 | router.afterEach(() => { 63 | NProgress.done() // finish progress bar 64 | }) 65 | -------------------------------------------------------------------------------- /src/abpZero/shared/abpMixin.js: -------------------------------------------------------------------------------- 1 | import { AppConsts } from '@/abpZero/shared/AppConsts'; 2 | import { PermissionCheckerService } from '@/abpZero/abp-vue-module/auth/permission-checker.service'; 3 | import { LocalizationService } from '@/abpZero/abp-vue-module/localization/localization.service'; 4 | import { ArrayToTreeConverterService } from '@/abpZero/utils/array-to-tree-converter.service.js'; 5 | import * as moment from 'moment'; 6 | let _localization=new LocalizationService(); 7 | let _permission=new PermissionCheckerService(); 8 | let _arrayToTreeConverterService=new ArrayToTreeConverterService(); 9 | const abpMixin={ 10 | methods: { 11 | l(key, ...args) { 12 | args.unshift(key); 13 | args.unshift(AppConsts.localization.defaultLocalizationSourceName); 14 | return this.ls.apply(this, args); 15 | }, 16 | ls(sourcename, key, ...args) { 17 | let localizedText = _localization.localize(key, sourcename); 18 | if (!localizedText) { 19 | localizedText = key; 20 | } 21 | if (!args || !args.length) { 22 | return localizedText; 23 | } 24 | args.unshift(localizedText); 25 | return abp.utils.formatString.apply(this, args); 26 | }, 27 | isGranted(permissionName) { 28 | return _permission.isGranted(permissionName); 29 | }, 30 | isGrantedAny(...permissions) { 31 | if (!permissions) { 32 | return false; 33 | } 34 | for (const permission of permissions) { 35 | if (this.isGranted(permission)) { 36 | return true; 37 | } 38 | } 39 | return false; 40 | }, 41 | s(key) { 42 | return abp.setting.get(key); 43 | }, 44 | dateTimeFormat(time,foramt){ 45 | if(time==="" || time===null || time===undefined){ 46 | return time; 47 | } 48 | if(foramt===undefined){ 49 | foramt='YYYY-MM-DD HH:mm:ss'; 50 | } 51 | return moment(time).format(foramt); 52 | }, 53 | arrayToTreeConverter(array, parentIdProperty, idProperty, parentIdValue, childrenProperty,warp,mappingList){ 54 | return _arrayToTreeConverterService.createTree(array, parentIdProperty, idProperty, parentIdValue, childrenProperty,warp,mappingList); 55 | } 56 | } 57 | } 58 | export default abpMixin; -------------------------------------------------------------------------------- /src/views/demo/other/modules/OrgModal.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 101 | -------------------------------------------------------------------------------- /src/components/Result/Result.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 59 | 60 | 110 | -------------------------------------------------------------------------------- /src/components/CountDown/CountDown.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 99 | 100 | 103 | -------------------------------------------------------------------------------- /src/abpZero/shared/common/session/app-session.service.js: -------------------------------------------------------------------------------- 1 | import { AbpMultiTenancyService } from '@/abpZero/abp-vue-module/multi-tenancy/abp-multi-tenancy.service'; 2 | import { SessionServiceProxy } from '@/abpZero/shared/service-proxies/SessionServiceProxy'; 3 | // 此服务修改放到store 的 appSession 里 4 | let AppSessionService = class AppSessionService { 5 | constructor() { 6 | this._sessionService = new SessionServiceProxy(); 7 | this._abpMultiTenancyService = new AbpMultiTenancyService(); 8 | } 9 | get application() { 10 | return this._application; 11 | } 12 | get user() { 13 | return this._user; 14 | } 15 | get userId() { 16 | return this.user ? this.user.id : null; 17 | } 18 | get tenant() { 19 | return this._tenant; 20 | } 21 | get tenancyName() { 22 | return this._tenant ? this.tenant.tenancyName : ''; 23 | } 24 | get tenantId() { 25 | return this.tenant ? this.tenant.id : null; 26 | } 27 | getShownLoginName() { 28 | const userName = this._user.userName; 29 | if (!this._abpMultiTenancyService.isEnabled) { 30 | return userName; 31 | } 32 | return (this._tenant ? this._tenant.tenancyName : '.') + '\\' + userName; 33 | } 34 | get theme() { 35 | return this._theme; 36 | } 37 | init() { 38 | return new Promise((resolve, reject) => { 39 | this._sessionService.getCurrentLoginInformations() 40 | .then(result=>{ 41 | this._application = result.application; 42 | this._user = result.user; 43 | this._tenant = result.tenant; 44 | this._theme = result.theme; 45 | resolve(result.theme); 46 | }) 47 | .catch(function (error) { 48 | reject(error); 49 | }); 50 | }); 51 | } 52 | changeTenantIfNeeded(tenantId) { 53 | if (this.isCurrentTenant(tenantId)) { 54 | return false; 55 | } 56 | abp.multiTenancy.setTenantIdCookie(tenantId); 57 | location.reload(); 58 | return true; 59 | } 60 | isCurrentTenant(tenantId) { 61 | let isTenant = tenantId > 0; 62 | if (!isTenant && !this.tenant) { 63 | return true; 64 | } 65 | if (!tenantId && this.tenant) { 66 | return false; 67 | } 68 | else if (tenantId && (!this.tenant || this.tenant.id !== tenantId)) { 69 | return false; 70 | } 71 | return true; 72 | } 73 | }; 74 | export { AppSessionService }; 75 | -------------------------------------------------------------------------------- /src/views/app/layout/chat/AddChatFriends.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 31 | 32 | -------------------------------------------------------------------------------- /src/components/tools/TwoStepCaptcha.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 83 | 90 | -------------------------------------------------------------------------------- /src/store/modules/tagsView.js: -------------------------------------------------------------------------------- 1 | const tagsView = { 2 | state: { 3 | cachedViews: [] 4 | }, 5 | mutations: { 6 | 7 | ADD_CACHED_VIEW: (state, view) => { 8 | if (state.cachedViews.includes(view.name)){ 9 | return 10 | } 11 | if(view.name){ 12 | state.cachedViews.push(view.name) 13 | } 14 | }, 15 | DEL_CACHED_VIEW: (state, view) => { 16 | for (const i of state.cachedViews) { 17 | if (i === view.name) { 18 | const index = state.cachedViews.indexOf(i) 19 | state.cachedViews.splice(index, 1) 20 | break 21 | } 22 | } 23 | }, 24 | DEL_OTHERS_CACHED_VIEWS: (state, view) => { 25 | for (const i of state.cachedViews) { 26 | if (i === view.name) { 27 | const index = state.cachedViews.indexOf(i) 28 | state.cachedViews = state.cachedViews.slice(index, index + 1) 29 | break 30 | } 31 | } 32 | }, 33 | 34 | DEL_ALL_CACHED_VIEWS: state => { 35 | state.cachedViews = [] 36 | } 37 | 38 | }, 39 | actions: { 40 | addView({ dispatch }, view) { 41 | dispatch('addCachedView', view) 42 | }, 43 | addCachedView({ commit }, view) { 44 | commit('ADD_CACHED_VIEW', view) 45 | }, 46 | 47 | delView({ dispatch, state }, view) { 48 | return new Promise(resolve => { 49 | dispatch('delCachedView', view) 50 | resolve({ 51 | cachedViews: [...state.cachedViews] 52 | }) 53 | }) 54 | }, 55 | delCachedView({ commit, state }, view) { 56 | return new Promise(resolve => { 57 | commit('DEL_CACHED_VIEW', view) 58 | resolve([...state.cachedViews]) 59 | }) 60 | }, 61 | 62 | delOthersViews({ dispatch, state }, view) { 63 | return new Promise(resolve => { 64 | dispatch('delOthersCachedViews', view) 65 | resolve({ 66 | cachedViews: [...state.cachedViews] 67 | }) 68 | }) 69 | }, 70 | delOthersCachedViews({ commit, state }, view) { 71 | return new Promise(resolve => { 72 | commit('DEL_OTHERS_CACHED_VIEWS', view) 73 | resolve([...state.cachedViews]) 74 | }) 75 | }, 76 | 77 | delAllViews({ dispatch, state }, view) { 78 | return new Promise(resolve => { 79 | dispatch('delAllCachedViews', view) 80 | resolve({ 81 | cachedViews: [...state.cachedViews] 82 | }) 83 | }) 84 | }, 85 | delAllCachedViews({ commit, state }) { 86 | return new Promise(resolve => { 87 | commit('DEL_ALL_CACHED_VIEWS') 88 | resolve([...state.cachedViews]) 89 | }) 90 | } 91 | } 92 | } 93 | 94 | export default tagsView 95 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // ie polyfill 2 | import '@babel/polyfill' 3 | 4 | //import Vue from 'vue' 5 | import App from './App.vue' 6 | import router from './router' 7 | import store from './store/' 8 | import { VueAxios } from './utils/request' 9 | 10 | // mock 11 | //import './mock' 12 | 13 | import bootstrap from './core/bootstrap' 14 | import './core/use' 15 | import './permission' // permission control 16 | import './utils/filter' // global filter 17 | 18 | Vue.config.productionTip = false 19 | 20 | // mount axios Vue.$http and this.$http 21 | //Vue.use(VueAxios) 22 | 23 | import './utils/dom/index' 24 | import * as _ from 'lodash' 25 | window._=_; 26 | import * as moment from 'moment'; 27 | window.moment=moment; 28 | import abpMixins from '@/abpZero/shared/abpMixin' 29 | 30 | Vue.mixin(abpMixins) 31 | 32 | 33 | import HttpClient from '@/abpZero/utils/HttpClient' 34 | window.httpClient=HttpClient 35 | 36 | //import './auth' 37 | /* eslint-disable no-new */ 38 | import appconfig from '@/abpZero/appconfig' 39 | import { AppAuthService } from '@/abpZero/app/shared/common/auth/app-auth.service'; 40 | import { ChatSignalrService } from '@/abpZero/app/shared/common/chat/chat-signalr.service'; 41 | import { UrlHelper } from '@/abpZero/shared/helpers/UrlHelper'; 42 | import { AppPreBootstrap } from '@/abpZero/AppPreBootstrap'; 43 | import { SignalRHelper } from '@/abpZero/shared/helpers/SignalRHelper'; 44 | window._chatSignalrService=new ChatSignalrService();//全局单例 45 | /* eslint-disable no-new */ 46 | (function () { 47 | 48 | abp.ui.setBusy(); 49 | 50 | AppPreBootstrap.run(appconfig, () => { 51 | handleLogoutRequest(new AppAuthService()); 52 | store.dispatch('init') //初始化appSession 53 | .then((result) => { 54 | if (result.user) { 55 | //SignalRHelper.initSignalR(); 56 | SignalRHelper.initSignalR(() => { _chatSignalrService.init(); }); 57 | } 58 | new Vue({ 59 | render: h => h(App), 60 | router, 61 | store, 62 | created: bootstrap, 63 | mounted () { 64 | abp.ui.clearBusy(); 65 | } 66 | }).$mount('#app') 67 | 68 | }).catch(() => {}) 69 | }); 70 | 71 | })(); 72 | 73 | function handleLogoutRequest(authService) { 74 | let currentUrl = UrlHelper.initialUrl; 75 | let returnUrl = UrlHelper.getReturnUrl(); 76 | if (currentUrl.indexOf(('account/logout')) >= 0 && returnUrl) { 77 | authService.logout(true, returnUrl); 78 | } 79 | } 80 | 81 | 82 | 83 | // new Vue({ 84 | // router, 85 | // store, 86 | // created: bootstrap, 87 | // render: h => h(App) 88 | // }).$mount('#app') 89 | --------------------------------------------------------------------------------