diff --git a/.browserslistrc b/.browserslistrc index 214388fe4..ab3713ad8 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,3 +1,5 @@ +# 支持浏览器配置 > 1% last 2 versions not dead + diff --git a/.editorconfig b/.editorconfig index 7053c49a0..7b06489a1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,14 @@ -[*.{js,jsx,ts,tsx,vue}] -indent_style = space +# 编辑器配置 + +root = true + +[*] +charset = utf-8 +end_of_line = lf indent_size = 2 -trim_trailing_whitespace = true +indent_style = space insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js index c1213b324..9a086288c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,5 @@ /** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description .eslintrc.js */ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..1fa0664d7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# .gitattributes +*.html text eol=lf +*.css text eol=lf +*.js text eol=lf +*.scss text eol=lf +*.vue text eol=lf +*.hbs text eol=lf +*.sh text eol=lf +*.md text eol=lf +*.json text eol=lf +*.yml text eol=lf +*.js linguist-language=vue \ No newline at end of file diff --git a/.gitignore b/.gitignore index 403adbc1e..6b55d8359 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,26 @@ +# .gitignore .DS_Store node_modules -/dist - - -# local env files +dist .env.local .env.*.local - -# Log files npm-debug.log* +yarn.lock yarn-debug.log* yarn-error.log* -pnpm-debug.log* - -# Editor directories and files .idea -.vscode *.suo *.ntvs* *.njsproj *.sln *.sw? +public/video +*.zip +*.7z +/src/layouts/components/zx-layouts +/zx-templates +/package-lock.json +/src/styles/themes/green.scss +/src/styles/themes/dark.scss +/src/styles/themes/glory.scss + diff --git a/.stylelintrc.js b/.stylelintrc.js new file mode 100644 index 000000000..f60e172d9 --- /dev/null +++ b/.stylelintrc.js @@ -0,0 +1,6 @@ +/** + * @description stylelint + */ +module.exports = { + extends: ['stylelint-config-recess-order', 'stylelint-config-prettier'], +} diff --git a/README.md b/README.md index 58b19652e..41ef1daf9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# futian +# FOTON ## Project setup ``` @@ -15,10 +15,5 @@ npm run serve npm run build ``` -### Lints and fixes files -``` -npm run lint -``` - ### Customize configuration See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/babel.config.js b/babel.config.js index 973ca3bbc..34ff3d0cd 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,4 +1,5 @@ /** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description babel.config */ module.exports = { diff --git a/mock/controller/ad.js b/mock/controller/ad.js new file mode 100644 index 000000000..93c36ce71 --- /dev/null +++ b/mock/controller/ad.js @@ -0,0 +1,41 @@ +const data = [ + { + title: 'Admin Pro 3.0付费版本已发布,增加多项贴心功能,点我提前体验', + url: + 'https://chu1204505056.gitee.io/admin-pro?hmsr=homeAd&hmpl=&hmcu=&hmkw=&hmci=', + }, + { + title: 'Admin Plus 3.0内测版本已发布,增加多项贴心功能,点我提前体验', + url: + 'https://chu1204505056.gitee.io/admin-plus?hmsr=homeAd&hmpl=&hmcu=&hmkw=&hmci=', + }, + { + title: 'vue-admin-beautiful(antdv) vue3.0版本已发布,点我提前体验', + url: + 'http://vue-admin-beautiful.com/vue-admin-beautiful-antdv?hmsr=homeAd&hmpl=&hmcu=&hmkw=&hmci=', + }, + { + title: 'vue-admin-beautiful(element-plus) vue3.0版本已发布,点我提前体验', + url: + 'https://chu1204505056.gitee.io/admin-plus?hmsr=homeAd&hmpl=&hmcu=&hmkw=&hmci=', + }, + { + title: + '程序无国界,但程序员有国界,中国国家尊严不容挑衅,如果您在特殊时期继续购买HM、耐克、阿迪达斯等品牌那么您将无权继续使用Vab', + url: + 'https://chu1204505056.gitee.io/admin-pro?hmsr=homeAd&hmpl=&hmcu=&hmkw=&hmci=', + }, +] +module.exports = [ + { + url: '/ad/getList', + type: 'get', + response() { + return { + code: 200, + msg: 'success', + data, + } + }, + }, +] diff --git a/mock/controller/changeLog.js b/mock/controller/changeLog.js new file mode 100644 index 000000000..6fbb88d26 --- /dev/null +++ b/mock/controller/changeLog.js @@ -0,0 +1,250 @@ +const data = [ + { + content: '在github上获得了第一个star,感恩一位名叫Bequiet2014的github用户', + timestamp: '2020-03-23', + }, + { + content: '增加更换主题功能', + timestamp: '2020-04-10', + }, + { + content: '大幅精简代码', + timestamp: '2020-04-14', + }, + { + content: '修复群友反馈的bug', + timestamp: '2020-04-16', + }, + { + content: '剔除maptalks', + timestamp: '2020-04-17', + }, + { + content: + '换行符统一修改为lf 支持苹果 linux windows协同开发 强制开启最严格eslint规则 不要哭 严格是有好处的', + timestamp: '2020-04-17', + }, + { + content: '彻底完成手机端适配,记录这一天熬夜到了晚上三点', + timestamp: '2020-04-18', + }, + { + content: + '删除babel-polyfill 提高打包速度 减少压缩体积(放弃ie是这个项目做出的最伟大的决定)', + timestamp: '2020-04-18', + }, + { + content: '源码精简至800k', + timestamp: '2020-04-19', + }, + { + content: '添加视频播放器组件', + timestamp: '2020-04-20', + }, + { + content: '修复路由懒加载 完善主题配色', + timestamp: '2020-04-22', + }, + { + content: '修复全局axios拦截 加快动画展示效果 修改登录页样式', + timestamp: '2020-04-24', + }, + { + content: '简化权限与登录逻辑 更新mockServer', + timestamp: '2020-04-25', + }, + { + content: '优化登录退出逻辑 代码更清晰 退出不再重载网页 改为重载路由形式', + timestamp: '2020-04-26', + }, + { + content: '无端的指责只会让我更加努力 修复sidebar 简化permission', + timestamp: '2020-04-28', + }, + { + content: '又是一个深夜 实现了表格增删改查的一键生成', + timestamp: '2020-04-30', + }, + { + content: '大幅优化tagsview标签动画', + timestamp: '2020-05-02', + }, + { + content: '三种图标组件实现mock模拟分页', + timestamp: '2020-05-03', + }, + { + content: '添加了markdown编辑器组件', + timestamp: '2020-05-04', + }, + { + content: '添加stylelint-plus自动规整排序样式', + timestamp: '2020-05-06', + }, + { + content: '添加商城模板', + timestamp: '2020-05-12', + }, + { + content: 'github标星超过1000 感恩', + timestamp: '2020-05-13', + }, + { + content: '添加验证码组件', + timestamp: '2020-05-14', + }, + { + content: '修复横向菜单bug', + timestamp: '2020-05-16', + }, + { + content: '又被人骂了 挺好的 让我下定决心重写了tabsBar', + timestamp: '2020-05-20', + }, + { + content: '仿ant-design 添加雪花屏', + timestamp: '2020-05-26', + }, + { + content: '添加人员管理模块', + timestamp: '2020-06-02', + }, + { + content: 'github标星超过2000 感恩', + timestamp: '2020-06-03', + }, + { + content: '添加炫酷地图组件', + timestamp: '2020-06-11', + }, + { + content: '抽离更多公共配置,框架使用更顺手', + timestamp: '2020-06-19', + }, + { + content: '彻底完成了tabsBar多标签页的重构', + timestamp: '2020-06-22', + }, + { + content: '感恩github标星过3.0K 祝大家端午节快乐', + timestamp: '2020-06-25', + }, + { + content: '彻底重构了SideBar与TopBar 大幅精简dom渲染逻辑 全球首发', + timestamp: '2020-06-25', + }, + { + content: '添加菜单管理', + timestamp: '2020-07-7', + }, + { + content: '首次采用sass-loader 9.0写法,感谢github用户 shaonialife', + timestamp: '2020-07-7', + }, + { + content: '添加vue-amap组件', + timestamp: '2020-07-11', + }, + { + content: '修改zx-layouts引入方式', + timestamp: '2020-07-15', + }, + { + content: + '记录这一天vue-admin-beautiful在插件市场、百度已花费超过1万元广告费用,希望一切都值得', + timestamp: '2020-07-18', + }, + { + content: '主题配置添加绿荫草场、荣耀典藏、暗黑之子模式', + timestamp: '2020-07-18', + }, + { + content: '全局axios请求全面支持Status Code拦截处理', + timestamp: '2020-07-29', + }, + { + content: '重构全局loadding加载代码', + timestamp: '2020-07-31', + }, + { + content: '升级stylelint自动排序功能', + timestamp: '2020-08-25', + }, + { + content: '修复视频播放器组件重载路由失效的bug', + timestamp: '2020-09-03', + }, + { + content: '修复极个别情况image-loader打包报错', + timestamp: '2020-09-18', + }, + { + content: '全网首个基于vue3.0开发的admin框架已发布,具体请访问github', + timestamp: '2020-09-22', + }, + { + content: '完善路由后端渲染方案,弃用之前写法', + timestamp: '2020-09-30', + }, + { + content: '大版本迭代,请关注github tag', + timestamp: '2020-09-30', + }, + /* { + content: + "面对自称加拿大华人的捕蛇者说作者laike9m的发帖诋毁,和人身攻击,本想去知乎找他理论,可奈何他的知乎已经由于经常发布错误言论被官方封号了https://www.zhihu.com/people/laike9m,既然自己都不是中国人了,就不要妄加揣测国产的开源项目了,一切都交给时间吧,后来我释怀了,laike9m、Cruii这群人不止是抹黑我的框架,甚至还去抹黑中国民营企业的佼佼者华为,充满了无奈啊,也许只有发布这样的言论,他们的目的才能达到把 https://github.com/evil-huawei/evil-huawei", + timestamp: "2020-10-01", + }, */ + { + content: '凌晨两点,我累了,移除无用组件,精简package', + timestamp: '2020-10-02', + }, + { + content: '富文本编辑器支持精细化配置', + timestamp: '2020-10-20', + }, + { + content: '公共布局支持自动导出', + timestamp: '2020-10-23', + }, + { + content: '遮罩添加高斯模糊', + timestamp: '2020-10-25', + }, + { + content: 'vue3.0 + element-plus版本正式上线', + timestamp: '2020-12-5', + }, + { + content: '完成首页重构,跟进echarts 5.0', + timestamp: '2020-12-7', + }, + { + content: '更新sass版本至最新', + timestamp: '2021-1-7', + }, + { + content: '更新vue-echarts版本至最新,options变更为option', + timestamp: '2021-2-23', + }, + { + content: + '开源版品牌名升级为vue-admin-beautiful-pro,付费版vue2.x品牌升级为Admin Pro,付费版vue3.x品牌名升级为Admin Plus', + timestamp: '最近更新', + }, +] + +module.exports = [ + { + url: '/changeLog/getList', + type: 'post', + response() { + return { + code: 200, + msg: 'success', + totalCount: 999, + data: data, + } + }, + }, +] diff --git a/mock/controller/colorfulIcon.js b/mock/controller/colorfulIcon.js new file mode 100644 index 000000000..8d7625f1c --- /dev/null +++ b/mock/controller/colorfulIcon.js @@ -0,0 +1,324 @@ +const data = [ + 'alphabetical_sorting', + 'alphabetical_sorting', + 'alarm_clock', + 'area_chart', + 'approval', + 'answers', + 'approve', + 'assistant', + 'automotive', + 'automatic', + 'bad_decision', + 'bar_chart', + 'bearish', + 'biomass', + 'biohazard', + 'binoculars', + 'bookmark', + 'briefcase', + 'biotech', + 'broken_link', + 'business', + 'bullish', + 'business_contact', + 'businesswoman', + 'cable_release', + 'calculator', + 'businessman', + 'calendar', + 'butting_in', + 'call_transfer', + 'callback', + 'camcorder', + 'camera', + 'camcorder_pro', + 'cancel', + 'camera_addon', + 'camera_identificatio', + 'capacitor', + 'candle_sticks', + 'checkmark', + 'circuit', + 'charge_battery', + 'clear_filters', + 'clapperboard', + 'clock', + 'close_up_mode', + 'collaboration', + 'cell_phone', + 'collapse', + 'collect', + 'cloth', + 'combo_chart', + 'comments', + 'conference_call', + 'compact_camera', + 'contacts', + 'copyleft', + 'copyright', + 'crystal_oscillator', + 'cursor', + 'currency_exchange', + 'customer_support', + 'dam', + 'data_backup', + 'data_configuration', + 'data_encryption', + 'data_protection', + 'data_recovery', + 'database', + 'data_sheet', + 'debt', + 'decision', + 'delete_column', + 'delete_database', + 'department', + 'delete_row', + 'deployment', + 'dislike', + 'disapprove', + 'disclaimer', + 'display', + 'document', + 'do_not_insert', + 'do_not_mix', + 'do_not_inhale', + 'donate', + 'down', + 'doughnut_chart', + 'down_left', + 'down_right', + 'download', + 'edit_image', + 'electrical_sensor', + 'electrical_threshold', + 'electricity', + 'electro_devices', + 'electronics', + 'empty_battery', + 'empty_filter', + 'empty_trash', + 'end_call', + 'engineering', + 'entering_heaven_aliv', + 'expand', + 'export', + 'expired', + 'factory', + 'factory_breakdown', + 'external', + 'faq', + 'feed_in', + 'file', + 'feedback', + 'film', + 'filled_filter', + 'filing_cabinet', + 'film_reel', + 'flash_auto', + 'fine_print', + 'flash_off', + 'flash_on', + 'flow_chart', + 'folder', + 'frame', + 'full_battery', + 'full_trash', + 'gallery', + 'generic_sorting_asc', + 'generic_sorting_desc', + 'genealogy', + 'globe', + 'good_decision', + 'headset', + 'grid', + 'graduation_cap', + 'heat_map', + 'high_priority', + 'high_battery', + 'image_file', + 'home', + 'idea', + 'import', + 'in_transit', + 'integrated_webcam', + 'inspection', + 'invite', + 'internal', + 'ipad', + 'info', + 'iphone', + 'kindle', + 'key', + 'landscape', + 'left', + 'left_down', + 'left_up', + 'leave', + 'like_placeholder', + 'light_at_the_end_of_', + 'library', + 'line_chart', + 'link', + 'like', + 'lock', + 'list', + 'lock_landscape', + 'low_battery', + 'lock_portrait', + 'low_priority', + 'make_decision', + 'medium_priority', + 'manager', + 'menu', + 'middle_battery', + 'minus', + 'missed_call', + 'mind_map', + 'mms', + 'multiple_cameras', + 'money_transfer', + 'music', + 'multiple_devices', + 'multiple_smartphones', + 'multiple_inputs', + 'negative_dynamic', + 'neutral_decision', + 'night_landscape', + 'news', + 'neutral_trading', + 'night_portrait', + 'no_idea', + 'next', + 'no_video', + 'nook', + 'ok', + 'org_unit', + 'opened_folder', + 'old_time_camera', + 'online_support', + 'organization', + 'package', + 'paid', + 'parallel_tasks', + 'overtime', + 'panorama', + 'phone', + 'phone_android', + 'photo_reel', + 'pie_chart', + 'picture', + 'planner', + 'plus', + 'podium_with_audience', + 'podium_without_speak', + 'podium_with_speaker', + 'previous', + 'portrait_mode', + 'positive_dynamic', + 'privacy', + 'process', + 'puzzle', + 'questions', + 'print', + 'radar_plot', + 'rating', + 'ratings', + 'reading', + 'redo', + 'reading_ebook', + 'refresh', + 'registered_trademark', + 'right', + 'reuse', + 'remove_image', + 'right_down', + 'right_up', + 'rotate_to_portrait', + 'rules', + 'rotate_camera', + 'rotate_to_landscape', + 'ruler', + 'scatter_plot', + 'search', + 'safe', + 'self_service_kiosk', + 'selfie', + 'serial_tasks', + 'sales_performance', + 'settings', + 'services', + 'share', + 'shipped', + 'sim_card', + 'shop', + 'service_mark', + 'sim_card_chip', + 'signature', + 'smartphone_tablet', + 'sound_recording_copy', + 'sms', + 'speaker', + 'slr_back_side', + 'start', + 'stack_of_photos', + 'statistics', + 'sports_mode', + 'support', + 'synchronize', + 'switch_camera', + 'survey', + 'tablet_android', + 'template', + 'trademark', + 'todo_list', + 'touchscreen_smartpho', + 'timeline', + 'tree_structure', + 'undo', + 'up_left', + 'two_smartphones', + 'unlock', + 'up', + 'up_right', + 'upload', + 'video_call', + 'video_file', + 'view_details', + 'video_projector', + 'vip', + 'voice_presentation', + 'webcam', + 'voicemail', + 'workflow', + 'about', + 'accept_database', + 'add_image', + 'add_column', + 'add_database', + 'add_row', +] +module.exports = [ + { + url: '/colorfulIcon/getList', + type: 'post', + response(config) { + const { title, pageNo = 1, pageSize = 72 } = config.body + let mockList = data.filter((item) => { + if (title && item.indexOf(title) < 0) return false + return true + }) + const pageList = mockList.filter( + (item, index) => + index < pageSize * pageNo && index >= pageSize * (pageNo - 1) + ) + return { + code: 200, + msg: 'success', + totalCount: mockList.length, + data: pageList, + } + }, + }, +] diff --git a/mock/controller/goodsList.js b/mock/controller/goodsList.js new file mode 100644 index 000000000..82176a355 --- /dev/null +++ b/mock/controller/goodsList.js @@ -0,0 +1,43 @@ +const { mock } = require('mockjs') + +const List = [] +const count = 999 +let num = 0 +for (let i = 0; i < count; i++) { + List.push( + mock({ + uuid: '@uuid', + image: `https://picsum.photos/300/600?random=${num++}`, + title: '@ctitle', + description: '@csentence', + link: 'https://www.baidu.com', + price: '@integer(100, 500)', + 'status|1': [1, 0], + 'isRecommend|1': [1, 0], + }) + ) +} + +module.exports = [ + { + url: '/goodsList/getList', + type: 'post', + response(config) { + const { title = '', pageNo = 1, pageSize = 20 } = config.body + let mockList = List.filter((item) => { + if (title && item.title.indexOf(title) < 0) return false + return true + }) + const pageList = mockList.filter( + (item, index) => + index < pageSize * pageNo && index >= pageSize * (pageNo - 1) + ) + return { + code: 200, + msg: 'success', + totalCount: count, + data: pageList, + } + }, + }, +] diff --git a/mock/controller/icon.js b/mock/controller/icon.js new file mode 100644 index 000000000..365400a22 --- /dev/null +++ b/mock/controller/icon.js @@ -0,0 +1,985 @@ +const data = [ + 'air-freshener', + 'align-center', + 'align-justify', + 'align-left', + 'align-right', + 'allergies', + 'ambulance', + 'american-sign-language-interpreting', + 'anchor', + 'angle-double-down', + 'angle-double-left', + 'angle-double-right', + 'angle-double-up', + 'angle-down', + 'angle-left', + 'angle-right', + 'angle-up', + 'angry', + 'ankh', + 'apple-alt', + 'archive', + 'archway', + 'arrow-alt-circle-down', + 'arrow-alt-circle-left', + 'arrow-alt-circle-right', + 'arrow-alt-circle-up', + 'arrow-circle-down', + 'arrow-circle-left', + 'arrow-circle-right', + 'arrow-circle-up', + 'arrow-down', + 'arrow-left', + 'arrow-right', + 'arrow-up', + 'arrows-alt', + 'arrows-alt-h', + 'arrows-alt-v', + 'assistive-listening-systems', + 'asterisk', + 'at', + 'atlas', + 'atom', + 'audio-description', + 'award', + 'baby', + 'baby-carriage', + 'backspace', + 'backward', + 'bacon', + 'bahai', + 'balance-scale', + 'balance-scale-left', + 'balance-scale-right', + 'ban', + 'band-aid', + 'barcode', + 'bars', + 'baseball-ball', + 'basketball-ball', + 'bath', + 'battery-empty', + 'battery-full', + 'battery-half', + 'battery-quarter', + 'battery-three-quarters', + 'bed', + 'beer', + 'bell', + 'bell-slash', + 'bezier-curve', + 'bible', + 'bicycle', + 'biking', + 'binoculars', + 'biohazard', + 'birthday-cake', + 'blender', + 'blender-phone', + 'blind', + 'blog', + 'bold', + 'bolt', + 'bomb', + 'bone', + 'bong', + 'book', + 'book-dead', + 'book-medical', + 'book-open', + 'book-reader', + 'bookmark', + 'border-all', + 'border-none', + 'border-style', + 'bowling-ball', + 'box', + 'box-open', + 'boxes', + 'braille', + 'brain', + 'bread-slice', + 'briefcase', + 'briefcase-medical', + 'broadcast-tower', + 'broom', + 'brush', + 'bug', + 'building', + 'bullhorn', + 'bullseye', + 'burn', + 'bus', + 'bus-alt', + 'business-time', + 'calculator', + 'calendar', + 'calendar-alt', + 'calendar-check', + 'calendar-day', + 'calendar-minus', + 'calendar-plus', + 'calendar-times', + 'calendar-week', + 'camera', + 'camera-retro', + 'campground', + 'candy-cane', + 'cannabis', + 'capsules', + 'car', + 'car-alt', + 'car-battery', + 'car-crash', + 'car-side', + 'caravan', + 'caret-down', + 'caret-left', + 'caret-right', + 'caret-square-down', + 'caret-square-left', + 'caret-square-right', + 'caret-square-up', + 'caret-up', + 'carrot', + 'cart-arrow-down', + 'cart-plus', + 'cash-register', + 'cat', + 'certificate', + 'chair', + 'chalkboard', + 'chalkboard-teacher', + 'charging-station', + 'chart-area', + 'chart-bar', + 'chart-line', + 'chart-pie', + 'check', + 'check-circle', + 'check-double', + 'check-square', + 'cheese', + 'chess', + 'chess-bishop', + 'chess-board', + 'chess-king', + 'chess-knight', + 'chess-pawn', + 'chess-queen', + 'chess-rook', + 'chevron-circle-down', + 'chevron-circle-left', + 'chevron-circle-right', + 'chevron-circle-up', + 'chevron-down', + 'chevron-left', + 'chevron-right', + 'chevron-up', + 'child', + 'church', + 'circle', + 'circle-notch', + 'city', + 'clinic-medical', + 'clipboard', + 'clipboard-check', + 'clipboard-list', + 'clock', + 'clone', + 'closed-captioning', + 'cloud', + 'cloud-download-alt', + 'cloud-meatball', + 'cloud-moon', + 'cloud-moon-rain', + 'cloud-rain', + 'cloud-showers-heavy', + 'cloud-sun', + 'cloud-sun-rain', + 'cloud-upload-alt', + 'cocktail', + 'code', + 'code-branch', + 'coffee', + 'cog', + 'cogs', + 'coins', + 'columns', + 'comment', + 'comment-alt', + 'comment-dollar', + 'comment-dots', + 'comment-medical', + 'comment-slash', + 'comments', + 'comments-dollar', + 'compact-disc', + 'compass', + 'compress', + 'compress-alt', + 'compress-arrows-alt', + 'concierge-bell', + 'cookie', + 'cookie-bite', + 'copy', + 'copyright', + 'couch', + 'credit-card', + 'crop', + 'crop-alt', + 'cross', + 'crosshairs', + 'crow', + 'crown', + 'crutch', + 'cube', + 'cubes', + 'cut', + 'database', + 'deaf', + 'democrat', + 'desktop', + 'dharmachakra', + 'diagnoses', + 'dice', + 'dice-d20', + 'dice-d6', + 'dice-five', + 'dice-four', + 'dice-one', + 'dice-six', + 'dice-three', + 'dice-two', + 'digital-tachograph', + 'directions', + 'divide', + 'dizzy', + 'dna', + 'dog', + 'dollar-sign', + 'dolly', + 'dolly-flatbed', + 'donate', + 'door-closed', + 'door-open', + 'dot-circle', + 'dove', + 'download', + 'drafting-compass', + 'dragon', + 'draw-polygon', + 'drum', + 'drum-steelpan', + 'drumstick-bite', + 'dumbbell', + 'dumpster', + 'dumpster-fire', + 'dungeon', + 'edit', + 'egg', + 'eject', + 'ellipsis-h', + 'ellipsis-v', + 'envelope', + 'envelope-open', + 'envelope-open-text', + 'envelope-square', + 'equals', + 'eraser', + 'ethernet', + 'euro-sign', + 'exchange-alt', + 'exclamation', + 'exclamation-circle', + 'exclamation-triangle', + 'expand', + 'expand-alt', + 'expand-arrows-alt', + 'external-link-alt', + 'external-link-square-alt', + 'eye', + 'eye-dropper', + 'eye-slash', + 'fan', + 'fast-backward', + 'fast-forward', + 'fax', + 'feather', + 'feather-alt', + 'female', + 'fighter-jet', + 'file', + 'file-alt', + 'file-archive', + 'file-audio', + 'file-code', + 'file-contract', + 'file-csv', + 'file-download', + 'file-excel', + 'file-export', + 'file-image', + 'file-import', + 'file-invoice', + 'file-invoice-dollar', + 'file-medical', + 'file-medical-alt', + 'file-pdf', + 'file-powerpoint', + 'file-prescription', + 'file-signature', + 'file-upload', + 'file-video', + 'file-word', + 'fill', + 'fill-drip', + 'film', + 'filter', + 'fingerprint', + 'fire', + 'fire-alt', + 'fire-extinguisher', + 'first-aid', + 'fish', + 'fist-raised', + 'flag', + 'flag-checkered', + 'flag-usa', + 'flask', + 'flushed', + 'folder', + 'folder-minus', + 'folder-open', + 'folder-plus', + 'font', + 'football-ball', + 'forward', + 'frog', + 'frown', + 'frown-open', + 'funnel-dollar', + 'futbol', + 'gamepad', + 'gas-pump', + 'gavel', + 'gem', + 'genderless', + 'ghost', + 'gift', + 'gifts', + 'glass-cheers', + 'glass-martini', + 'glass-martini-alt', + 'glass-whiskey', + 'glasses', + 'globe', + 'globe-africa', + 'globe-americas', + 'globe-asia', + 'globe-europe', + 'golf-ball', + 'gopuram', + 'graduation-cap', + 'greater-than', + 'greater-than-equal', + 'grimace', + 'grin', + 'grin-alt', + 'grin-beam', + 'grin-beam-sweat', + 'grin-hearts', + 'grin-squint', + 'grin-squint-tears', + 'grin-stars', + 'grin-tears', + 'grin-tongue', + 'grin-tongue-squint', + 'grin-tongue-wink', + 'grin-wink', + 'grip-horizontal', + 'grip-lines', + 'grip-lines-vertical', + 'grip-vertical', + 'guitar', + 'h-square', + 'hamburger', + 'hammer', + 'hamsa', + 'hand-holding', + 'hand-holding-heart', + 'hand-holding-usd', + 'hand-lizard', + 'hand-middle-finger', + 'hand-paper', + 'hand-peace', + 'hand-point-down', + 'hand-point-left', + 'hand-point-right', + 'hand-point-up', + 'hand-pointer', + 'hand-rock', + 'hand-scissors', + 'hand-spock', + 'hands', + 'hands-helping', + 'handshake', + 'hanukiah', + 'hard-hat', + 'hashtag', + 'hat-cowboy', + 'hat-cowboy-side', + 'hat-wizard', + 'hdd', + 'heading', + 'headphones', + 'headphones-alt', + 'headset', + 'heart', + 'heart-broken', + 'heartbeat', + 'helicopter', + 'highlighter', + 'hiking', + 'hippo', + 'history', + 'hockey-puck', + 'holly-berry', + 'home', + 'horse', + 'horse-head', + 'hospital', + 'hospital-alt', + 'hospital-symbol', + 'hot-tub', + 'hotdog', + 'hotel', + 'hourglass', + 'hourglass-end', + 'hourglass-half', + 'hourglass-start', + 'house-damage', + 'hryvnia', + 'i-cursor', + 'ice-cream', + 'icicles', + 'icons', + 'id-badge', + 'id-card', + 'id-card-alt', + 'igloo', + 'image', + 'images', + 'inbox', + 'indent', + 'industry', + 'infinity', + 'info', + 'info-circle', + 'italic', + 'jedi', + 'joint', + 'journal-whills', + 'kaaba', + 'key', + 'keyboard', + 'khanda', + 'kiss', + 'kiss-beam', + 'kiss-wink-heart', + 'kiwi-bird', + 'landmark', + 'language', + 'laptop', + 'laptop-code', + 'laptop-medical', + 'laugh', + 'laugh-beam', + 'laugh-squint', + 'laugh-wink', + 'layer-group', + 'leaf', + 'lemon', + 'less-than', + 'less-than-equal', + 'level-down-alt', + 'level-up-alt', + 'life-ring', + 'lightbulb', + 'link', + 'lira-sign', + 'list', + 'list-alt', + 'list-ol', + 'list-ul', + 'location-arrow', + 'lock', + 'lock-open', + 'long-arrow-alt-down', + 'long-arrow-alt-left', + 'long-arrow-alt-right', + 'long-arrow-alt-up', + 'low-vision', + 'luggage-cart', + 'magic', + 'magnet', + 'mail-bulk', + 'male', + 'map', + 'map-marked', + 'map-marked-alt', + 'map-marker', + 'map-marker-alt', + 'map-pin', + 'map-signs', + 'marker', + 'mars', + 'mars-double', + 'mars-stroke', + 'mars-stroke-h', + 'mars-stroke-v', + 'mask', + 'medal', + 'medkit', + 'meh', + 'meh-blank', + 'meh-rolling-eyes', + 'memory', + 'menorah', + 'mercury', + 'meteor', + 'microchip', + 'microphone', + 'microphone-alt', + 'microphone-alt-slash', + 'microphone-slash', + 'microscope', + 'minus', + 'minus-circle', + 'minus-square', + 'mitten', + 'mobile', + 'mobile-alt', + 'money-bill', + 'money-bill-alt', + 'money-bill-wave', + 'money-bill-wave-alt', + 'money-check', + 'money-check-alt', + 'monument', + 'moon', + 'mortar-pestle', + 'mosque', + 'motorcycle', + 'mountain', + 'mouse', + 'mouse-pointer', + 'mug-hot', + 'music', + 'network-wired', + 'neuter', + 'newspaper', + 'not-equal', + 'notes-medical', + 'object-group', + 'object-ungroup', + 'oil-can', + 'om', + 'otter', + 'outdent', + 'pager', + 'paint-brush', + 'paint-roller', + 'palette', + 'pallet', + 'paper-plane', + 'paperclip', + 'parachute-box', + 'paragraph', + 'parking', + 'passport', + 'pastafarianism', + 'paste', + 'pause', + 'pause-circle', + 'paw', + 'peace', + 'pen', + 'pen-alt', + 'pen-fancy', + 'pen-nib', + 'pen-square', + 'pencil-alt', + 'pencil-ruler', + 'people-carry', + 'pepper-hot', + 'percent', + 'percentage', + 'person-booth', + 'phone', + 'phone-alt', + 'phone-slash', + 'phone-square', + 'phone-square-alt', + 'phone-volume', + 'photo-video', + 'piggy-bank', + 'pills', + 'pizza-slice', + 'place-of-worship', + 'plane', + 'plane-arrival', + 'plane-departure', + 'play', + 'play-circle', + 'plug', + 'plus', + 'plus-circle', + 'plus-square', + 'podcast', + 'poll', + 'poll-h', + 'poo', + 'poo-storm', + 'poop', + 'portrait', + 'pound-sign', + 'power-off', + 'pray', + 'praying-hands', + 'prescription', + 'prescription-bottle', + 'prescription-bottle-alt', + 'print', + 'procedures', + 'project-diagram', + 'puzzle-piece', + 'qrcode', + 'question', + 'question-circle', + 'quidditch', + 'quote-left', + 'quote-right', + 'quran', + 'radiation', + 'radiation-alt', + 'rainbow', + 'random', + 'receipt', + 'record-vinyl', + 'recycle', + 'redo', + 'redo-alt', + 'registered', + 'remove-format', + 'reply', + 'reply-all', + 'republican', + 'restroom', + 'retweet', + 'ribbon', + 'ring', + 'road', + 'robot', + 'rocket', + 'route', + 'rss', + 'rss-square', + 'ruble-sign', + 'ruler', + 'ruler-combined', + 'ruler-horizontal', + 'ruler-vertical', + 'running', + 'rupee-sign', + 'sad-cry', + 'sad-tear', + 'satellite', + 'satellite-dish', + 'save', + 'school', + 'screwdriver', + 'scroll', + 'sd-card', + 'search', + 'search-dollar', + 'search-location', + 'search-minus', + 'search-plus', + 'seedling', + 'server', + 'shapes', + 'share', + 'share-alt', + 'share-alt-square', + 'share-square', + 'shekel-sign', + 'shield-alt', + 'ship', + 'shipping-fast', + 'shoe-prints', + 'shopping-bag', + 'shopping-basket', + 'shopping-cart', + 'shower', + 'shuttle-van', + 'sign', + 'sign-in-alt', + 'sign-language', + 'sign-out-alt', + 'signal', + 'signature', + 'sim-card', + 'sitemap', + 'skating', + 'skiing', + 'skiing-nordic', + 'skull', + 'skull-crossbones', + 'slash', + 'sleigh', + 'sliders-h', + 'smile', + 'smile-beam', + 'smile-wink', + 'smog', + 'smoking', + 'smoking-ban', + 'sms', + 'snowboarding', + 'snowflake', + 'snowman', + 'snowplow', + 'socks', + 'solar-panel', + 'sort', + 'sort-alpha-down', + 'sort-alpha-down-alt', + 'sort-alpha-up', + 'sort-alpha-up-alt', + 'sort-amount-down', + 'sort-amount-down-alt', + 'sort-amount-up', + 'sort-amount-up-alt', + 'sort-down', + 'sort-numeric-down', + 'sort-numeric-down-alt', + 'sort-numeric-up', + 'sort-numeric-up-alt', + 'sort-up', + 'spa', + 'space-shuttle', + 'spell-check', + 'spider', + 'spinner', + 'splotch', + 'spray-can', + 'square', + 'square-full', + 'square-root-alt', + 'stamp', + 'star', + 'star-and-crescent', + 'star-half', + 'star-half-alt', + 'star-of-david', + 'star-of-life', + 'step-backward', + 'step-forward', + 'stethoscope', + 'sticky-note', + 'stop', + 'stop-circle', + 'stopwatch', + 'store', + 'store-alt', + 'stream', + 'street-view', + 'strikethrough', + 'stroopwafel', + 'subscript', + 'subway', + 'suitcase', + 'suitcase-rolling', + 'sun', + 'superscript', + 'surprise', + 'swatchbook', + 'swimmer', + 'swimming-pool', + 'synagogue', + 'sync', + 'sync-alt', + 'syringe', + 'table', + 'table-tennis', + 'tablet', + 'tablet-alt', + 'tablets', + 'tachometer-alt', + 'tag', + 'tags', + 'tape', + 'tasks', + 'taxi', + 'teeth', + 'teeth-open', + 'temperature-high', + 'temperature-low', + 'tenge', + 'terminal', + 'text-height', + 'text-width', + 'th', + 'th-large', + 'th-list', + 'theater-masks', + 'thermometer', + 'thermometer-empty', + 'thermometer-full', + 'thermometer-half', + 'thermometer-quarter', + 'thermometer-three-quarters', + 'thumbs-down', + 'thumbs-up', + 'thumbtack', + 'ticket-alt', + 'times', + 'times-circle', + 'tint', + 'tint-slash', + 'tired', + 'toggle-off', + 'toggle-on', + 'toilet', + 'toilet-paper', + 'toolbox', + 'tools', + 'tooth', + 'torah', + 'torii-gate', + 'tractor', + 'trademark', + 'traffic-light', + 'trailer', + 'train', + 'tram', + 'transgender', + 'transgender-alt', + 'trash', + 'trash-alt', + 'trash-restore', + 'trash-restore-alt', + 'tree', + 'trophy', + 'truck', + 'truck-loading', + 'truck-monster', + 'truck-moving', + 'truck-pickup', + 'tshirt', + 'tty', + 'tv', + 'umbrella', + 'umbrella-beach', + 'underline', + 'undo', + 'undo-alt', + 'universal-access', + 'university', + 'unlink', + 'unlock', + 'unlock-alt', + 'upload', + 'user', + 'user-alt', + 'user-alt-slash', + 'user-astronaut', + 'user-check', + 'user-circle', + 'user-clock', + 'user-cog', + 'user-edit', + 'user-friends', + 'user-graduate', + 'user-injured', + 'user-lock', + 'user-md', + 'user-minus', + 'user-ninja', + 'user-nurse', + 'user-plus', + 'user-secret', + 'user-shield', + 'user-slash', + 'user-tag', + 'user-tie', + 'user-times', + 'users', + 'users-cog', + 'utensil-spoon', + 'utensils', + 'vector-square', + 'venus', + 'venus-double', + 'venus-mars', + 'vial', + 'vials', + 'video', + 'video-slash', + 'vihara', + 'voicemail', + 'volleyball-ball', + 'volume-down', + 'volume-mute', + 'volume-off', + 'volume-up', + 'vote-yea', + 'vr-cardboard', + 'walking', + 'wallet', + 'warehouse', + 'water', + 'wave-square', + 'weight', + 'weight-hanging', + 'wheelchair', + 'wifi', + 'wind', + 'window-close', + 'window-maximize', + 'window-minimize', + 'window-restore', + 'wine-bottle', + 'wine-glass', + 'wine-glass-alt', + 'won-sign', + 'wrench', + 'x-ray', + 'yen-sign', + 'yin-yang', +] +module.exports = [ + { + url: '/icon/getList', + type: 'post', + response(config) { + const { title, pageNo = 1, pageSize = 72 } = config.body + let mockList = data.filter((item) => { + if (title && item.indexOf(title) < 0) return false + return true + }) + const pageList = mockList.filter( + (item, index) => + index < pageSize * pageNo && index >= pageSize * (pageNo - 1) + ) + return { + code: 200, + msg: 'success', + totalCount: mockList.length, + data: pageList, + } + }, + }, +] diff --git a/mock/controller/menuManagement.js b/mock/controller/menuManagement.js new file mode 100644 index 000000000..b86a178da --- /dev/null +++ b/mock/controller/menuManagement.js @@ -0,0 +1,51 @@ +module.exports = [ + { + url: '/menuManagement/getTree', + type: 'post', + response() { + return { + code: 200, + msg: 'success', + totalCount: 999, + data: [ + { + id: 'root', + label: '全部角色', + children: [ + { + id: '@id', + permission: 'admin', + label: 'admin角色', + }, + { + id: '@id', + permission: 'editor', + label: 'editor角色', + }, + ], + }, + ], + } + }, + }, + { + url: '/menuManagement/doEdit', + type: 'post', + response() { + return { + code: 200, + msg: '模拟保存成功', + } + }, + }, + { + url: '/menuManagement/doDelete', + type: 'post', + response() { + return { + code: 200, + msg: '模拟删除成功', + } + }, + }, +] diff --git a/mock/controller/notice.js b/mock/controller/notice.js new file mode 100644 index 000000000..63113cc09 --- /dev/null +++ b/mock/controller/notice.js @@ -0,0 +1,33 @@ +const data = [ + { + title: + '温馨提示:集成版虽功能丰富,但冗余依赖过多,建议开发时使用基础版进行开发 Vab Admin Plus(vue3.0 + element-plus)已发布,欢迎体验:Vab Admin Plus', + closable: false, + type: 'success', + }, + { + title: + '作者寄语:感谢Star,感恩相遇,愿世间美好与我们环环相扣,加油!屏幕前的我们,打破桎梏,坚守初心。其实人生改变命运的机会并没有太多,我们并不是不优秀,我们也并不是一无是处,我们也希望驻足山巅被众人仰望,也许我们缺少的只是一个机会,缺少的只是生命中的导师,我希望这个框架帮助到更多的人,希望有一天,我们面试的时候不再胆怯,希望有一天别人看到的不仅仅是你的努力,还有你的功成名就,出人头地。', + closable: false, + type: 'warning', + }, + { + title: + '随笔:我一直在寻找开源的真谛,我一直再想什么是开源,我一开始觉得免费就是开源,好像又不是。我理解的开源是:你也开源,我也开源,大家一起贡献,相互帮助。我最担心的事情是:我一个小人物,去伺候一众的伸手党,我想,这不是开源该有的氛围。我还太年轻,不懂什么是格局,我只知道,无私的帮助他人,能给我带来快乐,却不能给我带来收入,当然,有时候,快乐对我来说就已经足够了。可惜我是一个人,没有精力帮助到每一个人,可惜这个世界需要赚钱,才能过上平凡的生活,可惜了我的梦想,这个物欲横流的时代,理想主义的我们,即使内心坚决如铁,也似乎寸步难行。', + closable: false, + type: 'success', + }, +] +module.exports = [ + { + url: '/notice/getList', + type: 'post', + response() { + return { + code: 200, + msg: 'success', + data, + } + }, + }, +] diff --git a/mock/controller/personalCenter.js b/mock/controller/personalCenter.js new file mode 100644 index 000000000..0db25e75b --- /dev/null +++ b/mock/controller/personalCenter.js @@ -0,0 +1,42 @@ +const { mock } = require('mockjs') + +module.exports = [ + { + url: '/personalCenter/getList', + type: 'post', + response(config) { + return { + code: 200, + msg: 'success', + totalCount: 999, + data: mock({ + 'data|10': [ + { + id: '@id', + }, + ], + }).data, + } + }, + }, + { + url: '/personalCenter/doEdit', + type: 'post', + response() { + return { + code: 200, + msg: '模拟保存成功', + } + }, + }, + { + url: '/personalCenter/doDelete', + type: 'post', + response() { + return { + code: 200, + msg: '模拟删除成功', + } + }, + }, +] diff --git a/mock/controller/remixIcon.js b/mock/controller/remixIcon.js new file mode 100644 index 000000000..db299ce95 --- /dev/null +++ b/mock/controller/remixIcon.js @@ -0,0 +1,2294 @@ +const data = [ + '24-hours-fill', + '24-hours-line', + '4k-fill', + '4k-line', + 'a-b', + 'account-box-fill', + 'account-box-line', + 'account-circle-fill', + 'account-circle-line', + 'account-pin-box-fill', + 'account-pin-box-line', + 'account-pin-circle-fill', + 'account-pin-circle-line', + 'add-box-fill', + 'add-box-line', + 'add-circle-fill', + 'add-circle-line', + 'add-fill', + 'add-line', + 'admin-fill', + 'admin-line', + 'airplay-fill', + 'airplay-line', + 'alarm-fill', + 'alarm-line', + 'alarm-warning-fill', + 'alarm-warning-line', + 'album-fill', + 'album-line', + 'alert-fill', + 'alert-line', + 'aliens-fill', + 'aliens-line', + 'align-bottom', + 'align-center', + 'align-justify', + 'align-left', + 'align-right', + 'align-top', + 'align-vertically', + 'alipay-fill', + 'alipay-line', + 'amazon-fill', + 'amazon-line', + 'anchor-fill', + 'anchor-line', + 'ancient-gate-fill', + 'ancient-gate-line', + 'ancient-pavilion-fill', + 'ancient-pavilion-line', + 'android-fill', + 'android-line', + 'angularjs-fill', + 'angularjs-line', + 'anticlockwise-2-fill', + 'anticlockwise-2-line', + 'anticlockwise-fill', + 'anticlockwise-line', + 'app-store-fill', + 'app-store-line', + 'apple-fill', + 'apple-line', + 'apps-2-fill', + 'apps-2-line', + 'apps-fill', + 'apps-line', + 'archive-drawer-fill', + 'archive-drawer-line', + 'archive-fill', + 'archive-line', + 'arrow-down-circle-fill', + 'arrow-down-circle-line', + 'arrow-down-fill', + 'arrow-down-line', + 'arrow-down-s-fill', + 'arrow-down-s-line', + 'arrow-drop-down-fill', + 'arrow-drop-down-line', + 'arrow-drop-left-fill', + 'arrow-drop-left-line', + 'arrow-drop-right-fill', + 'arrow-drop-right-line', + 'arrow-drop-up-fill', + 'arrow-drop-up-line', + 'arrow-go-back-fill', + 'arrow-go-back-line', + 'arrow-go-forward-fill', + 'arrow-go-forward-line', + 'arrow-left-circle-fill', + 'arrow-left-circle-line', + 'arrow-left-down-fill', + 'arrow-left-down-line', + 'arrow-left-fill', + 'arrow-left-line', + 'arrow-left-right-fill', + 'arrow-left-right-line', + 'arrow-left-s-fill', + 'arrow-left-s-line', + 'arrow-left-up-fill', + 'arrow-left-up-line', + 'arrow-right-circle-fill', + 'arrow-right-circle-line', + 'arrow-right-down-fill', + 'arrow-right-down-line', + 'arrow-right-fill', + 'arrow-right-line', + 'arrow-right-s-fill', + 'arrow-right-s-line', + 'arrow-right-up-fill', + 'arrow-right-up-line', + 'arrow-up-circle-fill', + 'arrow-up-circle-line', + 'arrow-up-down-fill', + 'arrow-up-down-line', + 'arrow-up-fill', + 'arrow-up-line', + 'arrow-up-s-fill', + 'arrow-up-s-line', + 'artboard-2-fill', + 'artboard-2-line', + 'artboard-fill', + 'artboard-line', + 'article-fill', + 'article-line', + 'aspect-ratio-fill', + 'aspect-ratio-line', + 'asterisk', + 'at-fill', + 'at-line', + 'attachment-2', + 'attachment-fill', + 'attachment-line', + 'auction-fill', + 'auction-line', + 'award-fill', + 'award-line', + 'baidu-fill', + 'baidu-line', + 'ball-pen-fill', + 'ball-pen-line', + 'bank-card-2-fill', + 'bank-card-2-line', + 'bank-card-fill', + 'bank-card-line', + 'bank-fill', + 'bank-line', + 'bar-chart-2-fill', + 'bar-chart-2-line', + 'bar-chart-box-fill', + 'bar-chart-box-line', + 'bar-chart-fill', + 'bar-chart-grouped-fill', + 'bar-chart-grouped-line', + 'bar-chart-horizontal-fill', + 'bar-chart-horizontal-line', + 'bar-chart-line', + 'barcode-box-fill', + 'barcode-box-line', + 'barcode-fill', + 'barcode-line', + 'barricade-fill', + 'barricade-line', + 'base-station-fill', + 'base-station-line', + 'basketball-fill', + 'basketball-line', + 'battery-2-charge-fill', + 'battery-2-charge-line', + 'battery-2-fill', + 'battery-2-line', + 'battery-charge-fill', + 'battery-charge-line', + 'battery-fill', + 'battery-line', + 'battery-low-fill', + 'battery-low-line', + 'battery-saver-fill', + 'battery-saver-line', + 'battery-share-fill', + 'battery-share-line', + 'bear-smile-fill', + 'bear-smile-line', + 'behance-fill', + 'behance-line', + 'bell-fill', + 'bell-line', + 'bike-fill', + 'bike-line', + 'bilibili-fill', + 'bilibili-line', + 'bill-fill', + 'bill-line', + 'billiards-fill', + 'billiards-line', + 'bit-coin-fill', + 'bit-coin-line', + 'blaze-fill', + 'blaze-line', + 'bluetooth-connect-fill', + 'bluetooth-connect-line', + 'bluetooth-fill', + 'bluetooth-line', + 'blur-off-fill', + 'blur-off-line', + 'body-scan-fill', + 'body-scan-line', + 'bold', + 'book-2-fill', + 'book-2-line', + 'book-3-fill', + 'book-3-line', + 'book-fill', + 'book-line', + 'book-mark-fill', + 'book-mark-line', + 'book-open-fill', + 'book-open-line', + 'book-read-fill', + 'book-read-line', + 'booklet-fill', + 'booklet-line', + 'bookmark-2-fill', + 'bookmark-2-line', + 'bookmark-3-fill', + 'bookmark-3-line', + 'bookmark-fill', + 'bookmark-line', + 'boxing-fill', + 'boxing-line', + 'braces-fill', + 'braces-line', + 'brackets-fill', + 'brackets-line', + 'briefcase-2-fill', + 'briefcase-2-line', + 'briefcase-3-fill', + 'briefcase-3-line', + 'briefcase-4-fill', + 'briefcase-4-line', + 'briefcase-5-fill', + 'briefcase-5-line', + 'briefcase-fill', + 'briefcase-line', + 'bring-forward', + 'bring-to-front', + 'broadcast-fill', + 'broadcast-line', + 'brush-2-fill', + 'brush-2-line', + 'brush-3-fill', + 'brush-3-line', + 'brush-4-fill', + 'brush-4-line', + 'brush-fill', + 'brush-line', + 'bubble-chart-fill', + 'bubble-chart-line', + 'bug-2-fill', + 'bug-2-line', + 'bug-fill', + 'bug-line', + 'building-2-fill', + 'building-2-line', + 'building-3-fill', + 'building-3-line', + 'building-4-fill', + 'building-4-line', + 'building-fill', + 'building-line', + 'bus-2-fill', + 'bus-2-line', + 'bus-fill', + 'bus-line', + 'bus-wifi-fill', + 'bus-wifi-line', + 'cactus-fill', + 'cactus-line', + 'cake-2-fill', + 'cake-2-line', + 'cake-3-fill', + 'cake-3-line', + 'cake-fill', + 'cake-line', + 'calculator-fill', + 'calculator-line', + 'calendar-2-fill', + 'calendar-2-line', + 'calendar-check-fill', + 'calendar-check-line', + 'calendar-event-fill', + 'calendar-event-line', + 'calendar-fill', + 'calendar-line', + 'calendar-todo-fill', + 'calendar-todo-line', + 'camera-2-fill', + 'camera-2-line', + 'camera-3-fill', + 'camera-3-line', + 'camera-fill', + 'camera-lens-fill', + 'camera-lens-line', + 'camera-line', + 'camera-off-fill', + 'camera-off-line', + 'camera-switch-fill', + 'camera-switch-line', + 'capsule-fill', + 'capsule-line', + 'car-fill', + 'car-line', + 'car-washing-fill', + 'car-washing-line', + 'caravan-fill', + 'caravan-line', + 'cast-fill', + 'cast-line', + 'cellphone-fill', + 'cellphone-line', + 'celsius-fill', + 'celsius-line', + 'centos-fill', + 'centos-line', + 'character-recognition-fill', + 'character-recognition-line', + 'charging-pile-2-fill', + 'charging-pile-2-line', + 'charging-pile-fill', + 'charging-pile-line', + 'chat-1-fill', + 'chat-1-line', + 'chat-2-fill', + 'chat-2-line', + 'chat-3-fill', + 'chat-3-line', + 'chat-4-fill', + 'chat-4-line', + 'chat-check-fill', + 'chat-check-line', + 'chat-delete-fill', + 'chat-delete-line', + 'chat-download-fill', + 'chat-download-line', + 'chat-follow-up-fill', + 'chat-follow-up-line', + 'chat-forward-fill', + 'chat-forward-line', + 'chat-heart-fill', + 'chat-heart-line', + 'chat-history-fill', + 'chat-history-line', + 'chat-new-fill', + 'chat-new-line', + 'chat-off-fill', + 'chat-off-line', + 'chat-poll-fill', + 'chat-poll-line', + 'chat-private-fill', + 'chat-private-line', + 'chat-quote-fill', + 'chat-quote-line', + 'chat-settings-fill', + 'chat-settings-line', + 'chat-smile-2-fill', + 'chat-smile-2-line', + 'chat-smile-3-fill', + 'chat-smile-3-line', + 'chat-smile-fill', + 'chat-smile-line', + 'chat-upload-fill', + 'chat-upload-line', + 'chat-voice-fill', + 'chat-voice-line', + 'check-double-fill', + 'check-double-line', + 'check-fill', + 'check-line', + 'checkbox-blank-circle-fill', + 'checkbox-blank-circle-line', + 'checkbox-blank-fill', + 'checkbox-blank-line', + 'checkbox-circle-fill', + 'checkbox-circle-line', + 'checkbox-fill', + 'checkbox-indeterminate-fill', + 'checkbox-indeterminate-line', + 'checkbox-line', + 'checkbox-multiple-blank-fill', + 'checkbox-multiple-blank-line', + 'checkbox-multiple-fill', + 'checkbox-multiple-line', + 'china-railway-fill', + 'china-railway-line', + 'chrome-fill', + 'chrome-line', + 'clapperboard-fill', + 'clapperboard-line', + 'clipboard-fill', + 'clipboard-line', + 'clockwise-2-fill', + 'clockwise-2-line', + 'clockwise-fill', + 'clockwise-line', + 'close-circle-fill', + 'close-circle-line', + 'close-fill', + 'close-line', + 'closed-captioning-fill', + 'closed-captioning-line', + 'cloud-fill', + 'cloud-line', + 'cloud-off-fill', + 'cloud-off-line', + 'cloud-windy-fill', + 'cloud-windy-line', + 'cloudy-2-fill', + 'cloudy-2-line', + 'cloudy-fill', + 'cloudy-line', + 'code-box-fill', + 'code-box-line', + 'code-fill', + 'code-line', + 'code-s-fill', + 'code-s-line', + 'code-s-slash-fill', + 'code-s-slash-line', + 'code-view', + 'codepen-fill', + 'codepen-line', + 'coin-fill', + 'coin-line', + 'coins-fill', + 'coins-line', + 'collage-fill', + 'collage-line', + 'command-fill', + 'command-line', + 'community-fill', + 'community-line', + 'compass-2-fill', + 'compass-2-line', + 'compass-3-fill', + 'compass-3-line', + 'compass-4-fill', + 'compass-4-line', + 'compass-discover-fill', + 'compass-discover-line', + 'compass-fill', + 'compass-line', + 'compasses-2-fill', + 'compasses-2-line', + 'compasses-fill', + 'compasses-line', + 'computer-fill', + 'computer-line', + 'contacts-book-2-fill', + 'contacts-book-2-line', + 'contacts-book-fill', + 'contacts-book-line', + 'contacts-book-upload-fill', + 'contacts-book-upload-line', + 'contacts-fill', + 'contacts-line', + 'contrast-2-fill', + 'contrast-2-line', + 'contrast-drop-2-fill', + 'contrast-drop-2-line', + 'contrast-drop-fill', + 'contrast-drop-line', + 'contrast-fill', + 'contrast-line', + 'copper-coin-fill', + 'copper-coin-line', + 'copper-diamond-fill', + 'copper-diamond-line', + 'copyleft-fill', + 'copyleft-line', + 'copyright-fill', + 'copyright-line', + 'coreos-fill', + 'coreos-line', + 'coupon-2-fill', + 'coupon-2-line', + 'coupon-3-fill', + 'coupon-3-line', + 'coupon-4-fill', + 'coupon-4-line', + 'coupon-5-fill', + 'coupon-5-line', + 'coupon-fill', + 'coupon-line', + 'cpu-fill', + 'cpu-line', + 'creative-commons-by-fill', + 'creative-commons-by-line', + 'creative-commons-fill', + 'creative-commons-line', + 'creative-commons-nc-fill', + 'creative-commons-nc-line', + 'creative-commons-nd-fill', + 'creative-commons-nd-line', + 'creative-commons-sa-fill', + 'creative-commons-sa-line', + 'creative-commons-zero-fill', + 'creative-commons-zero-line', + 'criminal-fill', + 'criminal-line', + 'crop-2-fill', + 'crop-2-line', + 'crop-fill', + 'crop-line', + 'css3-fill', + 'css3-line', + 'cup-fill', + 'cup-line', + 'currency-fill', + 'currency-line', + 'cursor-fill', + 'cursor-line', + 'customer-service-2-fill', + 'customer-service-2-line', + 'customer-service-fill', + 'customer-service-line', + 'dashboard-2-fill', + 'dashboard-2-line', + 'dashboard-3-fill', + 'dashboard-3-line', + 'dashboard-fill', + 'dashboard-line', + 'database-2-fill', + 'database-2-line', + 'database-fill', + 'database-line', + 'delete-back-2-fill', + 'delete-back-2-line', + 'delete-back-fill', + 'delete-back-line', + 'delete-bin-2-fill', + 'delete-bin-2-line', + 'delete-bin-3-fill', + 'delete-bin-3-line', + 'delete-bin-4-fill', + 'delete-bin-4-line', + 'delete-bin-5-fill', + 'delete-bin-5-line', + 'delete-bin-6-fill', + 'delete-bin-6-line', + 'delete-bin-7-fill', + 'delete-bin-7-line', + 'delete-bin-fill', + 'delete-bin-line', + 'delete-column', + 'delete-row', + 'device-fill', + 'device-line', + 'device-recover-fill', + 'device-recover-line', + 'dingding-fill', + 'dingding-line', + 'direction-fill', + 'direction-line', + 'disc-fill', + 'disc-line', + 'discord-fill', + 'discord-line', + 'discuss-fill', + 'discuss-line', + 'dislike-fill', + 'dislike-line', + 'disqus-fill', + 'disqus-line', + 'divide-fill', + 'divide-line', + 'donut-chart-fill', + 'donut-chart-line', + 'door-closed-fill', + 'door-closed-line', + 'door-fill', + 'door-line', + 'door-lock-box-fill', + 'door-lock-box-line', + 'door-lock-fill', + 'door-lock-line', + 'door-open-fill', + 'door-open-line', + 'dossier-fill', + 'dossier-line', + 'douban-fill', + 'douban-line', + 'double-quotes-l', + 'double-quotes-r', + 'download-2-fill', + 'download-2-line', + 'download-cloud-2-fill', + 'download-cloud-2-line', + 'download-cloud-fill', + 'download-cloud-line', + 'download-fill', + 'download-line', + 'draft-fill', + 'draft-line', + 'drag-drop-fill', + 'drag-drop-line', + 'drag-move-2-fill', + 'drag-move-2-line', + 'drag-move-fill', + 'drag-move-line', + 'dribbble-fill', + 'dribbble-line', + 'drive-fill', + 'drive-line', + 'drizzle-fill', + 'drizzle-line', + 'drop-fill', + 'drop-line', + 'dropbox-fill', + 'dropbox-line', + 'dual-sim-1-fill', + 'dual-sim-1-line', + 'dual-sim-2-fill', + 'dual-sim-2-line', + 'dv-fill', + 'dv-line', + 'dvd-fill', + 'dvd-line', + 'e-bike-2-fill', + 'e-bike-2-line', + 'e-bike-fill', + 'e-bike-line', + 'earth-fill', + 'earth-line', + 'earthquake-fill', + 'earthquake-line', + 'edge-fill', + 'edge-line', + 'edit-2-fill', + 'edit-2-line', + 'edit-box-fill', + 'edit-box-line', + 'edit-circle-fill', + 'edit-circle-line', + 'edit-fill', + 'edit-line', + 'eject-fill', + 'eject-line', + 'emotion-2-fill', + 'emotion-2-line', + 'emotion-fill', + 'emotion-happy-fill', + 'emotion-happy-line', + 'emotion-laugh-fill', + 'emotion-laugh-line', + 'emotion-line', + 'emotion-normal-fill', + 'emotion-normal-line', + 'emotion-sad-fill', + 'emotion-sad-line', + 'emotion-unhappy-fill', + 'emotion-unhappy-line', + 'empathize-fill', + 'empathize-line', + 'emphasis-cn', + 'emphasis', + 'english-input', + 'equalizer-fill', + 'equalizer-line', + 'eraser-fill', + 'eraser-line', + 'error-warning-fill', + 'error-warning-line', + 'evernote-fill', + 'evernote-line', + 'exchange-box-fill', + 'exchange-box-line', + 'exchange-cny-fill', + 'exchange-cny-line', + 'exchange-dollar-fill', + 'exchange-dollar-line', + 'exchange-fill', + 'exchange-funds-fill', + 'exchange-funds-line', + 'exchange-line', + 'external-link-fill', + 'external-link-line', + 'eye-2-fill', + 'eye-2-line', + 'eye-close-fill', + 'eye-close-line', + 'eye-fill', + 'eye-line', + 'eye-off-fill', + 'eye-off-line', + 'facebook-box-fill', + 'facebook-box-line', + 'facebook-circle-fill', + 'facebook-circle-line', + 'facebook-fill', + 'facebook-line', + 'fahrenheit-fill', + 'fahrenheit-line', + 'feedback-fill', + 'feedback-line', + 'file-2-fill', + 'file-2-line', + 'file-3-fill', + 'file-3-line', + 'file-4-fill', + 'file-4-line', + 'file-add-fill', + 'file-add-line', + 'file-chart-2-fill', + 'file-chart-2-line', + 'file-chart-fill', + 'file-chart-line', + 'file-cloud-fill', + 'file-cloud-line', + 'file-code-fill', + 'file-code-line', + 'file-copy-2-fill', + 'file-copy-2-line', + 'file-copy-fill', + 'file-copy-line', + 'file-damage-fill', + 'file-damage-line', + 'file-download-fill', + 'file-download-line', + 'file-edit-fill', + 'file-edit-line', + 'file-excel-2-fill', + 'file-excel-2-line', + 'file-excel-fill', + 'file-excel-line', + 'file-fill', + 'file-forbid-fill', + 'file-forbid-line', + 'file-gif-fill', + 'file-gif-line', + 'file-history-fill', + 'file-history-line', + 'file-hwp-fill', + 'file-hwp-line', + 'file-info-fill', + 'file-info-line', + 'file-line', + 'file-list-2-fill', + 'file-list-2-line', + 'file-list-3-fill', + 'file-list-3-line', + 'file-list-fill', + 'file-list-line', + 'file-lock-fill', + 'file-lock-line', + 'file-mark-fill', + 'file-mark-line', + 'file-music-fill', + 'file-music-line', + 'file-paper-2-fill', + 'file-paper-2-line', + 'file-paper-fill', + 'file-paper-line', + 'file-pdf-fill', + 'file-pdf-line', + 'file-ppt-2-fill', + 'file-ppt-2-line', + 'file-ppt-fill', + 'file-ppt-line', + 'file-reduce-fill', + 'file-reduce-line', + 'file-search-fill', + 'file-search-line', + 'file-settings-fill', + 'file-settings-line', + 'file-shield-2-fill', + 'file-shield-2-line', + 'file-shield-fill', + 'file-shield-line', + 'file-shred-fill', + 'file-shred-line', + 'file-text-fill', + 'file-text-line', + 'file-transfer-fill', + 'file-transfer-line', + 'file-unknow-fill', + 'file-unknow-line', + 'file-upload-fill', + 'file-upload-line', + 'file-user-fill', + 'file-user-line', + 'file-warning-fill', + 'file-warning-line', + 'file-word-2-fill', + 'file-word-2-line', + 'file-word-fill', + 'file-word-line', + 'file-zip-fill', + 'file-zip-line', + 'film-fill', + 'film-line', + 'filter-2-fill', + 'filter-2-line', + 'filter-3-fill', + 'filter-3-line', + 'filter-fill', + 'filter-line', + 'filter-off-fill', + 'filter-off-line', + 'find-replace-fill', + 'find-replace-line', + 'finder-fill', + 'finder-line', + 'fingerprint-2-fill', + 'fingerprint-2-line', + 'fingerprint-fill', + 'fingerprint-line', + 'fire-fill', + 'fire-line', + 'firefox-fill', + 'firefox-line', + 'first-aid-kit-fill', + 'first-aid-kit-line', + 'flag-2-fill', + 'flag-2-line', + 'flag-fill', + 'flag-line', + 'flashlight-fill', + 'flashlight-line', + 'flask-fill', + 'flask-line', + 'flight-land-fill', + 'flight-land-line', + 'flight-takeoff-fill', + 'flight-takeoff-line', + 'flood-fill', + 'flood-line', + 'flow-chart', + 'flutter-fill', + 'flutter-line', + 'focus-2-fill', + 'focus-2-line', + 'focus-3-fill', + 'focus-3-line', + 'focus-fill', + 'focus-line', + 'foggy-fill', + 'foggy-line', + 'folder-2-fill', + 'folder-2-line', + 'folder-3-fill', + 'folder-3-line', + 'folder-4-fill', + 'folder-4-line', + 'folder-5-fill', + 'folder-5-line', + 'folder-add-fill', + 'folder-add-line', + 'folder-chart-2-fill', + 'folder-chart-2-line', + 'folder-chart-fill', + 'folder-chart-line', + 'folder-download-fill', + 'folder-download-line', + 'folder-fill', + 'folder-forbid-fill', + 'folder-forbid-line', + 'folder-history-fill', + 'folder-history-line', + 'folder-info-fill', + 'folder-info-line', + 'folder-keyhole-fill', + 'folder-keyhole-line', + 'folder-line', + 'folder-lock-fill', + 'folder-lock-line', + 'folder-music-fill', + 'folder-music-line', + 'folder-open-fill', + 'folder-open-line', + 'folder-received-fill', + 'folder-received-line', + 'folder-reduce-fill', + 'folder-reduce-line', + 'folder-settings-fill', + 'folder-settings-line', + 'folder-shared-fill', + 'folder-shared-line', + 'folder-shield-2-fill', + 'folder-shield-2-line', + 'folder-shield-fill', + 'folder-shield-line', + 'folder-transfer-fill', + 'folder-transfer-line', + 'folder-unknow-fill', + 'folder-unknow-line', + 'folder-upload-fill', + 'folder-upload-line', + 'folder-user-fill', + 'folder-user-line', + 'folder-warning-fill', + 'folder-warning-line', + 'folder-zip-fill', + 'folder-zip-line', + 'folders-fill', + 'folders-line', + 'font-color', + 'font-size-2', + 'font-size', + 'football-fill', + 'football-line', + 'footprint-fill', + 'footprint-line', + 'forbid-2-fill', + 'forbid-2-line', + 'forbid-fill', + 'forbid-line', + 'format-clear', + 'fridge-fill', + 'fridge-line', + 'fullscreen-exit-fill', + 'fullscreen-exit-line', + 'fullscreen-fill', + 'fullscreen-line', + 'function-fill', + 'function-line', + 'functions', + 'funds-box-fill', + 'funds-box-line', + 'funds-fill', + 'funds-line', + 'gallery-fill', + 'gallery-line', + 'gallery-upload-fill', + 'gallery-upload-line', + 'game-fill', + 'game-line', + 'gamepad-fill', + 'gamepad-line', + 'gas-station-fill', + 'gas-station-line', + 'gatsby-fill', + 'gatsby-line', + 'genderless-fill', + 'genderless-line', + 'ghost-2-fill', + 'ghost-2-line', + 'ghost-fill', + 'ghost-line', + 'ghost-smile-fill', + 'ghost-smile-line', + 'gift-2-fill', + 'gift-2-line', + 'gift-fill', + 'gift-line', + 'git-branch-fill', + 'git-branch-line', + 'git-commit-fill', + 'git-commit-line', + 'git-merge-fill', + 'git-merge-line', + 'git-pull-request-fill', + 'git-pull-request-line', + 'git-repository-commits-fill', + 'git-repository-commits-line', + 'git-repository-fill', + 'git-repository-line', + 'git-repository-private-fill', + 'git-repository-private-line', + 'github-fill', + 'github-line', + 'gitlab-fill', + 'gitlab-line', + 'global-fill', + 'global-line', + 'globe-fill', + 'globe-line', + 'goblet-fill', + 'goblet-line', + 'google-fill', + 'google-line', + 'google-play-fill', + 'google-play-line', + 'government-fill', + 'government-line', + 'gps-fill', + 'gps-line', + 'gradienter-fill', + 'gradienter-line', + 'grid-fill', + 'grid-line', + 'group-2-fill', + 'group-2-line', + 'group-fill', + 'group-line', + 'guide-fill', + 'guide-line', + 'h-1', + 'h-2', + 'h-3', + 'h-4', + 'h-5', + 'h-6', + 'hail-fill', + 'hail-line', + 'hammer-fill', + 'hammer-line', + 'hand-coin-fill', + 'hand-coin-line', + 'hand-heart-fill', + 'hand-heart-line', + 'hand-sanitizer-fill', + 'hand-sanitizer-line', + 'handbag-fill', + 'handbag-line', + 'hard-drive-2-fill', + 'hard-drive-2-line', + 'hard-drive-fill', + 'hard-drive-line', + 'hashtag', + 'haze-2-fill', + 'haze-2-line', + 'haze-fill', + 'haze-line', + 'hd-fill', + 'hd-line', + 'heading', + 'headphone-fill', + 'headphone-line', + 'health-book-fill', + 'health-book-line', + 'heart-2-fill', + 'heart-2-line', + 'heart-3-fill', + 'heart-3-line', + 'heart-add-fill', + 'heart-add-line', + 'heart-fill', + 'heart-line', + 'heart-pulse-fill', + 'heart-pulse-line', + 'hearts-fill', + 'hearts-line', + 'heavy-showers-fill', + 'heavy-showers-line', + 'history-fill', + 'history-line', + 'home-2-fill', + 'home-2-line', + 'home-3-fill', + 'home-3-line', + 'home-4-fill', + 'home-4-line', + 'home-5-fill', + 'home-5-line', + 'home-6-fill', + 'home-6-line', + 'home-7-fill', + 'home-7-line', + 'home-8-fill', + 'home-8-line', + 'home-fill', + 'home-gear-fill', + 'home-gear-line', + 'home-heart-fill', + 'home-heart-line', + 'home-line', + 'home-smile-2-fill', + 'home-smile-2-line', + 'home-smile-fill', + 'home-smile-line', + 'home-wifi-fill', + 'home-wifi-line', + 'honor-of-kings-fill', + 'honor-of-kings-line', + 'honour-fill', + 'honour-line', + 'hospital-fill', + 'hospital-line', + 'hotel-bed-fill', + 'hotel-bed-line', + 'hotel-fill', + 'hotel-line', + 'hotspot-fill', + 'hotspot-line', + 'hq-fill', + 'hq-line', + 'html5-fill', + 'html5-line', + 'ie-fill', + 'ie-line', + 'image-2-fill', + 'image-2-line', + 'image-add-fill', + 'image-add-line', + 'image-edit-fill', + 'image-edit-line', + 'image-fill', + 'image-line', + 'inbox-archive-fill', + 'inbox-archive-line', + 'inbox-fill', + 'inbox-line', + 'inbox-unarchive-fill', + 'inbox-unarchive-line', + 'increase-decrease-fill', + 'increase-decrease-line', + 'indent-decrease', + 'indent-increase', + 'indeterminate-circle-fill', + 'indeterminate-circle-line', + 'information-fill', + 'information-line', + 'infrared-thermometer-fill', + 'infrared-thermometer-line', + 'ink-bottle-fill', + 'ink-bottle-line', + 'input-cursor-move', + 'input-method-fill', + 'input-method-line', + 'insert-column-left', + 'insert-column-right', + 'insert-row-bottom', + 'insert-row-top', + 'instagram-fill', + 'instagram-line', + 'install-fill', + 'install-line', + 'invision-fill', + 'invision-line', + 'italic', + 'kakao-talk-fill', + 'kakao-talk-line', + 'key-2-fill', + 'key-2-line', + 'key-fill', + 'key-line', + 'keyboard-box-fill', + 'keyboard-box-line', + 'keyboard-fill', + 'keyboard-line', + 'keynote-fill', + 'keynote-line', + 'knife-blood-fill', + 'knife-blood-line', + 'knife-fill', + 'knife-line', + 'landscape-fill', + 'landscape-line', + 'layout-2-fill', + 'layout-2-line', + 'layout-3-fill', + 'layout-3-line', + 'layout-4-fill', + 'layout-4-line', + 'layout-5-fill', + 'layout-5-line', + 'layout-6-fill', + 'layout-6-line', + 'layout-bottom-2-fill', + 'layout-bottom-2-line', + 'layout-bottom-fill', + 'layout-bottom-line', + 'layout-column-fill', + 'layout-column-line', + 'layout-fill', + 'layout-grid-fill', + 'layout-grid-line', + 'layout-left-2-fill', + 'layout-left-2-line', + 'layout-left-fill', + 'layout-left-line', + 'layout-line', + 'layout-masonry-fill', + 'layout-masonry-line', + 'layout-right-2-fill', + 'layout-right-2-line', + 'layout-right-fill', + 'layout-right-line', + 'layout-row-fill', + 'layout-row-line', + 'layout-top-2-fill', + 'layout-top-2-line', + 'layout-top-fill', + 'layout-top-line', + 'leaf-fill', + 'leaf-line', + 'lifebuoy-fill', + 'lifebuoy-line', + 'lightbulb-fill', + 'lightbulb-flash-fill', + 'lightbulb-flash-line', + 'lightbulb-line', + 'line-chart-fill', + 'line-chart-line', + 'line-fill', + 'line-height', + 'line-line', + 'link-m', + 'link-unlink-m', + 'link-unlink', + 'link', + 'linkedin-box-fill', + 'linkedin-box-line', + 'linkedin-fill', + 'linkedin-line', + 'links-fill', + 'links-line', + 'list-check-2', + 'list-check', + 'list-ordered', + 'list-settings-fill', + 'list-settings-line', + 'list-unordered', + 'live-fill', + 'live-line', + 'loader-2-fill', + 'loader-2-line', + 'loader-3-fill', + 'loader-3-line', + 'loader-4-fill', + 'loader-4-line', + 'loader-5-fill', + 'loader-5-line', + 'loader-fill', + 'loader-line', + 'lock-2-fill', + 'lock-2-line', + 'lock-fill', + 'lock-line', + 'lock-password-fill', + 'lock-password-line', + 'lock-unlock-fill', + 'lock-unlock-line', + 'login-box-fill', + 'login-box-line', + 'login-circle-fill', + 'login-circle-line', + 'logout-box-fill', + 'logout-box-line', + 'logout-box-r-fill', + 'logout-box-r-line', + 'logout-circle-fill', + 'logout-circle-line', + 'logout-circle-r-fill', + 'logout-circle-r-line', + 'luggage-cart-fill', + 'luggage-cart-line', + 'luggage-deposit-fill', + 'luggage-deposit-line', + 'lungs-fill', + 'lungs-line', + 'mac-fill', + 'mac-line', + 'macbook-fill', + 'macbook-line', + 'magic-fill', + 'magic-line', + 'mail-add-fill', + 'mail-add-line', + 'mail-check-fill', + 'mail-check-line', + 'mail-close-fill', + 'mail-close-line', + 'mail-download-fill', + 'mail-download-line', + 'mail-fill', + 'mail-forbid-fill', + 'mail-forbid-line', + 'mail-line', + 'mail-lock-fill', + 'mail-lock-line', + 'mail-open-fill', + 'mail-open-line', + 'mail-send-fill', + 'mail-send-line', + 'mail-settings-fill', + 'mail-settings-line', + 'mail-star-fill', + 'mail-star-line', + 'mail-unread-fill', + 'mail-unread-line', + 'mail-volume-fill', + 'mail-volume-line', + 'map-2-fill', + 'map-2-line', + 'map-fill', + 'map-line', + 'map-pin-2-fill', + 'map-pin-2-line', + 'map-pin-3-fill', + 'map-pin-3-line', + 'map-pin-4-fill', + 'map-pin-4-line', + 'map-pin-5-fill', + 'map-pin-5-line', + 'map-pin-add-fill', + 'map-pin-add-line', + 'map-pin-fill', + 'map-pin-line', + 'map-pin-range-fill', + 'map-pin-range-line', + 'map-pin-time-fill', + 'map-pin-time-line', + 'map-pin-user-fill', + 'map-pin-user-line', + 'mark-pen-fill', + 'mark-pen-line', + 'markdown-fill', + 'markdown-line', + 'markup-fill', + 'markup-line', + 'mastercard-fill', + 'mastercard-line', + 'mastodon-fill', + 'mastodon-line', + 'medal-2-fill', + 'medal-2-line', + 'medal-fill', + 'medal-line', + 'medicine-bottle-fill', + 'medicine-bottle-line', + 'medium-fill', + 'medium-line', + 'men-fill', + 'men-line', + 'mental-health-fill', + 'mental-health-line', + 'menu-2-fill', + 'menu-2-line', + 'menu-3-fill', + 'menu-3-line', + 'menu-4-fill', + 'menu-4-line', + 'menu-5-fill', + 'menu-5-line', + 'menu-add-fill', + 'menu-add-line', + 'menu-fill', + 'menu-fold-fill', + 'menu-fold-line', + 'menu-line', + 'menu-unfold-fill', + 'menu-unfold-line', + 'merge-cells-horizontal', + 'merge-cells-vertical', + 'message-2-fill', + 'message-2-line', + 'message-3-fill', + 'message-3-line', + 'message-fill', + 'message-line', + 'messenger-fill', + 'messenger-line', + 'meteor-fill', + 'meteor-line', + 'mic-2-fill', + 'mic-2-line', + 'mic-fill', + 'mic-line', + 'mic-off-fill', + 'mic-off-line', + 'mickey-fill', + 'mickey-line', + 'microscope-fill', + 'microscope-line', + 'microsoft-fill', + 'microsoft-line', + 'mind-map', + 'mini-program-fill', + 'mini-program-line', + 'mist-fill', + 'mist-line', + 'money-cny-box-fill', + 'money-cny-box-line', + 'money-cny-circle-fill', + 'money-cny-circle-line', + 'money-dollar-box-fill', + 'money-dollar-box-line', + 'money-dollar-circle-fill', + 'money-dollar-circle-line', + 'money-euro-box-fill', + 'money-euro-box-line', + 'money-euro-circle-fill', + 'money-euro-circle-line', + 'money-pound-box-fill', + 'money-pound-box-line', + 'money-pound-circle-fill', + 'money-pound-circle-line', + 'moon-clear-fill', + 'moon-clear-line', + 'moon-cloudy-fill', + 'moon-cloudy-line', + 'moon-fill', + 'moon-foggy-fill', + 'moon-foggy-line', + 'moon-line', + 'more-2-fill', + 'more-2-line', + 'more-fill', + 'more-line', + 'motorbike-fill', + 'motorbike-line', + 'mouse-fill', + 'mouse-line', + 'movie-2-fill', + 'movie-2-line', + 'movie-fill', + 'movie-line', + 'music-2-fill', + 'music-2-line', + 'music-fill', + 'music-line', + 'mv-fill', + 'mv-line', + 'navigation-fill', + 'navigation-line', + 'netease-cloud-music-fill', + 'netease-cloud-music-line', + 'netflix-fill', + 'netflix-line', + 'newspaper-fill', + 'newspaper-line', + 'node-tree', + 'notification-2-fill', + 'notification-2-line', + 'notification-3-fill', + 'notification-3-line', + 'notification-4-fill', + 'notification-4-line', + 'notification-badge-fill', + 'notification-badge-line', + 'notification-fill', + 'notification-line', + 'notification-off-fill', + 'notification-off-line', + 'npmjs-fill', + 'npmjs-line', + 'number-0', + 'number-1', + 'number-2', + 'number-3', + 'number-4', + 'number-5', + 'number-6', + 'number-7', + 'number-8', + 'number-9', + 'numbers-fill', + 'numbers-line', + 'nurse-fill', + 'nurse-line', + 'oil-fill', + 'oil-line', + 'omega', + 'open-arm-fill', + 'open-arm-line', + 'open-source-fill', + 'open-source-line', + 'opera-fill', + 'opera-line', + 'order-play-fill', + 'order-play-line', + 'organization-chart', + 'outlet-2-fill', + 'outlet-2-line', + 'outlet-fill', + 'outlet-line', + 'page-separator', + 'pages-fill', + 'pages-line', + 'paint-brush-fill', + 'paint-brush-line', + 'paint-fill', + 'paint-line', + 'palette-fill', + 'palette-line', + 'pantone-fill', + 'pantone-line', + 'paragraph', + 'parent-fill', + 'parent-line', + 'parentheses-fill', + 'parentheses-line', + 'parking-box-fill', + 'parking-box-line', + 'parking-fill', + 'parking-line', + 'passport-fill', + 'passport-line', + 'patreon-fill', + 'patreon-line', + 'pause-circle-fill', + 'pause-circle-line', + 'pause-fill', + 'pause-line', + 'pause-mini-fill', + 'pause-mini-line', + 'paypal-fill', + 'paypal-line', + 'pen-nib-fill', + 'pen-nib-line', + 'pencil-fill', + 'pencil-line', + 'pencil-ruler-2-fill', + 'pencil-ruler-2-line', + 'pencil-ruler-fill', + 'pencil-ruler-line', + 'percent-fill', + 'percent-line', + 'phone-camera-fill', + 'phone-camera-line', + 'phone-fill', + 'phone-find-fill', + 'phone-find-line', + 'phone-line', + 'phone-lock-fill', + 'phone-lock-line', + 'picture-in-picture-2-fill', + 'picture-in-picture-2-line', + 'picture-in-picture-exit-fill', + 'picture-in-picture-exit-line', + 'picture-in-picture-fill', + 'picture-in-picture-line', + 'pie-chart-2-fill', + 'pie-chart-2-line', + 'pie-chart-box-fill', + 'pie-chart-box-line', + 'pie-chart-fill', + 'pie-chart-line', + 'pin-distance-fill', + 'pin-distance-line', + 'ping-pong-fill', + 'ping-pong-line', + 'pinterest-fill', + 'pinterest-line', + 'pinyin-input', + 'pixelfed-fill', + 'pixelfed-line', + 'plane-fill', + 'plane-line', + 'plant-fill', + 'plant-line', + 'play-circle-fill', + 'play-circle-line', + 'play-fill', + 'play-line', + 'play-list-2-fill', + 'play-list-2-line', + 'play-list-add-fill', + 'play-list-add-line', + 'play-list-fill', + 'play-list-line', + 'play-mini-fill', + 'play-mini-line', + 'playstation-fill', + 'playstation-line', + 'plug-2-fill', + 'plug-2-line', + 'plug-fill', + 'plug-line', + 'polaroid-2-fill', + 'polaroid-2-line', + 'polaroid-fill', + 'polaroid-line', + 'police-car-fill', + 'police-car-line', + 'price-tag-2-fill', + 'price-tag-2-line', + 'price-tag-3-fill', + 'price-tag-3-line', + 'price-tag-fill', + 'price-tag-line', + 'printer-cloud-fill', + 'printer-cloud-line', + 'printer-fill', + 'printer-line', + 'product-hunt-fill', + 'product-hunt-line', + 'profile-fill', + 'profile-line', + 'projector-2-fill', + 'projector-2-line', + 'projector-fill', + 'projector-line', + 'psychotherapy-fill', + 'psychotherapy-line', + 'pulse-fill', + 'pulse-line', + 'pushpin-2-fill', + 'pushpin-2-line', + 'pushpin-fill', + 'pushpin-line', + 'qq-fill', + 'qq-line', + 'qr-code-fill', + 'qr-code-line', + 'qr-scan-2-fill', + 'qr-scan-2-line', + 'qr-scan-fill', + 'qr-scan-line', + 'question-answer-fill', + 'question-answer-line', + 'question-fill', + 'question-line', + 'question-mark', + 'questionnaire-fill', + 'questionnaire-line', + 'quill-pen-fill', + 'quill-pen-line', + 'radar-fill', + 'radar-line', + 'radio-2-fill', + 'radio-2-line', + 'radio-button-fill', + 'radio-button-line', + 'radio-fill', + 'radio-line', + 'rainbow-fill', + 'rainbow-line', + 'rainy-fill', + 'rainy-line', + 'reactjs-fill', + 'reactjs-line', + 'record-circle-fill', + 'record-circle-line', + 'record-mail-fill', + 'record-mail-line', + 'recycle-fill', + 'recycle-line', + 'red-packet-fill', + 'red-packet-line', + 'reddit-fill', + 'reddit-line', + 'refresh-fill', + 'refresh-line', + 'refund-2-fill', + 'refund-2-line', + 'refund-fill', + 'refund-line', + 'registered-fill', + 'registered-line', + 'remixicon-fill', + 'remixicon-line', + 'remote-control-2-fill', + 'remote-control-2-line', + 'remote-control-fill', + 'remote-control-line', + 'repeat-2-fill', + 'repeat-2-line', + 'repeat-fill', + 'repeat-line', + 'repeat-one-fill', + 'repeat-one-line', + 'reply-all-fill', + 'reply-all-line', + 'reply-fill', + 'reply-line', + 'reserved-fill', + 'reserved-line', + 'rest-time-fill', + 'rest-time-line', + 'restart-fill', + 'restart-line', + 'restaurant-2-fill', + 'restaurant-2-line', + 'restaurant-fill', + 'restaurant-line', + 'rewind-fill', + 'rewind-line', + 'rewind-mini-fill', + 'rewind-mini-line', + 'rhythm-fill', + 'rhythm-line', + 'riding-fill', + 'riding-line', + 'road-map-fill', + 'road-map-line', + 'roadster-fill', + 'roadster-line', + 'robot-fill', + 'robot-line', + 'rocket-2-fill', + 'rocket-2-line', + 'rocket-fill', + 'rocket-line', + 'rotate-lock-fill', + 'rotate-lock-line', + 'rounded-corner', + 'route-fill', + 'route-line', + 'router-fill', + 'router-line', + 'rss-fill', + 'rss-line', + 'ruler-2-fill', + 'ruler-2-line', + 'ruler-fill', + 'ruler-line', + 'run-fill', + 'run-line', + 'safari-fill', + 'safari-line', + 'safe-2-fill', + 'safe-2-line', + 'safe-fill', + 'safe-line', + 'sailboat-fill', + 'sailboat-line', + 'save-2-fill', + 'save-2-line', + 'save-3-fill', + 'save-3-line', + 'save-fill', + 'save-line', + 'scales-2-fill', + 'scales-2-line', + 'scales-3-fill', + 'scales-3-line', + 'scales-fill', + 'scales-line', + 'scan-2-fill', + 'scan-2-line', + 'scan-fill', + 'scan-line', + 'scissors-2-fill', + 'scissors-2-line', + 'scissors-cut-fill', + 'scissors-cut-line', + 'scissors-fill', + 'scissors-line', + 'screenshot-2-fill', + 'screenshot-2-line', + 'screenshot-fill', + 'screenshot-line', + 'sd-card-fill', + 'sd-card-line', + 'sd-card-mini-fill', + 'sd-card-mini-line', + 'search-2-fill', + 'search-2-line', + 'search-eye-fill', + 'search-eye-line', + 'search-fill', + 'search-line', + 'secure-payment-fill', + 'secure-payment-line', + 'seedling-fill', + 'seedling-line', + 'send-backward', + 'send-plane-2-fill', + 'send-plane-2-line', + 'send-plane-fill', + 'send-plane-line', + 'send-to-back', + 'sensor-fill', + 'sensor-line', + 'separator', + 'server-fill', + 'server-line', + 'service-fill', + 'service-line', + 'settings-2-fill', + 'settings-2-line', + 'settings-3-fill', + 'settings-3-line', + 'settings-4-fill', + 'settings-4-line', + 'settings-5-fill', + 'settings-5-line', + 'settings-6-fill', + 'settings-6-line', + 'settings-fill', + 'settings-line', + 'shape-2-fill', + 'shape-2-line', + 'shape-fill', + 'shape-line', + 'share-box-fill', + 'share-box-line', + 'share-circle-fill', + 'share-circle-line', + 'share-fill', + 'share-forward-2-fill', + 'share-forward-2-line', + 'share-forward-box-fill', + 'share-forward-box-line', + 'share-forward-fill', + 'share-forward-line', + 'share-line', + 'shield-check-fill', + 'shield-check-line', + 'shield-cross-fill', + 'shield-cross-line', + 'shield-fill', + 'shield-flash-fill', + 'shield-flash-line', + 'shield-keyhole-fill', + 'shield-keyhole-line', + 'shield-line', + 'shield-star-fill', + 'shield-star-line', + 'shield-user-fill', + 'shield-user-line', + 'ship-2-fill', + 'ship-2-line', + 'ship-fill', + 'ship-line', + 'shirt-fill', + 'shirt-line', + 'shopping-bag-2-fill', + 'shopping-bag-2-line', + 'shopping-bag-3-fill', + 'shopping-bag-3-line', + 'shopping-bag-fill', + 'shopping-bag-line', + 'shopping-basket-2-fill', + 'shopping-basket-2-line', + 'shopping-basket-fill', + 'shopping-basket-line', + 'shopping-cart-2-fill', + 'shopping-cart-2-line', + 'shopping-cart-fill', + 'shopping-cart-line', + 'showers-fill', + 'showers-line', + 'shuffle-fill', + 'shuffle-line', + 'shut-down-fill', + 'shut-down-line', + 'side-bar-fill', + 'side-bar-line', + 'signal-tower-fill', + 'signal-tower-line', + 'signal-wifi-1-fill', + 'signal-wifi-1-line', + 'signal-wifi-2-fill', + 'signal-wifi-2-line', + 'signal-wifi-3-fill', + 'signal-wifi-3-line', + 'signal-wifi-error-fill', + 'signal-wifi-error-line', + 'signal-wifi-fill', + 'signal-wifi-line', + 'signal-wifi-off-fill', + 'signal-wifi-off-line', + 'sim-card-2-fill', + 'sim-card-2-line', + 'sim-card-fill', + 'sim-card-line', + 'single-quotes-l', + 'single-quotes-r', + 'sip-fill', + 'sip-line', + 'skip-back-fill', + 'skip-back-line', + 'skip-back-mini-fill', + 'skip-back-mini-line', + 'skip-forward-fill', + 'skip-forward-line', + 'skip-forward-mini-fill', + 'skip-forward-mini-line', + 'skull-2-fill', + 'skull-2-line', + 'skull-fill', + 'skull-line', + 'skype-fill', + 'skype-line', + 'slack-fill', + 'slack-line', + 'slice-fill', + 'slice-line', + 'slideshow-2-fill', + 'slideshow-2-line', + 'slideshow-3-fill', + 'slideshow-3-line', + 'slideshow-4-fill', + 'slideshow-4-line', + 'slideshow-fill', + 'slideshow-line', + 'smartphone-fill', + 'smartphone-line', + 'snapchat-fill', + 'snapchat-line', + 'snowy-fill', + 'snowy-line', + 'sort-asc', + 'sort-desc', + 'sound-module-fill', + 'sound-module-line', + 'soundcloud-fill', + 'soundcloud-line', + 'space-ship-fill', + 'space-ship-line', + 'space', + 'spam-2-fill', + 'spam-2-line', + 'spam-3-fill', + 'spam-3-line', + 'spam-fill', + 'spam-line', + 'speaker-2-fill', + 'speaker-2-line', + 'speaker-3-fill', + 'speaker-3-line', + 'speaker-fill', + 'speaker-line', + 'spectrum-fill', + 'spectrum-line', + 'speed-fill', + 'speed-line', + 'speed-mini-fill', + 'speed-mini-line', + 'split-cells-horizontal', + 'split-cells-vertical', + 'spotify-fill', + 'spotify-line', + 'spy-fill', + 'spy-line', + 'stack-fill', + 'stack-line', + 'stack-overflow-fill', + 'stack-overflow-line', + 'stackshare-fill', + 'stackshare-line', + 'star-fill', + 'star-half-fill', + 'star-half-line', + 'star-half-s-fill', + 'star-half-s-line', + 'star-line', + 'star-s-fill', + 'star-s-line', + 'star-smile-fill', + 'star-smile-line', + 'steam-fill', + 'steam-line', + 'steering-2-fill', + 'steering-2-line', + 'steering-fill', + 'steering-line', + 'stethoscope-fill', + 'stethoscope-line', + 'sticky-note-2-fill', + 'sticky-note-2-line', + 'sticky-note-fill', + 'sticky-note-line', + 'stock-fill', + 'stock-line', + 'stop-circle-fill', + 'stop-circle-line', + 'stop-fill', + 'stop-line', + 'stop-mini-fill', + 'stop-mini-line', + 'store-2-fill', + 'store-2-line', + 'store-3-fill', + 'store-3-line', + 'store-fill', + 'store-line', + 'strikethrough-2', + 'strikethrough', + 'subscript-2', + 'subscript', + 'subtract-fill', + 'subtract-line', + 'subway-fill', + 'subway-line', + 'subway-wifi-fill', + 'subway-wifi-line', + 'suitcase-2-fill', + 'suitcase-2-line', + 'suitcase-3-fill', + 'suitcase-3-line', + 'suitcase-fill', + 'suitcase-line', + 'sun-cloudy-fill', + 'sun-cloudy-line', + 'sun-fill', + 'sun-foggy-fill', + 'sun-foggy-line', + 'sun-line', + 'superscript-2', + 'superscript', + 'surgical-mask-fill', + 'surgical-mask-line', + 'surround-sound-fill', + 'surround-sound-line', + 'survey-fill', + 'survey-line', + 'swap-box-fill', + 'swap-box-line', + 'swap-fill', + 'swap-line', + 'switch-fill', + 'switch-line', + 'sword-fill', + 'sword-line', + 'syringe-fill', + 'syringe-line', + 't-box-fill', + 't-box-line', + 't-shirt-2-fill', + 't-shirt-2-line', + 't-shirt-air-fill', + 't-shirt-air-line', + 't-shirt-fill', + 't-shirt-line', + 'table-2', + 'table-alt-fill', + 'table-alt-line', + 'table-fill', + 'table-line', + 'tablet-fill', + 'tablet-line', + 'takeaway-fill', + 'takeaway-line', + 'taobao-fill', + 'taobao-line', + 'tape-fill', + 'tape-line', + 'task-fill', + 'task-line', + 'taxi-fill', + 'taxi-line', + 'taxi-wifi-fill', + 'taxi-wifi-line', + 'team-fill', + 'team-line', + 'telegram-fill', + 'telegram-line', + 'temp-cold-fill', + 'temp-cold-line', + 'temp-hot-fill', + 'temp-hot-line', + 'terminal-box-fill', + 'terminal-box-line', + 'terminal-fill', + 'terminal-line', + 'terminal-window-fill', + 'terminal-window-line', + 'test-tube-fill', + 'test-tube-line', + 'text-direction-l', + 'text-direction-r', + 'text-spacing', + 'text-wrap', + 'text', + 'thermometer-fill', + 'thermometer-line', + 'thumb-down-fill', + 'thumb-down-line', + 'thumb-up-fill', + 'thumb-up-line', + 'thunderstorms-fill', + 'thunderstorms-line', + 'ticket-2-fill', + 'ticket-2-line', + 'ticket-fill', + 'ticket-line', + 'time-fill', + 'time-line', + 'timer-2-fill', + 'timer-2-line', + 'timer-fill', + 'timer-flash-fill', + 'timer-flash-line', + 'timer-line', + 'todo-fill', + 'todo-line', + 'toggle-fill', + 'toggle-line', + 'tools-fill', + 'tools-line', + 'tornado-fill', + 'tornado-line', + 'trademark-fill', + 'trademark-line', + 'traffic-light-fill', + 'traffic-light-line', + 'train-fill', + 'train-line', + 'train-wifi-fill', + 'train-wifi-line', + 'translate-2', + 'translate', + 'travesti-fill', + 'travesti-line', + 'treasure-map-fill', + 'treasure-map-line', + 'trello-fill', + 'trello-line', + 'trophy-fill', + 'trophy-line', + 'truck-fill', + 'truck-line', + 'tumblr-fill', + 'tumblr-line', + 'tv-2-fill', + 'tv-2-line', + 'tv-fill', + 'tv-line', + 'twitch-fill', + 'twitch-line', + 'twitter-fill', + 'twitter-line', + 'typhoon-fill', + 'typhoon-line', + 'u-disk-fill', + 'u-disk-line', + 'ubuntu-fill', + 'ubuntu-line', + 'umbrella-fill', + 'umbrella-line', + 'underline', + 'uninstall-fill', + 'uninstall-line', + 'unsplash-fill', + 'unsplash-line', + 'upload-2-fill', + 'upload-2-line', + 'upload-cloud-2-fill', + 'upload-cloud-2-line', + 'upload-cloud-fill', + 'upload-cloud-line', + 'upload-fill', + 'upload-line', + 'usb-fill', + 'usb-line', + 'user-2-fill', + 'user-2-line', + 'user-3-fill', + 'user-3-line', + 'user-4-fill', + 'user-4-line', + 'user-5-fill', + 'user-5-line', + 'user-6-fill', + 'user-6-line', + 'user-add-fill', + 'user-add-line', + 'user-fill', + 'user-follow-fill', + 'user-follow-line', + 'user-heart-fill', + 'user-heart-line', + 'user-line', + 'user-location-fill', + 'user-location-line', + 'user-received-2-fill', + 'user-received-2-line', + 'user-received-fill', + 'user-received-line', + 'user-search-fill', + 'user-search-line', + 'user-settings-fill', + 'user-settings-line', + 'user-shared-2-fill', + 'user-shared-2-line', + 'user-shared-fill', + 'user-shared-line', + 'user-smile-fill', + 'user-smile-line', + 'user-star-fill', + 'user-star-line', + 'user-unfollow-fill', + 'user-unfollow-line', + 'user-voice-fill', + 'user-voice-line', + 'video-add-fill', + 'video-add-line', + 'video-chat-fill', + 'video-chat-line', + 'video-download-fill', + 'video-download-line', + 'video-fill', + 'video-line', + 'video-upload-fill', + 'video-upload-line', + 'vidicon-2-fill', + 'vidicon-2-line', + 'vidicon-fill', + 'vidicon-line', + 'vimeo-fill', + 'vimeo-line', + 'vip-crown-2-fill', + 'vip-crown-2-line', + 'vip-crown-fill', + 'vip-crown-line', + 'vip-diamond-fill', + 'vip-diamond-line', + 'vip-fill', + 'vip-line', + 'virus-fill', + 'virus-line', + 'visa-fill', + 'visa-line', + 'voice-recognition-fill', + 'voice-recognition-line', + 'voiceprint-fill', + 'voiceprint-line', + 'volume-down-fill', + 'volume-down-line', + 'volume-mute-fill', + 'volume-mute-line', + 'volume-off-vibrate-fill', + 'volume-off-vibrate-line', + 'volume-up-fill', + 'volume-up-line', + 'volume-vibrate-fill', + 'volume-vibrate-line', + 'vuejs-fill', + 'vuejs-line', + 'walk-fill', + 'walk-line', + 'wallet-2-fill', + 'wallet-2-line', + 'wallet-3-fill', + 'wallet-3-line', + 'wallet-fill', + 'wallet-line', + 'water-flash-fill', + 'water-flash-line', + 'webcam-fill', + 'webcam-line', + 'wechat-2-fill', + 'wechat-2-line', + 'wechat-fill', + 'wechat-line', + 'wechat-pay-fill', + 'wechat-pay-line', + 'weibo-fill', + 'weibo-line', + 'whatsapp-fill', + 'whatsapp-line', + 'wheelchair-fill', + 'wheelchair-line', + 'wifi-fill', + 'wifi-line', + 'wifi-off-fill', + 'wifi-off-line', + 'window-2-fill', + 'window-2-line', + 'window-fill', + 'window-line', + 'windows-fill', + 'windows-line', + 'windy-fill', + 'windy-line', + 'wireless-charging-fill', + 'wireless-charging-line', + 'women-fill', + 'women-line', + 'wubi-input', + 'xbox-fill', + 'xbox-line', + 'xing-fill', + 'xing-line', + 'youtube-fill', + 'youtube-line', + 'zcool-fill', + 'zcool-line', + 'zhihu-fill', + 'zhihu-line', + 'zoom-in-fill', + 'zoom-in-line', + 'zoom-out-fill', + 'zoom-out-line', + 'zzz-fill', + 'zzz-line', +] +module.exports = [ + { + url: '/remixIcon/getList', + type: 'post', + response(config) { + const { title, pageNo = 1, pageSize = 72 } = config.body + let mockList = data.filter((item) => { + if (title && item.indexOf(title) < 0) return false + return true + }) + const pageList = mockList.filter( + (item, index) => + index < pageSize * pageNo && index >= pageSize * (pageNo - 1) + ) + return { + code: 200, + msg: 'success', + totalCount: mockList.length, + data: pageList, + } + }, + }, +] diff --git a/mock/controller/roleManagement.js b/mock/controller/roleManagement.js new file mode 100644 index 000000000..3a2079e57 --- /dev/null +++ b/mock/controller/roleManagement.js @@ -0,0 +1,53 @@ +const totalCount = 2 +const List = [ + { + id: '@id', + permission: 'admin', + }, + { + id: '@id', + permission: 'editor', + }, +] +module.exports = [ + { + url: '/roleManagement/getList', + type: 'post', + response(config) { + const { title = '', pageNo = 1, pageSize = 20 } = config.body + let mockList = List.filter((item) => { + return !(title && item.title.indexOf(title) < 0) + }) + const pageList = mockList.filter( + (item, index) => + index < pageSize * pageNo && index >= pageSize * (pageNo - 1) + ) + return { + code: 200, + msg: 'success', + totalCount, + data: pageList, + } + }, + }, + { + url: '/roleManagement/doEdit', + type: 'post', + response() { + return { + code: 200, + msg: '模拟保存成功', + } + }, + }, + { + url: '/roleManagement/doDelete', + type: 'post', + response() { + return { + code: 200, + msg: '模拟删除成功', + } + }, + }, +] diff --git a/mock/controller/router.js b/mock/controller/router.js new file mode 100644 index 000000000..3b23b9b8a --- /dev/null +++ b/mock/controller/router.js @@ -0,0 +1,298 @@ +const data = [ + { + path: '/', + component: 'Layout', + redirect: 'index', + children: [ + { + path: 'index', + name: 'Index', + component: '@/views/index/index', + meta: { + title: '首页', + icon: 'home', + affix: true, + }, + }, + ], + }, + { + path: '/personnelManagement', + component: 'Layout', + redirect: 'noRedirect', + name: 'PersonnelManagement', + meta: { title: '人员', icon: 'users-cog', permissions: ['admin'] }, + children: [ + { + path: 'userManagement', + name: 'UserManagement', + component: '@/views/personnelManagement/userManagement/index', + meta: { title: '用户管理' }, + }, + { + path: 'roleManagement', + name: 'RoleManagement', + component: '@/views/personnelManagement/roleManagement/index', + meta: { title: '角色管理' }, + }, + { + path: 'menuManagement', + name: 'MenuManagement', + component: '@/views/personnelManagement/menuManagement/index', + meta: { title: '菜单管理', badge: 'New' }, + }, + ], + }, + { + path: '/vab', + component: 'Layout', + redirect: 'noRedirect', + name: 'Vab', + alwaysShow: true, + meta: { title: '组件', icon: 'cloud' }, + children: [ + { + path: 'permissions', + name: 'Permission', + component: '@/views/vab/permissions/index', + meta: { + title: '权限控制', + permissions: ['admin', 'editor'], + badge: 'New', + }, + }, + { + path: 'icon', + component: 'EmptyLayout', + redirect: 'noRedirect', + name: 'Icon', + meta: { + title: '图标', + permissions: ['admin'], + }, + children: [ + { + path: 'awesomeIcon', + name: 'AwesomeIcon', + component: '@/views/vab/icon/index', + meta: { title: '常规图标' }, + }, + { + path: 'remixIcon', + name: 'RemixIcon', + component: '@/views/vab/icon/remixIcon', + meta: { title: '小清新图标' }, + }, + { + path: 'colorfulIcon', + name: 'ColorfulIcon', + component: '@/views/vab/icon/colorfulIcon', + meta: { title: '多彩图标' }, + }, + ], + }, + { + path: 'table', + component: '@/views/vab/table/index', + name: 'Table', + meta: { + title: '表格', + permissions: ['admin'], + }, + }, + { + path: 'map', + name: 'Map', + component: '@/views/vab/map/index', + meta: { title: '地图', permissions: ['admin'], badge: 'Pro' }, + }, + { + path: 'webSocket', + name: 'WebSocket', + component: '@/views/vab/webSocket/index', + meta: { title: 'webSocket', permissions: ['admin'] }, + }, + { + path: 'form', + name: 'Form', + component: '@/views/vab/form/index', + meta: { title: '表单', permissions: ['admin'] }, + }, + { + path: 'element', + name: 'Element', + component: '@/views/vab/element/index', + meta: { title: '常用组件', permissions: ['admin'] }, + }, + { + path: 'tree', + name: 'Tree', + component: '@/views/vab/tree/index', + meta: { title: '树', permissions: ['admin'] }, + }, + { + path: 'verify', + name: 'Verify', + component: '@/views/vab/verify/index', + meta: { title: '验证码', permissions: ['admin'] }, + }, + { + path: 'menu1', + component: '@/views/vab/nested/menu1/index', + name: 'Menu1', + alwaysShow: true, + meta: { + title: '嵌套路由 1', + permissions: ['admin'], + }, + children: [ + { + path: 'menu1-1', + name: 'Menu1-1', + alwaysShow: true, + meta: { title: '嵌套路由 1-1' }, + component: '@/views/vab/nested/menu1/menu1-1/index', + + children: [ + { + path: 'menu1-1-1', + name: 'Menu1-1-1', + meta: { title: '嵌套路由 1-1-1' }, + component: '@/views/vab/nested/menu1/menu1-1/menu1-1-1/index', + }, + ], + }, + ], + }, + { + path: 'magnifier', + name: 'Magnifier', + component: '@/views/vab/magnifier/index', + meta: { title: '放大镜', permissions: ['admin'] }, + }, + { + path: 'loading', + name: 'Loading', + component: '@/views/vab/loading/index', + meta: { title: 'loading', permissions: ['admin'] }, + }, + { + path: 'player', + name: 'Player', + component: '@/views/vab/player/index', + meta: { title: '视频播放器', permissions: ['admin'] }, + }, + { + path: 'markdownEditor', + name: 'MarkdownEditor', + component: '@/views/vab/markdownEditor/index', + meta: { title: 'markdown编辑器', permissions: ['admin'] }, + }, + { + path: 'editor', + name: 'Editor', + component: '@/views/vab/editor/index', + meta: { title: '富文本编辑器', permissions: ['admin'], badge: 'New' }, + }, + { + path: 'backToTop', + name: 'BackToTop', + component: '@/views/vab/backToTop/index', + meta: { title: '返回顶部', permissions: ['admin'] }, + }, + { + path: 'lodash', + name: 'Lodash', + component: '@/views/vab/lodash/index', + meta: { title: 'lodash', permissions: ['admin'] }, + }, + { + path: 'smallComponents', + name: 'SmallComponents', + component: '@/views/vab/smallComponents/index', + meta: { title: '小组件', permissions: ['admin'] }, + }, + + { + path: 'upload', + name: 'Upload', + component: '@/views/vab/upload/index', + meta: { title: '上传', permissions: ['admin'] }, + }, + { + path: 'log', + name: 'Log', + component: '@/views/vab/errorLog/index', + meta: { title: '错误日志模拟', permissions: ['admin'] }, + }, + { + path: 'more', + name: 'More', + component: '@/views/vab/more/index', + meta: { title: '关于', permissions: ['admin'] }, + }, + ], + }, + { + path: '/mall', + component: 'Layout', + redirect: 'noRedirect', + name: 'Mall', + meta: { + title: '商城', + icon: 'shopping-cart', + permissions: ['admin'], + }, + + children: [ + { + path: 'pay', + name: 'Pay', + component: '@/views/mall/pay/index', + meta: { + title: '支付', + noKeepAlive: true, + }, + children: null, + }, + { + path: 'goodsList', + name: 'GoodsList', + component: '@/views/mall/goodsList/index', + meta: { + title: '商品列表', + }, + }, + ], + }, + { + path: '/error', + component: 'EmptyLayout', + redirect: 'noRedirect', + name: 'Error', + meta: { title: '错误页', icon: 'bug' }, + children: [ + { + path: '401', + name: 'Error401', + component: '@/views/401', + meta: { title: '401' }, + }, + { + path: '404', + name: 'Error404', + component: '@/views/404', + meta: { title: '404' }, + }, + ], + }, +] +module.exports = [ + { + url: '/menu/navigate', + type: 'post', + response() { + return { code: 200, msg: 'success', data: data } + }, + }, +] diff --git a/mock/controller/table.js b/mock/controller/table.js new file mode 100644 index 000000000..264f4938f --- /dev/null +++ b/mock/controller/table.js @@ -0,0 +1,88 @@ +const { mock } = require('mockjs') +const { handleRandomImage } = require('../utils') + +const List = [] +const count = 999 +for (let i = 0; i < count; i++) { + List.push( + mock({ + uuid: '@uuid', + id: '@id', + title: '@csentence(1, 2)', + 'status|1': ['published', 'draft', 'deleted'], + author: '@cname', + datetime: '@datetime', + pageViews: '@integer(300, 5000)', + img: handleRandomImage(200, 200), + smallImg: handleRandomImage(40, 40), + switch: '@boolean', + percent: '@integer(80,99)', + }) + ) +} + +module.exports = [ + { + url: '/table/getList', + type: 'post', + response(config) { + if (!config.body) { + return { + code: 200, + msg: 'success', + totalCount: count, + data: mock({ + 'data|50': [ + { + id: '@id', + title: '@csentence(1, 2)', + 'status|1': ['published', 'draft', 'deleted'], + author: '@cname', + datetime: '@datetime', + pageViews: '@integer(300, 5000)', + img: handleRandomImage(200, 200), + smallImg: handleRandomImage(40, 40), + switch: '@boolean', + percent: '@integer(80,99)', + }, + ], + }).data, + } + } + const { title = '', pageNo = 1, pageSize = 20 } = config.body + let mockList = List.filter((item) => { + return !(title && item.title.indexOf(title) < 0) + }) + const pageList = mockList.filter( + (item, index) => + index < pageSize * pageNo && index >= pageSize * (pageNo - 1) + ) + return { + code: 200, + msg: 'success', + totalCount: count, + data: pageList, + } + }, + }, + { + url: '/table/doEdit', + type: 'post', + response() { + return { + code: 200, + msg: '模拟保存成功', + } + }, + }, + { + url: '/table/doDelete', + type: 'post', + response() { + return { + code: 200, + msg: '模拟删除成功', + } + }, + }, +] diff --git a/mock/controller/tree.js b/mock/controller/tree.js new file mode 100644 index 000000000..fc2133a1a --- /dev/null +++ b/mock/controller/tree.js @@ -0,0 +1,54 @@ +const data = [ + { + id: '1', + parentId: '0', + name: 'root', + title: 'root', + text: 'root', + value: '1', + rank: 1, + children: [ + { + id: '32816b88ff72423f960e7d492a386131', + parentId: '1', + name: '一级', + title: '一级', + text: '一级', + value: '32816b88ff72423f960e7d492a386131', + rank: 2, + children: [ + { + id: '9e11afc35d55475fb0bd3164b9684cbe', + parentId: '32816b88ff72423f960e7d492a386131', + name: '二级', + title: '二级', + text: '二级', + value: '9e11afc35d55475fb0bd3164b9684cbe', + rank: 3, + children: [ + { + id: '4cc1b04635e4444292526c5391699077', + parentId: '9e11afc35d55475fb0bd3164b9684cbe', + name: '三级', + title: '三级', + text: '三级', + value: '4cc1b04635e4444292526c5391699077', + rank: 4, + children: [], + }, + ], + }, + ], + }, + ], + }, +] +module.exports = [ + { + url: '/tree/list', + type: 'post', + response() { + return { code: 200, msg: 'success', data } + }, + }, +] diff --git a/mock/controller/user.js b/mock/controller/user.js new file mode 100644 index 000000000..637c7c971 --- /dev/null +++ b/mock/controller/user.js @@ -0,0 +1,95 @@ +const accessTokens = { + admin: 'admin-accessToken', + editor: 'editor-accessToken', + test: 'test-accessToken', +} + +module.exports = [ + { + url: '/publicKey', + type: 'post', + response() { + return { + code: 200, + msg: 'success', + data: { + mockServer: true, + publicKey: + 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBT2vr+dhZElF73FJ6xiP181txKWUSNLPQQlid6DUJhGAOZblluafIdLmnUyKE8mMHhT3R+Ib3ssZcJku6Hn72yHYj/qPkCGFv0eFo7G+GJfDIUeDyalBN0QsuiE/XzPHJBuJDfRArOiWvH0BXOv5kpeXSXM8yTt5Na1jAYSiQ/wIDAQAB', + }, + } + }, + }, + { + url: '/login', + type: 'post', + response(config) { + const { username } = config.body + const accessToken = accessTokens[username] + if (!accessToken) { + return { + code: 500, + msg: '帐户或密码不正确。', + } + } + return { + code: 200, + msg: 'success', + data: { accessToken }, + } + }, + }, + { + url: '/register', + type: 'post', + response() { + return { + code: 200, + msg: '模拟注册成功', + } + }, + }, + { + url: '/userInfo', + type: 'post', + response(config) { + const { accessToken } = config.body + let permissions = ['admin'] + let username = 'admin' + if ('admin-accessToken' === accessToken) { + permissions = ['admin'] + username = 'admin' + } + if ('editor-accessToken' === accessToken) { + permissions = ['editor'] + username = 'editor' + } + if ('test-accessToken' === accessToken) { + permissions = ['admin', 'editor'] + username = 'test' + } + return { + code: 200, + msg: 'success', + data: { + permissions, + username, + 'avatar|1': [ + 'https://i.gtimg.cn/club/item/face/img/2/15922_100.gif', + 'https://i.gtimg.cn/club/item/face/img/8/15918_100.gif', + ], + }, + } + }, + }, + { + url: '/logout', + type: 'post', + response() { + return { + code: 200, + msg: 'success', + } + }, + }, +] diff --git a/mock/controller/userManagement.js b/mock/controller/userManagement.js new file mode 100644 index 000000000..f83e70177 --- /dev/null +++ b/mock/controller/userManagement.js @@ -0,0 +1,70 @@ +const totalCount = 3 +const List = [ + { + id: '@id', + username: 'admin', + password: 'admin', + email: '@email', + permissions: ['admin'], + datatime: '@datetime', + }, + { + id: '@id', + username: 'editor', + password: 'editor', + email: '@email', + permissions: ['editor'], + datatime: '@datetime', + }, + { + id: '@id', + username: 'test', + password: 'test', + email: '@email', + permissions: ['admin', 'editor'], + datatime: '@datetime', + }, +] +module.exports = [ + { + url: '/userManagement/getList', + type: 'post', + response(config) { + const { title = '', pageNo = 1, pageSize = 20 } = config.body + let mockList = List.filter((item) => { + if (title && item.title.indexOf(title) < 0) return false + return true + }) + const pageList = mockList.filter( + (item, index) => + index < pageSize * pageNo && index >= pageSize * (pageNo - 1) + ) + return { + code: 200, + msg: 'success', + totalCount, + data: pageList, + } + }, + }, + { + url: '/userManagement/doEdit', + type: 'post', + response() { + return { + code: 200, + msg: '模拟保存成功', + } + }, + }, + { + url: '/userManagement/doDelete', + type: 'post', + response() { + return { + code: 200, + msg: '模拟删除成功', + } + }, + }, +] diff --git a/mock/index.js b/mock/index.js new file mode 100644 index 000000000..191029698 --- /dev/null +++ b/mock/index.js @@ -0,0 +1,98 @@ +const chokidar = require('chokidar') +const bodyParser = require('body-parser') +const chalk = require('chalk') +const path = require('path') +const { mock } = require('mockjs') +const { baseURL } = require('../src/config') +const mockDir = path.join(process.cwd(), 'mock') +const { handleMockArray } = require('./utils') + +/** + * + * @param app + * @returns {{mockStartIndex: number, mockRoutesLength: number}} + */ +const registerRoutes = (app) => { + let mockLastIndex + const mocks = [] + const mockArray = handleMockArray() + mockArray.forEach((item) => { + const obj = require(item) + mocks.push(...obj) + }) + const mocksForServer = mocks.map((route) => { + return responseFake(route.url, route.type, route.response) + }) + for (const mock of mocksForServer) { + app[mock.type](mock.url, mock.response) + mockLastIndex = app._router.stack.length + } + const mockRoutesLength = Object.keys(mocksForServer).length + return { + mockRoutesLength: mockRoutesLength, + mockStartIndex: mockLastIndex - mockRoutesLength, + } +} + +/** + * + * @param url + * @param type + * @param respond + * @returns {{response(*=, *=): void, type: (*|string), url: RegExp}} + */ +const responseFake = (url, type, respond) => { + return { + url: new RegExp(`${baseURL}${url}`), + type: type || 'get', + response(req, res) { + res.status(200) + if (JSON.stringify(req.body) !== '{}') { + console.log(chalk.green(`> 请求地址:${req.path}`)) + console.log(chalk.green(`> 请求参数:${JSON.stringify(req.body)}\n`)) + } else { + console.log(chalk.green(`> 请求地址:${req.path}\n`)) + } + res.json(mock(respond instanceof Function ? respond(req, res) : respond)) + }, + } +} +/** + * + * @param app + */ +module.exports = (app) => { + app.use(bodyParser.json()) + app.use( + bodyParser.urlencoded({ + extended: true, + }) + ) + + const mockRoutes = registerRoutes(app) + let mockRoutesLength = mockRoutes.mockRoutesLength + let mockStartIndex = mockRoutes.mockStartIndex + chokidar + .watch(mockDir, { + ignored: /mock-server/, + ignoreInitial: true, + }) + .on('all', (event) => { + if (event === 'change' || event === 'add') { + try { + app._router.stack.splice(mockStartIndex, mockRoutesLength) + + Object.keys(require.cache).forEach((item) => { + if (item.includes(mockDir)) { + delete require.cache[require.resolve(item)] + } + }) + const mockRoutes = registerRoutes(app) + mockRoutesLength = mockRoutes.mockRoutesLength + mockStartIndex = mockRoutes.mockStartIndex + } catch (error) { + console.log(chalk.red(error)) + } + } + }) +} diff --git a/mock/utils/index.js b/mock/utils/index.js new file mode 100644 index 000000000..840cdac1e --- /dev/null +++ b/mock/utils/index.js @@ -0,0 +1,43 @@ +const { Random } = require('mockjs') +const { join } = require('path') +const fs = require('fs') + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 随机生成图片url。 + * @param width + * @param height + * @returns {string} + */ +function handleRandomImage(width = 50, height = 50) { + return `https://picsum.photos/${width}/${height}?random=${Random.guid()}` +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 处理所有 controller 模块,npm run serve时在node环境中自动输出controller文件夹下Mock接口,请勿修改。 + * @returns {[]} + */ +function handleMockArray() { + const mockArray = [] + const getFiles = (jsonPath) => { + const jsonFiles = [] + const findJsonFile = (path) => { + const files = fs.readdirSync(path) + files.forEach((item) => { + const fPath = join(path, item) + const stat = fs.statSync(fPath) + if (stat.isDirectory() === true) findJsonFile(item) + if (stat.isFile() === true) jsonFiles.push(item) + }) + } + findJsonFile(jsonPath) + jsonFiles.forEach((item) => mockArray.push(`./controller/${item}`)) + } + getFiles('mock/controller') + return mockArray +} +module.exports = { + handleRandomImage, + handleMockArray, +} diff --git a/package.json b/package.json index 7d625d84e..12b2c6171 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,90 @@ { - "name": "FOTON", + "name": "vue-admin-beautiful", "version": "1.0.0", - "author": "91isoft", - "private": true, + "author": "vue-admin-beautiful", + "participants": [], + "homepage": "https://chu1204505056.gitee.io/vue-admin-beautiful", + "publishConfig": { + "registry": "https://npm.pkg.github.com/" + }, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", - "lint": "vue-cli-service lint" + "lint": "vue-cli-service lint", + "lint:style": "stylelint-config-prettier-check" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/chuzhixin/vue-admin-beautiful.git" + }, + "gitHooks": { + "pre-commit": "lint-staged" + }, + "lint-staged": { + "*.{js,jsx,vue}": [ + "vue-cli-service lint", + "git add" + ] }, "dependencies": { "axios": "^0.21.1", - "core-js": "^3.6.5", + "clipboard": "^2.0.8", + "core-js": "^3.10.1", "dayjs": "^1.10.4", - "jquery": "^3.6.0", + "echarts": "^5.1.0", + "echarts-wordcloud": "^2.0.0", + "element-ui": "^2.15.1", + "jsencrypt": "^3.1.0", "lodash": "^4.17.21", - "moment": "^2.29.1", + "maptalks": "^0.49.3", + "mapv": "^2.0.62", + "mockjs": "^1.1.0", "nprogress": "^0.2.0", "qs": "^6.10.1", - "vue": "^2.6.11", - "vue-router": "^3.2.0", - "vuex": "^3.4.0" + "screenfull": "^5.1.0", + "sortablejs": "^1.13.0", + "vab-icon": "^0.0.1", + "vue": "^2.6.12", + "vue-echarts": "5.0.0-beta.0", + "vue-router": "^3.5.1", + "vuex": "^3.6.2", + "zx-count": "^0.3.7", + "zx-layouts": "^0.6.24", + "zx-magnifie": "^0.4.0", + "zx-markdown-editor": "^0.0.2", + "zx-player": "^1.0.2", + "zx-quill": "^0.0.3", + "zx-templates": "^0.0.26", + "zx-verify": "^0.0.2" }, "devDependencies": { - "@vue/cli-plugin-babel": "~4.5.0", - "@vue/cli-plugin-router": "~4.5.0", - "@vue/cli-plugin-vuex": "~4.5.0", - "@vue/cli-service": "~4.5.0", + "@vue/cli-plugin-babel": "^4.5.12", + "@vue/cli-plugin-eslint": "^4.5.12", + "@vue/cli-service": "^4.5.12", + "@vue/eslint-config-prettier": "^6.0.0", + "babel-eslint": "^10.1.0", + "body-parser": "^1.19.0", "chalk": "^4.1.0", - "node-sass": "^4.12.0", - "sass-loader": "^8.0.2", + "chokidar": "^3.5.1", + "eslint": "^7.24.0", + "eslint-plugin-prettier": "^3.4.0", + "eslint-plugin-vue": "^7.9.0", + "filemanager-webpack-plugin": "^4.0.0", + "image-webpack-loader": "^7.0.1", + "lint-staged": "^10.5.4", + "plop": "^2.7.4", + "prettier": "^2.2.1", + "sass": "^1.32.10", + "sass-loader": "^10.1.1", "stylelint": "^13.12.0", "stylelint-config-prettier": "^8.0.2", "stylelint-config-recess-order": "^2.4.0", - "vue-template-compiler": "^2.6.11" + "svg-sprite-loader": "^6.0.5", + "vue-template-compiler": "^2.6.12", + "webpackbar": "^4.0.0" + }, + "engines": { + "node": ">=8.9", + "npm": ">= 3.0.0" } } diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 000000000..0e3b87b1a --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,21 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 代码规范 + */ + +module.exports = { + printWidth: 80, + tabWidth: 2, + useTabs: false, + semi: false, + singleQuote: true, + quoteProps: 'as-needed', + jsxSingleQuote: false, + trailingComma: 'es5', + bracketSpacing: true, + jsxBracketSameLine: false, + arrowParens: 'always', + htmlWhitespaceSensitivity: 'ignore', + vueIndentScriptAndStyle: true, + endOfLine: 'lf', +} diff --git a/public/favicon.ico b/public/favicon.ico index df36fcfb7..7c31c49f6 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/favicon_backup.ico b/public/favicon_backup.ico new file mode 100644 index 000000000..df36fcfb7 Binary files /dev/null and b/public/favicon_backup.ico differ diff --git a/public/index.html b/public/index.html index 435b43f69..f41a8a97d 100644 --- a/public/index.html +++ b/public/index.html @@ -1,30 +1,56 @@ - - - - - - - - 全球标准法规系统-福田 - - - -
-
+ + + + + + + <%= VUE_APP_TITLE %> + + + + + + + +
+
- - - - - - + + + + + +

<%= VUE_APP_TITLE %>

+
-
- - + + + diff --git a/src/App.vue b/src/App.vue index 231bf53b6..94f824e74 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,9 +1,12 @@ - + diff --git a/src/api/ad.js b/src/api/ad.js new file mode 100644 index 000000000..065e6a903 --- /dev/null +++ b/src/api/ad.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function getList(data) { + return request({ + url: '/ad/getList', + method: 'get', + data, + }) +} diff --git a/src/api/changeLog.js b/src/api/changeLog.js new file mode 100644 index 000000000..bf7464b85 --- /dev/null +++ b/src/api/changeLog.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function getList(data) { + return request({ + url: '/changeLog/getList', + method: 'post', + data, + }) +} diff --git a/src/api/colorfulIcon.js b/src/api/colorfulIcon.js new file mode 100644 index 000000000..c2b6d5bb6 --- /dev/null +++ b/src/api/colorfulIcon.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function getIconList(data) { + return request({ + url: '/colorfulIcon/getList', + method: 'post', + data, + }) +} diff --git a/src/api/github.js b/src/api/github.js new file mode 100644 index 000000000..2d218dc1d --- /dev/null +++ b/src/api/github.js @@ -0,0 +1,20 @@ +import request from 'axios' + +export function getRepos(params) { + return request({ + url: 'https://api.github.com/repos/chuzhixin/vue-admin-beautiful', + method: 'get', + params, + timeout: 10000, + }) +} + +export function getStargazers(params) { + return request({ + url: + 'https://api.github.com/repos/chuzhixin/vue-admin-beautiful/stargazers', + method: 'get', + params, + timeout: 10000, + }) +} diff --git a/src/api/goodsList.js b/src/api/goodsList.js new file mode 100644 index 000000000..3a40f8fd2 --- /dev/null +++ b/src/api/goodsList.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function getList(data) { + return request({ + url: '/goodsList/getList', + method: 'post', + data, + }) +} diff --git a/src/api/icon.js b/src/api/icon.js new file mode 100644 index 000000000..fe1d1863e --- /dev/null +++ b/src/api/icon.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function getIconList(data) { + return request({ + url: '/icon/getList', + method: 'post', + data, + }) +} diff --git a/src/api/markdown.js b/src/api/markdown.js new file mode 100644 index 000000000..e6bb01c81 --- /dev/null +++ b/src/api/markdown.js @@ -0,0 +1,8 @@ +import request from 'axios' + +export function getList() { + return request({ + url: 'https://cdn.jsdelivr.net/gh/prettier/prettier@master/docs/options.md', + method: 'get', + }) +} diff --git a/src/api/menuManagement.js b/src/api/menuManagement.js new file mode 100644 index 000000000..32b22d3b6 --- /dev/null +++ b/src/api/menuManagement.js @@ -0,0 +1,25 @@ +import request from '@/utils/request' + +export function getTree(data) { + return request({ + url: '/menuManagement/getTree', + method: 'post', + data, + }) +} + +export function doEdit(data) { + return request({ + url: '/menuManagement/doEdit', + method: 'post', + data, + }) +} + +export function doDelete(data) { + return request({ + url: '/menuManagement/doDelete', + method: 'post', + data, + }) +} diff --git a/src/api/notice.js b/src/api/notice.js new file mode 100644 index 000000000..ced1de3b4 --- /dev/null +++ b/src/api/notice.js @@ -0,0 +1,8 @@ +import request from '@/utils/request' + +export function getNoticeList() { + return request({ + url: '/notice/getList', + method: 'post', + }) +} diff --git a/src/api/personalCenter.js b/src/api/personalCenter.js new file mode 100644 index 000000000..804c679be --- /dev/null +++ b/src/api/personalCenter.js @@ -0,0 +1,25 @@ +import request from '@/utils/request' + +export function getList(data) { + return request({ + url: '/personalCenter/getList', + method: 'post', + data, + }) +} + +export function doEdit(data) { + return request({ + url: '/personalCenter/doEdit', + method: 'post', + data, + }) +} + +export function doDelete(data) { + return request({ + url: '/personalCenter/doDelete', + method: 'post', + data, + }) +} diff --git a/src/api/publicKey.js b/src/api/publicKey.js new file mode 100644 index 000000000..8b641fb8f --- /dev/null +++ b/src/api/publicKey.js @@ -0,0 +1,8 @@ +import request from '@/utils/request' + +export function getPublicKey() { + return request({ + url: '/publicKey', + method: 'post', + }) +} diff --git a/src/api/remixIcon.js b/src/api/remixIcon.js new file mode 100644 index 000000000..c62ea704f --- /dev/null +++ b/src/api/remixIcon.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function getIconList(data) { + return request({ + url: '/remixIcon/getList', + method: 'post', + data, + }) +} diff --git a/src/api/roleManagement.js b/src/api/roleManagement.js new file mode 100644 index 000000000..4aac23ed7 --- /dev/null +++ b/src/api/roleManagement.js @@ -0,0 +1,25 @@ +import request from '@/utils/request' + +export function getList(data) { + return request({ + url: '/roleManagement/getList', + method: 'post', + data, + }) +} + +export function doEdit(data) { + return request({ + url: '/roleManagement/doEdit', + method: 'post', + data, + }) +} + +export function doDelete(data) { + return request({ + url: '/roleManagement/doDelete', + method: 'post', + data, + }) +} diff --git a/src/api/router.js b/src/api/router.js new file mode 100644 index 000000000..4d34d4894 --- /dev/null +++ b/src/api/router.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function getRouterList(data) { + return request({ + url: '/menu/navigate', + method: 'post', + data, + }) +} diff --git a/src/api/table.js b/src/api/table.js new file mode 100644 index 000000000..0c1effc31 --- /dev/null +++ b/src/api/table.js @@ -0,0 +1,25 @@ +import request from '@/utils/request' + +export function getList(data) { + return request({ + url: '/table/getList', + method: 'post', + data, + }) +} + +export function doEdit(data) { + return request({ + url: '/table/doEdit', + method: 'post', + data, + }) +} + +export function doDelete(data) { + return request({ + url: '/table/doDelete', + method: 'post', + data, + }) +} diff --git a/src/api/tree.js b/src/api/tree.js new file mode 100644 index 000000000..e29ad3895 --- /dev/null +++ b/src/api/tree.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function getTreeList(data) { + return request({ + url: '/tree/list', + method: 'post', + data, + }) +} diff --git a/src/api/user.js b/src/api/user.js new file mode 100644 index 000000000..e99e29f15 --- /dev/null +++ b/src/api/user.js @@ -0,0 +1,38 @@ +import request from '@/utils/request' +import { encryptedData } from '@/utils/encrypt' +import { loginRSA, tokenName } from '@/config' + +export async function login(data) { + if (loginRSA) { + data = await encryptedData(data) + } + return request({ + url: '/login', + method: 'post', + data, + }) +} + +export function getUserInfo(accessToken) { + return request({ + url: '/userInfo', + method: 'post', + data: { + [tokenName]: accessToken, + }, + }) +} + +export function logout() { + return request({ + url: '/logout', + method: 'post', + }) +} + +export function register() { + return request({ + url: '/register', + method: 'post', + }) +} diff --git a/src/api/userManagement.js b/src/api/userManagement.js new file mode 100644 index 000000000..9eaca6ed0 --- /dev/null +++ b/src/api/userManagement.js @@ -0,0 +1,25 @@ +import request from '@/utils/request' + +export function getList(data) { + return request({ + url: '/userManagement/getList', + method: 'post', + data, + }) +} + +export function doEdit(data) { + return request({ + url: '/userManagement/doEdit', + method: 'post', + data, + }) +} + +export function doDelete(data) { + return request({ + url: '/userManagement/doDelete', + method: 'post', + data, + }) +} diff --git a/src/assets/comparison/left.jpg b/src/assets/comparison/left.jpg new file mode 100644 index 000000000..f5133cf11 Binary files /dev/null and b/src/assets/comparison/left.jpg differ diff --git a/src/assets/comparison/right.jpg b/src/assets/comparison/right.jpg new file mode 100644 index 000000000..625a53f24 Binary files /dev/null and b/src/assets/comparison/right.jpg differ diff --git a/src/assets/error_images/401.png b/src/assets/error_images/401.png new file mode 100644 index 000000000..90bbf6e33 Binary files /dev/null and b/src/assets/error_images/401.png differ diff --git a/src/assets/error_images/404.png b/src/assets/error_images/404.png new file mode 100644 index 000000000..14fa72555 Binary files /dev/null and b/src/assets/error_images/404.png differ diff --git a/src/assets/error_images/cloud.png b/src/assets/error_images/cloud.png new file mode 100644 index 000000000..247c06b41 Binary files /dev/null and b/src/assets/error_images/cloud.png differ diff --git a/src/assets/ewm.png b/src/assets/ewm.png new file mode 100644 index 000000000..a97a6ad46 Binary files /dev/null and b/src/assets/ewm.png differ diff --git a/src/assets/login_images/background.jpg b/src/assets/login_images/background.jpg new file mode 100644 index 000000000..87ac4969b Binary files /dev/null and b/src/assets/login_images/background.jpg differ diff --git a/src/assets/pro.png b/src/assets/pro.png new file mode 100644 index 000000000..09bcef5b3 Binary files /dev/null and b/src/assets/pro.png differ diff --git a/src/assets/qr_logo/lqr_logo.png b/src/assets/qr_logo/lqr_logo.png new file mode 100644 index 000000000..f81db4620 Binary files /dev/null and b/src/assets/qr_logo/lqr_logo.png differ diff --git a/src/assets/zfb_699.jpg b/src/assets/zfb_699.jpg new file mode 100644 index 000000000..4760e7c35 Binary files /dev/null and b/src/assets/zfb_699.jpg differ diff --git a/src/assets/zfb_799.jpg b/src/assets/zfb_799.jpg new file mode 100644 index 000000000..ff1060437 Binary files /dev/null and b/src/assets/zfb_799.jpg differ diff --git a/src/colorfulIcon/index.js b/src/colorfulIcon/index.js new file mode 100644 index 000000000..2dd876e1f --- /dev/null +++ b/src/colorfulIcon/index.js @@ -0,0 +1,13 @@ +const req = require.context('./svg', false, /\.svg$/), + requireAll = (requireContext) => { + /*let a = requireContext.keys().map(requireContext); + let arr = []; + for (let i = 0; i < a.length; i++) { + console.log(); + let icon = a[i].default.id; + arr.push(icon); + } + console.log(JSON.stringify(arr));*/ + return requireContext.keys().map(requireContext) + } +requireAll(req) diff --git a/src/colorfulIcon/svg/alphabetical_sorting.svg b/src/colorfulIcon/svg/alphabetical_sorting.svg new file mode 100644 index 000000000..3cfa035b0 --- /dev/null +++ b/src/colorfulIcon/svg/alphabetical_sorting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/colorfulIcon/svg/vab.svg b/src/colorfulIcon/svg/vab.svg new file mode 100644 index 000000000..5c46271c7 --- /dev/null +++ b/src/colorfulIcon/svg/vab.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/components/SelectTree/index.vue b/src/components/SelectTree/index.vue new file mode 100644 index 000000000..bacc7dd83 --- /dev/null +++ b/src/components/SelectTree/index.vue @@ -0,0 +1,201 @@ + + + + + + diff --git a/src/components/VabCharge/index.vue b/src/components/VabCharge/index.vue new file mode 100644 index 000000000..609da971c --- /dev/null +++ b/src/components/VabCharge/index.vue @@ -0,0 +1,191 @@ + + + + + diff --git a/src/components/VabProfile/index.vue b/src/components/VabProfile/index.vue new file mode 100644 index 000000000..7f291dd17 --- /dev/null +++ b/src/components/VabProfile/index.vue @@ -0,0 +1,313 @@ + + + + + diff --git a/src/components/VabSnow/index.vue b/src/components/VabSnow/index.vue new file mode 100644 index 000000000..5131d525b --- /dev/null +++ b/src/components/VabSnow/index.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/src/components/VabUpload/index.vue b/src/components/VabUpload/index.vue new file mode 100644 index 000000000..31d83a505 --- /dev/null +++ b/src/components/VabUpload/index.vue @@ -0,0 +1,255 @@ + + + + + diff --git a/src/config/index.js b/src/config/index.js index 144fa272b..77c723e9e 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,3 +1,8 @@ +/** + * @description 3个子配置,通用配置|主题配置|网络配置导出 + */ const setting = require('./setting.config') const theme = require('./theme.config') -module.exports = Object.assign({}, setting, theme) +const network = require('./net.config') +const routes = require('./routes') +module.exports = Object.assign({}, setting, theme, network, routes) diff --git a/src/config/net.config.js b/src/config/net.config.js new file mode 100644 index 000000000..335b8bf8d --- /dev/null +++ b/src/config/net.config.js @@ -0,0 +1,18 @@ +/** + * @description 导出默认网路配置 + **/ +const network = { + //配后端数据的接收方式application/json;charset=UTF-8或者application/x-www-form-urlencoded;charset=UTF-8 + contentType: 'application/json;charset=UTF-8', + //消息框消失时间 + messageDuration: 3000, + //最长请求时间 + requestTimeout: 5000, + //操作正常code,支持String、Array、int多种类型 + successCode: [200, 0], + //登录失效code + invalidCode: 402, + //无权限code + noPermissionCode: 401, +} +module.exports = network diff --git a/src/config/permission.js b/src/config/permission.js new file mode 100644 index 000000000..25aaf6ce7 --- /dev/null +++ b/src/config/permission.js @@ -0,0 +1,88 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 路由守卫,目前两种模式:all模式与intelligence模式 + */ +import router from '@/router' +import store from '@/store' +import VabProgress from 'nprogress' +import 'nprogress/nprogress.css' +import getPageTitle from '@/utils/pageTitle' +import { + authentication, + loginInterception, + progressBar, + recordRoute, + routesWhiteList, +} from '@/config' + +VabProgress.configure({ + easing: 'ease', + speed: 500, + trickleSpeed: 200, + showSpinner: false, +}) +router.beforeResolve(async (to, from, next) => { + if (progressBar) VabProgress.start() + let hasToken = store.getters['user/accessToken'] + + if (!loginInterception) hasToken = true + + store.dispatch('user/getUserInfo') + + store.dispatch('routes/setAllRoutes') + + next() + // if (hasToken) { + // if (to.path === '/login') { + // next({ path: '/' }) + // if (progressBar) VabProgress.done() + // } else { + // const hasPermissions = + // store.getters['user/permissions'] && + // store.getters['user/permissions'].length > 0 + // if (hasPermissions) { + // next() + // } else { + // try { + // let permissions + // if (!loginInterception) { + // //settings.js loginInterception为false时,创建虚拟权限 + // await store.dispatch('user/setPermissions', ['admin']) + // permissions = ['admin'] + // } else { + // // permissions = await store.dispatch('user/getUserInfo') + // permissions = ['admin'] + // } + // + // let accessRoutes = [] + // if (authentication === 'intelligence') { + // accessRoutes = await store.dispatch('routes/setRoutes', permissions) + // } else if (authentication === 'all') { + // accessRoutes = await store.dispatch('routes/setAllRoutes') + // } + // router.addRoutes(accessRoutes) + // next({ ...to, replace: true }) + // } catch { + // await store.dispatch('user/resetAccessToken') + // if (progressBar) VabProgress.done() + // } + // } + // } + // } else { + // if (routesWhiteList.indexOf(to.path) !== -1) { + // next() + // } else { + // if (recordRoute) { + // next(`/login?redirect=${to.path}`) + // } else { + // next('/login') + // } + // + // if (progressBar) VabProgress.done() + // } + // } + document.title = getPageTitle(to.meta.title) +}) +router.afterEach(() => { + if (progressBar) VabProgress.done() +}) diff --git a/src/config/routes.js b/src/config/routes.js new file mode 100644 index 000000000..3c23356a1 --- /dev/null +++ b/src/config/routes.js @@ -0,0 +1,293 @@ +const data = [ + { + path: '/', + component: 'Layout', + redirect: 'index', + children: [ + { + path: 'index', + name: 'Index', + component: '@/views/index/index', + meta: { + title: '首页', + icon: 'home', + affix: true, + }, + }, + ], + }, + { + path: '/personnelManagement', + component: 'Layout', + redirect: 'noRedirect', + name: 'PersonnelManagement', + meta: { title: '人员', icon: 'users-cog', permissions: ['admin'] }, + children: [ + { + path: 'userManagement', + name: 'UserManagement', + component: '@/views/personnelManagement/userManagement/index', + meta: { title: '用户管理' }, + }, + { + path: 'roleManagement', + name: 'RoleManagement', + component: '@/views/personnelManagement/roleManagement/index', + meta: { title: '角色管理' }, + }, + { + path: 'menuManagement', + name: 'MenuManagement', + component: '@/views/personnelManagement/menuManagement/index', + meta: { title: '菜单管理', badge: 'New' }, + }, + ], + }, + { + path: '/vab', + component: 'Layout', + redirect: 'noRedirect', + name: 'Vab', + alwaysShow: true, + meta: { title: '组件', icon: 'cloud' }, + children: [ + { + path: 'permissions', + name: 'Permission', + component: '@/views/vab/permissions/index', + meta: { + title: '权限控制', + permissions: ['admin', 'editor'], + badge: 'New', + }, + }, + { + path: 'icon', + component: 'EmptyLayout', + redirect: 'noRedirect', + name: 'Icon', + meta: { + title: '图标', + permissions: ['admin'], + }, + children: [ + { + path: 'awesomeIcon', + name: 'AwesomeIcon', + component: '@/views/vab/icon/index', + meta: { title: '常规图标' }, + }, + { + path: 'remixIcon', + name: 'RemixIcon', + component: '@/views/vab/icon/remixIcon', + meta: { title: '小清新图标' }, + }, + { + path: 'colorfulIcon', + name: 'ColorfulIcon', + component: '@/views/vab/icon/colorfulIcon', + meta: { title: '多彩图标' }, + }, + ], + }, + { + path: 'table', + component: '@/views/vab/table/index', + name: 'Table', + meta: { + title: '表格', + permissions: ['admin'], + }, + }, + { + path: 'map', + name: 'Map', + component: '@/views/vab/map/index', + meta: { title: '地图', permissions: ['admin'], badge: 'Pro' }, + }, + { + path: 'webSocket', + name: 'WebSocket', + component: '@/views/vab/webSocket/index', + meta: { title: 'webSocket', permissions: ['admin'] }, + }, + { + path: 'form', + name: 'Form', + component: '@/views/vab/form/index', + meta: { title: '表单', permissions: ['admin'] }, + }, + { + path: 'element', + name: 'Element', + component: '@/views/vab/element/index', + meta: { title: '常用组件', permissions: ['admin'] }, + }, + { + path: 'tree', + name: 'Tree', + component: '@/views/vab/tree/index', + meta: { title: '树', permissions: ['admin'] }, + }, + { + path: 'verify', + name: 'Verify', + component: '@/views/vab/verify/index', + meta: { title: '验证码', permissions: ['admin'] }, + }, + { + path: 'menu1', + component: '@/views/vab/nested/menu1/index', + name: 'Menu1', + alwaysShow: true, + meta: { + title: '嵌套路由 1', + permissions: ['admin'], + }, + children: [ + { + path: 'menu1-1', + name: 'Menu1-1', + alwaysShow: true, + meta: { title: '嵌套路由 1-1' }, + component: '@/views/vab/nested/menu1/menu1-1/index', + + children: [ + { + path: 'menu1-1-1', + name: 'Menu1-1-1', + meta: { title: '嵌套路由 1-1-1' }, + component: '@/views/vab/nested/menu1/menu1-1/menu1-1-1/index', + }, + ], + }, + ], + }, + { + path: 'magnifier', + name: 'Magnifier', + component: '@/views/vab/magnifier/index', + meta: { title: '放大镜', permissions: ['admin'] }, + }, + { + path: 'loading', + name: 'Loading', + component: '@/views/vab/loading/index', + meta: { title: 'loading', permissions: ['admin'] }, + }, + { + path: 'player', + name: 'Player', + component: '@/views/vab/player/index', + meta: { title: '视频播放器', permissions: ['admin'] }, + }, + { + path: 'markdownEditor', + name: 'MarkdownEditor', + component: '@/views/vab/markdownEditor/index', + meta: { title: 'markdown编辑器', permissions: ['admin'] }, + }, + { + path: 'editor', + name: 'Editor', + component: '@/views/vab/editor/index', + meta: { title: '富文本编辑器', permissions: ['admin'], badge: 'New' }, + }, + { + path: 'backToTop', + name: 'BackToTop', + component: '@/views/vab/backToTop/index', + meta: { title: '返回顶部', permissions: ['admin'] }, + }, + { + path: 'lodash', + name: 'Lodash', + component: '@/views/vab/lodash/index', + meta: { title: 'lodash', permissions: ['admin'] }, + }, + { + path: 'smallComponents', + name: 'SmallComponents', + component: '@/views/vab/smallComponents/index', + meta: { title: '小组件', permissions: ['admin'] }, + }, + + { + path: 'upload', + name: 'Upload', + component: '@/views/vab/upload/index', + meta: { title: '上传', permissions: ['admin'] }, + }, + { + path: 'log', + name: 'Log', + component: '@/views/vab/errorLog/index', + meta: { title: '错误日志模拟', permissions: ['admin'] }, + }, + { + path: 'more', + name: 'More', + component: '@/views/vab/more/index', + meta: { title: '关于', permissions: ['admin'] }, + }, + ], + }, + { + path: '/mall', + component: 'Layout', + redirect: 'noRedirect', + name: 'Mall', + meta: { + title: '商城', + icon: 'shopping-cart', + permissions: ['admin'], + }, + + children: [ + { + path: 'pay', + name: 'Pay', + component: '@/views/mall/pay/index', + meta: { + title: '支付', + noKeepAlive: true, + }, + children: null, + }, + { + path: 'goodsList', + name: 'GoodsList', + component: '@/views/mall/goodsList/index', + meta: { + title: '商品列表', + }, + }, + ], + }, + { + path: '/error', + component: 'EmptyLayout', + redirect: 'noRedirect', + name: 'Error', + meta: { title: '错误页', icon: 'bug' }, + children: [ + { + path: '401', + name: 'Error401', + component: '@/views/401', + meta: { title: '401' }, + }, + { + path: '404', + name: 'Error404', + component: '@/views/404', + meta: { title: '404' }, + }, + ], + }, +] +const routes = { + routes: data, +} +module.exports = routes diff --git a/src/config/setting.config.js b/src/config/setting.config.js index f1bcf1a08..176d4b830 100644 --- a/src/config/setting.config.js +++ b/src/config/setting.config.js @@ -8,19 +8,65 @@ const setting = { outputDir: 'dist', // 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录。 assetsDir: 'static', + // 开发环境每次保存时是否输出为eslint编译警告 + lintOnSave: true, // 进行编译的依赖 transpileDependencies: [], - baseURL: - process.env.NODE_ENV === 'development' - ? '/' - : '/', - // 标题 - title: 'FOTON', - // 开发环境端口号 + // 默认的接口地址 如果是开发环境和生产环境走vab-mock-server,当然你也可以选择自己配置成需要的接口地址 + baseURL: process.env.NODE_ENV === 'development' ? '/api/' : '/api/', + //标题 (包括初次加载雪花屏的标题 页面的标题 浏览器的标题) + title: '福田汽车', + //简写 + abbreviation: 'vab', + //开发环境端口号 devPort: '8888', - // 版本号 + //版本号 version: process.env.VUE_APP_VERSION, - // 需要自动注入并加载的模块 - providePlugin: { maptalks: 'maptalks', 'window.maptalks': 'maptalks' } + //这一项非常重要!请务必保留MIT协议下package.json及copyright作者信息 即可免费商用,不遵守此项约定你将无法使用该框架,如需自定义版权信息请联系QQ1204505056 + copyright: 'vab', + //是否显示页面底部自定义版权信息 + footerCopyright: true, + //是否显示顶部进度条 + progressBar: true, + //缓存路由的最大数量 + keepAliveMaxNum: 99, + // 路由模式,可选值为 history 或 hash + routerMode: 'hash', + //不经过token校验的路由 + routesWhiteList: ['/login', '/register', '/404', '/401'], + //加载时显示文字 + loadingText: '正在加载中...', + //token名称 + tokenName: 'accessToken', + //token在localStorage、sessionStorage存储的key的名称 + tokenTableName: 'vue-admin-beautiful-2021', + //token存储位置localStorage sessionStorage + storage: 'localStorage', + //token失效回退到登录页时是否记录本次的路由 + recordRoute: true, + //是否显示logo,不显示时设置false,显示时请填写remixIcon图标名称,暂时只支持设置remixIcon + logo: 'vuejs-fill', + //是否显示在页面高亮错误 + errorLog: ['development', 'production'], + //是否开启登录拦截 + loginInterception: true, + //是否开启登录RSA加密 + loginRSA: true, + //intelligence和all两种方式,前者后端权限只控制permissions不控制view文件的import(前后端配合,减轻后端工作量),all方式完全交给后端前端只负责加载 + authentication: 'intelligence', + //vertical布局时是否只保持一个子菜单的展开 + uniqueOpened: true, + //vertical布局时默认展开的菜单path,使用逗号隔开建议只展开一个 + defaultOopeneds: ['/vab'], + //需要加loading层的请求,防止重复提交 + debounce: ['doEdit'], + //需要自动注入并加载的模块 + providePlugin: { maptalks: 'maptalks', 'window.maptalks': 'maptalks' }, + //npm run build时是否自动生成7z压缩包 + build7z: false, + //代码生成机生成在view下的文件夹名称 + templateFolder: 'project', + //是否显示终端donation打印 + donation: true, } module.exports = setting diff --git a/src/config/settings.js b/src/config/settings.js new file mode 100644 index 000000000..c070a6691 --- /dev/null +++ b/src/config/settings.js @@ -0,0 +1,6 @@ +/** + * @description 3个子配置,通用配置|主题配置|网络配置 + */ +//默认配置 +const { setting, theme, network } = require('./') +module.exports = Object.assign({}, setting, theme, network) diff --git a/src/layouts/EmptyLayout.vue b/src/layouts/EmptyLayout.vue new file mode 100644 index 000000000..98240aef8 --- /dev/null +++ b/src/layouts/EmptyLayout.vue @@ -0,0 +1,3 @@ + diff --git a/src/layouts/components/Permissions/index.js b/src/layouts/components/Permissions/index.js new file mode 100644 index 000000000..7d2d86e2c --- /dev/null +++ b/src/layouts/components/Permissions/index.js @@ -0,0 +1,13 @@ +import permissions from './permissions' + +const install = function (Vue) { + Vue.directive('permissions', permissions) +} + +if (window.Vue) { + window['permissions'] = permissions + Vue.use(install) +} + +permissions.install = install +export default permissions diff --git a/src/layouts/components/Permissions/permissions.js b/src/layouts/components/Permissions/permissions.js new file mode 100644 index 000000000..74ac1f64a --- /dev/null +++ b/src/layouts/components/Permissions/permissions.js @@ -0,0 +1,13 @@ +import store from '@/store' + +export default { + inserted(element, binding) { + const { value } = binding + const permissions = store.getters['user/permissions'] + if (value && value instanceof Array && value.length > 0) { + const hasPermission = permissions.some((role) => value.includes(role)) + if (!hasPermission) + element.parentNode && element.parentNode.removeChild(element) + } + }, +} diff --git a/src/layouts/components/VabAd/index.vue b/src/layouts/components/VabAd/index.vue new file mode 100644 index 000000000..54416bf06 --- /dev/null +++ b/src/layouts/components/VabAd/index.vue @@ -0,0 +1,70 @@ + + + + diff --git a/src/layouts/components/VabAppMain/index.vue b/src/layouts/components/VabAppMain/index.vue new file mode 100644 index 000000000..a76adc47c --- /dev/null +++ b/src/layouts/components/VabAppMain/index.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/src/layouts/components/VabAvatar/index.vue b/src/layouts/components/VabAvatar/index.vue new file mode 100644 index 000000000..67dc68bb6 --- /dev/null +++ b/src/layouts/components/VabAvatar/index.vue @@ -0,0 +1,105 @@ + + + + diff --git a/src/layouts/components/VabBreadcrumb/index.vue b/src/layouts/components/VabBreadcrumb/index.vue new file mode 100644 index 000000000..0259bccba --- /dev/null +++ b/src/layouts/components/VabBreadcrumb/index.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/layouts/components/VabColorfullIcon/index.vue b/src/layouts/components/VabColorfullIcon/index.vue new file mode 100644 index 000000000..840de3cab --- /dev/null +++ b/src/layouts/components/VabColorfullIcon/index.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/src/layouts/components/VabErrorLog/index.vue b/src/layouts/components/VabErrorLog/index.vue new file mode 100644 index 000000000..fd7170a47 --- /dev/null +++ b/src/layouts/components/VabErrorLog/index.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/src/layouts/components/VabFullScreenBar/index.vue b/src/layouts/components/VabFullScreenBar/index.vue new file mode 100644 index 000000000..fedf502f7 --- /dev/null +++ b/src/layouts/components/VabFullScreenBar/index.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/layouts/components/VabGithubCorner/index.vue b/src/layouts/components/VabGithubCorner/index.vue new file mode 100644 index 000000000..23341f330 --- /dev/null +++ b/src/layouts/components/VabGithubCorner/index.vue @@ -0,0 +1,75 @@ + + + + diff --git a/src/layouts/components/VabLogo/index.vue b/src/layouts/components/VabLogo/index.vue new file mode 100644 index 000000000..c07c1b258 --- /dev/null +++ b/src/layouts/components/VabLogo/index.vue @@ -0,0 +1,92 @@ + + + diff --git a/src/layouts/components/VabNavBar/index.vue b/src/layouts/components/VabNavBar/index.vue new file mode 100644 index 000000000..9a2a63404 --- /dev/null +++ b/src/layouts/components/VabNavBar/index.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/src/layouts/components/VabQueryForm/VabQueryFormBottomPanel.vue b/src/layouts/components/VabQueryForm/VabQueryFormBottomPanel.vue new file mode 100644 index 000000000..327dc83af --- /dev/null +++ b/src/layouts/components/VabQueryForm/VabQueryFormBottomPanel.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/layouts/components/VabQueryForm/VabQueryFormLeftPanel.vue b/src/layouts/components/VabQueryForm/VabQueryFormLeftPanel.vue new file mode 100644 index 000000000..ee23a7821 --- /dev/null +++ b/src/layouts/components/VabQueryForm/VabQueryFormLeftPanel.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/layouts/components/VabQueryForm/VabQueryFormRightPanel.vue b/src/layouts/components/VabQueryForm/VabQueryFormRightPanel.vue new file mode 100644 index 000000000..e74aa14ea --- /dev/null +++ b/src/layouts/components/VabQueryForm/VabQueryFormRightPanel.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/layouts/components/VabQueryForm/VabQueryFormTopPanel.vue b/src/layouts/components/VabQueryForm/VabQueryFormTopPanel.vue new file mode 100644 index 000000000..7a48e3f57 --- /dev/null +++ b/src/layouts/components/VabQueryForm/VabQueryFormTopPanel.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/layouts/components/VabQueryForm/index.vue b/src/layouts/components/VabQueryForm/index.vue new file mode 100644 index 000000000..f49a6bb3f --- /dev/null +++ b/src/layouts/components/VabQueryForm/index.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/layouts/components/VabRemixIcon/index.vue b/src/layouts/components/VabRemixIcon/index.vue new file mode 100644 index 000000000..1f45271a1 --- /dev/null +++ b/src/layouts/components/VabRemixIcon/index.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/src/layouts/components/VabSideBar/components/VabMenuItem.vue b/src/layouts/components/VabSideBar/components/VabMenuItem.vue new file mode 100644 index 000000000..3ed8d1b0d --- /dev/null +++ b/src/layouts/components/VabSideBar/components/VabMenuItem.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/layouts/components/VabSideBar/components/VabSideBarItem.vue b/src/layouts/components/VabSideBar/components/VabSideBarItem.vue new file mode 100644 index 000000000..baaac70eb --- /dev/null +++ b/src/layouts/components/VabSideBar/components/VabSideBarItem.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/src/layouts/components/VabSideBar/components/VabSubmenu.vue b/src/layouts/components/VabSideBar/components/VabSubmenu.vue new file mode 100644 index 000000000..58d90c5cd --- /dev/null +++ b/src/layouts/components/VabSideBar/components/VabSubmenu.vue @@ -0,0 +1,60 @@ + + + diff --git a/src/layouts/components/VabSideBar/index.vue b/src/layouts/components/VabSideBar/index.vue new file mode 100644 index 000000000..c01103f74 --- /dev/null +++ b/src/layouts/components/VabSideBar/index.vue @@ -0,0 +1,141 @@ + + + diff --git a/src/layouts/components/VabTabsBar/index.vue b/src/layouts/components/VabTabsBar/index.vue new file mode 100644 index 000000000..221037a01 --- /dev/null +++ b/src/layouts/components/VabTabsBar/index.vue @@ -0,0 +1,297 @@ + + + + + diff --git a/src/layouts/components/VabThemeBar/index.vue b/src/layouts/components/VabThemeBar/index.vue new file mode 100644 index 000000000..bd09aba24 --- /dev/null +++ b/src/layouts/components/VabThemeBar/index.vue @@ -0,0 +1,259 @@ + + + + + + diff --git a/src/layouts/components/VabTopBar/index.vue b/src/layouts/components/VabTopBar/index.vue new file mode 100644 index 000000000..07602f61b --- /dev/null +++ b/src/layouts/components/VabTopBar/index.vue @@ -0,0 +1,224 @@ + + + + diff --git a/src/layouts/components/index.js b/src/layouts/components/index.js new file mode 100644 index 000000000..96c2f141e --- /dev/null +++ b/src/layouts/components/index.js @@ -0,0 +1,28 @@ +module.exports = { + webpackBarName: 'vue-admin-beautiful', + webpackBanner: + ' build: vue-admin-beautiful \n vue-admin-beautiful author: chuzhixin 1204505056@qq.com \n vue-admin-beautiful QQ Group(QQ群): 972435319、1139183756 \n time: ', + donationConsole() { + const chalk = require('chalk') + console.log( + chalk.green( + `> 欢迎使用vue-admin-beautiful,开源地址:https://github.com/chuzhixin/vue-admin-beautiful` + ) + ) + + console.log( + chalk.green( + `> pro版演示地址:http://chu1204505056.gitee.io/admin-pro?hmsr=console&hmpl=&hmcu=&hmkw=&hmci=` + ) + ) + + console.log( + chalk.green( + `> 使用中出现任何问题可加QQ群反馈,获取基础版、文档,请我们喝杯咖啡(如若情况不允许,请勿勉强):https://gitee.com/chu1204505056/vue-admin-beautiful#vue-admin-beautiful-%E5%89%8D%E7%AB%AF%E8%AE%A8%E8%AE%BA-qq-%E7%BE%A4` + ) + ) + + console.log(chalk.green(`> 如果您不希望显示以上信息,可在config中配置关闭`)) + console.log('\n') + }, +} diff --git a/src/layouts/export.js b/src/layouts/export.js new file mode 100644 index 000000000..54af5005b --- /dev/null +++ b/src/layouts/export.js @@ -0,0 +1,17 @@ +/** + * @description 公共布局及样式自动引入 + */ + +import Vue from 'vue' + +const requireComponents = require.context('./components', true, /\.vue$/) +requireComponents.keys().forEach((fileName) => { + const componentConfig = requireComponents(fileName) + const componentName = componentConfig.default.name + Vue.component(componentName, componentConfig.default || componentConfig) +}) + +const requireThemes = require.context('@/styles/themes', true, /\.scss$/) +requireThemes.keys().forEach((fileName) => { + require(`@/styles/themes/${fileName.slice(2)}`) +}) diff --git a/src/layouts/index.vue b/src/layouts/index.vue new file mode 100644 index 000000000..ef5c93215 --- /dev/null +++ b/src/layouts/index.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/src/main.js b/src/main.js index ed9a3722e..bf1b47ce1 100644 --- a/src/main.js +++ b/src/main.js @@ -1,12 +1,14 @@ import Vue from 'vue' -import App from './App.vue' -import router from './router' +import App from './App' import store from './store' - +import router from './router' +import './plugins' +import '@/layouts/export' Vue.config.productionTip = false new Vue({ + el: '#vue-admin-beautiful', router, store, - render: h => h(App) -}).$mount('#foton') + render: (h) => h(App), +}) diff --git a/src/plugins/echarts.js b/src/plugins/echarts.js new file mode 100644 index 000000000..f600f836c --- /dev/null +++ b/src/plugins/echarts.js @@ -0,0 +1,4 @@ +import 'echarts' +import VabChart from 'vue-echarts' + +export default VabChart diff --git a/src/plugins/element.js b/src/plugins/element.js new file mode 100644 index 000000000..3b91ff0ac --- /dev/null +++ b/src/plugins/element.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import ElementUI from 'element-ui' +import 'element-ui/lib/theme-chalk/display.css' + +import '@/styles/element-variables.scss' + +Vue.use(ElementUI, { + size: 'small', +}) diff --git a/src/plugins/index.js b/src/plugins/index.js new file mode 100644 index 000000000..52e17fba3 --- /dev/null +++ b/src/plugins/index.js @@ -0,0 +1,17 @@ +/* 公共引入,勿随意修改,修改时需经过确认 */ +import Vue from 'vue' +import './element' +import './support' +import '@/styles/vab.scss' +import '@/remixIcon' +import '@/colorfulIcon' +import '@/config/permission' +import '@/utils/errorLog' +import './vabIcon' +import VabPermissions from '@/layouts/components/Permissions' +import Vab from '@/utils/vab' +import VabCount from 'zx-count' + +Vue.use(Vab) +Vue.use(VabPermissions) +Vue.use(VabCount) diff --git a/src/plugins/support.js b/src/plugins/support.js new file mode 100644 index 000000000..391064970 --- /dev/null +++ b/src/plugins/support.js @@ -0,0 +1,20 @@ +import { MessageBox } from 'element-ui' +import { donation } from '@/config' +import { dependencies, repository } from '../../package.json' + +if (!!window.ActiveXObject || 'ActiveXObject' in window) { + MessageBox({ + title: '温馨提示', + message: + '自2015年3月起,微软已宣布弃用IE,且不再对IE提供任何更新维护,请点击此处访问微软官网更新浏览器,如果您使用的是双核浏览器,请您切换浏览器内核为极速模式', + type: 'warning', + showClose: false, + showConfirmButton: false, + closeOnClickModal: false, + closeOnPressEscape: false, + closeOnHashChange: false, + dangerouslyUseHTMLString: true, + }) +} +if (!dependencies['vab-icon'] || !dependencies['zx-layouts']) + document.body.innerHTML = '' diff --git a/src/plugins/vabIcon.js b/src/plugins/vabIcon.js new file mode 100644 index 000000000..634bca731 --- /dev/null +++ b/src/plugins/vabIcon.js @@ -0,0 +1,4 @@ +import Vue from 'vue' +import VabIcon from 'vab-icon' + +Vue.component('VabIcon', VabIcon) diff --git a/src/plugins/vabMagnifier.js b/src/plugins/vabMagnifier.js new file mode 100644 index 000000000..748fda247 --- /dev/null +++ b/src/plugins/vabMagnifier.js @@ -0,0 +1,3 @@ +import VabMagnifier from 'zx-magnifie' + +export default VabMagnifier diff --git a/src/plugins/vabMarkdownEditor.js b/src/plugins/vabMarkdownEditor.js new file mode 100644 index 000000000..03686b69c --- /dev/null +++ b/src/plugins/vabMarkdownEditor.js @@ -0,0 +1,5 @@ +import ZxMarkdownEditor from 'zx-markdown-editor' +import 'zx-markdown-editor/dist/zx-markdown-editor.css' + +const VabMarkdownEditor = ZxMarkdownEditor +export default VabMarkdownEditor diff --git a/src/plugins/vabPlayer.js b/src/plugins/vabPlayer.js new file mode 100644 index 000000000..69bf8a798 --- /dev/null +++ b/src/plugins/vabPlayer.js @@ -0,0 +1,3 @@ +import { VabPlayerMp4, VabPlayerHls, VabPlayerFlv } from 'zx-player' + +export { VabPlayerMp4, VabPlayerHls, VabPlayerFlv } diff --git a/src/plugins/vabQuill.js b/src/plugins/vabQuill.js new file mode 100644 index 000000000..eae4bbf4c --- /dev/null +++ b/src/plugins/vabQuill.js @@ -0,0 +1,4 @@ +import 'zx-quill/dist/zx-quill.css' +import VabQuill from 'zx-quill' + +export default VabQuill diff --git a/src/plugins/vabVerify.js b/src/plugins/vabVerify.js new file mode 100644 index 000000000..e98ae43e2 --- /dev/null +++ b/src/plugins/vabVerify.js @@ -0,0 +1,4 @@ +import VabVerify from 'zx-verify' +import 'zx-verify/dist/zx-verify.css' + +export default VabVerify diff --git a/src/remixIcon/index.js b/src/remixIcon/index.js new file mode 100644 index 000000000..2dd876e1f --- /dev/null +++ b/src/remixIcon/index.js @@ -0,0 +1,13 @@ +const req = require.context('./svg', false, /\.svg$/), + requireAll = (requireContext) => { + /*let a = requireContext.keys().map(requireContext); + let arr = []; + for (let i = 0; i < a.length; i++) { + console.log(); + let icon = a[i].default.id; + arr.push(icon); + } + console.log(JSON.stringify(arr));*/ + return requireContext.keys().map(requireContext) + } +requireAll(req) diff --git a/src/remixIcon/svg/qq-fill.svg b/src/remixIcon/svg/qq-fill.svg new file mode 100644 index 000000000..1f446b117 --- /dev/null +++ b/src/remixIcon/svg/qq-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/remixIcon/svg/vuejs-fill.svg b/src/remixIcon/svg/vuejs-fill.svg new file mode 100644 index 000000000..1b1a1371b --- /dev/null +++ b/src/remixIcon/svg/vuejs-fill.svg @@ -0,0 +1 @@ + diff --git a/src/router/index.js b/src/router/index.js index e44cdcd85..902c6ed5e 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,27 +1,400 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description router全局配置,如有必要可分文件抽离,其中asyncRoutes只有在intelligence模式下才会用到,vip文档中已提供路由的基础图标与小清新图标的配置方案,请仔细阅读 + */ + import Vue from 'vue' import VueRouter from 'vue-router' -import Home from '../views/Home.vue' +import Layout from '@/layouts' +import EmptyLayout from '@/layouts/EmptyLayout' +import { publicPath, routerMode } from '@/config' Vue.use(VueRouter) - -const routes = [ +export const constantRoutes = [ { - path: '/', - name: 'Home', - component: Home + path: '/login', + component: () => import('@/views/login/index'), + hidden: true, }, { - path: '/about', - name: 'About', - // route level code-splitting - // this generates a separate chunk (about.[hash].js) for this route - // which is lazy-loaded when the route is visited. - component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') - } + path: '/register', + component: () => import('@/views/register/index'), + hidden: true, + }, + { + path: '/401', + name: '401', + component: () => import('@/views/401'), + hidden: true, + }, + { + path: '/404', + name: '404', + component: () => import('@/views/404'), + hidden: true, + }, +] + +export const asyncRoutes = [ + { + path: '/', + component: Layout, + redirect: 'index', + children: [ + { + path: 'index', + name: 'Index', + component: () => import('@/views/index/index'), + meta: { + title: '首页', + icon: 'home', + affix: true, + }, + }, + ], + }, + /* { + path: "/test", + component: Layout, + redirect: "noRedirect", + children: [ + { + path: "test", + name: "Test", + component: () => import("@/views/test/index"), + meta: { + title: "test", + icon: "marker", + permissions: ["admin"], + }, + }, + ], + }, */ + + { + path: '/vab', + component: Layout, + redirect: 'noRedirect', + name: 'Vab', + alwaysShow: true, + meta: { title: '组件', icon: 'box-open' }, + children: [ + { + path: 'permissions', + name: 'Permission', + component: () => import('@/views/vab/permissions/index'), + meta: { + title: '角色权限', + permissions: ['admin', 'editor'], + }, + }, + { + path: 'icon', + component: EmptyLayout, + redirect: 'noRedirect', + name: 'Icon', + meta: { + title: '图标', + permissions: ['admin'], + }, + children: [ + { + path: 'awesomeIcon', + name: 'AwesomeIcon', + component: () => import('@/views/vab/icon/index'), + meta: { title: '常规图标' }, + }, + { + path: 'remixIcon', + name: 'RemixIcon', + component: () => import('@/views/vab/icon/remixIcon'), + meta: { title: '小清新图标' }, + }, + { + path: 'colorfulIcon', + name: 'ColorfulIcon', + component: () => import('@/views/vab/icon/colorfulIcon'), + meta: { title: '多彩图标' }, + }, + ], + }, + { + path: 'table', + component: () => import('@/views/vab/table/index'), + name: 'Table', + meta: { + title: '表格', + permissions: ['admin'], + }, + }, + { + path: 'map', + component: () => import('@/views/vab/map/index'), + name: 'Map', + meta: { + title: '地图', + permissions: ['admin'], + }, + }, + + { + path: 'webSocket', + name: 'WebSocket', + component: () => import('@/views/vab/webSocket/index'), + meta: { title: 'webSocket', permissions: ['admin'] }, + }, + { + path: 'form', + name: 'Form', + component: () => import('@/views/vab/form/index'), + meta: { title: '表单', permissions: ['admin'] }, + }, + { + path: 'element', + name: 'Element', + component: () => import('@/views/vab/element/index'), + meta: { title: '常用组件', permissions: ['admin'] }, + }, + { + path: 'tree', + name: 'Tree', + component: () => import('@/views/vab/tree/index'), + meta: { title: '树', permissions: ['admin'] }, + }, + { + path: 'verify', + name: 'Verify', + component: () => import('@/views/vab/verify/index'), + meta: { title: '验证码', permissions: ['admin'] }, + }, + { + path: 'menu1', + component: () => import('@/views/vab/nested/menu1/index'), + name: 'Menu1', + alwaysShow: true, + meta: { + title: '嵌套路由 1', + permissions: ['admin'], + }, + children: [ + { + path: 'menu1-1', + name: 'Menu1-1', + alwaysShow: true, + meta: { title: '嵌套路由 1-1' }, + component: () => import('@/views/vab/nested/menu1/menu1-1/index'), + + children: [ + { + path: 'menu1-1-1', + name: 'Menu1-1-1', + meta: { title: '嵌套路由 1-1-1' }, + component: () => + import('@/views/vab/nested/menu1/menu1-1/menu1-1-1/index'), + }, + ], + }, + ], + }, + { + path: 'magnifier', + name: 'Magnifier', + component: () => import('@/views/vab/magnifier/index'), + meta: { title: '放大镜', permissions: ['admin'] }, + }, + { + path: 'loading', + name: 'Loading', + component: () => import('@/views/vab/loading/index'), + meta: { title: 'loading', permissions: ['admin'] }, + }, + { + path: 'player', + name: 'Player', + component: () => import('@/views/vab/player/index'), + meta: { title: '视频播放器', permissions: ['admin'] }, + }, + { + path: 'markdownEditor', + name: 'MarkdownEditor', + component: () => import('@/views/vab/markdownEditor/index'), + meta: { title: 'markdown编辑器', permissions: ['admin'] }, + }, + { + path: 'editor', + name: 'Editor', + component: () => import('@/views/vab/editor/index'), + meta: { + title: '富文本编辑器', + permissions: ['admin'], + badge: 'New', + }, + }, + { + path: 'backToTop', + name: 'BackToTop', + component: () => import('@/views/vab/backToTop/index'), + meta: { title: '返回顶部', permissions: ['admin'] }, + }, + { + path: 'lodash', + name: 'Lodash', + component: () => import('@/views/vab/lodash/index'), + meta: { title: 'lodash', permissions: ['admin'] }, + }, + { + path: 'smallComponents', + name: 'SmallComponents', + component: () => import('@/views/vab/smallComponents/index'), + meta: { title: '小组件', permissions: ['admin'] }, + }, + + { + path: 'upload', + name: 'Upload', + component: () => import('@/views/vab/upload/index'), + meta: { title: '上传', permissions: ['admin'] }, + }, + { + path: 'log', + name: 'Log', + component: () => import('@/views/vab/errorLog/index'), + meta: { title: '错误日志模拟', permissions: ['admin'] }, + }, + { + path: + 'https://github.com/chuzhixin/vue-admin-beautiful?utm_source=gold_browser_extension', + name: 'ExternalLink', + meta: { + title: '外链', + target: '_blank', + permissions: ['admin', 'editor'], + badge: 'New', + }, + }, + { + path: 'more', + name: 'More', + component: () => import('@/views/vab/more/index'), + meta: { title: '关于', permissions: ['admin'] }, + }, + ], + }, + { + path: '/personnelManagement', + component: Layout, + redirect: 'noRedirect', + name: 'PersonnelManagement', + meta: { title: '配置', icon: 'users-cog', permissions: ['admin'] }, + children: [ + { + path: 'userManagement', + name: 'UserManagement', + component: () => + import('@/views/personnelManagement/userManagement/index'), + meta: { title: '用户管理' }, + }, + { + path: 'roleManagement', + name: 'RoleManagement', + component: () => + import('@/views/personnelManagement/roleManagement/index'), + meta: { title: '角色管理' }, + }, + { + path: 'menuManagement', + name: 'MenuManagement', + component: () => + import('@/views/personnelManagement/menuManagement/index'), + meta: { title: '菜单管理', badge: 'New' }, + }, + ], + }, + { + path: '/mall', + component: Layout, + redirect: 'noRedirect', + name: 'Mall', + meta: { + title: '商城', + icon: 'shopping-cart', + permissions: ['admin'], + }, + + children: [ + { + path: 'pay', + name: 'Pay', + component: () => import('@/views/mall/pay/index'), + meta: { + title: '支付', + noKeepAlive: true, + }, + children: null, + }, + { + path: 'goodsList', + name: 'GoodsList', + component: () => import('@/views/mall/goodsList/index'), + meta: { + title: '商品列表', + }, + }, + ], + }, + { + path: '/error', + component: EmptyLayout, + redirect: 'noRedirect', + name: 'Error', + meta: { title: '错误页', icon: 'bug' }, + children: [ + { + path: '401', + name: 'Error401', + component: () => import('@/views/401'), + meta: { title: '401' }, + }, + { + path: '404', + name: 'Error404', + component: () => import('@/views/404'), + meta: { title: '404' }, + }, + ], + }, + { + path: '*', + redirect: '/404', + hidden: true, + }, ] const router = new VueRouter({ - routes + base: publicPath, + mode: routerMode, + scrollBehavior: () => ({ + y: 0, + }), + routes: [...constantRoutes, ...asyncRoutes], }) +//注释的地方是允许路由重复点击,如果你觉得框架路由跳转规范太过严格可选择放开 +const originalPush = VueRouter.prototype.push +VueRouter.prototype.push = function push(location, onResolve, onReject) { + if (onResolve || onReject) + return originalPush.call(this, location, onResolve, onReject) + return originalPush.call(this, location).catch((err) => err) +} + +export function resetRouter() { + router.matcher = new VueRouter({ + base: publicPath, + mode: routerMode, + scrollBehavior: () => ({ + y: 0, + }), + routes: [...constantRoutes, ...asyncRoutes], + }).matcher +} export default router diff --git a/src/store/index.js b/src/store/index.js index f453a2fe4..0b20b4e74 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,4 +1,5 @@ /** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 导入所有 vuex 模块,自动加入namespaced:true,用于解决vuex命名冲突,请勿修改。 */ @@ -16,6 +17,6 @@ Object.keys(modules).forEach((key) => { modules[key]['namespaced'] = true }) const store = new Vuex.Store({ - modules + modules, }) export default store diff --git a/src/store/modules/errorLog.js b/src/store/modules/errorLog.js new file mode 100644 index 000000000..941c6e58c --- /dev/null +++ b/src/store/modules/errorLog.js @@ -0,0 +1,28 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 异常捕获的状态拦截,请勿修改 + */ + +const state = () => ({ + errorLogs: [], +}) +const getters = { + errorLogs: (state) => state.errorLogs, +} +const mutations = { + addErrorLog(state, errorLog) { + state.errorLogs.push(errorLog) + }, + clearErrorLog: (state) => { + state.errorLogs.splice(0) + }, +} +const actions = { + addErrorLog({ commit }, errorLog) { + commit('addErrorLog', errorLog) + }, + clearErrorLog({ commit }) { + commit('clearErrorLog') + }, +} +export default { state, getters, mutations, actions } diff --git a/src/store/modules/routes.js b/src/store/modules/routes.js new file mode 100644 index 000000000..562dd9a84 --- /dev/null +++ b/src/store/modules/routes.js @@ -0,0 +1,52 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 路由拦截状态管理,目前两种模式:all模式与intelligence模式,其中partialRoutes是菜单暂未使用 + */ +import { asyncRoutes, constantRoutes } from '@/router' +import { getRouterList } from '@/api/router' +import { convertRouter, filterAsyncRoutes } from '@/utils/handleRoutes' +import { routes } from '@/config' + +const state = () => ({ + routes: [], + partialRoutes: [], +}) +const getters = { + routes: (state) => state.routes, + partialRoutes: (state) => state.partialRoutes, +} +const mutations = { + setRoutes(state, routes) { + state.routes = constantRoutes.concat(routes) + }, + setAllRoutes(state, routes) { + state.routes = constantRoutes.concat(routes) + }, + setPartialRoutes(state, routes) { + state.partialRoutes = constantRoutes.concat(routes) + }, +} +const actions = { + async setRoutes({ commit }, permissions) { + //开源版只过滤动态路由permissions,admin不再默认拥有全部权限 + const finallyAsyncRoutes = await filterAsyncRoutes( + [...asyncRoutes], + permissions + ) + commit('setRoutes', finallyAsyncRoutes) + return finallyAsyncRoutes + }, + async setAllRoutes({ commit }) { + // let { data } = await getRouterList() + let data = routes + data.push({ path: '*', redirect: '/404', hidden: true }) + let accessRoutes = convertRouter(data) + commit('setAllRoutes', accessRoutes) + return accessRoutes + }, + setPartialRoutes({ commit }, accessRoutes) { + commit('setPartialRoutes', accessRoutes) + return accessRoutes + }, +} +export default { state, getters, mutations, actions } diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js new file mode 100644 index 000000000..fc7cf9de5 --- /dev/null +++ b/src/store/modules/settings.js @@ -0,0 +1,75 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 所有全局配置的状态管理,如无必要请勿修改 + */ + +import defaultSettings from '@/config' + +const { tabsBar, logo, layout, header, themeBar } = defaultSettings +const theme = + JSON.parse(localStorage.getItem('vue-admin-beautiful-theme')) || '' +const state = () => ({ + tabsBar: theme.tabsBar || tabsBar, + logo, + collapse: false, + layout: theme.layout || layout, + header: theme.header || header, + device: 'desktop', + themeBar, +}) +const getters = { + collapse: (state) => state.collapse, + device: (state) => state.device, + header: (state) => state.header, + layout: (state) => state.layout, + logo: (state) => state.logo, + tabsBar: (state) => state.tabsBar, + themeBar: (state) => state.themeBar, +} +const mutations = { + changeLayout: (state, layout) => { + if (layout) state.layout = layout + }, + changeHeader: (state, header) => { + if (header) state.header = header + }, + changeTabsBar: (state, tabsBar) => { + if (tabsBar) state.tabsBar = tabsBar + }, + changeCollapse: (state) => { + state.collapse = !state.collapse + }, + foldSideBar: (state) => { + state.collapse = true + }, + openSideBar: (state) => { + state.collapse = false + }, + toggleDevice: (state, device) => { + state.device = device + }, +} +const actions = { + changeLayout({ commit }, layout) { + commit('changeLayout', layout) + }, + changeHeader({ commit }, header) { + commit('changeHeader', header) + }, + changeTabsBar({ commit }, tabsBar) { + commit('changeTabsBar', tabsBar) + }, + changeCollapse({ commit }) { + commit('changeCollapse') + }, + foldSideBar({ commit }) { + commit('foldSideBar') + }, + openSideBar({ commit }) { + commit('openSideBar') + }, + toggleDevice({ commit }, device) { + commit('toggleDevice', device) + }, +} +export default { state, getters, mutations, actions } diff --git a/src/store/modules/table.js b/src/store/modules/table.js new file mode 100644 index 000000000..bf57c090c --- /dev/null +++ b/src/store/modules/table.js @@ -0,0 +1,23 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 代码生成机状态管理 + */ + +const state = () => ({ + srcCode: '', +}) +const getters = { + srcTableCode: (state) => state.srcCode, +} + +const mutations = { + setTableCode(state, srcCode) { + state.srcCode = srcCode + }, +} +const actions = { + setTableCode({ commit }, srcCode) { + commit('setTableCode', srcCode) + }, +} +export default { state, getters, mutations, actions } diff --git a/src/store/modules/tabsBar.js b/src/store/modules/tabsBar.js new file mode 100644 index 000000000..254d06288 --- /dev/null +++ b/src/store/modules/tabsBar.js @@ -0,0 +1,112 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description tabsBar多标签页逻辑,前期借鉴了很多开源项目发现都有个共同的特点很繁琐并不符合框架设计的初衷,后来在github用户hipi的启发下完成了重构,请勿修改 + */ + +const state = () => ({ + visitedRoutes: [], +}) +const getters = { + visitedRoutes: (state) => state.visitedRoutes, +} +const mutations = { + addVisitedRoute(state, route) { + let target = state.visitedRoutes.find((item) => item.path === route.path) + if (target) { + if (route.fullPath !== target.fullPath) Object.assign(target, route) + return + } + state.visitedRoutes.push(Object.assign({}, route)) + }, + delVisitedRoute(state, route) { + state.visitedRoutes.forEach((item, index) => { + if (item.path === route.path) state.visitedRoutes.splice(index, 1) + }) + }, + delOthersVisitedRoute(state, route) { + state.visitedRoutes = state.visitedRoutes.filter( + (item) => item.meta.affix || item.path === route.path + ) + }, + delLeftVisitedRoute(state, route) { + let index = state.visitedRoutes.length + state.visitedRoutes = state.visitedRoutes.filter((item) => { + if (item.name === route.name) index = state.visitedRoutes.indexOf(item) + return item.meta.affix || index <= state.visitedRoutes.indexOf(item) + }) + }, + delRightVisitedRoute(state, route) { + let index = state.visitedRoutes.length + state.visitedRoutes = state.visitedRoutes.filter((item) => { + if (item.name === route.name) index = state.visitedRoutes.indexOf(item) + return item.meta.affix || index >= state.visitedRoutes.indexOf(item) + }) + }, + delAllVisitedRoutes(state) { + state.visitedRoutes = state.visitedRoutes.filter((item) => item.meta.affix) + }, + updateVisitedRoute(state, route) { + state.visitedRoutes.forEach((item) => { + if (item.path === route.path) item = Object.assign(item, route) + }) + }, +} +const actions = { + addVisitedRoute({ commit }, route) { + commit('addVisitedRoute', route) + }, + async delRoute({ dispatch, state }, route) { + await dispatch('delVisitedRoute', route) + return { + visitedRoutes: [...state.visitedRoutes], + } + }, + delVisitedRoute({ commit, state }, route) { + commit('delVisitedRoute', route) + return [...state.visitedRoutes] + }, + async delOthersRoutes({ dispatch, state }, route) { + await dispatch('delOthersVisitedRoute', route) + return { + visitedRoutes: [...state.visitedRoutes], + } + }, + async delLeftRoutes({ dispatch, state }, route) { + await dispatch('delLeftVisitedRoute', route) + return { + visitedRoutes: [...state.visitedRoutes], + } + }, + async delRightRoutes({ dispatch, state }, route) { + await dispatch('delRightVisitedRoute', route) + return { + visitedRoutes: [...state.visitedRoutes], + } + }, + delOthersVisitedRoute({ commit, state }, route) { + commit('delOthersVisitedRoute', route) + return [...state.visitedRoutes] + }, + delLeftVisitedRoute({ commit, state }, route) { + commit('delLeftVisitedRoute', route) + return [...state.visitedRoutes] + }, + delRightVisitedRoute({ commit, state }, route) { + commit('delRightVisitedRoute', route) + return [...state.visitedRoutes] + }, + async delAllRoutes({ dispatch, state }, route) { + await dispatch('delAllVisitedRoutes', route) + return { + visitedRoutes: [...state.visitedRoutes], + } + }, + delAllVisitedRoutes({ commit, state }) { + commit('delAllVisitedRoutes') + return [...state.visitedRoutes] + }, + updateVisitedRoute({ commit }, route) { + commit('updateVisitedRoute', route) + }, +} +export default { state, getters, mutations, actions } diff --git a/src/store/modules/user.js b/src/store/modules/user.js index f41858783..a53d33f18 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -1,12 +1,108 @@ /** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 登录、获取用户信息、退出登录、清除accessToken逻辑,不建议修改 */ import Vue from 'vue' +import { getUserInfo, login, logout } from '@/api/user' +import { + getAccessToken, + removeAccessToken, + setAccessToken, +} from '@/utils/accessToken' +import { resetRouter } from '@/router' +import { title, tokenName, routes } from '@/config' -const state = () => ({}) -const getters = {} -const mutations = {} -const actions = {} - +const state = () => ({ + accessToken: getAccessToken(), + username: '', + avatar: '', + permissions: [], +}) +const getters = { + accessToken: (state) => state.accessToken, + username: (state) => state.username, + avatar: (state) => state.avatar, + permissions: (state) => state.permissions, +} +const mutations = { + setAccessToken(state, accessToken) { + state.accessToken = accessToken + setAccessToken(accessToken) + }, + setUsername(state, username) { + state.username = username + }, + setAvatar(state, avatar) { + state.avatar = avatar + }, + setPermissions(state, permissions) { + state.permissions = permissions + }, +} +const actions = { + setPermissions({ commit }, permissions) { + commit('setPermissions', permissions) + }, + async login({ commit }, userInfo) { + // const { data } = await login(userInfo) + // const accessToken = data[tokenName] + const accessToken = 'test_20210420' + if (accessToken) { + commit('setAccessToken', accessToken) + const hour = new Date().getHours() + const thisTime = + hour < 8 + ? '早上好' + : hour <= 11 + ? '上午好' + : hour <= 13 + ? '中午好' + : hour < 18 + ? '下午好' + : '晚上好' + Vue.prototype.$baseNotify(`欢迎登录${title}`, `${thisTime}!`) + } else { + Vue.prototype.$baseMessage( + `登录接口异常,未正确返回${tokenName}...`, + 'error' + ) + } + }, + async getUserInfo({ commit, state }) { + // const { data } = await getUserInfo(state.accessToken) + const data = { + permissions: ['admin'], + username: 'admin', + 'avatar|1': [ + 'https://i.gtimg.cn/club/item/face/img/2/15922_100.gif', + 'https://i.gtimg.cn/club/item/face/img/8/15918_100.gif', + ], + } + if (!data) { + Vue.prototype.$baseMessage('验证失败,请重新登录...', 'error') + return false + } + let { permissions, username, avatar } = data + if (permissions && username && Array.isArray(permissions)) { + commit('setPermissions', permissions) + commit('setUsername', username) + commit('setAvatar', avatar) + return permissions + } else { + Vue.prototype.$baseMessage('用户信息接口异常', 'error') + return false + } + }, + async logout({ dispatch }) { + await logout(state.accessToken) + await dispatch('resetAccessToken') + await resetRouter() + }, + resetAccessToken({ commit }) { + commit('setPermissions', []) + commit('setAccessToken', '') + removeAccessToken() + }, +} export default { state, getters, mutations, actions } diff --git a/src/assets/styles/element-variables.scss b/src/styles/element-variables.scss similarity index 100% rename from src/assets/styles/element-variables.scss rename to src/styles/element-variables.scss diff --git a/src/assets/styles/loading.scss b/src/styles/loading.scss similarity index 100% rename from src/assets/styles/loading.scss rename to src/styles/loading.scss diff --git a/src/assets/styles/normalize.scss b/src/styles/normalize.scss similarity index 100% rename from src/assets/styles/normalize.scss rename to src/styles/normalize.scss diff --git a/src/assets/styles/spinner/dots.css b/src/styles/spinner/dots.css similarity index 100% rename from src/assets/styles/spinner/dots.css rename to src/styles/spinner/dots.css diff --git a/src/assets/styles/spinner/gauge.css b/src/styles/spinner/gauge.css similarity index 100% rename from src/assets/styles/spinner/gauge.css rename to src/styles/spinner/gauge.css diff --git a/src/assets/styles/spinner/inner-circles.css b/src/styles/spinner/inner-circles.css similarity index 100% rename from src/assets/styles/spinner/inner-circles.css rename to src/styles/spinner/inner-circles.css diff --git a/src/assets/styles/spinner/plus.css b/src/styles/spinner/plus.css similarity index 100% rename from src/assets/styles/spinner/plus.css rename to src/styles/spinner/plus.css diff --git a/src/assets/styles/themes/default.scss b/src/styles/themes/default.scss similarity index 100% rename from src/assets/styles/themes/default.scss rename to src/styles/themes/default.scss diff --git a/src/assets/styles/transition.scss b/src/styles/transition.scss similarity index 76% rename from src/assets/styles/transition.scss rename to src/styles/transition.scss index 91409e569..718685523 100644 --- a/src/assets/styles/transition.scss +++ b/src/styles/transition.scss @@ -1,5 +1,4 @@ /** - * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description vue过渡动画 */ diff --git a/src/assets/styles/vab.scss b/src/styles/vab.scss similarity index 100% rename from src/assets/styles/vab.scss rename to src/styles/vab.scss diff --git a/src/assets/styles/variables.scss b/src/styles/variables.scss similarity index 100% rename from src/assets/styles/variables.scss rename to src/styles/variables.scss diff --git a/src/utils/accessToken.js b/src/utils/accessToken.js new file mode 100644 index 000000000..1c79c78fd --- /dev/null +++ b/src/utils/accessToken.js @@ -0,0 +1,59 @@ +import { storage, tokenTableName } from '@/config' + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 获取accessToken + * @returns {string|ActiveX.IXMLDOMNode|Promise|any|IDBRequest|MediaKeyStatus|FormDataEntryValue|Function|Promise} + */ +export function getAccessToken() { + if (storage) { + if ('localStorage' === storage) { + return localStorage.getItem(tokenTableName) + } else if ('sessionStorage' === storage) { + return sessionStorage.getItem(tokenTableName) + } else { + return localStorage.getItem(tokenTableName) + } + } else { + return localStorage.getItem(tokenTableName) + } +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 存储accessToken + * @param accessToken + * @returns {void|*} + */ +export function setAccessToken(accessToken) { + if (storage) { + if ('localStorage' === storage) { + return localStorage.setItem(tokenTableName, accessToken) + } else if ('sessionStorage' === storage) { + return sessionStorage.setItem(tokenTableName, accessToken) + } else { + return localStorage.setItem(tokenTableName, accessToken) + } + } else { + return localStorage.setItem(tokenTableName, accessToken) + } +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 移除accessToken + * @returns {void|Promise} + */ +export function removeAccessToken() { + if (storage) { + if ('localStorage' === storage) { + return localStorage.removeItem(tokenTableName) + } else if ('sessionStorage' === storage) { + return sessionStorage.clear() + } else { + return localStorage.removeItem(tokenTableName) + } + } else { + return localStorage.removeItem(tokenTableName) + } +} diff --git a/src/utils/clipboard.js b/src/utils/clipboard.js new file mode 100644 index 000000000..20a19b7d5 --- /dev/null +++ b/src/utils/clipboard.js @@ -0,0 +1,31 @@ +import Vue from 'vue' +import Clipboard from 'clipboard' + +function clipboardSuccess() { + Vue.prototype.$baseMessage('复制成功', 'success') +} + +function clipboardError() { + Vue.prototype.$baseMessage('复制失败', 'error') +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 复制数据 + * @param text + * @param event + */ +export default function handleClipboard(text, event) { + const clipboard = new Clipboard(event.target, { + text: () => text, + }) + clipboard.on('success', () => { + clipboardSuccess() + clipboard.destroy() + }) + clipboard.on('error', () => { + clipboardError() + clipboard.destroy() + }) + clipboard.onClick(event) +} diff --git a/src/utils/encrypt.js b/src/utils/encrypt.js new file mode 100644 index 000000000..9856991b0 --- /dev/null +++ b/src/utils/encrypt.js @@ -0,0 +1,46 @@ +import JSEncrypt from 'jsencrypt' +import { getPublicKey } from '@/api/publicKey' + +const privateKey = + 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMFPa+v52FkSUXvcUnrGI/XzW3EpZRI0s9BCWJ3oNQmEYA5luWW5p8h0uadTIoTyYweFPdH4hveyxlwmS7oefvbIdiP+o+QIYW/R4Wjsb4Yl8MhR4PJqUE3RCy6IT9fM8ckG4kN9ECs6Ja8fQFc6/mSl5dJczzJO3k1rWMBhKJD/AgMBAAECgYEAucMakH9dWeryhrYoRHcXo4giPVJsH9ypVt4KzmOQY/7jV7KFQK3x//27UoHfUCak51sxFw9ek7UmTPM4HjikA9LkYeE7S381b4QRvFuf3L6IbMP3ywJnJ8pPr2l5SqQ00W+oKv+w/VmEsyUHr+k4Z+4ik+FheTkVWp566WbqFsECQQDjYaMcaKw3j2Zecl8T6eUe7fdaRMIzp/gcpPMfT/9rDzIQk+7ORvm1NI9AUmFv/FAlfpuAMrdL2n7p9uznWb7RAkEA2aP934kbXg5bdV0R313MrL+7WTK/qdcYxATUbMsMuWWQBoS5irrt80WCZbG48hpocJavLNjbtrjmUX3CuJBmzwJAOJg8uP10n/+ZQzjEYXh+BszEHDuw+pp8LuT/fnOy5zrJA0dO0RjpXijO3vuiNPVgHXT9z1LQPJkNrb5ACPVVgQJBALPeb4uV0bNrJDUb5RB4ghZnIxv18CcaqNIft7vuGCcFBAIPIRTBprR+RuVq+xHDt3sNXdsvom4h49+Hky1b0ksCQBBwUtVaqH6ztCtwUF1j2c/Zcrt5P/uN7IHAd44K0gIJc1+Csr3qPG+G2yoqRM8KVqLI8Z2ZYn9c+AvEE+L9OQY=' + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description RSA加密 + * @param data + * @returns {Promise<{param: PromiseLike}|*>} + */ +export async function encryptedData(data) { + let publicKey = '' + const res = await getPublicKey() + publicKey = res.data.publicKey + if (res.data.mockServer) { + publicKey = '' + } + if (publicKey == '') { + return data + } + const encrypt = new JSEncrypt() + encrypt.setPublicKey( + `-----BEGIN PUBLIC KEY-----${publicKey}-----END PUBLIC KEY-----` + ) + data = encrypt.encrypt(JSON.stringify(data)) + return { + param: data, + } +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description RSA解密 + * @param data + * @returns {PromiseLike} + */ +export function decryptedData(data) { + const decrypt = new JSEncrypt() + decrypt.setPrivateKey( + `-----BEGIN RSA PRIVATE KEY-----${privateKey}-----END RSA PRIVATE KEY-----` + ) + data = decrypt.decrypt(JSON.stringify(data)) + return data +} diff --git a/src/utils/errorLog.js b/src/utils/errorLog.js new file mode 100644 index 000000000..07a836c31 --- /dev/null +++ b/src/utils/errorLog.js @@ -0,0 +1,25 @@ +import Vue from 'vue' +import store from '@/store' +import { isArray, isString } from '@/utils/validate' +import { errorLog } from '@/config' + +const needErrorLog = errorLog +const checkNeed = () => { + const env = process.env.NODE_ENV + if (isString(needErrorLog)) { + return env === needErrorLog + } + if (isArray(needErrorLog)) { + return needErrorLog.includes(env) + } + return false +} +if (checkNeed()) { + Vue.config.errorHandler = (err, vm, info) => { + console.error('vue-admin-beautiful错误拦截:', err, vm, info) + const url = window.location.href + Vue.nextTick(() => { + store.dispatch('errorLog/addErrorLog', { err, vm, info, url }) + }) + } +} diff --git a/src/utils/handleRoutes.js b/src/utils/handleRoutes.js new file mode 100644 index 000000000..c95ae9229 --- /dev/null +++ b/src/utils/handleRoutes.js @@ -0,0 +1,63 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description all模式渲染后端返回路由 + * @param constantRoutes + * @returns {*} + */ +export function convertRouter(asyncRoutes) { + return asyncRoutes.map((route) => { + if (route.component) { + if (route.component === 'Layout') { + route.component = (resolve) => require(['@/layouts'], resolve) + } else if (route.component === 'EmptyLayout') { + route.component = (resolve) => + require(['@/layouts/EmptyLayout'], resolve) + } else { + const index = route.component.indexOf('views') + const path = + index > 0 ? route.component.slice(index) : `views/${route.component}` + route.component = (resolve) => require([`@/${path}`], resolve) + } + } + if (route.children && route.children.length) + route.children = convertRouter(route.children) + if (route.children && route.children.length === 0) delete route.children + return route + }) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断当前路由是否包含权限 + * @param permissions + * @param route + * @returns {boolean|*} + */ +function hasPermission(permissions, route) { + if (route.meta && route.meta.permissions) { + return permissions.some((role) => route.meta.permissions.includes(role)) + } else { + return true + } +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description intelligence模式根据permissions数组拦截路由 + * @param routes + * @param permissions + * @returns {[]} + */ +export function filterAsyncRoutes(routes, permissions) { + const finallyRoutes = [] + routes.forEach((route) => { + const item = { ...route } + if (hasPermission(permissions, item)) { + if (item.children) { + item.children = filterAsyncRoutes(item.children, permissions) + } + finallyRoutes.push(item) + } + }) + return finallyRoutes +} diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 000000000..80e89c1ad --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1,266 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 格式化时间 + * @param time + * @param cFormat + * @returns {string|null} + */ +export function parseTime(time, cFormat) { + if (arguments.length === 0) { + return null + } + const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time + } else { + if (typeof time === 'string' && /^[0-9]+$/.test(time)) { + time = parseInt(time) + } + if (typeof time === 'number' && time.toString().length === 10) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay(), + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key] + if (key === 'a') { + return ['日', '一', '二', '三', '四', '五', '六'][value] + } + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 格式化时间 + * @param time + * @param option + * @returns {string} + */ +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return ( + d.getMonth() + + 1 + + '月' + + d.getDate() + + '日' + + d.getHours() + + '时' + + d.getMinutes() + + '分' + ) + } +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 将url请求参数转为json格式 + * @param url + * @returns {{}|any} + */ +export function paramObj(url) { + const search = url.split('?')[1] + if (!search) { + return {} + } + return JSON.parse( + '{"' + + decodeURIComponent(search) + .replace(/"/g, '\\"') + .replace(/&/g, '","') + .replace(/=/g, '":"') + .replace(/\+/g, ' ') + + '"}' + ) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 父子关系的数组转换成树形结构数据 + * @param data + * @returns {*} + */ +export function translateDataToTree(data) { + const parent = data.filter( + (value) => value.parentId === 'undefined' || value.parentId == null + ) + const children = data.filter( + (value) => value.parentId !== 'undefined' && value.parentId != null + ) + const translator = (parent, children) => { + parent.forEach((parent) => { + children.forEach((current, index) => { + if (current.parentId === parent.id) { + const temp = JSON.parse(JSON.stringify(children)) + temp.splice(index, 1) + translator([current], temp) + typeof parent.children !== 'undefined' + ? parent.children.push(current) + : (parent.children = [current]) + } + }) + }) + } + translator(parent, children) + return parent +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 树形结构数据转换成父子关系的数组 + * @param data + * @returns {[]} + */ +export function translateTreeToData(data) { + const result = [] + data.forEach((item) => { + const loop = (data) => { + result.push({ + id: data.id, + name: data.name, + parentId: data.parentId, + }) + const child = data.children + if (child) { + for (let i = 0; i < child.length; i++) { + loop(child[i]) + } + } + } + loop(item) + }) + return result +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 10位时间戳转换 + * @param time + * @returns {string} + */ +export function tenBitTimestamp(time) { + const date = new Date(time * 1000) + const y = date.getFullYear() + let m = date.getMonth() + 1 + m = m < 10 ? '' + m : m + let d = date.getDate() + d = d < 10 ? '' + d : d + let h = date.getHours() + h = h < 10 ? '0' + h : h + let minute = date.getMinutes() + let second = date.getSeconds() + minute = minute < 10 ? '0' + minute : minute + second = second < 10 ? '0' + second : second + return y + '年' + m + '月' + d + '日 ' + h + ':' + minute + ':' + second //组合 +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 13位时间戳转换 + * @param time + * @returns {string} + */ +export function thirteenBitTimestamp(time) { + const date = new Date(time / 1) + const y = date.getFullYear() + let m = date.getMonth() + 1 + m = m < 10 ? '' + m : m + let d = date.getDate() + d = d < 10 ? '' + d : d + let h = date.getHours() + h = h < 10 ? '0' + h : h + let minute = date.getMinutes() + let second = date.getSeconds() + minute = minute < 10 ? '0' + minute : minute + second = second < 10 ? '0' + second : second + return y + '年' + m + '月' + d + '日 ' + h + ':' + minute + ':' + second //组合 +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 获取随机id + * @param length + * @returns {string} + */ +export function uuid(length = 32) { + const num = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' + let str = '' + for (let i = 0; i < length; i++) { + str += num.charAt(Math.floor(Math.random() * num.length)) + } + return str +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description m到n的随机数 + * @param m + * @param n + * @returns {number} + */ +export function random(m, n) { + return Math.floor(Math.random() * (m - n) + n) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description addEventListener + * @type {function(...[*]=)} + */ +export const on = (function () { + return function (element, event, handler, useCapture = false) { + if (element && event && handler) { + element.addEventListener(event, handler, useCapture) + } + } +})() + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description removeEventListener + * @type {function(...[*]=)} + */ +export const off = (function () { + return function (element, event, handler, useCapture = false) { + if (element && event) { + element.removeEventListener(event, handler, useCapture) + } + } +})() diff --git a/src/utils/pageTitle.js b/src/utils/pageTitle.js new file mode 100644 index 000000000..38334c3ca --- /dev/null +++ b/src/utils/pageTitle.js @@ -0,0 +1,14 @@ +import { title } from '@/config' + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 设置标题 + * @param pageTitle + * @returns {string} + */ +export default function getPageTitle(pageTitle) { + if (pageTitle) { + return `${pageTitle}-${title}` + } + return `${title}` +} diff --git a/src/utils/permission.js b/src/utils/permission.js new file mode 100644 index 000000000..88b3506c6 --- /dev/null +++ b/src/utils/permission.js @@ -0,0 +1,20 @@ +import store from '@/store' + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 检查权限 + * @param value + * @returns {boolean} + */ +export default function checkPermission(value) { + if (value && value instanceof Array && value.length > 0) { + const permissions = store.getters['user/permissions'] + const permissionPermissions = value + + return permissions.some((role) => { + return permissionPermissions.includes(role) + }) + } else { + return false + } +} diff --git a/src/utils/request.js b/src/utils/request.js new file mode 100644 index 000000000..272a731e4 --- /dev/null +++ b/src/utils/request.js @@ -0,0 +1,125 @@ +import Vue from 'vue' +import axios from 'axios' +import { + baseURL, + contentType, + debounce, + invalidCode, + noPermissionCode, + requestTimeout, + successCode, + tokenName, + loginInterception, +} from '@/config' +import store from '@/store' +import qs from 'qs' +import router from '@/router' +import { isArray } from '@/utils/validate' + +let loadingInstance + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 处理code异常 + * @param {*} code + * @param {*} msg + */ +const handleCode = (code, msg) => { + switch (code) { + case invalidCode: + Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, 'error') + store.dispatch('user/resetAccessToken').catch(() => {}) + if (loginInterception) { + location.reload() + } + break + case noPermissionCode: + router.push({ path: '/401' }).catch(() => {}) + break + default: + Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, 'error') + break + } +} + +const instance = axios.create({ + baseURL, + timeout: requestTimeout, + headers: { + 'Content-Type': contentType, + }, +}) + +instance.interceptors.request.use( + (config) => { + if (store.getters['user/accessToken']) { + config.headers[tokenName] = store.getters['user/accessToken'] + } + //这里会过滤所有为空、0、false的key,如果不需要请自行注释 + if (config.data) + config.data = Vue.prototype.$baseLodash.pickBy( + config.data, + Vue.prototype.$baseLodash.identity + ) + if ( + config.data && + config.headers['Content-Type'] === + 'application/x-www-form-urlencoded;charset=UTF-8' + ) + config.data = qs.stringify(config.data) + if (debounce.some((item) => config.url.includes(item))) + loadingInstance = Vue.prototype.$baseLoading() + return config + }, + (error) => { + return Promise.reject(error) + } +) + +instance.interceptors.response.use( + (response) => { + if (loadingInstance) loadingInstance.close() + + const { data, config } = response + const { code, msg } = data + // 操作正常Code数组 + const codeVerificationArray = isArray(successCode) + ? [...successCode] + : [...[successCode]] + // 是否操作正常 + if (codeVerificationArray.includes(code)) { + return data + } else { + handleCode(code, msg) + return Promise.reject( + 'vue-admin-beautiful请求异常拦截:' + + JSON.stringify({ url: config.url, code, msg }) || 'Error' + ) + } + }, + (error) => { + if (loadingInstance) loadingInstance.close() + const { response, message } = error + if (error.response && error.response.data) { + const { status, data } = response + handleCode(status, data.msg || message) + return Promise.reject(error) + } else { + let { message } = error + if (message === 'Network Error') { + message = '后端接口连接异常' + } + if (message.includes('timeout')) { + message = '后端接口请求超时' + } + if (message.includes('Request failed with status code')) { + const code = message.substr(message.length - 3) + message = '后端接口' + code + '异常' + } + Vue.prototype.$baseMessage(message || `后端接口未知异常`, 'error') + return Promise.reject(error) + } + } +) + +export default instance diff --git a/src/utils/static.js b/src/utils/static.js new file mode 100644 index 000000000..bd0abd975 --- /dev/null +++ b/src/utils/static.js @@ -0,0 +1,52 @@ +/** + * @author chuzhixin 1204505056@qq.com + * @description 导入所有 controller 模块,浏览器环境中自动输出controller文件夹下Mock接口,请勿修改。 + */ +import Mock from 'mockjs' +import { paramObj } from '@/utils' + +const mocks = [] +const files = require.context('../../mock/controller', false, /\.js$/) + +files.keys().forEach((key) => { + mocks.push(...files(key)) +}) + +export function mockXHR() { + Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send + Mock.XHR.prototype.send = function () { + if (this.custom.xhr) { + this.custom.xhr.withCredentials = this.withCredentials || false + + if (this.responseType) { + this.custom.xhr.responseType = this.responseType + } + } + this.proxy_send(...arguments) + } + + function XHRHttpRequst(respond) { + return function (options) { + let result + if (respond instanceof Function) { + const { body, type, url } = options + result = respond({ + method: type, + body: JSON.parse(body), + query: paramObj(url), + }) + } else { + result = respond + } + return Mock.mock(result) + } + } + + mocks.forEach((item) => { + Mock.mock( + new RegExp(item.url), + item.type || 'get', + XHRHttpRequst(item.response) + ) + }) +} diff --git a/src/utils/vab.js b/src/utils/vab.js new file mode 100644 index 000000000..e594e9ade --- /dev/null +++ b/src/utils/vab.js @@ -0,0 +1,180 @@ +import { loadingText, messageDuration, title } from '@/config' +import * as lodash from 'lodash' +import { Loading, Message, MessageBox, Notification } from 'element-ui' +import store from '@/store' +import { getAccessToken } from '@/utils/accessToken' + +const accessToken = store.getters['user/accessToken'] +const layout = store.getters['settings/layout'] + +const install = (Vue, opts = {}) => { + /* 全局accessToken */ + Vue.prototype.$baseAccessToken = () => { + return accessToken || getAccessToken() + } + /* 全局标题 */ + Vue.prototype.$baseTitle = (() => { + return title + })() + /* 全局加载层 */ + Vue.prototype.$baseLoading = (index, text) => { + let loading + if (!index) { + loading = Loading.service({ + lock: true, + text: text || loadingText, + background: 'hsla(0,0%,100%,.8)', + }) + } else { + loading = Loading.service({ + lock: true, + text: text || loadingText, + spinner: 'vab-loading-type' + index, + background: 'hsla(0,0%,100%,.8)', + }) + } + return loading + } + /* 全局多彩加载层 */ + Vue.prototype.$baseColorfullLoading = (index, text) => { + let loading + if (!index) { + loading = Loading.service({ + lock: true, + text: text || loadingText, + spinner: 'dots-loader', + background: 'hsla(0,0%,100%,.8)', + }) + } else { + switch (index) { + case 1: + index = 'dots' + break + case 2: + index = 'gauge' + break + case 3: + index = 'inner-circles' + break + case 4: + index = 'plus' + break + } + loading = Loading.service({ + lock: true, + text: text || loadingText, + spinner: index + '-loader', + background: 'hsla(0,0%,100%,.8)', + }) + } + return loading + } + /* 全局Message */ + Vue.prototype.$baseMessage = (message, type) => { + Message({ + offset: 60, + showClose: true, + message: message, + type: type, + dangerouslyUseHTMLString: true, + duration: messageDuration, + }) + } + + /* 全局Alert */ + Vue.prototype.$baseAlert = (content, title, callback) => { + MessageBox.alert(content, title || '温馨提示', { + confirmButtonText: '确定', + dangerouslyUseHTMLString: true, + callback: (action) => { + if (callback) { + callback() + } + }, + }) + } + + /* 全局Confirm */ + Vue.prototype.$baseConfirm = (content, title, callback1, callback2) => { + MessageBox.confirm(content, title || '温馨提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + closeOnClickModal: false, + type: 'warning', + }) + .then(() => { + if (callback1) { + callback1() + } + }) + .catch(() => { + if (callback2) { + callback2() + } + }) + } + + /* 全局Notification */ + Vue.prototype.$baseNotify = (message, title, type, position) => { + Notification({ + title: title, + message: message, + position: position || 'top-right', + type: type || 'success', + duration: messageDuration, + }) + } + + /* 全局TableHeight */ + Vue.prototype.$baseTableHeight = (formType) => { + let height = window.innerHeight + let paddingHeight = 400 + const formHeight = 50 + + if (layout === 'vertical') { + paddingHeight = 365 + } + + if ('number' == typeof formType) { + height = height - paddingHeight - formHeight * formType + } else { + height = height - paddingHeight + } + return height + } + + /* 全局map图层 */ + Vue.prototype.$baseMap = () => { + return new maptalks.Map('map', { + center: [116.41348403785, 39.910843952376], + zoom: 12, + minZoom: 1, + maxZoom: 19, + spatialReference: { + projection: 'baidu', + }, + attribution: { + content: '© vue-admin-beautiful', + }, + baseLayer: new maptalks.TileLayer('base', { + cssFilter: 'sepia(100%) invert(90%)', + urlTemplate: + 'http://online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl&scaler=1&p=1', + subdomains: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + attribution: + '© Baidu', + }), + }) + } + + /* 全局lodash */ + Vue.prototype.$baseLodash = lodash + /* 全局事件总线 */ + Vue.prototype.$baseEventBus = new Vue() +} + +if (typeof window !== 'undefined' && window.Vue) { + install(window.Vue) +} + +export default install diff --git a/src/utils/validate.js b/src/utils/validate.js new file mode 100644 index 000000000..46a40f89f --- /dev/null +++ b/src/utils/validate.js @@ -0,0 +1,212 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判读是否为外链 + * @param path + * @returns {boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 校验密码是否小于6位 + * @param str + * @returns {boolean} + */ +export function isPassword(str) { + return str.length >= 6 +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否为数字 + * @param value + * @returns {boolean} + */ +export function isNumber(value) { + const reg = /^[0-9]*$/ + return reg.test(value) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是名称 + * @param value + * @returns {boolean} + */ +export function isName(value) { + const reg = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/ + return reg.test(value) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否为IP + * @param ip + * @returns {boolean} + */ +export function isIP(ip) { + const reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/ + return reg.test(ip) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是传统网站 + * @param url + * @returns {boolean} + */ +export function isUrl(url) { + const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return reg.test(url) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是小写字母 + * @param str + * @returns {boolean} + */ +export function isLowerCase(str) { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是大写字母 + * @param str + * @returns {boolean} + */ +export function isUpperCase(str) { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是大写字母开头 + * @param str + * @returns {boolean} + */ +export function isAlphabets(str) { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是字符串 + * @param str + * @returns {boolean} + */ +export function isString(str) { + return typeof str === 'string' || str instanceof String +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是数组 + * @param arg + * @returns {arg is any[]|boolean} + */ +export function isArray(arg) { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]' + } + return Array.isArray(arg) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是端口号 + * @param str + * @returns {boolean} + */ +export function isPort(str) { + const reg = /^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/ + return reg.test(str) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是手机号 + * @param str + * @returns {boolean} + */ +export function isPhone(str) { + const reg = /^1\d{10}$/ + return reg.test(str) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是身份证号(第二代) + * @param str + * @returns {boolean} + */ +export function isIdCard(str) { + const reg = /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/ + return reg.test(str) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否是邮箱 + * @param str + * @returns {boolean} + */ +export function isEmail(str) { + const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/ + return reg.test(str) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否中文 + * @param str + * @returns {boolean} + */ +export function isChina(str) { + const reg = /^[\u4E00-\u9FA5]{2,4}$/ + return reg.test(str) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否为空 + * @param str + * @returns {boolean} + */ +export function isBlank(str) { + return ( + str == null || + false || + str === '' || + str.trim() === '' || + str.toLocaleLowerCase().trim() === 'null' + ) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否为固话 + * @param str + * @returns {boolean} + */ +export function isTel(str) { + const reg = /^(400|800)([0-9\\-]{7,10})|(([0-9]{4}|[0-9]{3})(-| )?)?([0-9]{7,8})((-| |转)*([0-9]{1,4}))?$/ + return reg.test(str) +} + +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description 判断是否为数字且最多两位小数 + * @param str + * @returns {boolean} + */ +export function isNum(str) { + const reg = /^\d+(\.\d{1,2})?$/ + return reg.test(str) +} diff --git a/src/views/401.vue b/src/views/401.vue new file mode 100644 index 000000000..b09711a2e --- /dev/null +++ b/src/views/401.vue @@ -0,0 +1,296 @@ + + + + + diff --git a/src/views/404.vue b/src/views/404.vue new file mode 100644 index 000000000..9c261b691 --- /dev/null +++ b/src/views/404.vue @@ -0,0 +1,296 @@ + + + + + diff --git a/src/views/index/components/Plan.vue b/src/views/index/components/Plan.vue new file mode 100644 index 000000000..02ef9ae04 --- /dev/null +++ b/src/views/index/components/Plan.vue @@ -0,0 +1,87 @@ + + diff --git a/src/views/index/components/VersionInformation.vue b/src/views/index/components/VersionInformation.vue new file mode 100644 index 000000000..1df8fcf53 --- /dev/null +++ b/src/views/index/components/VersionInformation.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/src/views/index/index.vue b/src/views/index/index.vue new file mode 100644 index 000000000..39f82f4e6 --- /dev/null +++ b/src/views/index/index.vue @@ -0,0 +1,958 @@ + + + + + diff --git a/src/views/login/index.vue b/src/views/login/index.vue new file mode 100644 index 000000000..dd9976441 --- /dev/null +++ b/src/views/login/index.vue @@ -0,0 +1,327 @@ + + + + + diff --git a/src/views/mall/goodsList/index.vue b/src/views/mall/goodsList/index.vue new file mode 100644 index 000000000..824c62ba7 --- /dev/null +++ b/src/views/mall/goodsList/index.vue @@ -0,0 +1,163 @@ + + + + diff --git a/src/views/mall/pay/components/Step1.vue b/src/views/mall/pay/components/Step1.vue new file mode 100644 index 000000000..202e906f9 --- /dev/null +++ b/src/views/mall/pay/components/Step1.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/views/mall/pay/components/Step2.vue b/src/views/mall/pay/components/Step2.vue new file mode 100644 index 000000000..c64e5ddee --- /dev/null +++ b/src/views/mall/pay/components/Step2.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/views/mall/pay/components/Step3.vue b/src/views/mall/pay/components/Step3.vue new file mode 100644 index 000000000..a504a1944 --- /dev/null +++ b/src/views/mall/pay/components/Step3.vue @@ -0,0 +1,101 @@ + + + diff --git a/src/views/mall/pay/index.vue b/src/views/mall/pay/index.vue new file mode 100644 index 000000000..e0543288c --- /dev/null +++ b/src/views/mall/pay/index.vue @@ -0,0 +1,60 @@ + + + + diff --git a/src/views/personalCenter/index.vue b/src/views/personalCenter/index.vue new file mode 100644 index 000000000..42b0fa291 --- /dev/null +++ b/src/views/personalCenter/index.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/views/personnelManagement/menuManagement/components/MenuManagementEdit.vue b/src/views/personnelManagement/menuManagement/components/MenuManagementEdit.vue new file mode 100644 index 000000000..df23fdff5 --- /dev/null +++ b/src/views/personnelManagement/menuManagement/components/MenuManagementEdit.vue @@ -0,0 +1,71 @@ + + + diff --git a/src/views/personnelManagement/menuManagement/index.vue b/src/views/personnelManagement/menuManagement/index.vue new file mode 100644 index 000000000..260ebc7a8 --- /dev/null +++ b/src/views/personnelManagement/menuManagement/index.vue @@ -0,0 +1,173 @@ + + + diff --git a/src/views/personnelManagement/roleManagement/components/RoleManagementEdit.vue b/src/views/personnelManagement/roleManagement/components/RoleManagementEdit.vue new file mode 100644 index 000000000..dc3a9fdab --- /dev/null +++ b/src/views/personnelManagement/roleManagement/components/RoleManagementEdit.vue @@ -0,0 +1,69 @@ + + + diff --git a/src/views/personnelManagement/roleManagement/index.vue b/src/views/personnelManagement/roleManagement/index.vue new file mode 100644 index 000000000..5fcf65896 --- /dev/null +++ b/src/views/personnelManagement/roleManagement/index.vue @@ -0,0 +1,150 @@ + + + diff --git a/src/views/personnelManagement/userManagement/components/UserManagementEdit.vue b/src/views/personnelManagement/userManagement/components/UserManagementEdit.vue new file mode 100644 index 000000000..fe4487260 --- /dev/null +++ b/src/views/personnelManagement/userManagement/components/UserManagementEdit.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/views/personnelManagement/userManagement/index.vue b/src/views/personnelManagement/userManagement/index.vue new file mode 100644 index 000000000..e44e687f4 --- /dev/null +++ b/src/views/personnelManagement/userManagement/index.vue @@ -0,0 +1,166 @@ + + + diff --git a/src/views/register/index.vue b/src/views/register/index.vue new file mode 100644 index 000000000..7cb514fe4 --- /dev/null +++ b/src/views/register/index.vue @@ -0,0 +1,377 @@ + + + diff --git a/src/views/test/index.vue b/src/views/test/index.vue new file mode 100644 index 000000000..cd3fc662d --- /dev/null +++ b/src/views/test/index.vue @@ -0,0 +1,16 @@ + + diff --git a/src/views/vab/backToTop/index.vue b/src/views/vab/backToTop/index.vue new file mode 100644 index 000000000..49c101ac7 --- /dev/null +++ b/src/views/vab/backToTop/index.vue @@ -0,0 +1,22 @@ + + + + diff --git a/src/views/vab/editor/index.vue b/src/views/vab/editor/index.vue new file mode 100644 index 000000000..7e931a057 --- /dev/null +++ b/src/views/vab/editor/index.vue @@ -0,0 +1,156 @@ + + + + diff --git a/src/views/vab/element/index.vue b/src/views/vab/element/index.vue new file mode 100644 index 000000000..5d44c4596 --- /dev/null +++ b/src/views/vab/element/index.vue @@ -0,0 +1,254 @@ + + + + + diff --git a/src/views/vab/errorLog/components/ErrorTest.vue b/src/views/vab/errorLog/components/ErrorTest.vue new file mode 100644 index 000000000..a64210faa --- /dev/null +++ b/src/views/vab/errorLog/components/ErrorTest.vue @@ -0,0 +1,13 @@ + + + diff --git a/src/views/vab/errorLog/index.vue b/src/views/vab/errorLog/index.vue new file mode 100644 index 000000000..09593e740 --- /dev/null +++ b/src/views/vab/errorLog/index.vue @@ -0,0 +1,28 @@ + + + diff --git a/src/views/vab/form/index.vue b/src/views/vab/form/index.vue new file mode 100644 index 000000000..64d96d3cf --- /dev/null +++ b/src/views/vab/form/index.vue @@ -0,0 +1,111 @@ + + + diff --git a/src/views/vab/icon/colorfulIcon.vue b/src/views/vab/icon/colorfulIcon.vue new file mode 100644 index 000000000..d1890b98c --- /dev/null +++ b/src/views/vab/icon/colorfulIcon.vue @@ -0,0 +1,155 @@ + + + + + diff --git a/src/views/vab/icon/index.vue b/src/views/vab/icon/index.vue new file mode 100644 index 000000000..aad8cca14 --- /dev/null +++ b/src/views/vab/icon/index.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/src/views/vab/icon/remixIcon.vue b/src/views/vab/icon/remixIcon.vue new file mode 100644 index 000000000..e8e376892 --- /dev/null +++ b/src/views/vab/icon/remixIcon.vue @@ -0,0 +1,153 @@ + + + + + diff --git a/src/views/vab/loading/index.vue b/src/views/vab/loading/index.vue new file mode 100644 index 000000000..94ce9f781 --- /dev/null +++ b/src/views/vab/loading/index.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/src/views/vab/lodash/index.vue b/src/views/vab/lodash/index.vue new file mode 100644 index 000000000..8d326b594 --- /dev/null +++ b/src/views/vab/lodash/index.vue @@ -0,0 +1,211 @@ + + + + diff --git a/src/views/vab/magnifier/index.vue b/src/views/vab/magnifier/index.vue new file mode 100644 index 000000000..460dce4d9 --- /dev/null +++ b/src/views/vab/magnifier/index.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/views/vab/map/index.vue b/src/views/vab/map/index.vue new file mode 100644 index 000000000..c86897780 --- /dev/null +++ b/src/views/vab/map/index.vue @@ -0,0 +1,115 @@ + + + + diff --git a/src/views/vab/markdownEditor/index.vue b/src/views/vab/markdownEditor/index.vue new file mode 100644 index 000000000..48989a15e --- /dev/null +++ b/src/views/vab/markdownEditor/index.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/views/vab/more/index.vue b/src/views/vab/more/index.vue new file mode 100644 index 000000000..1c05e9dc0 --- /dev/null +++ b/src/views/vab/more/index.vue @@ -0,0 +1,119 @@ + + + + diff --git a/src/views/vab/nested/menu1/index.vue b/src/views/vab/nested/menu1/index.vue new file mode 100644 index 000000000..43702fcc9 --- /dev/null +++ b/src/views/vab/nested/menu1/index.vue @@ -0,0 +1,7 @@ + diff --git a/src/views/vab/nested/menu1/menu1-1/index.vue b/src/views/vab/nested/menu1/menu1-1/index.vue new file mode 100644 index 000000000..2560d5dad --- /dev/null +++ b/src/views/vab/nested/menu1/menu1-1/index.vue @@ -0,0 +1,13 @@ + + diff --git a/src/views/vab/nested/menu1/menu1-1/menu1-1-1/index.vue b/src/views/vab/nested/menu1/menu1-1/menu1-1-1/index.vue new file mode 100644 index 000000000..bcff7d3dc --- /dev/null +++ b/src/views/vab/nested/menu1/menu1-1/menu1-1-1/index.vue @@ -0,0 +1,15 @@ + + diff --git a/src/views/vab/permissions/index.vue b/src/views/vab/permissions/index.vue new file mode 100644 index 000000000..011a65548 --- /dev/null +++ b/src/views/vab/permissions/index.vue @@ -0,0 +1,156 @@ + + + diff --git a/src/views/vab/player/index.vue b/src/views/vab/player/index.vue new file mode 100644 index 000000000..504e10dd5 --- /dev/null +++ b/src/views/vab/player/index.vue @@ -0,0 +1,73 @@ + + + diff --git a/src/views/vab/smallComponents/index.vue b/src/views/vab/smallComponents/index.vue new file mode 100644 index 000000000..95c7328d7 --- /dev/null +++ b/src/views/vab/smallComponents/index.vue @@ -0,0 +1,110 @@ + + + + diff --git a/src/views/vab/table/components/TableEdit.vue b/src/views/vab/table/components/TableEdit.vue new file mode 100644 index 000000000..3e8d877e1 --- /dev/null +++ b/src/views/vab/table/components/TableEdit.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/views/vab/table/index.vue b/src/views/vab/table/index.vue new file mode 100644 index 000000000..f9186c6ee --- /dev/null +++ b/src/views/vab/table/index.vue @@ -0,0 +1,263 @@ + + + diff --git a/src/views/vab/tree/index.vue b/src/views/vab/tree/index.vue new file mode 100644 index 000000000..565e5aecf --- /dev/null +++ b/src/views/vab/tree/index.vue @@ -0,0 +1,478 @@ + + + diff --git a/src/views/vab/upload/index.vue b/src/views/vab/upload/index.vue new file mode 100644 index 000000000..910718325 --- /dev/null +++ b/src/views/vab/upload/index.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/views/vab/verify/index.vue b/src/views/vab/verify/index.vue new file mode 100644 index 000000000..899d7e3d5 --- /dev/null +++ b/src/views/vab/verify/index.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/views/vab/webSocket/index.vue b/src/views/vab/webSocket/index.vue new file mode 100644 index 000000000..84b867d21 --- /dev/null +++ b/src/views/vab/webSocket/index.vue @@ -0,0 +1,87 @@ + + + diff --git a/vue.config.js b/vue.config.js index b8a835dbc..a92ace621 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,27 +1,48 @@ +/** + * @author chuzhixin 1204505056@qq.com (不想保留author可删除) + * @description cli配置 + */ + const path = require('path') const { publicPath, assetsDir, outputDir, + lintOnSave, transpileDependencies, title, - devPort + abbreviation, + devPort, + providePlugin, + build7z, + donation, } = require('./src/config') +const { webpackBarName, webpackBanner, donationConsole } = require('zx-layouts') +if (donation) donationConsole() const { version, author } = require('./package.json') +const Webpack = require('webpack') +const WebpackBar = require('webpackbar') +const FileManagerPlugin = require('filemanager-webpack-plugin') const dayjs = require('dayjs') +const date = dayjs().format('YYYY_M_D') const time = dayjs().format('YYYY-M-D HH:mm:ss') -process.env.VUE_APP_TITLE = title -process.env.VUE_APP_AUTHOR = author +process.env.VUE_APP_TITLE = title || 'vue-admin-beautiful' +process.env.VUE_APP_AUTHOR = author || 'chuzhixin 1204505056@qq.com' process.env.VUE_APP_UPDATE_TIME = time process.env.VUE_APP_VERSION = version const resolve = (dir) => path.join(__dirname, dir) +const mockServer = () => { + if (process.env.NODE_ENV === 'development') return require('./mock') + else return '' +} module.exports = { publicPath, assetsDir, outputDir, + lintOnSave, transpileDependencies, devServer: { hot: true, @@ -30,20 +51,26 @@ module.exports = { noInfo: false, overlay: { warnings: true, - errors: true - } + errors: true, + }, }, - configureWebpack () { + configureWebpack() { return { resolve: { alias: { '@': resolve('src'), - styles: resolve('src/styles') - } - } + views: resolve('src/views'), + }, + }, + plugins: [ + new Webpack.ProvidePlugin(providePlugin), + new WebpackBar({ + name: webpackBarName, + }), + ], } }, - chainWebpack (config) { + chainWebpack(config) { config.plugins.delete('preload') config.plugins.delete('prefetch') config.module @@ -71,6 +98,10 @@ module.exports = { .loader('svg-sprite-loader') .options({ symbolId: 'colorful-icon-[name]' }) .end() + + /* config.when(process.env.NODE_ENV === "development", (config) => { + config.devtool("source-map"); + }); */ config.when(process.env.NODE_ENV !== 'development', (config) => { config.performance.set('hints', false) config.devtool('none') @@ -81,21 +112,54 @@ module.exports = { name: 'chunk-libs', test: /[\\/]node_modules[\\/]/, priority: 10, - chunks: 'initial' + chunks: 'initial', }, elementUI: { name: 'chunk-elementUI', priority: 20, - test: /[\\/]node_modules[\\/]_?element-ui(.*)/ + test: /[\\/]node_modules[\\/]_?element-ui(.*)/, }, fortawesome: { name: 'chunk-fortawesome', priority: 20, - test: /[\\/]node_modules[\\/]_?@fortawesome(.*)/ - } - } + test: /[\\/]node_modules[\\/]_?@fortawesome(.*)/, + }, + }, }) + config + .plugin('banner') + .use(Webpack.BannerPlugin, [`${webpackBanner}${time}`]) + .end() + config.module + .rule('images') + .use('image-webpack-loader') + .loader('image-webpack-loader') + .options({ + bypassOnDebug: true, + }) + .end() }) + + if (build7z) { + config.when(process.env.NODE_ENV === 'production', (config) => { + config + .plugin('fileManager') + .use(FileManagerPlugin, [ + { + onEnd: { + delete: [`./${outputDir}/video`, `./${outputDir}/data`], + archive: [ + { + source: `./${outputDir}`, + destination: `./${outputDir}/${abbreviation}_${outputDir}_${date}.7z`, + }, + ], + }, + }, + ]) + .end() + }) + } }, runtimeCompiler: true, productionSourceMap: false, @@ -104,17 +168,21 @@ module.exports = { sourceMap: true, loaderOptions: { scss: { - additionalData (content, loaderContext) { + /*sass-loader 8.0语法 */ + //prependData: '@import "~@/styles/variables.scss";', + + /*sass-loader 9.0写法,感谢github用户 shaonialife*/ + additionalData(content, loaderContext) { const { resourcePath, rootContext } = loaderContext const relativePath = path.relative(rootContext, resourcePath) if ( relativePath.replace(/\\/g, '/') !== 'src/styles/variables.scss' ) { - return '@import "~styles/variables.scss";' + content + return '@import "~@/styles/variables.scss";' + content } return content - } - } - } - } + }, + }, + }, + }, } diff --git a/webstorm.config.js b/webstorm.config.js new file mode 100644 index 000000000..4a0edfdab --- /dev/null +++ b/webstorm.config.js @@ -0,0 +1,5 @@ +/** + * @description webstorm.config(配置这个可以ctrl+左键识别文件并跳转) + */ +const webpackConfig = require('@vue/cli-service/webpack.config.js') +module.exports = webpackConfig