├── vue.config.js ├── public ├── favicon.ico ├── loading.gif └── index.html ├── babel.config.js ├── src ├── sa-resources │ ├── com-view │ │ ├── kulian.png │ │ ├── sa-404.vue │ │ ├── sa-500.vue │ │ ├── sa-403.vue │ │ ├── sa-login.vue │ │ └── sa-home.vue │ ├── index │ │ ├── admin-logo.png │ │ ├── admin-util.js │ │ ├── sa-index.css │ │ ├── sa-theme.css │ │ ├── sa-index.vue │ │ └── sa-index.js │ ├── sa-admin-init.js │ └── sa-menu-list.js ├── App.vue ├── sa-view │ ├── role │ │ ├── mock-data.js │ │ ├── menu-list.vue │ │ ├── menu-setup.vue │ │ └── role-list.vue │ ├── case │ │ ├── mock-joptions.js │ │ ├── mock-data-list.js │ │ ├── query-table-case-add.vue │ │ ├── submit-form.vue │ │ ├── query-table-case.vue │ │ └── query-p-case.vue │ ├── home │ │ ├── mock-data-list.js │ │ └── swiper-list.vue │ ├── user │ │ ├── user-add.vue │ │ ├── user-list.vue │ │ └── data-list.js │ ├── article │ │ ├── data-list.js │ │ ├── art-add.vue │ │ └── art-list.vue │ └── cfg │ │ └── system-cfg.vue ├── main.js └── static │ ├── sa.css │ └── sa.js ├── .gitignore ├── package.json ├── README.md └── LICENSE /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | runtimeCompiler: true 3 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/click33/sa-vue-admin/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/click33/sa-vue-admin/HEAD/public/loading.gif -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/sa-resources/com-view/kulian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/click33/sa-vue-admin/HEAD/src/sa-resources/com-view/kulian.png -------------------------------------------------------------------------------- /src/sa-resources/index/admin-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/click33/sa-vue-admin/HEAD/src/sa-resources/index/admin-logo.png -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | 23 | sa-vue-admin.rar -------------------------------------------------------------------------------- /src/sa-view/role/mock-data.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | code: 200, 3 | msg: 'ok', 4 | data: [{ 5 | "id": 1, 6 | "role_name": "普通用户", 7 | "role_info": "普通用户", 8 | "is_lock": 1, 9 | "create_time": "2018-08-17T02:33:14.000+0000" 10 | }, { 11 | "id": 11, 12 | "role_name": "顶级管理员", 13 | "role_info": "最高权限", 14 | "is_lock": 1, 15 | "create_time": "2018-08-17T02:33:14.000+0000" 16 | }, { 17 | "id": 101, 18 | "role_name": "超级管理员", 19 | "role_info": "最高权限", 20 | "is_lock": 2, 21 | "create_time": "2018-08-17T03:24:01.000+0000" 22 | }] 23 | } 24 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | Vue.config.productionTip = false; 5 | 6 | 7 | // 安装 element-ui 8 | import ElementUI from 'element-ui'; 9 | import 'element-ui/lib/theme-chalk/index.css'; 10 | Vue.use(ElementUI); 11 | 12 | // 安装jquery与layer, (cdn方式引入的直接挂载到原型) 13 | Vue.prototype.$ = window.$; 14 | Vue.prototype.layer = window.layer; 15 | 16 | // 安装sa对象 17 | import sa from './static/sa.js'; 18 | Vue.prototype.sa = sa; 19 | import './static/sa.css'; 20 | 21 | // 安装sa_admin初始化方法 22 | import SaAdminInIt from './sa-resources/sa-admin-init.js'; 23 | Vue.prototype.SaAdminInIt = SaAdminInIt; 24 | 25 | 26 | // 打开vue 27 | new Vue({ 28 | render: h => h(App) 29 | }).$mount('#app') 30 | -------------------------------------------------------------------------------- /src/sa-view/case/mock-joptions.js: -------------------------------------------------------------------------------- 1 | module.exports = [ // 级联选项 2 | { 3 | value: 'v1', 4 | label: '选项1', 5 | children: [ 6 | { 7 | value: 'v1-1', 8 | label: '选项1-1', 9 | children: [ 10 | {value: 'v1-1-1', label: '选项1-1-1'}, 11 | {value: 'v1-1-2', label: '选项1-1-2'}, 12 | {value: 'v1-1-3', label: '选项1-1-3'}, 13 | ] 14 | }, 15 | {value: 'v1-2', label: '选项1-2'}, 16 | {value: 'v1-3', label: '选项1-3'}, 17 | ] 18 | }, 19 | { 20 | value: 'v2', 21 | label: '选项2', 22 | children: [ 23 | {value: 'v2-1', label: '选项2-1'}, 24 | {value: 'v2-2', label: '选项2-2'} 25 | ] 26 | }, 27 | { 28 | value: 'v3', 29 | label: '选项3', 30 | children: [ 31 | {value: 'v3-1', label: '选项3-1'}, 32 | {value: 'v3-2', label: '选项3-2'} 33 | ] 34 | } 35 | ]; -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= htmlWebpackPlugin.options.title %> 10 | 11 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sa-vue-admin", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.4", 12 | "element-ui": "^2.13.0", 13 | "marked": "^0.8.0", 14 | "vue": "^2.6.11", 15 | "vue-awesome-swiper": "^3.1.3", 16 | "wangeditor": "^3.1.1" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-babel": "^4.2.0", 20 | "@vue/cli-plugin-eslint": "^4.2.0", 21 | "@vue/cli-service": "^4.2.0", 22 | "babel-eslint": "^10.0.3", 23 | "eslint": "^6.7.2", 24 | "eslint-plugin-vue": "^6.1.2", 25 | "vue-template-compiler": "^2.6.11" 26 | }, 27 | "eslintConfig": { 28 | "root": true, 29 | "env": { 30 | "node": true, 31 | "jquery": true 32 | }, 33 | "extends": [ 34 | "plugin:vue/essential", 35 | "eslint:recommended" 36 | ], 37 | "parserOptions": { 38 | "parser": "babel-eslint" 39 | }, 40 | "rules": {} 41 | }, 42 | "browserslist": [ 43 | "> 1%", 44 | "last 2 versions" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /src/sa-resources/com-view/sa-404.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 37 | 38 | 50 | 51 | -------------------------------------------------------------------------------- /src/sa-resources/com-view/sa-500.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 37 | 38 | 50 | 51 | -------------------------------------------------------------------------------- /src/sa-resources/com-view/sa-403.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 37 | 38 | 50 | 51 | -------------------------------------------------------------------------------- /src/sa-view/home/mock-data-list.js: -------------------------------------------------------------------------------- 1 | // 模拟数据 2 | module.exports = { 3 | code: 200, 4 | msg: 'ok', 5 | data: [{ 6 | "id": 1, 7 | "title": "壁纸1", 8 | "img_src": "https://color-test.oss-cn-qingdao.aliyuncs.com/sa-admin/32.jpg", 9 | "type": 1, 10 | "link": "", 11 | "click_count": 21, 12 | "create_time": "2019-05-23T03:41:48.000+0000", 13 | "status": 1, 14 | "sort": 1, 15 | "is_update": false 16 | }, { 17 | "id": 2, 18 | "title": "壁纸2", 19 | "img_src": "https://color-test.oss-cn-qingdao.aliyuncs.com/sa-admin/46.png", 20 | "type": 1, 21 | "link": "", 22 | "click_count": 54, 23 | "create_time": "2019-05-23T03:43:15.000+0000", 24 | "status": 1, 25 | "sort": 2, 26 | "is_update": false 27 | }, { 28 | "id": 3, 29 | "title": "壁纸3", 30 | "img_src": "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2020/01/20/15795058964941214251195.jpg", 31 | "type": 1, 32 | "link": "", 33 | "click_count": 16, 34 | "create_time": "2019-05-23T03:11:16.000+0000", 35 | "status": 1, 36 | "sort": 3, 37 | "is_update": false 38 | }, { 39 | "id": 4, 40 | "title": "壁纸4", 41 | "img_src": "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2020/01/20/1579506018108368398487.png", 42 | "type": 1, 43 | "link": "", 44 | "click_count": 91, 45 | "create_time": "2019-05-23T03:11:16.000+0000", 46 | "status": 1, 47 | "sort": 3, 48 | "is_update": false 49 | }], 50 | dataCount: 1433 51 | } 52 | -------------------------------------------------------------------------------- /src/sa-view/role/menu-list.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 28 | 29 | 52 | -------------------------------------------------------------------------------- /src/sa-view/user/user-add.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 41 | 42 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/sa-view/case/mock-data-list.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | code: 200, 3 | msg: 'ok', 4 | data: [ 5 | { 6 | id: 101, 7 | name: '苹果', 8 | icon: 'el-icon-apple', 9 | avatar: 'http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2020/01/19/15794196587511189314194.png', 10 | money: 1999, 11 | status: 1, 12 | create_time: new Date() 13 | }, 14 | { 15 | id: 102, 16 | name: '香蕉', 17 | icon: 'el-icon-apple', 18 | avatar: 'http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2020/01/19/15794196587511189314194.png', 19 | money: 2555, 20 | status: 1, 21 | create_time: new Date() 22 | }, 23 | { 24 | id: 103, 25 | name: '茄子', 26 | icon: 'el-icon-cherry', 27 | avatar: 'http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2020/01/19/15794196587511189314194.png', 28 | money: 4999, 29 | status: 1, 30 | create_time: new Date() 31 | }, 32 | { 33 | id: 104, 34 | name: '芸豆', 35 | icon: 'el-icon-cherry', 36 | avatar: 'http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2020/01/19/15794196587511189314194.png', 37 | money: 5555, 38 | status: 1, 39 | create_time: new Date() 40 | }, 41 | { 42 | id: 105, 43 | name: '木须肉', 44 | icon: 'el-icon-umbrella', 45 | avatar: 'http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2020/01/19/15794196587511189314194.png', 46 | money: 10000, 47 | status: 1, 48 | create_time: new Date() 49 | }, 50 | { 51 | id: 106, 52 | name: '回锅肉', 53 | icon: 'el-icon-umbrella', 54 | avatar: 'http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2020/01/19/15794196587511189314194.png', 55 | money: 10000, 56 | status: 1, 57 | create_time: new Date() 58 | }, 59 | { 60 | id: 107, 61 | name: '辣子鸡', 62 | icon: 'el-icon-user', 63 | avatar: 'http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2020/01/19/15794196587511189314194.png', 64 | money: 99999, 65 | status: 2, 66 | create_time: new Date() 67 | }, 68 | { 69 | id: 108, 70 | name: '大萝贝', 71 | icon: 'el-icon-user', 72 | avatar: 'http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2020/01/19/15794196587511189314194.png', 73 | money: 23333, 74 | status: 2, 75 | create_time: new Date() 76 | }, 77 | ], 78 | dataCount: 1244 79 | } -------------------------------------------------------------------------------- /src/sa-view/article/data-list.js: -------------------------------------------------------------------------------- 1 | // 模拟数据 2 | module.exports = { 3 | code: 200, 4 | msg: 'ok', 5 | data: [{ 6 | "id": 20, 7 | "title": "那个清晨", 8 | "content": "那个清晨一大早,便被母亲叫起。我有些不满,平常我是总要在床上多赖一会儿的。可当我迷迷糊糊的看到母亲紧绷的脸庞时...", 9 | "see_count": 356, 10 | "like_count": 55, 11 | "share_count": 13, 12 | "create_username": '省长', 13 | "is_public": 1, 14 | "create_time": "2019-05-12" 15 | }, 16 | { 17 | "id": 17, 18 | "title": "人生,就是一场抵达", 19 | "content": "庄子说,人生天地之间,若白驹过隙,忽然而已。人生就是一场抵达,我们总以为来日方长...", 20 | "see_count": 200, 21 | "like_count": 12, 22 | "share_count": 6, 23 | "create_username": '小言', 24 | "is_public": 1, 25 | "create_time": "2019-05-12" 26 | }, 27 | { 28 | "id": 11, 29 | "title": "气质女生与世界先生", 30 | "content": "我把衣柜翻了个底朝天,花花绿绿地堆了满床。谢雨帆盘腿坐在电脑前打游戏,嗑着瓜子眼皮也不抬一下,半晌才悠悠地吐出一句...", 31 | "see_count": 240, 32 | "like_count": 22, 33 | "share_count": 15, 34 | "create_username": 'fan哈', 35 | "is_public": 2, 36 | "create_time": "2019-05-10" 37 | }, 38 | { 39 | "id": 9, 40 | "title": "善待朋友,珍惜拥有", 41 | "content": "不要弄丢,一个对你好的人;不要漠视,一份待你深的情。不是谁都能包容你的臭脾气,更不是谁都能一直等下去...", 42 | "see_count": 2420, 43 | "like_count": 122, 44 | "share_count": 95, 45 | "create_username": '小丸子', 46 | "is_public": 2, 47 | "create_time": "2019-05-5" 48 | }, 49 | { 50 | "id": 7, 51 | "title": "爱是淡淡的忧伤", 52 | "content": "回味初识的那一段深情爱恋,清晰的面容,熟悉的身影,悦耳的声音,在某个深夜恍然想起。那是一个深秋的夜晚,我们相拥而坐...", 53 | "see_count": 320, 54 | "like_count": 12, 55 | "share_count": 5, 56 | "create_username": '不值一提', 57 | "is_public": 1, 58 | "create_time": "2019-05-1" 59 | }, 60 | { 61 | "id": 6, 62 | "title": "男人看了沉默,女人看了流泪", 63 | "content": "男人看了沉默,女人看了流泪男人看了沉默,女人看了流泪男人看了沉默,女人看了流泪男人看了沉默,女人看了流泪...", 64 | "see_count": 3220, 65 | "like_count": 11, 66 | "share_count": 1, 67 | "create_username": 'UC震惊部', 68 | "is_public": 1, 69 | "create_time": "2019-05-1" 70 | }, 71 | { 72 | "id": 5, 73 | "title": "爱是淡淡的忧伤", 74 | "content": "回味初识的那一段深情爱恋,清晰的面容,熟悉的身影,悦耳的声音,在某个深夜恍然想起。那是一个深秋的夜晚,我们相拥而坐...", 75 | "see_count": 320, 76 | "like_count": 12, 77 | "share_count": 5, 78 | "create_username": '不值一提', 79 | "is_public": 1, 80 | "create_time": "2019-05-1" 81 | } 82 | ], 83 | dataCount: 6379 84 | } 85 | -------------------------------------------------------------------------------- /src/sa-view/cfg/system-cfg.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 37 | 38 | 78 | -------------------------------------------------------------------------------- /src/sa-resources/com-view/sa-login.vue: -------------------------------------------------------------------------------- 1 | 24 | 60 | 61 | 91 | 92 | -------------------------------------------------------------------------------- /src/sa-resources/sa-admin-init.js: -------------------------------------------------------------------------------- 1 | /* 2 | 在开发时,如无必要,请不要直接魔改模板的代码,以免在运行时出现意外bug 3 | 这是模板为你预留一个文件,用来对接你的业务逻辑 4 | */ 5 | 6 | // import Vue from 'vue'; 7 | export default function(sa_admin, sa) { 8 | 9 | 10 | // ================================= 示例:模板基础信息 ================================= 11 | sa_admin.title = 'SA-后台模板'; 12 | // sa_admin.logo_url = '图片地址'; // 设置logo图标地址 默认值空, 代表使用:./admin-logo.png 13 | // sa_admin.icon_url = '图片地址'; // 设置icon图标地址 默认值空, 代表使用:./favicon.ico 14 | 15 | 16 | // ================================= 示例:初始化菜单 ================================= 17 | sa_admin.initMenu(); // 初始化菜单, 不传参代表默认显示所有菜单 菜单在 ./sa-menu-list.js 里, 18 | // sa_admin.initMenu(['1', '1-1', '1-2', '4']); // 传入一个id数组, 显示指定菜单 19 | 20 | 21 | // ================================= 示例:设置头像昵称 ================================= 22 | // 23 | sa_admin.$nextTick(function() { 24 | sa_admin.user = { 25 | username: 'root', // 昵称 26 | avatar: document.querySelector('.admin-logo').src // 头像地址 27 | } 28 | }) 29 | 30 | 31 | // ================================= 示例:js操作菜单 ================================= 32 | 33 | // sa_admin.showHome(); // 显示主页选项卡 34 | // sa_admin.showTabById('home'); // 显示一个选项卡, 根据id 35 | // sa_admin.closeTabById('5-1'); // 关闭一个选项卡,根据 id ( 第二个参数可填关闭后的回调函数 ) 36 | // sa_admin.showMenuById('5-2'); // 打开一个 菜单,根据 id 37 | 38 | // 跨窗口通信 39 | // sa_admin.getView('5-2').f5();// 获取指定视图的组件对象,并调用其f5()函数,一般用于跨窗口通信 40 | 41 | // 以下写法,调用tab打开新窗口 42 | // var tab = {id: '5-1', name: '用户添加', view: () => import('@/sa-view/user/user-add.vue'), params: {username: '王铁汉2'} }; 43 | // sa_admin.showTab(tab); 44 | 45 | 46 | // ================================= 示例:设置登录后的头像处,下拉可以出现的选项 ================================= 47 | sa_admin.dropList = [ // 头像点击处可操作的选项 48 | { 49 | name: '我的资料', 50 | click: function() { 51 | sa_admin.$message('点击了我的资料,你可以参照文档重写此函数'); 52 | } 53 | }, 54 | { 55 | name: '切换账号', 56 | click: function() { 57 | sa_admin.openLogin(); // 打开登陆视图 58 | } 59 | }, 60 | { 61 | name: '退出登录', 62 | click: function() { 63 | // sa_admin.$message('点击了退出登录,你可以参照文档重写此函数'); 64 | sa.confirm('退出登录?', function() { 65 | sa.ajax2('/acc/exit', function() { 66 | sa.alert('注销成功', function() { 67 | sa_admin.openLogin(); 68 | }) 69 | }, {msg: '正在注销'}); 70 | }); 71 | } 72 | } 73 | ] 74 | 75 | 76 | // 初始化模板, 必须调用 77 | sa_admin.init(); 78 | // 或者这样: 79 | // sa_admin.init({ 80 | // is_show_tabbar: false, // 关闭tabbar栏, 取而显示的是一个面包屑导航栏 81 | // is_reme_open: false // 是否记住上一次最后打开的窗口, 默认为true, 配置为false后, 每次刷新不再自动打开上一次最后打开的窗口(也不再有锚链接智能tab调准) 82 | // }); 83 | 84 | 85 | 86 | // ================================= 关于鉴权 ================================= 87 | // sa_admin内部封装了鉴权功能, 可以参考以下api 88 | 89 | // var arr = ['1', '2', '3', 'a', 'b', 'c']; // 一般由后端提供接口返回当前会话所具有的权限码集合 90 | // sa.setAuth(arr); // 设置当前账号具有的权限码集合 91 | 92 | // sa.checkAuth('a'); // 鉴权会通过 93 | // sa.checkAuth('y'); // 鉴权会失败, 弹窗提示无权限 94 | 95 | // sa.clearAuth(); // 在注销登录时, 可以清除当前会话所有权限码 96 | 97 | // // 具有指定权限才能显示某个按钮, 示例: 98 | // 99 | 100 | 101 | 102 | 103 | } 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # sa-admin (vue单页版) 3 | 4 | 一个多窗口后台模板,流畅、易上手、提高生产力,演示地址:[http://sa-vue-admin.dev33.cn](http://sa-vue-admin.dev33.cn) 5 | 6 | 如果你不太熟悉vue单页应用,[点我访问iframe版](https://github.com/click33/sa-admin) 7 | 8 | :kissing_closed_eyes: :kissing_closed_eyes: :kissing_closed_eyes: :kissing_closed_eyes: :kissing_closed_eyes: :kissing_closed_eyes: :kissing_closed_eyes: :kissing_closed_eyes: :kissing_closed_eyes: :kissing_closed_eyes: :kissing_closed_eyes: :kissing_closed_eyes: :blush: :blush: :blush: :heart: :heart: :heart: 9 | 10 | ## 优点 11 | - 上手简单:提供完整彻底的封装,不用修改一行源代码即可轻松集成 12 | - 示例全面:提供大量常见增删改查示例,提高你的生产力 13 | 14 | ## 需求提交 15 | - 我们深知一个优秀的项目需要海纳百川,[点我在线提交需求](http://sa-app.dev33.cn/wall.html?name=sa-admin) 16 | 17 | 18 | ## 框架选型 19 | - JS引擎:[Vue @2.6.11](https://cn.vuejs.org/) 20 | - 脚手架:[@vue/cli @4.0.5](https://cli.vuejs.org/zh/) 21 | - UI框架:[Element-UI @2.13.0](https://element.eleme.cn/#/zh-CN) 22 | - web弹层:[layer @3.1.1](http://layer.layui.com/) 23 | - 切页动画:[Swiper @4.5.0](https://www.swiper.com.cn/) 24 | 25 | 26 | ## 功能 27 | - 菜单:支持一、二、三级菜单,并开放一系列接口方便的使用js操作菜单 28 | - 主题:目前保留八种主题:蓝色、绿色、白色、灰色、灰色-展开、pro钛合金、沉淀式黑蓝、简约式灰蓝(切换主题时,可自动保存你的喜好,下次打开时仍然有效) 29 | - 折叠:折叠或收缩菜单,并且监听窗口大小变化,在拉伸窗口时自动折叠或收缩菜单,自动响应式 30 | - tabbar栏: 31 | - 卡片堆积:多卡片自动堆积,与菜单双向关联,切换tab卡时自动显示左侧菜单 32 | - 拖动手势:tab卡支持拖拽手势,上拖全屏打开、下拽悬浮打开、左拽快速关闭 33 | - 右键菜单:在tab上右击,可以:刷新、复制、关闭、关闭其它、关闭所有、悬浮打开、全屏打开、取消 34 | - 双击菜单:双击tabbar空白处,可以显示添加新tab窗口 35 | - 切换动画:集成swiper,滑动、淡入、方块、3D流、3D翻转,五种高大上切换动画,任你选择! 36 | - 开放接口:开放一系列api,助你方便的使用js操作tabbar栏,具体请查看集成文档 37 | - 锚链:tab切换自动更改hash锚链,同时监听锚链改变tab,可灵活的用鼠标前后键切换tab窗口 (如不需要此功能可在初始化时关闭) 38 | - 窗口:可在初始化时,设置是否显示tabbar栏,来控制它是多窗口还是单窗口,具体见使用文档 39 | - 更新:功能不断更新中... 你有好的想法也可以告诉我,加群一起交流吧 40 | - 文档:使用说明,见文档 41 | 42 | 43 | ## 开始使用 44 | #### 1、获取源码 45 | - 方式1:通过gitee或者github下载 46 | - 方式2:通过Git Bash: 47 | ``` 48 | git clone https://github.com/click33/sa-vue-admin.git 49 | ``` 50 | 51 | #### 2、运行 52 | - 获取源码后,在项目根目录执行cmd命令: 53 | ``` 54 | 安装依赖:npm i 55 | 运行项目:npm run serve 56 | ``` 57 | - 根据控制台输出的路径,访问浏览器,一般为:http://localhost:8080/ 58 | 59 | #### 3、使用注意 60 | - 在开发时,如无必要,请不要直接魔改模板的代码,以免在运行时出现意外bug 61 | - 在 sa-resourecs 文件下,有个 sa-admin-init.js, 这是模板为你预留一个文件,用来对接你的业务逻辑 62 | - 在这个文件里,你可以: 63 | ``` 64 | sa_admin.title = 'SA-后台模板'; // 设置模板标题 65 | sa_admin.logo_url = '图片地址'; // 设置logo图标地址 默认值空, 代表使用:./admin-logo.png 66 | sa_admin.icon_url = '图片地址'; // 设置icon图标地址 默认值空, 代表使用:./favicon.ico 67 | sa_admin.initMenu(); // 初始化菜单, 不传参代表默认显示所有菜单 菜单在 ./sa-menu-list.js 里定义, 68 | // sa_admin.initMenu(['1', '1-1', '1-2', '4']); // 传入一个id数组, 显示指定菜单 69 | // sa.checkAuth('a'); // 鉴权认证 (sa_admin内部封装了鉴权功能) 70 | sa_admin.init(); // 初始化模板, 必须调用 71 | ``` 72 | 73 | - 以上示例在 sa-admin-init.js 文件里均有详细的注释说明,如果还要明白的地方,可以加入qq群询问 74 | 75 | 76 | ## 贡献代码 77 | 1. 在github上fork一份到自己的仓库 78 | 2. clone自己的仓库到本地电脑 79 | 3. 在本地电脑修改、commit、push 80 | 4. 提交pr(点击:New Pull Request)(提交pr前请保证自己fork的仓库是最新版本,如若不是先强制更新一下) 81 | 5. 等待合并 82 | 83 | ## 建议贡献的地方 84 | - 更多登录模板 85 | - 修复源码现有bug,或增加新的实用功能(比如:流畅的tab左右拖拽排序) 86 | - 更多demo示例:比如针对element-ui一些复杂组件的示例,或者其它一些常见js库的集成使用 87 | - 如果更新实用功能,可在文档友情链接处留下自己的推广链接 88 | 89 | 90 | ## QQ群 91 | QQ交流群:[782974737 点击加入](https://jq.qq.com/?_wv=1027&k=5DHN5Ib) 92 | 93 | ## 截图 94 | 95 | ![截图](https://color-test.oss-cn-qingdao.aliyuncs.com/sa-vue-admin/2.jpg) 96 | ![截图](https://color-test.oss-cn-qingdao.aliyuncs.com/sa-vue-admin/1.jpg) 97 | ![截图](https://color-test.oss-cn-qingdao.aliyuncs.com/sa-vue-admin/3.jpg) 98 | 99 | 见演示说明地址 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/sa-resources/index/admin-util.js: -------------------------------------------------------------------------------- 1 | // ======================== 一些工具方法 ======================== 2 | 3 | export default { 4 | // 删除数组某个元素 5 | arrayDelete: function(arr, item){ 6 | var index = arr.indexOf(item); 7 | if (index > -1) { 8 | arr.splice(index, 1); 9 | } 10 | }, 11 | 12 | //执行一个函数, 解决layer拉伸或者最大化的时候,iframe高度不能自适应的问题 13 | solveLayerBug: function(index) { 14 | var selected = '#layui-layer' + index; 15 | var height = $(selected).height(); 16 | var title_height = $(selected).find('.layui-layer-title').height(); 17 | $(selected).find('iframe').css('height', (height - title_height) + 'px'); 18 | // var selected = '#layui-layer' + index; 19 | // var height = $(selected).height(); 20 | // var title_height = $(selected).find('.layui-layer-title').height(); 21 | // $(selected).find('iframe').css('height', (height - title_height) + 'px'); 22 | }, 23 | 24 | // 全屏 25 | fullScreen: function(){ 26 | if(document.documentElement.RequestFullScreen){ 27 | document.documentElement.RequestFullScreen(); 28 | } 29 | //兼容火狐 30 | if(document.documentElement.mozRequestFullScreen){ 31 | document.documentElement.mozRequestFullScreen(); 32 | } 33 | //兼容谷歌等可以webkitRequestFullScreen也可以webkitRequestFullscreen 34 | if(document.documentElement.webkitRequestFullScreen){ 35 | document.documentElement.webkitRequestFullScreen(); 36 | } 37 | //兼容IE,只能写msRequestFullscreen 38 | if(document.documentElement.msRequestFullscreen){ 39 | document.documentElement.msRequestFullscreen(); 40 | } 41 | }, 42 | 43 | // 退出全屏 44 | fullScreenNormal: function() { 45 | if(document.exitFullScreen){ 46 | document.exitFullscreen() 47 | } 48 | // //兼容火狐 49 | // console.log(document.mozExitFullScreen) 50 | if(document.mozCancelFullScreen){ 51 | document.mozCancelFullScreen() 52 | } 53 | // //兼容谷歌等 54 | if(document.webkitExitFullscreen){ 55 | document.webkitExitFullscreen() 56 | } 57 | // //兼容IE 58 | if(document.msExitFullscreen){ 59 | document.msExitFullscreen() 60 | } 61 | }, 62 | 63 | 64 | // ======================== 菜单集合相关 ======================== 65 | 66 | // 将一维平面数组转换为 Tree 菜单 (根据其指定的parent_id添加到其父菜单的childList) 67 | arrayToTree: function(menu_list) { 68 | for (var i = 0; i < menu_list.length; i++) { 69 | var menu = menu_list[i]; 70 | // 添加到其指定的父菜单的childList 71 | if(menu.parent_id) { 72 | var parent_menu = this.getMenuById(menu_list, menu.parent_id); 73 | if(parent_menu) { 74 | parent_menu.childList = parent_menu.childList || []; 75 | parent_menu.childList.push(menu); 76 | menu_list.splice(i, 1); // 从一维中删除 77 | i--; 78 | } 79 | } 80 | } 81 | return menu_list; 82 | }, 83 | 84 | 85 | // 将 menu_list 处理一下 86 | refMenuList: function(menu_list) { 87 | for (var i = 0; i < menu_list.length; i++) { 88 | var menu = menu_list[i]; 89 | // 有子项的递归处理 90 | if(menu.childList){ 91 | menu.children = menu.childList; 92 | this.refMenuList(menu.childList); 93 | } 94 | } 95 | return menu_list; 96 | }, 97 | 98 | 99 | 100 | // 返回指定 index 的menu 101 | getMenuById: function(menuList, id) { 102 | for (var i = 0; i < menuList.length; i++) { 103 | var menu = menuList[i]; 104 | if(menu.id + '' == id + '') { 105 | return menu; 106 | } 107 | // 如果是二级或多级 108 | if(menu.childList) { 109 | var menu2 = this.getMenuById(menu.childList, id); 110 | if(menu2 != null) { 111 | return menu2; 112 | } 113 | } 114 | } 115 | return null; 116 | } 117 | 118 | 119 | 120 | 121 | } 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/sa-view/role/menu-setup.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 49 | 50 | 115 | -------------------------------------------------------------------------------- /src/sa-view/case/query-table-case-add.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 46 | 47 | 144 | -------------------------------------------------------------------------------- /src/sa-view/article/art-add.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 45 | 46 | 47 | 128 | -------------------------------------------------------------------------------- /src/sa-view/case/submit-form.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 67 | 68 | 128 | -------------------------------------------------------------------------------- /src/sa-resources/sa-menu-list.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | // 一个菜单可以包括的所有属性 4 | // { 5 | // id: '12345', // 菜单id, 必须唯一 6 | // name: '用户中心', // 菜单名称, 同时也是tab选项卡上显示的名称 7 | // icon: 'el-icon-user', // 菜单图标, 参考地址: https://element.eleme.cn/#/zh-CN/component/icon 8 | // info: '管理所有用户', // 菜单介绍, 在菜单预览和分配权限时会有显示 9 | // view: () => import('@/sa-view/user/user-list.vue'), // 指向的视图 10 | // params: {}, // 视图参数 11 | // url: 'sa-html/user/user-list.html', // 菜单指向地址, 用iframe打开它 (配置后,view属性失效) 12 | // is_blank: false, // 如果指定了url, 此属性决定是否从新窗口打开 13 | // is_show: true, // 是否显示, 默认true 14 | // childList: [ // 指定这个菜单所有的子菜单, 子菜单可以继续指定子菜单, 至多支持三级菜单 15 | // // .... 16 | // ], 17 | // click: function(sa_admin, sa) { // 配置一个函数, 点击菜单时, 会执行这个函数 18 | // console.log(sa); 19 | // } 20 | // } 21 | 22 | 23 | /* 菜单集合 */ 24 | export default [ 25 | { 26 | id: '2', 27 | name: '各种示例', 28 | icon: 'el-icon-search', 29 | info: '增删改查各种常用组件示例', 30 | childList: [ 31 | {id: '2-1', name: '查询参数示例', view: () => import('@/sa-view/case/query-p-case.vue')}, 32 | {id: '2-2', name: '表格显示示例', view: () => import('@/sa-view/case/query-table-case.vue')}, 33 | {id: '2-3', name: '表单提交示例', view: () => import('@/sa-view/case/submit-form.vue')} 34 | ] 35 | }, 36 | { 37 | id: '3', 38 | name: '首页设置', 39 | icon: 'el-icon-table-lamp', 40 | info: '首页的一些设置', 41 | childList: [ 42 | {id: '3-2', name: '轮播图设置', view: () => import('@/sa-view/home/swiper-list.vue')} 43 | ] 44 | }, 45 | { 46 | id: '4', 47 | name: '权限控制', 48 | icon: 'el-icon-unlock', 49 | info: '对系统角色权限的分配等设计,敏感度较高,请谨慎授权', 50 | childList: [ 51 | {id: '4-1', name: '角色列表', view: () => import('@/sa-view/role/role-list.vue') }, 52 | {id: '4-2', name: '菜单预览', view: () => import('@/sa-view/role/menu-list.vue') } 53 | ] 54 | }, 55 | { 56 | id: '5', 57 | name: '用户管理', 58 | icon: 'el-icon-user', 59 | info: '对用户列表、添加、统计等等...', 60 | childList: [ 61 | {id: '5-2', name: '用户列表', view: () => import('@/sa-view/user/user-list.vue') }, 62 | {id: '5-1', name: '用户添加', view: () => import('@/sa-view/user/user-add.vue'), params: {username: '王铁汉'} }, 63 | ], 64 | 65 | // let template = '
'; 66 | // menu.view = ()=> ({template: template}); 67 | 68 | }, 69 | { 70 | id: '6', 71 | name: '文章管理', 72 | icon: 'el-icon-document-copy', 73 | info: '对文章的增删改查、维护', 74 | childList: [ 75 | {id: '6-1', name: '文章列表', view: () => import('@/sa-view/article/art-list.vue') }, 76 | ] 77 | }, 78 | // ========= 示例 指定一个函数, 点击菜单时执行这个函数 ================ 79 | { 80 | id: '7', 81 | name: '系统设置', 82 | icon: 'el-icon-setting', 83 | childList: [ 84 | {id: '7-1', name: '登录页', click: function(sa_admin, sa) { // 点击这个菜单时将执行这个函数 85 | console.log(sa_admin); 86 | console.log(sa); 87 | sa_admin.openLogin(); // 打开登录页面 88 | }}, 89 | {id: '7-2', name: '403无权限', click: function(sa_admin) { 90 | sa_admin.open403(); // 打开403页面 91 | }}, 92 | {id: '7-3', name: '404未找到', click: function(sa_admin) { 93 | sa_admin.open404(); // 打开404页面 94 | }}, 95 | {id: '7-10', name: '500有错误', click: function(sa_admin) { 96 | sa_admin.open500(); // 打开500页面 97 | }}, 98 | {id: '7-11', name: '服务器设置', view: () => import('@/sa-view/cfg/system-cfg.vue') }, 99 | ] 100 | }, 101 | // ========= 示例 外部链接 点击从新窗口打开 ================ 102 | { 103 | id: '8', 104 | name: '外部链接', 105 | icon: 'el-icon-link', 106 | info: '示例:外部链接', 107 | childList: [ 108 | {id: '8-1', name: '百度一下', url: 'https://www.baidu.com/', is_blank: true}, 109 | {id: '8-6', name: '颜值排行榜', url: 'http://yanzhi21.com/', is_blank: true}, 110 | {id: '8-7', name: 'jq22插件库', url: 'http://www.jq22.com/', is_blank: true}, 111 | {id: '8-2', name: 'uni-app', url: 'https://uniapp.dcloud.io/', is_blank: true}, 112 | {id: '8-11', name: 'sa-admin-iframe版', url: 'http://sa-admin.dev33.cn/', is_blank: true}, 113 | {id: '8-3', name: 'sa-token', url: 'http://sa-token.dev33.cn/',}, // iframe打开 114 | {id: '8-4', name: 'SqlFly', url: 'https://sqlfly.dev33.cn/',}, // iframe打开 115 | ] 116 | }, 117 | { 118 | id: '9', 119 | name: '一个隐藏菜单', 120 | view: () => import('@/sa-view/role/role-list.vue'), 121 | is_show: false// 隐藏 122 | }, 123 | {id: '1-11', name: '在线论坛', url: 'http://applist.dev33.cn/applist-admin/html/ser-comment/w-list.html?sid=4nqjfqbyw6fh', is_show: false}, 124 | 125 | 126 | 127 | ] 128 | 129 | -------------------------------------------------------------------------------- /src/sa-view/article/art-list.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 83 | 84 | 135 | -------------------------------------------------------------------------------- /src/sa-view/role/role-list.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 91 | 92 | 165 | -------------------------------------------------------------------------------- /src/sa-view/user/user-list.vue: -------------------------------------------------------------------------------- 1 | 85 | 86 | 139 | 140 | 142 | -------------------------------------------------------------------------------- /src/sa-view/case/query-table-case.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 95 | 96 | 160 | -------------------------------------------------------------------------------- /src/static/sa.css: -------------------------------------------------------------------------------- 1 | /** 公共css */ 2 | *{margin: 0px;padding: 0px;} 3 | html{font-size: 10px; height: 100%;} 4 | body{font-size: 1.4rem; height: 100%; background-color: #eeeeee; color: #333;} 5 | body{font-family: "Helvetica Neue", Helvetica, "PingFang SC", Tahoma, Arial, sans-serif;} 6 | a{text-decoration: none;} 7 | a:hover{} 8 | /* h1,h2,h3,h4,h5,h6{font-weight: 400;} */ 9 | hr{background-color : #ddd; height: 1px; border: none;} 10 | input,select{outline: 0;} 11 | 12 | /* input type=number时不显示按钮 */ 13 | input::-webkit-outer-spin-button, 14 | input::-webkit-inner-spin-button { 15 | -webkit-appearance: none !important; 16 | margin: 0; 17 | } 18 | 19 | /* 居中形式的img */ 20 | .cover-img{object-fit: cover; object-position: 50% 30%;} 21 | 22 | /* ajax2加载时的转圈圈样式 */ 23 | .ajax-layer-load.layui-layer-dialog{min-width: 0px !important; background-color: rgba(0,0,0,0.85);} 24 | .ajax-layer-load.layui-layer-dialog .layui-layer-content{padding: 10px 20px 10px 40px; color: #FFF;} 25 | .ajax-layer-load.layui-layer-dialog .layui-layer-content .layui-layer-ico{width: 20px; height: 20px; background-size: 20px 20px; top: 12px; } 26 | 27 | 28 | /* layer图片预览时, 左右键永远显示 */ 29 | .layui-layer-imgbar, .layui-layer-imguide{display: block !important;} 30 | .layui-layer-iconext.layui-layer-imgprev{position: fixed; left: 50;} 31 | .layui-layer-iconext.layui-layer-imgnext{position: fixed; right: 50;} 32 | 33 | 34 | /* ===================== 整体面板 ===================== */ 35 | /* vue盒子 */ 36 | .vue-box{/* padding-bottom: 20px; */font-size: 14px; width: 100%; height: 100%; overflow: auto; background-color: #EEE;} 37 | 38 | /* 内容-面板 */ 39 | .c-panel{margin: 1em 1em; padding: 1em 1.5em; background-color: #fff; color: #333; /* box-shadow: 0 0 5px #eee; */} 40 | .c-title{font-size: 14px; font-weight: bold; line-height: 2em; margin-bottom: 3px;} 41 | .c-title span{font-weight: 400; font-size: 0.85em; padding-left: 1em; color: #888;} 42 | 43 | 44 | /* ===================== 表单相关 ===================== */ 45 | .c-panel .el-form{padding-top: 10px; padding-bottom: 5px;} 46 | .c-panel .el-form-item{min-width: 278px;} 47 | .c-panel .el-form-item__label,.c-panel .c-label{width: 100px;} 48 | .c-panel .c-label{display: inline-block;} 49 | .c-panel .el-form .el-input{width: 178px;} 50 | .c-remark{color: #888; margin-left: 0.5em; font-size: 0.9em;} 51 | 52 | /* 标签 */ 53 | .c-panel .c-tag{padding: 0px 15px; height: 22px; line-height: 22px; border-radius: 0px; border: 0px;} 54 | /* 复选框 */ 55 | .c-panel .el-checkbox,.c-panel .el-radio{margin-right: 20px;} 56 | /* 禁用input的样式 */ 57 | .c-panel .el-input.is-disabled .el-input__inner{color: #999;} 58 | 59 | /* 表格的表头颜色深一点 */ 60 | .c-panel .el-table__header tr th{background-color: #F5F5F5;} 61 | 62 | /* 调整圆角大小: 输入框、文本域、按钮、 */ 63 | .c-panel .el-input__inner, 64 | .c-panel .el-textarea__inner, 65 | .c-panel .el-button{border-radius: 2px !important;} 66 | 67 | 68 | /* 单选button,圆角限制2px */ 69 | .el-radio-button:first-child .el-radio-button__inner{border-radius: 2px 0 0 2px !important;} 70 | .el-radio-button:last-child .el-radio-button__inner{border-radius: 0 2px 2px 0 !important;} 71 | 72 | /* 单选按钮, 文字版 */ 73 | .s-radio-text{} 74 | .s-radio-text .el-radio__input{display: none;} 75 | .s-radio-text .el-radio__input.is-checked+.el-radio__label{font-weight: 700;} 76 | .s-radio-text .el-radio__label{padding-left: 0px; } 77 | .s-radio-text .el-radio__label:hover{text-decoration:underline;} 78 | .s-radio-text .hover-line:hover{text-decoration: underline; cursor: pointer;} 79 | .s-radio-text .el-form-item__content{position: relative; top: -2px;} 80 | 81 | /* 表格里操作按钮的样式调整 */ 82 | .c-btn{padding: 4px 6px; font-size: 12px !important; border-radius: 1px;} 83 | .c-btn.el-button--primary{background-color: #3296FA;} 84 | .c-btn.el-button--primary:hover{background-color: #067CF3; border-color: #067CF3;} 85 | .c-btn.el-button--danger{background-color: #CA4242;} 86 | .c-btn.el-button--danger:hover{background-color: #A00C0C; border-color: #A00C0C;} 87 | .c-btn.el-button--success:hover{background-color: #2B9939; border-color: #2B9939;} 88 | 89 | /* 分页盒子调整一下间距 */ 90 | .page-box{padding: 2em 0 1em 0; } 91 | 92 | /* ===================== 一些特殊元素 ===================== */ 93 | 94 | 95 | /* 流体表单 */ 96 | .cj-form.vue-box{padding-bottom: 0px; height: 100vh; background-color: #FFF;} 97 | .cj-form .c-panel{box-shadow: 0 0 0; margin-top: 0px; margin-bottom: 0px; padding-top: 2.5em; padding-bottom: 0px;} 98 | .cj-form .c-panel .el-form .el-input{width: 100%;} 99 | 100 | /* 没有内边距的dialog */ 101 | .full-dialog .el-dialog__body{padding: 0px;} 102 | .full-h-dialog .el-dialog__header{padding: 0px;} 103 | /* .full-dialog .el-dialog__header{padding: 0px;} */ 104 | 105 | .iframe-view-box{width: 100%; height: 100%; overflow: hidden;} 106 | .iframe-view{width: 100%; height: 100%; border: 0px; background-color: #EEE;} 107 | 108 | 109 | /* ===================== 表单相关 - 旧方案 ===================== */ 110 | 111 | /* 内容-item */ 112 | .c-item {min-width: 280px; min-height: 32px; line-height: 32px; padding-right: 20px; display: inline-block; margin: 0.5em 0;} 113 | .c-item.br{display: block;} 114 | /* label样式 */ 115 | .c-item .c-label{width: 5em; color: #333; display: inline-block; text-align: right;} 116 | /* input宽度等样式调整 */ 117 | .c-item .el-input{width: 200px;} 118 | /* 禁用input的样式 */ 119 | .c-item .el-input.is-disabled .el-input__inner{color: #999;} 120 | /* 链接 行高设置 */ 121 | .c-item .el-link{line-height: 1.6em;} 122 | 123 | 124 | /* ===================== 自定义一下滚动条 ===================== */ 125 | /*滚动条的宽度*/ 126 | .vue-box::-webkit-scrollbar {width:9px;height:9px;} 127 | 128 | /*外层轨道。可以用display:none让其不显示,也可以添加背景图片,颜色改变显示效果*/ 129 | .vue-box::-webkit-scrollbar-track { 130 | width: 6px; 131 | background-color:#F1F1F1; 132 | -webkit-border-radius: 2em; 133 | -moz-border-radius: 2em; 134 | border-radius:2em; 135 | } 136 | /*滚动条的设置*/ 137 | .vue-box::-webkit-scrollbar-thumb { 138 | background-color:#C1C1C1; 139 | background-clip:padding-box; 140 | min-height:28px; 141 | -webkit-border-radius: 2em; 142 | -moz-border-radius: 2em; 143 | border-radius:2em; 144 | } 145 | /*滚动条移上去的背景*/ 146 | /* .vue-box::-webkit-scrollbar-thumb:hover {background-color:#fff;} */ -------------------------------------------------------------------------------- /src/sa-view/home/swiper-list.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 109 | 110 | 177 | 178 | -------------------------------------------------------------------------------- /src/sa-view/case/query-p-case.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 89 | 90 | 180 | -------------------------------------------------------------------------------- /src/sa-resources/index/sa-index.css: -------------------------------------------------------------------------------- 1 | /* index样式(局部样式) */ 2 | *{margin: 0; padding: 0; } 3 | html,body{height: 100%; background-color: #EEE;} 4 | .app{height: 100%; font-size: 16px; font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;} 5 | /* .app *{transition: all 0.2s !important; } */ 6 | 7 | 8 | /* 左边 */ 9 | .nav-left{ 10 | width: 200px; 11 | height: 100%; 12 | position: fixed; 13 | left: 0px; 14 | z-index: 110; 15 | overflow: hidden; 16 | transition: all 0.2s; 17 | } 18 | .nav-left *{transition: all 0.2s !important; } 19 | /* 左上 logo部分 */ 20 | .nav-left-top{width: 100%; height: 85px; overflow: hidden; line-height: 85px; cursor: pointer; transform: none;} 21 | .nav-left .admin-logo{width: 40px; height: 40px; border-radius: 50%; vertical-align: middle; margin: 0.5em; margin-left: 1em; margin-right: 0.7em;} 22 | /* ==== .nav-left-top:hover{color: #FFF;} */ 23 | 24 | /* 左下 */ 25 | .nav-left-bottom{ 26 | width: 100%; 27 | height: calc(100% - 80px); 28 | overflow: hidden; 29 | } 30 | .nav-left-bottom i[class^=el-icon-]{font-size: 16px;} 31 | 32 | /* 1 2 配合,把滚动条隐藏 */ 33 | .nav-left .menu-box-1{width: 220px; height: 100%; overflow-y: auto;} 34 | .nav-left .menu-box-2{width: 201px;} 35 | 36 | .nav-left .el-submenu__title .menu-name{margin-left: 4px;} 37 | 38 | /* 尺寸调整, 二级和三级的左内边距向右扩大 */ 39 | .nav-left .el-submenu__title,.nav-left .el-menu-item{height: 45px !important; line-height: 45px !important;} 40 | .nav-left .el-submenu .el-menu-item,.nav-left .el-submenu .el-submenu .el-submenu__title{height: 40px !important; line-height: 40px !important; padding-left: 4em !important;} 41 | 42 | .nav-left .el-submenu .el-submenu .el-menu-item{padding-left: 5em !important;} 43 | 44 | /* 隐藏右边框 */ 45 | .nav-left .el-menu{border: 0px;} 46 | 47 | 48 | /* -------- 右边 --------- */ 49 | .nav-right{ 50 | width: calc(100% - 200px); 51 | height: 100%; 52 | position: fixed; 53 | left: 200px; 54 | transition: all 0.2s; 55 | } 56 | 57 | /* 第一行建筑物 */ 58 | .nav-right-1{position: relative; height: 49px; line-height: 49px; border-bottom: 1px #DDD solid; } 59 | .nav-right-1 *{transition: all 0.2s;} 60 | 61 | .nav-right-1 .tools-left{border: 0px #000 solid; float: left;} 62 | .nav-right-1 .tools-right{float: right;} 63 | .nav-right-1 .tool-fox{padding: 0 1em; margin-right: 4px; display: inline-block; cursor: pointer;} 64 | .nav-right-1 .tool-fox i{margin-right: 3px;} 65 | 66 | .nav-right-1 .user-info{position: relative; top: -2px;} 67 | .nav-right-1 .user-avatar{width: 30px; height: 30px; border-radius: 50%; vertical-align: middle;} 68 | .nav-right-1 .user-info .user-name{font-size: 0.9em;} 69 | 70 | 71 | /*800px之下*/ 72 | @media(max-width: 800px) { 73 | .nav-right-1 .tools-right{display: none;} 74 | } 75 | 76 | /* 第二行建筑物 */ 77 | .nav-right-2,.nav-right-bre{height: 35px; position: relative; z-index: 110; box-shadow: 0 2px 2px #CCC;} 78 | .nav-right-2>div{height: 100%; position: absolute;} 79 | 80 | .nav-right-bre{line-height: 35px; padding-left: 1em; background-color: #FFF; cursor: pointer; display: none;} 81 | .nav-right-bre .el-breadcrumb__item{line-height: 35px; font-size: 13px; } 82 | /* .nav-right-bre .el-breadcrumb__item:hover{cursor: pointer; text-decoration: underline;} */ 83 | 84 | .nav-right-2 .towards-left,.nav-right-2 .towards-right{width: 24px; text-align: center; background-color: #FFF; cursor: pointer; line-height: 35px;} 85 | .nav-right-2 .towards-left{border-right: 1px #eee solid;} 86 | .nav-right-2 .towards-right{border-left: 1px #eee solid; right: 0px;} 87 | .nav-right-2 .towards-left:hover i,.nav-right-2 .towards-right:hover i{font-size: 1.1em;font-weight: bold;} 88 | 89 | .nav-right-2 .towards-middle{width: 10000px; overflow: auto;/* calc(100% - 50px) */ left: 25px;background-color: #EEE;} 90 | .nav-right-2 .tab-title-box{display: inline-block; position: absolute; left: 0px; transition: all 0.2s;} 91 | .nav-right-2 .tab-title{font-size: 13px; cursor: pointer; float: left; transition: all 0.15s;white-space: nowrap;overflow: hidden;text-decoration: none; color: #333;} 92 | .nav-right-2 .tab-title-2{padding: 0px 10px; height: 35px; margin-right: 1px; background-color: #FFF; line-height: 35px; } 93 | .nav-right-2 .tab-title-2{transition: padding 0.1s, margin 0.1s;} 94 | .nav-right-2 .tab-title-2 *{transition: all 0.0s;} 95 | /* .tab-title .el-icon-caret-right{color: #EEE; font-size: 1.7em; position: relative; top: 4px;} */ 96 | .nav-right-2 .tab-title .el-icon-close{display: inline-block; border-radius: 50%; padding: 1px; color: #ccc; margin-left: -4px;} 97 | .nav-right-2 .tab-title .el-icon-close:hover{background-color: red; color: #FFF;} 98 | .nav-right-2 .tab-title span{display: inline-block; margin-left: 10px; margin-right: 10px;} 99 | .nav-right-2 .tab-title:hover span,.nav-right-2 .tab-native span{font-weight: bold;} 100 | 101 | /* 第三杠 */ 102 | .nav-right-3{width: 100%; height: calc(100% - 85px); position: relative; overflow: hidden;} 103 | .sa-swiper{width: 100%; height: 100%;} 104 | .view-fox{width: 100%; height: 100%; overflow: auto; background-color: #EEE;} 105 | .view-fox>*{width: 100%; height: 100%; overflow: auto;} 106 | 107 | /* .fas{transition: all 0s;} */ 108 | .at-form-fox, 109 | .at-form-fox *{transition: all 0s;} 110 | 111 | /* 右键菜单 样式 */ 112 | .right-box { 113 | position: fixed; 114 | z-index: 2147483647; 115 | transition: max-height 0.2s; 116 | outline:none; 117 | max-height: 0px; 118 | overflow: hidden; 119 | box-shadow: 3px 3px 3px #666; 120 | } 121 | .right-box-2{font-size: 0.8em; padding: 0.5em 0; border: 1px #aaa solid; border-radius: 1px; background-color: #FFF;} 122 | .right-box-2>div {line-height: 2.2em; padding-left: 0.7em; padding-right: 1.8em; cursor: pointer; white-space: nowrap;} 123 | .right-box-2>div:hover {background-color: #ddd;color: #2D8CF0;} 124 | .right-box-2>div i{ margin-right: 8px;} 125 | 126 | 127 | /* 菜单折叠时候样式调整 */ 128 | .fold .nav-left{width: 64px;} 129 | .fold .nav-left .admin-logo{width: 40px; height: 40px; margin-left: 0.8em;} 130 | .fold .nav-right{width: calc(100% - 64px); left: 64px; } 131 | /* .app .is_fold_right.nav-right{width: calc(100% - 64px); left: 64px; } */ 132 | 133 | /* .nav-right-3 包裹了太多iframe,不能让它参与动画,因为实在太TM卡了 */ 134 | .nav-right-3{ /* 修改为fixed布局 */ width: calc(100% - 200px); left: 200px; position: fixed;} 135 | .is_fold_right .nav-right-3{width: calc(100% - 64px); left: 64px;} 136 | 137 | 138 | 139 | /* 菜单折叠时 部分元素隐藏 */ 140 | .fold .nav-left .nav-title, 141 | .fold .nav-left .menu-name, 142 | .fold .nav-left .el-submenu__icon-arrow 143 | {display: none;} 144 | 145 | /* 最高层级 */ 146 | .z-index-max{z-index: 2147483647;} 147 | 148 | 149 | /* 遮罩样式 */ 150 | .shade-fox{position: absolute; z-index: 1000000; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); color: #FFF; top: 0px;} 151 | .shade-fox{display: flex; justify-content: center; align-items: center} 152 | 153 | 154 | /* 如果是隐藏tabbar模式 */ 155 | .hide-tabbar .nav-right-2{display: none;} 156 | .hide-tabbar .nav-right-bre{display: block;} 157 | .hide-tabbar .nav-right-1{border-bottom: 1px #eeeeee solid;} 158 | 159 | 160 | /* 去除掉便签的大边框 */ 161 | .layui-layer-input{outline: 0; box-shadow: none !important; padding: 0.5em !important; font-family: 'Times New Roman', Times, serif;} 162 | 163 | /* 164 | 所有污染样式: 165 | .nav-* .at-form-fox .shade-fox 166 | */ -------------------------------------------------------------------------------- /src/sa-resources/index/sa-theme.css: -------------------------------------------------------------------------------- 1 | /* index样式(主题样式) */ 2 | 3 | /* 样式调整为继承父级 */ 4 | .app .nav-left .el-submenu__title i, 5 | .app .nav-left .el-menu-item i, 6 | .app .nav-right-1 .el-dropdown, 7 | .app .nav-right-2 .tab-title:hover .el-icon-caret-right, 8 | .app .nav-right-2 .tab-title.tab-native .el-icon-caret-right { 9 | color: inherit; 10 | } 11 | 12 | .app .nav-left .el-menu, 13 | .app .nav-left .el-submenu, 14 | .app .nav-left .el-submenu__title, 15 | .app .nav-left .el-submenu .el-submenu .el-submenu__title, 16 | .app .nav-left .el-menu-item { 17 | color: inherit; 18 | background-color: inherit; 19 | } 20 | 21 | /* 切换主题时,停止动画 */ 22 | .app .themeToggling .nav-left,.themeToggling .nav-left *{transition: none !important;} 23 | .app .themeToggling .nav-left-top,.themeToggling .nav-left-top *{transition: all 0.2s !important;} 24 | 25 | 26 | /* ========================== 主题 - 0 默认样式 蓝色 ========================== */ 27 | .theme-0 {} 28 | 29 | /* 左边栏背景色,前景色 */ 30 | .theme-0 .nav-left { 31 | background-color: #222; 32 | color: #FFF; 33 | } 34 | 35 | /* 二级菜单背景色 */ 36 | .theme-0 .nav-left .el-submenu .el-menu-item, 37 | .theme-0 .nav-left .el-submenu .el-submenu .el-submenu__title{ 38 | background-color: #000; 39 | } 40 | 41 | /* 所有菜单悬浮样式*/ 42 | .theme-0 .nav-left .el-submenu__title:hover, 43 | .theme-0 .nav-left .el-submenu .el-submenu .el-submenu__title:hover, 44 | .theme-0 .nav-left .el-menu-item:hover{ 45 | background-color: #4E5465; 46 | } 47 | /* 所有菜单选中时 */ 48 | .theme-0 .nav-left .el-menu-item.is-active { 49 | background-color: #2D8CF0; 50 | color: #FFF; 51 | } 52 | 53 | /* 工具栏背景色颜色, 前景色 */ 54 | .theme-0 .nav-right-1 { 55 | color: #333; 56 | background-color: #FFF; 57 | } 58 | 59 | /* 工具栏悬浮颜色 */ 60 | .theme-0 .nav-right-1 .tool-fox:hover { 61 | background-color: #EEE; 62 | } 63 | 64 | /* 卡片栏悬浮颜色和选中颜色 */ 65 | .theme-0 .nav-right-2 .tab-title:hover, 66 | .theme-0 .nav-right-2 .tab-native.tab-title { 67 | color: #2D8CF0; 68 | } 69 | 70 | 71 | /* ========================== 主题-1 什么也不覆盖 即:全部取默认样式 ========================== */ 72 | .theme-1 {} 73 | 74 | /* ========================== 主题-2 绿色 ========================== */ 75 | .theme-2 {} 76 | 77 | /* 所有菜单选中时 */ 78 | .theme-2 .nav-left .el-menu-item.is-active { 79 | background-color: #009688; 80 | } 81 | 82 | /* 卡片栏悬浮颜色和选中颜色 */ 83 | .theme-2 .nav-right-2 .tab-title:hover, 84 | .theme-2 .nav-right-2 .tab-native.tab-title { 85 | color: #009688; 86 | } 87 | 88 | /* ========================== 主题-3 白色 清爽 ========================== */ 89 | .theme-3 {} 90 | 91 | /* 非标准附加样式 */ 92 | .theme-3 .nav-left-top{box-shadow: 0 2px 2px #CCC; width: 199px; border-right: 1px #DDD solid;} 93 | .theme-3 .nav-left-bottom{width: 199px; border-right: 1px #DDD solid;} 94 | .theme-3.fold .nav-left-bottom{width: 63px;} 95 | /* .theme-3 .nav-right-1,.theme-3 .nav-right-2{border-left: 1px #eee solid;} */ 96 | 97 | /* 左边栏背景色,前景色 */ 98 | .theme-3 .nav-left { 99 | background-color: #FFF; 100 | color: #333; 101 | } 102 | 103 | /* 二级菜单背景色 */ 104 | .theme-3 .nav-left .el-submenu .el-menu-item, 105 | .theme-3 .nav-left .el-submenu .el-submenu .el-submenu__title{ 106 | background-color: #fafafa; 107 | } 108 | 109 | /* 所有菜单悬浮样式时, 以及选中时 */ 110 | .theme-3 .nav-left .el-submenu__title:hover, 111 | .theme-3 .nav-left .el-submenu .el-submenu .el-submenu__title:hover, 112 | .theme-3 .nav-left .el-menu-item:hover, 113 | .theme-3 .nav-left .el-menu-item.is-active { 114 | background-color: #ECF5FF; 115 | color: #409EFF; 116 | } 117 | 118 | 119 | /* ========================== 主题-4 上黑 下白 ========================== */ 120 | .theme-4 {} 121 | 122 | /* 非标准附加样式 */ 123 | .theme-4 .nav-left-top{height: 49px; line-height: 49px; text-indent: 2em; background-color: #222; color: #EEE;} 124 | .theme-4 .nav-left-top .admin-logo{display: none;} 125 | 126 | .theme-4 .nav-left-bottom{width: 199px; border-right: 1px #DDD solid;} 127 | .theme-4.fold .nav-left-bottom{width: 63px;} 128 | .theme-4 .nav-left .el-submenu__title, 129 | .theme-4 .nav-left .el-menu-item{transition: background-color 0.1s;} 130 | 131 | /* 左边栏背景色,前景色 */ 132 | .theme-4 .nav-left { 133 | background-color: #EEE; 134 | color: #333; 135 | } 136 | 137 | /* 二级菜单背景色 */ 138 | .theme-4 .nav-left .el-submenu .el-menu-item, 139 | .theme-4 .nav-left .el-submenu .el-submenu .el-submenu__title{ 140 | background-color: #DDD; 141 | } 142 | 143 | /* 所有菜单悬浮样式时 */ 144 | .theme-4 .nav-left .el-submenu__title:hover, 145 | .theme-4 .nav-left .el-submenu .el-submenu .el-submenu__title:hover, 146 | .theme-4 .nav-left .el-menu-item:hover{ 147 | background-color: #ECF5FF; 148 | } 149 | /* 所有菜单选中时 */ 150 | .theme-4 .nav-left .el-menu-item.is-active { 151 | background-color: #009688; 152 | color: #FFF; 153 | } 154 | 155 | /* 卡片栏悬浮颜色和选中颜色 */ 156 | .theme-4 .nav-right-2 .tab-title:hover, 157 | .theme-4 .nav-right-2 .tab-native.tab-title { 158 | color: #009688; 159 | } 160 | 161 | /* 工具栏背景色颜色, 前景色 */ 162 | .theme-4 .nav-right-1 { 163 | color: #EEE; 164 | background-color: #222; 165 | } 166 | 167 | /* 工具栏悬浮颜色 */ 168 | .theme-4 .nav-right-1 .tool-fox:hover { 169 | background-color: #444; 170 | } 171 | 172 | 173 | 174 | /* ========================== 主题-5 灰色站看 (仿微信公众平台)0分 ========================== */ 175 | 176 | .theme-5 {} 177 | 178 | .theme-5 .nav-left-top{box-shadow: 0 2px 2px #CCC;} 179 | .theme-5 .nav-left-top, 180 | .theme-5 .nav-left-bottom{width: 199px; border-right: 1px #DDD solid;} 181 | .theme-5.fold .nav-left-bottom{width: 63px;} 182 | .theme-5 .nav-left .el-submenu__icon-arrow.el-icon-arrow-down{display: none;} 183 | .theme-5 .nav-left-bottom .menu-box-2{padding-bottom: 200px;} 184 | 185 | /* 左边栏背景色,前景色 */ 186 | .theme-5 .nav-left { 187 | background-color: #EEE; 188 | color: #000; 189 | } 190 | 191 | /* 二级菜单背景色 */ 192 | .theme-5 .nav-left .el-submenu .el-menu-item, 193 | .theme-5 .nav-left .el-submenu .el-submenu .el-submenu__title{ 194 | background-color: #EEE; 195 | } 196 | 197 | /* 所有菜单悬浮样式时, 以及选中时 */ 198 | .theme-5 .nav-left .el-submenu__title:hover, 199 | .theme-5 .nav-left .el-submenu .el-submenu .el-submenu__title:hover, 200 | .theme-5 .nav-left .el-menu-item:hover, 201 | .theme-5 .nav-left .el-menu-item.is-active:hover { 202 | background-color: #ddd; 203 | color: #1AAD5E; 204 | } 205 | .theme-5 .nav-left .el-menu-item.is-active {background-color: #eee; color: #1AAD5E;} 206 | 207 | /* 卡片栏悬浮颜色和选中颜色 */ 208 | .theme-5 .nav-right-2 .tab-title:hover, 209 | .theme-5 .nav-right-2 .tab-native.tab-title { 210 | color: #1AAD5E; 211 | } 212 | 213 | 214 | /* ========================== 主题-6 钛合金 (仿 iview admin pro) ========================== */ 215 | .theme-6 {} 216 | 217 | /* 所有菜单选中时 */ 218 | .theme-6 .nav-left .el-menu-item.is-active { 219 | background-color: #805322; 220 | color: #FFF; 221 | } 222 | 223 | /* 卡片栏悬浮颜色和选中颜色 */ 224 | .theme-6 .nav-right-2 .tab-title:hover, 225 | .theme-6 .nav-right-2 .tab-native.tab-title {color: #805322;} 226 | 227 | /* 工具栏背景色颜色, 前景色 */ 228 | .theme-6 .nav-right-1 {color: #EEE;background-color: #222;} 229 | 230 | /* 工具栏悬浮颜色 */ 231 | .theme-6 .nav-right-1 .tool-fox:hover {background-color: #444;} 232 | 233 | 234 | /* ========================== 主题-7 沉淀式黑蓝 ========================== */ 235 | .theme-7 {} 236 | /* 工具栏背景色颜色, 前景色 */ 237 | .theme-7 .nav-right-1 {color: #EEE;background-color: #222;} 238 | /* 工具栏悬浮颜色 */ 239 | .theme-7 .nav-right-1 .tool-fox:hover {background-color: #444;} 240 | 241 | /* ========================== 主题-8 简约式灰蓝 ========================== */ 242 | .theme-8 {} 243 | 244 | /* 菜单悬浮着色 */ 245 | .theme-8 .nav-left .el-menu-item.is-active { 246 | background-color: #4E5465; 247 | } -------------------------------------------------------------------------------- /src/sa-resources/com-view/sa-home.vue: -------------------------------------------------------------------------------- 1 | 21 | 27 | 28 | 180 | 181 | 268 | 269 | -------------------------------------------------------------------------------- /src/sa-view/user/data-list.js: -------------------------------------------------------------------------------- 1 | // 模拟数据 2 | module.exports = { 3 | code: 200, 4 | msg: 'ok', 5 | data: [{ 6 | "id": 12001, 7 | "username": "省长", 8 | "avatar": "https://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/default_head/2.png?x-oss-process=style/yasuo", 9 | "tell": '这人懒,啥也没有留下', 10 | "sex": "男", 11 | "photo_list": [ 12 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/02/02/1549112799839261454077.jpeg?x-oss-process=style/yasuo", 13 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/02/02/15491128027001242326540.jpeg?x-oss-process=style/yasuo", 14 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/02/02/1549112804446367923451.jpeg?x-oss-process=style/yasuo", 15 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/02/02/1549112806223866151639.jpeg?x-oss-process=style/yasuo", 16 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/02/02/15491128087691462190017.jpeg?x-oss-process=style/yasuo" 17 | ], 18 | "create_type": "账号注册", 19 | "create_time": "2019-02-09 20:22:00", 20 | "status": 1 21 | 22 | }, 23 | { 24 | "id": 12002, 25 | "username": "小言", 26 | "avatar": "https://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/default_head/3.png?x-oss-process=style/yasuo", 27 | "tell": '人懒,啥也没留', 28 | "sex": "女", 29 | "photo_list": [ 30 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486414241411760471758.jpg?x-oss-process=style/yasuo", 31 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548641424112923395306.jpg?x-oss-process=style/yasuo", 32 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548641424124307645243.jpg?x-oss-process=style/yasuo", 33 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486414241301079583057.jpg?x-oss-process=style/yasuo", 34 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486414241351214293219.jpg?x-oss-process=style/yasuo", 35 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548641424156586322884.jpg?x-oss-process=style/yasuo" 36 | ], 37 | "create_type": "账号注册", 38 | "create_time": "2019-02-11 13:22:41", 39 | "status": 1 40 | 41 | }, 42 | { 43 | "id": 12003, 44 | "username": "旧城人凉", 45 | "avatar": "https://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/default_head/4.png?x-oss-process=style/yasuo", 46 | "tell": '人懒,啥也没留', 47 | "sex": "女", 48 | "photo_list": [ 49 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/29/15487010332581464190738.jpg?x-oss-process=style/yasuo", 50 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/29/15487010332521119691172.jpg?x-oss-process=style/yasuo", 51 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/29/15487010332621622298950.jpg?x-oss-process=style/yasuo", 52 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/29/15487010332711954721691.jpg?x-oss-process=style/yasuo", 53 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/29/1548701033275197287565.jpg?x-oss-process=style/yasuo", 54 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/29/1548701033277157114772.jpg?x-oss-process=style/yasuo", 55 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/29/15487010332801673263633.jpg?x-oss-process=style/yasuo" 56 | ], 57 | "create_type": "账号注册", 58 | "create_time": "2019-02-11 8:22:56", 59 | "status": 1 60 | 61 | }, 62 | { 63 | "id": 12004, 64 | "username": "苦巷深桥", 65 | "avatar": "https://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/default_head/5.png?x-oss-process=style/yasuo", 66 | "tell": '人懒,啥也没留', 67 | "sex": "女", 68 | "photo_list": [ 69 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548641148401699190597.jpg?x-oss-process=style/yasuo", 70 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548641148403368860642.jpg?x-oss-process=style/yasuo", 71 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548641148447490522299.jpg?x-oss-process=style/yasuo", 72 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486411484491404458881.jpg?x-oss-process=style/yasuo", 73 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486411484521613746711.jpg?x-oss-process=style/yasuo", 74 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548641148452436812749.jpg?x-oss-process=style/yasuo" 75 | ], 76 | "create_type": "账号注册", 77 | "create_time": "2019-02-13 12:22:12", 78 | "status": 1 79 | 80 | }, 81 | { 82 | "id": 12005, 83 | "username": "三重门", 84 | "avatar": "https://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/default_head/6.png?x-oss-process=style/yasuo", 85 | "tell": '人懒,啥也没留', 86 | "sex": "女", 87 | "photo_list": [ 88 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548639217745410394722.jpg?x-oss-process=style/yasuo", 89 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486392177481726898108.jpg?x-oss-process=style/yasuo", 90 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486392177473938812.jpg?x-oss-process=style/yasuo", 91 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486392177582114379277.jpg?x-oss-process=style/yasuo", 92 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486392177631301168776.jpg?x-oss-process=style/yasuo" 93 | ], 94 | "create_type": "邮箱注册", 95 | "create_time": "2019-02-13 9:52:00", 96 | "status": 1 97 | 98 | }, 99 | { 100 | "id": 12006, 101 | "username": "红尘几度欢颜笑", 102 | "avatar": "https://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/default_head/7.png?x-oss-process=style/yasuo", 103 | "tell": '人懒,啥也没留', 104 | "sex": "女", 105 | "photo_list": [ 106 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486396582311677880214.jpg?x-oss-process=style/yasuo", 107 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486396582112088482959.jpg?x-oss-process=style/yasuo", 108 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486396582201751882045.jpg?x-oss-process=style/yasuo", 109 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548639658226225134395.jpg?x-oss-process=style/yasuo", 110 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486396582351948796029.jpg?x-oss-process=style/yasuo", 111 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548639658257423792653.jpg?x-oss-process=style/yasuo", 112 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548639658260413362409.jpg?x-oss-process=style/yasuo", 113 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486396582701933849474.jpg?x-oss-process=style/yasuo" 114 | ], 115 | "create_type": "邮箱注册", 116 | "create_time": "2019-03-13 20:12:54", 117 | "status": 1 118 | }, 119 | { 120 | "id": 12007, 121 | "username": "许你春秋", 122 | "avatar": "https://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/default_head/8.png?x-oss-process=style/yasuo", 123 | "tell": '人懒,啥也没留', 124 | "sex": "女", 125 | "photo_list": [ 126 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548638841214290095149.jpg?x-oss-process=style/yasuo", 127 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548638841227809145772.jpg?x-oss-process=style/yasuo", 128 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548638841240270928767.jpg?x-oss-process=style/yasuo", 129 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548638841244906005971.jpg?x-oss-process=style/yasuo", 130 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/154863884124713283459.jpg?x-oss-process=style/yasuo" 131 | ], 132 | "create_type": "邮箱注册", 133 | "create_time": "2019-03-16 20:22:00", 134 | "status": 1 135 | }, 136 | { 137 | "id": 12008, 138 | "username": "李灵涵", 139 | "avatar": "https://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/default_head/9.png?x-oss-process=style/yasuo", 140 | "tell": '人懒,啥也没留', 141 | "sex": "女", 142 | "photo_list": [ 143 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486374854182024752471.jpg?x-oss-process=style/yasuo", 144 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548637485458718538949.jpg?x-oss-process=style/yasuo", 145 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548637485455211731533.jpg?x-oss-process=style/yasuo", 146 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548637485460724239093.jpg?x-oss-process=style/yasuo", 147 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486374854672002390350.jpg?x-oss-process=style/yasuo", 148 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548637485473210846647.jpg?x-oss-process=style/yasuo", 149 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548637485471190844084.jpg?x-oss-process=style/yasuo" 150 | ], 151 | "create_type": "邮箱注册", 152 | "create_time": "2019-03-18 20:22:00", 153 | "status": 1 154 | }, 155 | { 156 | "id": 12009, 157 | "username": "樱桃", 158 | "avatar": "https://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/default_head/10.png?x-oss-process=style/yasuo", 159 | "tell": '人懒,啥也没留', 160 | "sex": "女", 161 | "photo_list": [ 162 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548637231837487154029.jpg?x-oss-process=style/yasuo", 163 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/15486372318271509628451.jpg?x-oss-process=style/yasuo", 164 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548637231848392706841.jpg?x-oss-process=style/yasuo", 165 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548637231857929283080.jpg?x-oss-process=style/yasuo", 166 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548637231873648635582.jpg?x-oss-process=style/yasuo" 167 | ], 168 | "create_type": "邮箱注册", 169 | "create_time": "2019-03-22 20:22:00", 170 | "status": 2 171 | }, 172 | { 173 | "id": 12010, 174 | "username": "碘盐", 175 | "avatar": "https://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/default_head/1.png?x-oss-process=style/yasuo", 176 | "tell": '人懒,啥也没留', 177 | "sex": "女", 178 | "photo_list": [ 179 | "http://color-test.oss-cn-qingdao.aliyuncs.com/dyc/img/2019/01/28/1548635034223276859883.jpg?x-oss-process=style/yasuo" 180 | ], 181 | "create_type": "手机号呢注册", 182 | "create_time": "2019-03-22 20:33:00", 183 | "status": 2 184 | }, 185 | ], 186 | dataCount: 3306 187 | } -------------------------------------------------------------------------------- /src/sa-resources/index/sa-index.vue: -------------------------------------------------------------------------------- 1 | 5 | 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /src/static/sa.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | import Vue from 'vue'; 4 | 5 | // =========================== sa对象封装一系列工具方法 =========================== 6 | var sa = { 7 | version: '2.1', 8 | update_time: '2020-2-13', 9 | info: '改了loading框的样式' 10 | }; 11 | 12 | // =========================== 当前环境配置 ======================================= 13 | (function(){ 14 | // 公司开发环境 15 | var cfg_dev = { 16 | api_url: 'http://localhost:8000', // 所有ajax请求接口父地址 17 | web_url: 'http://www.baidu.com' // 此项目前台地址 (此配置项非必须) 18 | } 19 | // 服务器测试环境 20 | var cfg_test = { 21 | api_url: 'http://www.baidu.com', 22 | web_url: 'http://www.baidu.com' 23 | } 24 | // 服务器测试环境 25 | var cfg_prod = { 26 | api_url: 'http://www.baidu.com', 27 | web_url: 'http://www.baidu.com' 28 | } 29 | sa.cfg = cfg_dev; // 最终环境 , 上线前请选择正确的环境 30 | })(); 31 | 32 | 33 | // =========================== ajax的封装 ======================================= 34 | (function(){ 35 | 36 | /** 37 | 对jquery的ajax再封装, 包括: 全局的baseURL, 跨域配置, 自动loading图标, 自动的异常函数处理等等... 38 | 如果你觉得此函数难以理解, 也可以不用, 可以用你比较熟悉的原生写法 39 | 如果你对axios更熟悉, 也可以引入axios, 步骤为: 40 | 先在cmd中安装: 41 | npm install axios -S 42 | 然后在main.js里引入 43 | import axios from 'axios' 44 | Vue.prototype.$axios = axios; 45 | axios文档: 46 | https://www.kancloud.cn/yunye/axios/234845 47 | 在此暂不赘述 48 | 这个ajax假设你的接口会返回以下格式的内容 49 | { 50 | "code": 200, 51 | "msg": "ok", 52 | "data": [] 53 | } 54 | 如果返回的不是这个格式, 你可能需要改动一下源码, 要么改动服务端适应此ajax, 要么改动这个ajax适应你的服务端 55 | * @param {Object} url 请求地址 56 | * @param {Object} data 请求参数 57 | * @param {Object} success200 当返回的code码==200时的回调函数 58 | * @param {Object} 其它配置,可配置项有: 59 | { 60 | msg: '', // 默认的提示文字 填null为不提示 61 | type: 'get', // 设定请求类型 默认post 62 | baseUrl: '', // ajax请求拼接的父路径 默认取 sa.cfg.apu_url 63 | sleep: 0, // ajax模拟的延时毫秒数, 默认0 64 | success500: fn, // code码等于500时的回调函数 (一般代表服务器错误) 65 | success403: fn, // code码等于403时的回调函数 (一般代表无权限) 66 | success401: fn, // code码等于401时的回调函数 (一般代表未登录) 67 | errorfn: fn, // ajax发生错误时的回调函数 (一般是ajax请求本身发生了错误) 68 | complete: fn, // ajax无论成功还是失败都会执行的回调函数 69 | } 70 | */ 71 | sa.ajax = function(url, data, success200, cfg){ 72 | 73 | // 如果是简写模式(省略了data参数) 74 | if(typeof data === 'function'){ 75 | cfg = success200; 76 | success200 = data; 77 | data = {}; 78 | } 79 | 80 | // 默认配置 81 | var defaultCfg = { 82 | msg: '努力加载中...', // 提示语 83 | baseUrl: (url.indexOf('http') === 0 ? '' : sa.cfg.api_url),// 父url,拼接在url前面 84 | sleep: 0, // 休眠n毫秒处理回调函数 85 | type: 'post', // 默认请求类型 86 | success200: success200, // code=200, 代表成功 87 | success500: function(res){ // code=500, 代表失败 88 | return layer.alert('失败:' + res.msg); 89 | }, 90 | success403: function(res){ // code=403, 代表权限不足 91 | return layer.alert("权限不足," + res.msg, {icon: 5}); 92 | }, 93 | success401: function(res){ // code=401, 代表未登录 94 | return layer.confirm("您当前暂未登录,是否立即登录?", {}, function(){ 95 | layer.closeAll(); 96 | return Vue.prototype.sa_admin.openLogin(); 97 | }); 98 | }, 99 | errorfn: function(error){ // ajax发生异常时的默认处理函数 100 | return layer.alert("异常:" + error.message); 101 | }, 102 | complete: function(xhr, ts) { // 成功失败都会执行 103 | 104 | } 105 | } 106 | 107 | // 将调用者的配置和默认配置合并 108 | cfg = sa.extendJson(cfg, defaultCfg); 109 | 110 | // 打印请求地址和参数, 以便调试 111 | console.log("请求地址:" + cfg.baseUrl + url); 112 | console.log("请求参数:" + JSON.stringify(data)); 113 | 114 | // 开始显示loading图标 115 | if(cfg.msg != null){ 116 | sa.loading(cfg.msg); 117 | } 118 | 119 | // 开始请求ajax 120 | return $.ajax({ 121 | url: cfg.baseUrl + url, 122 | type: cfg.type, 123 | data: data, 124 | dataType: 'json', 125 | xhrFields: { 126 | withCredentials: true // 携带跨域cookie 127 | }, 128 | crossDomain: true, 129 | beforeSend: function(xhr) { 130 | xhr.setRequestHeader('X-Requested-With','XMLHttpRequest'); 131 | }, 132 | success: function(res){ 133 | setTimeout(function() { 134 | sa.hideLoading(); 135 | // 如果相应的处理函数存在 136 | if(cfg['success' + res.code] != undefined) { 137 | return cfg['success' + res.code](res); 138 | } 139 | layer.alert('未知状态码:' + JSON.stringify(res)); 140 | }, cfg.sleep); 141 | }, 142 | error: function(xhr, type, errorThrown){ 143 | setTimeout(function() { 144 | sa.hideLoading(); 145 | return cfg.errorfn(xhr, type, errorThrown); 146 | }, cfg.sleep); 147 | }, 148 | complete: cfg.complete 149 | }); 150 | 151 | }; 152 | 153 | 154 | 155 | // 模拟一个ajax 156 | // 请注意: 本模板中所有ajax请求调用的均为此模拟函数 157 | sa.ajax2 = function(url, data, success200, cfg){ 158 | // 如果是简写模式(省略了data参数) 159 | if(typeof data === 'function'){ 160 | cfg = success200; 161 | success200 = data; 162 | data = {}; 163 | } 164 | // 几个默认配置 165 | cfg = cfg || {}; 166 | // 设定一个默认的提示文字 167 | if(cfg.msg == undefined || cfg.msg == null || cfg.msg == '') { 168 | cfg.msg = '正在努力加载...'; 169 | } 170 | // 默认延时函数 171 | if(cfg.sleep == undefined || cfg.sleep == null || cfg.sleep == '' || cfg.sleep == 0) { 172 | cfg.sleep = 600; 173 | } 174 | // 默认的模拟数据 175 | cfg.res = cfg.res || { 176 | code: 200, 177 | msg: 'ok', 178 | data: [] 179 | } 180 | // 开始loding 181 | sa.loading(cfg.msg); 182 | // 模拟ajax的延时 183 | setTimeout(function() { 184 | sa.hideLoading(); // 隐藏掉转圈圈 185 | success200(cfg.res); 186 | }, cfg.sleep) 187 | }; 188 | 189 | })(); 190 | 191 | 192 | // =========================== 封装弹窗相关函数 ======================================= 193 | (function() { 194 | 195 | // ============== 小提示 ===================== 196 | var me = sa; 197 | layer.ready(function(){}); 198 | 199 | // tips提示文字 200 | me.msg = function(msg, cfg) { 201 | msg = msg || '操作成功'; 202 | layer.msg(msg, cfg); 203 | }; 204 | 205 | // 操作成功的提示 206 | me.ok = function(msg) { 207 | msg = msg || '操作成功'; 208 | layer.msg(msg, {anim: 0, icon: 1 }); 209 | } 210 | me.ok2 = function(msg) { 211 | msg = msg || '操作成功'; 212 | layer.msg(msg, {anim: 0, icon: 6 }); 213 | } 214 | 215 | // 操作失败的提示 216 | me.error = function(msg) { 217 | msg = msg || '操作失败'; 218 | layer.msg(msg, {anim: 6, icon: 2 }); 219 | } 220 | me.error2 = function(msg) { 221 | msg = msg || '操作失败'; 222 | layer.msg(msg, {anim: 6, icon: 5 }); 223 | } 224 | 225 | // alert弹窗 [text=提示文字, cfg=配置(可省略), okFn=点击确定之后的回调函数] 226 | me.alert = function(text, okFn) { 227 | // 开始弹窗 228 | layer.alert(text, function(index) { 229 | layer.close(index); 230 | if(okFn) { 231 | okFn(); 232 | } 233 | }); 234 | }; 235 | 236 | // 询问框 [text=提示文字, okFn=点击确定之后的回调函数] 237 | me.confirm = function(text, okFn) { 238 | layer.confirm(text, {}, function(index) { 239 | layer.close(index); 240 | if(okFn) { 241 | okFn(); 242 | } 243 | }.bind(this)); 244 | }; 245 | 246 | // 输入框 [title=提示文字, okFn=点击确定后的回调函数, formType=输入框类型(0=文本,1=密码,2=多行文本域) 可省略, value=默认值 可省略 ] 247 | me.prompt = function(title, okFn, formType, value) { 248 | layer.prompt({ 249 | title: title, 250 | formType: formType, 251 | value: value 252 | }, function(pass, index){ 253 | layer.close(index); 254 | if(okFn) { 255 | okFn(pass); 256 | } 257 | }); 258 | } 259 | 260 | // 打开loading 261 | me.loading = function(msg) { 262 | layer.closeAll(); // 开始前先把所有弹窗关了 263 | return layer.msg(msg, {icon: 16, shade: 0.3, time: 1000 * 20, skin: 'ajax-layer-load' }); 264 | }; 265 | 266 | // 隐藏loading 267 | me.hideLoading = function() { 268 | layer.closeAll(); 269 | }; 270 | 271 | // ============== 一些常用弹窗 ===================== 272 | 273 | // 大窗显示一个图片 274 | // 参数: src=地址、w=宽度(默认80%)、h=高度(默认80%) 275 | me.showImage = function(src, w, h) { 276 | w = w || '80%'; 277 | h = h || '80%'; 278 | var content = '
' + 279 | '' + 280 | '
'; 281 | layer.open({ 282 | type: 1, 283 | title: false, 284 | shadeClose: true, 285 | closeBtn: 0, 286 | area: [w, h], //宽高 287 | content: content 288 | }); 289 | } 290 | 291 | // 预览一组图片 292 | // srcList=图片路径数组, index=打开立即显示哪张(可填下标, 也可填写src路径) 293 | me.showImageList = function(srcList, index) { 294 | // 如果填的是个string 295 | if(typeof srcList === 'string') { 296 | try{ 297 | srcList = JSON.parse(srcList); 298 | }catch(e){ 299 | srcList = []; 300 | } 301 | } 302 | // 如果填的是路径 303 | index = index || 0; 304 | if(typeof index === 'string') { 305 | index = srcList.indexOf(index); 306 | index = (index == -1 ? 0 : index); 307 | } 308 | 309 | // 开始展示 310 | var arr_list = []; 311 | srcList.forEach(function(item) { 312 | arr_list.push({ 313 | alt: '左右键切换', 314 | pid: 1, 315 | src: item, 316 | thumb: item 317 | }) 318 | }) 319 | layer.photos({ 320 | photos: { 321 | title: '', 322 | id: new Date().getTime(), 323 | start: index, 324 | data: arr_list 325 | } 326 | ,anim: 5 //0-6的选择,指定弹出图片动画类型,默认随机(请注意,3.0之前的版本用shift参数) 327 | }); 328 | } 329 | 330 | // 显示一个iframe 331 | // 参数: 标题,地址,宽,高 , 点击遮罩是否关闭, 默认false 332 | me.showIframe = function(title, url, w, h, shadeClose) { 333 | // 参数修正 334 | w = w || '95%'; 335 | h = h || '95%'; 336 | shadeClose = (shadeClose === undefined ? false : shadeClose); 337 | // 弹出面板 338 | var index = layer.open({ 339 | type: 2, 340 | title: title, // 标题 341 | shadeClose: shadeClose, // 是否点击遮罩关闭 342 | maxmin: true, // 显示最大化按钮 343 | shade: 0.8, // 遮罩透明度 344 | scrollbar: false, // 屏蔽掉外层的滚动条 345 | moveOut: true, // 是否可拖动到外面 346 | area: [w, h], // 大小 347 | content: url, // 传值 348 | // 解决拉伸或者最大化的时候,iframe高度不能自适应的问题 349 | resizing: function(layero) { 350 | solveLayerBug(index); 351 | } 352 | }); 353 | // 解决拉伸或者最大化的时候,iframe高度不能自适应的问题 354 | $('#layui-layer' + index + ' .layui-layer-max').click(function() { 355 | setTimeout(function() { 356 | solveLayerBug(index); 357 | }, 200) 358 | }) 359 | } 360 | me.showView = me.showIframe; 361 | 362 | // 显示一个iframe, 底部按钮方式 363 | // 参数: 标题,地址,点击确定按钮执行的代码(在子窗口执行),宽,高 364 | me.showIframe2 = function(title, url, evalStr, w, h) { 365 | // 参数修正 366 | w = w || '95%'; 367 | h = h || '95%'; 368 | // 弹出面板 369 | var index = layer.open({ 370 | type: 2, 371 | title: title, // 标题 372 | closeBtn: (title ? 1 : 0), // 是否显示关闭按钮 373 | btn: ['确定', '取消'], 374 | shadeClose: false, // 是否点击遮罩关闭 375 | maxmin: true, // 显示最大化按钮 376 | shade: 0.8, // 遮罩透明度 377 | scrollbar: false, // 屏蔽掉外层的滚动条 378 | moveOut: true, // 是否可拖动到外面 379 | area: [w, h], // 大小 380 | content: url, // 传值 381 | // 解决拉伸或者最大化的时候,iframe高度不能自适应的问题 382 | resizing: function(layero) { 383 | 384 | }, 385 | yes: function(index, layero) { 386 | var iframe = document.getElementById('layui-layer-iframe' + index); 387 | var iframeWindow = iframe.contentWindow; 388 | iframeWindow.eval(evalStr); 389 | } 390 | }); 391 | } 392 | 393 | 394 | // 当前iframe关闭自身 (在iframe中调用) 395 | me.closeCurrIframe = function() { 396 | try{ 397 | var index = parent.layer.getFrameIndex(window.name); //先得到当前iframe层的索引 398 | parent.layer.close(index); //再执行关闭 399 | }catch(e){ 400 | //TODO handle the exception 401 | } 402 | } 403 | me.closeCurrView = me.closeCurrIframe; 404 | 405 | 406 | //执行一个函数, 解决layer拉伸或者最大化的时候,iframe高度不能自适应的问题 407 | function solveLayerBug(index) { 408 | var selected = '#layui-layer' + index; 409 | var height = $(selected).height(); 410 | var title_height = $(selected).find('.layui-layer-title').height(); 411 | $(selected).find('iframe').css('height', (height - title_height) + 'px'); 412 | } 413 | 414 | 415 | })(); 416 | 417 | 418 | // =========================== 常用util函数封装 ======================================= 419 | (function () { 420 | 421 | // 超级对象 422 | var me = sa; 423 | 424 | // =========================== 常用util函数封装 ======================================= 425 | if(true) { 426 | 427 | // 从url中查询到指定参数值 428 | me.p = function(name, defaultValue){ 429 | var query = window.location.search.substring(1); 430 | var vars = query.split("&"); 431 | for (var i=0;i 0 ? cha : 0 - cha); 526 | if(cha < (86400 * 1000)) { 527 | return me.forDate2(d); 528 | } 529 | return me.forDate(d, way); 530 | } 531 | 532 | // 返回时间差, 此格式数组:[x, x, x, 天, 时, 分, 秒] 533 | me.getSJC = function (small_time, big_time) { 534 | var date1 = new Date(small_time); //开始时间 535 | var date2 = new Date(big_time); //结束时间 536 | var date3 = date2.getTime() - date1.getTime(); //时间差秒 537 | //计算出相差天数 538 | var days = Math.floor(date3 / (24 * 3600 * 1000)); 539 | 540 | //计算出小时数 541 | var leave1 = date3 % (24 * 3600 * 1000); //计算天数后剩余的毫秒数 542 | var hours = Math.floor(leave1 / (3600 * 1000)); 543 | 544 | //计算相差分钟数 545 | var leave2 = leave1 % (3600 * 1000); //计算小时数后剩余的毫秒数 546 | var minutes = Math.floor(leave2 / (60 * 1000)); 547 | 548 | //计算相差秒数 549 | var leave3 = leave2 % (60 * 1000); //计算分钟数后剩余的毫秒数 550 | var seconds = Math.round(leave3 / 1000); 551 | 552 | // 返回数组 553 | return [0, 0, 0, days, hours, minutes, seconds]; 554 | } 555 | 556 | // 将日期,加上指定天数 557 | me.dateAdd = function(d, n) { 558 | var s = new Date(d).getTime(); 559 | s += 86400000 * n; 560 | return new Date(s); 561 | } 562 | 563 | // 转化json,出错返回默认值 564 | me.JSONParse = function(obj, default_obj){ 565 | try{ 566 | return JSON.parse(obj) || default_obj; 567 | }catch(e){ 568 | return default_obj || {}; 569 | } 570 | } 571 | 572 | // 截取指定长度字符,默认50 573 | me.maxLength = function (str, length) { 574 | length = length || 50; 575 | if(!str){ 576 | return ""; 577 | } 578 | return (str.length > length) ? str.substr(0, length) + ' ...' : str; 579 | }, 580 | 581 | // 过滤掉标签 582 | me.text = function(str){ 583 | if(!str){ 584 | return ""; 585 | } 586 | return str.replace(/<[^>]+>/g,""); 587 | } 588 | 589 | // 为指定集合的每一项元素添加上is_update属性 590 | me.listAU = function(list){ 591 | list.forEach(function(ts){ 592 | ts.is_update = false; 593 | }) 594 | return list; 595 | } 596 | 597 | // 获得一段文字中所有图片的路径 598 | me.getSrcList = function(str){ 599 | try{ 600 | var imgReg = /|\/>)/gi; //匹配图片(g表示匹配所有结果i表示区分大小写) 601 | var srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i; //匹配src属性 602 | var arr = str.match(imgReg); // 图片数组 603 | var srcList = []; 604 | for (var i = 0; i < arr.length; i++) { 605 | var src = arr[i].match(srcReg); 606 | srcList.push(src[1]); 607 | } 608 | return srcList; 609 | } catch (e){ 610 | return []; 611 | } 612 | } 613 | 614 | // 无精度损失的乘法 615 | me.accMul = function(arg1, arg2) { 616 | var m = 0, 617 | s1 = arg1.toString(), 618 | s2 = arg2.toString(), 619 | t; 620 | 621 | t = s1.split("."); 622 | // 判断有没有小数位,避免出错 623 | if (t[1]) { 624 | m += t[1].length 625 | } 626 | 627 | t = s2.split("."); 628 | if (t[1]) { 629 | m += t[1].length 630 | } 631 | 632 | return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m) 633 | } 634 | 635 | // 正则验证是否为手机号 636 | me.isPhone = function(str) { 637 | str = str + ''; 638 | if((/^1[34578]\d{9}$/.test(str))){ 639 | return true; 640 | } 641 | return false; 642 | } 643 | 644 | // 产生随机字符串 645 | me.randomString = function(len) { 646 |   len = len || 32; 647 |   var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/ 648 |   var maxPos = $chars.length; 649 |   var str = ''; 650 |   for (i = 0; i < len; i++) { 651 |     str += $chars.charAt(Math.floor(Math.random() * maxPos)); 652 |   } 653 |   return str; 654 | } 655 | 656 | 657 | // == if 结束 658 | } 659 | 660 | // =========================== 数组操作 ======================================= 661 | if (true) { 662 | 663 | 664 | // 从数组里获取数据,根据指定数据 665 | me.arrayGet = function(arr, prop, value){ 666 | for (var i = 0; i < arr.length; i++) { 667 | if(arr[i][prop] == value){ 668 | return arr[i]; 669 | } 670 | } 671 | return null; 672 | } 673 | 674 | // 从数组删除指定记录 675 | me.arrayDelete = function(arr, item){ 676 | var index = arr.indexOf(item); 677 | if (index > -1) { 678 | arr.splice(index, 1); 679 | } 680 | } 681 | 682 | // 从数组删除指定id的记录 683 | me.arrayDeleteById = function(arr, id){ 684 | var item = me.arrayGet(arr, 'id', id); 685 | me.arrayDelete(arr, item); 686 | } 687 | 688 | // 将数组B添加到数组A的开头 689 | me.unshiftArray = function(arrA, arrB){ 690 | if(arrB){ 691 | arrB.reverse().forEach(function(ts){ 692 | arrA.unshift(ts); 693 | }) 694 | } 695 | return arrA; 696 | } 697 | 698 | // 将数组B添加到数组A的末尾 699 | me.pushArray = function(arrA, arrB){ 700 | if(arrB){ 701 | arrB.forEach(function(ts){ 702 | arrA.push(ts); 703 | }) 704 | } 705 | return arrA; 706 | } 707 | 708 | // == if 结束 709 | } 710 | 711 | // =========================== 浏览器相关 ======================================= 712 | if (true) { 713 | 714 | // set cookie 值 715 | me.setCookie = function setCookie(cname, cvalue, exdays) { 716 | exdays = exdays || 30; 717 | var d = new Date(); 718 | d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); 719 | var expires = "expires=" + d.toGMTString(); 720 | document.cookie = cname + "=" + escape(cvalue) + "; " + expires + "; path=/"; 721 | } 722 | 723 | // get cookie 值 724 | me.getCookie = function(objName){ 725 | var arrStr = document.cookie.split("; "); 726 | for (var i = 0; i < arrStr.length; i++) { 727 | var temp = arrStr[i].split("="); 728 | if (temp[0] == objName){ 729 | return unescape(temp[1]) 730 | }; 731 | } 732 | return ""; 733 | } 734 | 735 | // 复制指定文本 736 | me.copyText = function(str){ 737 | var oInput = document.createElement('input'); 738 | oInput.value = str; 739 | document.body.appendChild(oInput); 740 | oInput.select(); // 选择对象 741 | document.execCommand("Copy"); // 执行浏览器复制命令 742 | oInput.className = 'oInput'; 743 | oInput.style.display='none'; 744 | } 745 | 746 | // jquery序列化表单增强版: 排除空值 747 | me.serializeNotNull = function(selected){ 748 | var serStr = $(selected).serialize(); 749 | return serStr.split("&").filter(function(str){return !str.endsWith("=")}).join("&"); 750 | } 751 | 752 | // 将cookie序列化为k=v形式 753 | me.strCookie = function(){ 754 | return document.cookie.replace(/; /g,"&"); 755 | } 756 | 757 | // 回到顶部 758 | me.goTop = function() { 759 | function smoothscroll(){ 760 | var currentScroll = document.documentElement.scrollTop || document.body.scrollTop; 761 | if (currentScroll > 0) { 762 | window.requestAnimationFrame(smoothscroll); 763 | window.scrollTo (0,currentScroll - (currentScroll/5)); 764 | } 765 | }; 766 | smoothscroll(); 767 | } 768 | 769 | 770 | 771 | // == if 结束 772 | } 773 | 774 | // =========================== javascript对象操作 ======================================= 775 | if (true) { 776 | // 去除json对象中的空值 777 | me.removeNull = function(obj){ 778 | var newObj = {}; 779 | if(obj != undefined && obj != null) { 780 | for(var key in obj) { 781 | if(obj[key] === undefined || obj[key] === null || obj[key] == '') { 782 | // 783 | } else { 784 | newObj[key] = obj[key]; 785 | } 786 | } 787 | } 788 | return newObj; 789 | } 790 | 791 | // JSON 浅拷贝, 返回拷贝后的obj 792 | me.copyJSON = function(obj){ 793 | if(obj === null || obj === undefined) { 794 | return obj; 795 | }; 796 | var new_obj = {}; 797 | for(var key in obj) { 798 | new_obj[key] = obj [key]; 799 | } 800 | return new_obj; 801 | } 802 | 803 | // json合并, 将 defaulet配置项 转移到 user配置项里 并返回 user配置项 804 | me.extendJson = function(userOption, defaultOption) { 805 | if(!userOption) { 806 | return defaultOption; 807 | }; 808 | for(var key in defaultOption) { 809 | if(userOption[key] === undefined) { 810 | userOption[key] = defaultOption[key]; 811 | } else if(userOption[key] == null){ 812 | 813 | } else if(typeof userOption[key] == "object") { 814 | me.extendJson(userOption[key], defaultOption[key]); //深度匹配 815 | } 816 | } 817 | return userOption; 818 | } 819 | 820 | // == if 结束 821 | } 822 | 823 | // =========================== 本地集合存储 ======================================= 824 | if (true) { 825 | 826 | // 获取指定key的list 827 | me.keyListGet = function(key){ 828 | try{ 829 | var str = localStorage.getItem('LIST_' + key); 830 | if(str == undefined || str == null || str =='' || str == 'undefined' || typeof(JSON.parse(str)) == 'string'){ 831 | //alert('key' + str); 832 | str = '[]'; 833 | } 834 | return JSON.parse(str); 835 | }catch(e){ 836 | return []; 837 | } 838 | }, 839 | 840 | me.keyListSet = function(key, list){ 841 | localStorage.setItem('LIST_' + key, JSON.stringify(list)); 842 | }, 843 | 844 | me.keyListHas = function(key, item){ 845 | var arr2 = me.keyListGet(key); 846 | return arr2.indexOf(item) != -1; 847 | }, 848 | 849 | me.keyListAdd = function(key, item){ 850 | var arr = me.keyListGet(key); 851 | arr.push(item); 852 | me.keyListSet(key,arr); 853 | }, 854 | 855 | me.keyListRemove = function(key, item){ 856 | var arr = me.keyListGet(key); 857 | var index = arr.indexOf(item); 858 | if (index > -1) { 859 | arr.splice(index, 1); 860 | } 861 | me.keyListSet(key,arr); 862 | } 863 | 864 | // == if 结束 865 | } 866 | 867 | })(); 868 | 869 | 870 | // =========================== $sys 有关当前系统的方法 一般不能复制到别的项目中用 ======================================= 871 | (function(){ 872 | 873 | // 超级对象 874 | var me = {}; 875 | sa.$sys = me; 876 | 877 | // 定义key 878 | var pcode_key = 'permission_code'; 879 | 880 | // 写入当前会话的权限码集合 881 | sa.setAuth = function(codeList) { 882 | sa.keyListSet(pcode_key, codeList); 883 | } 884 | 885 | // 清除当前会话的权限码集合 886 | sa.clearAuth = function() { 887 | sa.keyListSet(pcode_key, []); 888 | } 889 | 890 | // 检查当前会话是否拥有一个权限码, 返回true和false 891 | sa.isAuth = function(pcode) { 892 | return sa.keyListHas(pcode_key, pcode); 893 | } 894 | 895 | // 检查当前会话是否拥有一个权限码, 如果没有, 则跳转到无权限页面 896 | // 注意: 非二级目录页面请注意调整路径问题 897 | sa.checkAuth = function(pcode) { 898 | var is_have = sa.keyListHas(pcode_key, pcode); 899 | if(is_have == false) { 900 | Vue.prototype.sa_admin.open403(); 901 | throw '暂无权限: ' + pcode; 902 | } 903 | } 904 | 905 | 906 | 907 | 908 | 909 | })(); 910 | 911 | 912 | // =========================== $page 跳页面相关 避免一次变动,到处乱改 ======================================= 913 | (function(){ 914 | 915 | // 超级对象 916 | var me={}; 917 | sa.$page = me; 918 | 919 | 920 | 921 | })(); 922 | 923 | 924 | // 如果当前是Vue环境, 则挂在到Vue示例 925 | if(window.Vue && !Vue.prototype.sa) { 926 | Vue.prototype.sa = sa; 927 | } 928 | 929 | // 对外开放, 在模块化时解开此注释 930 | export default sa; 931 | 932 | -------------------------------------------------------------------------------- /src/sa-resources/index/sa-index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import saMenuList from './../sa-menu-list.js'; // 菜单集合 3 | import sa_admin_code_util from './admin-util.js'; // admin代码util 4 | import { swiper, swiperSlide } from 'vue-awesome-swiper'; // 组件 swiper 5 | import saLogin from './../com-view/sa-login.vue'; // 组件 login 6 | import sa403 from './../com-view/sa-403.vue'; // 组件 403 7 | import sa404 from './../com-view/sa-404.vue'; // 组件 404 8 | import sa500 from './../com-view/sa-500.vue'; // 组件 500 9 | // sa_admin对象 10 | export default { 11 | components: { 12 | swiper, 13 | swiperSlide, 14 | saLogin, 15 | sa403, 16 | sa404, 17 | sa500 18 | }, 19 | data: function() { 20 | // 首页 21 | var homeTab = { 22 | id: 'home', // 唯一标识 23 | name: '首页', 24 | view: ()=> import('./../com-view/sa-home.vue'), 25 | hide_close: true, // 隐藏关闭键 26 | is_rend: true, 27 | } 28 | 29 | return { 30 | version: 'v1.0.3', // 当前版本 31 | update_time: '2020-03-05', // 更新日期 32 | title: '',//'SA-后台模板', // 页面标题 33 | logo_url: '', // logo地址 34 | icon_url: '', // icon地址 35 | github_url: 'https://github.com/click33/sa-vue-admin', // github地址 36 | default_active: '0', // 默认的高亮菜单id 37 | default_openeds: [], // 默认的打开数组 38 | unique_opened: true, // 是否保持只打开一个 39 | menuList: [], // 菜单集合 40 | homeTab: homeTab, // 主页tab 41 | nativeTab: homeTab, // 当前正显示的Tab 42 | tabList: [homeTab], // 页面集合 43 | atTitle: '', // 添加窗口时: 标题 44 | atUrl: '', // 添加窗口时: 地址 45 | scrollX: 0 ,// 滚动条位置 46 | // rightMaxHeight: 0, // 右键菜单的最高高度 (控制是否展开) 47 | // rightZB: {x: 0, y: 0} ,// 右键菜单坐标 48 | rightTab: null, // 右键正在操作的tab 49 | rightShow: false, // 右键菜单是否正在显示 50 | rightStyle: { // 卡片标题右键菜单的样式 51 | left: '0px', // 坐标x 52 | top: '0px', // 坐标y 53 | maxHeight: '0px' // 右键菜单的最高高度 (控制是否展开) 54 | }, 55 | is_drag: false, // 当前是否正在拖拽 56 | dragTab: null, // 当前正在拖拽的tab 57 | is_fold: false, // 菜单是否折叠 58 | is_fold_right: false, // 右边是否折叠(将右边盒子折叠与菜单折叠分开,这样可以减少动画的卡顿现象) 59 | is_full_screen: false ,// 是否全屏 60 | user: null ,// user信息 61 | now_time: '加载中...' ,// 当前时间 62 | switchV: localStorage.getItem('switchV') || 'fade', // 切换效果 63 | switchList: [ // 切换动画数组 64 | {name: '淡入', value: 'fade'}, 65 | {name: '滑动', value: 'slide'}, 66 | {name: '方块', value: 'cube'}, 67 | {name: '3D流', value: 'coverflow'}, 68 | {name: '3D翻转', value: 'flip'} 69 | ], 70 | themeV: localStorage.getItem('themeV') || '1', // 当前主题值 71 | themeList: [ // 主题数组 72 | {name: '蓝色', value: '1', show_all: false}, 73 | {name: '绿色', value: '2', show_all: false}, 74 | {name: '白色', value: '3', show_all: false}, 75 | {name: '灰色', value: '4', show_all: false}, 76 | {name: '灰色-展开', value: '5', show_all: true}, 77 | {name: 'pro钛合金', value: '6', show_all: false}, 78 | {name: '沉淀式黑蓝', value: '7', show_all: false}, 79 | {name: '简约式灰蓝', value: '8', show_all: false}, 80 | ], 81 | themeToggling: false, // 主题是否正在切换 82 | dropList: [], // 头像处下拉列表菜单 83 | mySwiper: null, // swiper相关 84 | is_show_tabbar: true, // 是否显示tab栏 85 | breMenuList: [homeTab], // 面包屑导航栏的tab数据 86 | is_reme_open: true, // 是否记住上一次最后打开的窗口 87 | // swiper配置 88 | swiperOption:{ 89 | autoplay: false, // 自动切换 90 | effect: localStorage.getItem('switchV') || 'fade', // 切换效果 91 | }, 92 | dialogTab: null ,// dialogTab信息 93 | } 94 | }, 95 | watch: { 96 | // 监听全屏动作 97 | is_full_screen: function(newValue) { 98 | if(newValue) { 99 | sa_admin_code_util.fullScreen(); 100 | } else { 101 | sa_admin_code_util.fullScreenNormal(); 102 | } 103 | }, 104 | // 监听title改变时, 页面title也跟着切换 105 | title: function(newValue) { 106 | document.querySelector('title').innerHTML = newValue; 107 | }, 108 | // 监听 icon_url 网页图标 109 | icon_url: function(newValue) { 110 | var icon_url = newValue; 111 | var icon_target = document.querySelector('.admin-icon'); 112 | if(icon_target) { 113 | icon_target.setAttribute('href', icon_url); 114 | } 115 | } 116 | }, 117 | computed: { 118 | }, 119 | methods: { 120 | // ------------------- 初始化相关 -------------------- 121 | // 初始化模板, 此方法只可调用一次 122 | init: function(option) { 123 | // 如果不填写 124 | option = option || {}; 125 | 126 | // 一些属性 127 | this.is_show_tabbar = (option.is_show_tabbar === undefined ? this.is_show_tabbar : option.is_show_tabbar); // 是否显示tabbar栏 128 | this.is_reme_open = (option.is_reme_open === undefined ? this.is_reme_open : option.is_reme_open); // 是否记住上一次最后打开的窗口 129 | 130 | // 初始化swiper对象 131 | this.initSwiper(); 132 | 133 | // 打印版本等信息 134 | if(option.printVesion !== false) { 135 | this.printVesion(); 136 | } 137 | 138 | // 开始一些初始化动作 139 | this.showTabByHash(); // 打开上次最后的一个窗口 140 | window.onresize(); // 手动触发一下窗口变动监听 141 | }, 142 | // ------------------- 对外预留接口 -------------------- 143 | // show_list 为指定显示的id集合(注意是id的集合),为空时代表显示所有 144 | initMenu: function(show_list) { 145 | this.setMenuList(saMenuList, show_list); 146 | }, 147 | // 写入菜单,可以是一个一维数组(指定好parent_id),也可以是一个已经渲染好的tree数组 148 | // show_list 为指定显示的id集合(注意是id的集合),为空时代表显示所有 149 | setMenuList: function(menu_list, show_list) { 150 | // 转化为string 便于比较 151 | if(show_list) { 152 | for (var i = 0; i < show_list.length; i++) { 153 | show_list[i] = show_list[i] + ''; 154 | } 155 | } 156 | menu_list = this.arrayToTree(menu_list); 157 | menu_list = this.refMenuList(menu_list, show_list); 158 | this.menuList = menu_list; 159 | }, 160 | // 将一维平面数组转换为 Tree 菜单 (根据其指定的parent_id添加到其父菜单的childList) 161 | arrayToTree: function(menu_list) { 162 | for (var i = 0; i < menu_list.length; i++) { 163 | var menu = menu_list[i]; 164 | // 添加到其指定的父菜单的childList 165 | if(menu.parent_id) { 166 | var parent_menu = this.getMenuById(menu_list, menu.parent_id); 167 | if(parent_menu) { 168 | parent_menu.childList = parent_menu.childList || []; 169 | parent_menu.childList.push(menu); 170 | menu_list.splice(i, 1); // 从一维中删除 171 | i--; 172 | } 173 | } 174 | } 175 | return menu_list; 176 | }, 177 | // 将 menu_list 处理一下 178 | refMenuList: function(menu_list, show_list, parent_id) { 179 | for (var i = 0; i < menu_list.length; i++) { 180 | var menu = menu_list[i]; 181 | menu.is_show = (menu.is_show === false ? false : true); 182 | menu.parent_id = menu.parent_id || parent_id || 0; 183 | // 隐藏的给去掉 184 | // if(menu.is_show === false) { 185 | // sa_admin_code_util.arrayDelete(menu_list, menu); 186 | // i--; 187 | // continue; 188 | // } 189 | // 如果指定了 show_list,并且 menu.id 不在 show_list 里,划掉 190 | if(show_list && show_list.indexOf(menu.id) == -1) { 191 | // sa_admin_code_util.arrayDelete(menu_list, menu); 192 | // i--; 193 | // continue; 194 | menu.is_show = false; 195 | } 196 | // 有子项的递归处理 197 | if(menu.childList && menu.childList.length > 0){ 198 | this.refMenuList(menu.childList, show_list, menu.id); // 递归处理 199 | } 200 | } 201 | return menu_list; 202 | }, 203 | 204 | // ------------------- 对外预留 end -------------------- 205 | // 打开所有菜单的折叠 206 | show_all_menu: function() { 207 | var default_openeds = []; 208 | for (var i = 0; i < this.menuList.length; i++) { 209 | default_openeds.push(this.menuList[i].id); 210 | if(this.menuList[i].childList) { 211 | for (var j = 0; j < this.menuList[i].childList.length; j++) { 212 | default_openeds.push(this.menuList[i].childList[j].id); 213 | } 214 | } 215 | } 216 | this.default_openeds = default_openeds; 217 | }, 218 | // 切换主题 219 | toggleTheme: function(command) { 220 | // 调整动画,避免卡顿 221 | this.themeToggling = true; 222 | setTimeout(function() { 223 | this.themeToggling = false; 224 | }.bind(this), 1000); 225 | 226 | // 开始切换 227 | this.themeV = command + ""; 228 | localStorage.setItem('themeV', command); 229 | for (var i = 0; i < this.themeList.length; i++) { 230 | if(this.themeList[i].value + '' == command + '') { 231 | if(this.themeList[i].show_all) { 232 | this.show_all_menu(); 233 | this.unique_opened = false; 234 | } else { 235 | this.default_openeds = []; 236 | this.unique_opened = true; 237 | } 238 | // 给个提示 239 | if(window.dsadasdwdwawd) { 240 | this.$message('切换成功,' + this.themeList[i].name); 241 | } 242 | window.dsadasdwdwawd = true; 243 | } 244 | } 245 | }, 246 | // 切换翻页方式 247 | toggleSwitch: function(command) { 248 | this.switchV = command + ""; 249 | localStorage.setItem('switchV', command); 250 | 251 | this.$confirm('此动画效果将在您刷新页面之后生效,是否立即刷新?', { 252 | confirmButtonText: '确定', 253 | cancelButtonText: '取消', 254 | type: 'warning' 255 | }).then(function() { 256 | location.reload(); 257 | }).catch(function() { 258 | 259 | }); 260 | 261 | }, 262 | // 处理userinfo的下拉点击 263 | handleCommand: function(command) { 264 | this.dropList.forEach(function(drop) { 265 | if(drop.name == command) { 266 | drop.click(); 267 | } 268 | }) 269 | }, 270 | // 退出登录 271 | login_out: function() { 272 | console.log('退出登录'); 273 | }, 274 | // 折叠菜单 275 | fold_start: function() { 276 | this.is_fold_right = true; 277 | this.updateSlideSize(100); // swipre重新计算大小 278 | // 如果打开的 iframe 在五个以内 浏览器压力很小 就立刻展开菜单, 279 | // 如果打开 iframe 超过5个,浏览器就比较有压力, 此时会卡顿短暂时间,此时延时折叠菜单,让动画显得没那么卡 280 | if(this.tabList.length <= 5) { 281 | this.is_fold = true; 282 | } else { 283 | setTimeout(function() { 284 | this.is_fold = true; 285 | }.bind(this), 100); 286 | } 287 | }, 288 | // 展开菜单 289 | fold_end: function() { 290 | this.is_fold = false; 291 | // 延时200ms执行,让它没那么卡 292 | setTimeout(function() { 293 | this.is_fold_right = false; 294 | this.updateSlideSize(); // swipre重新计算大小 295 | }.bind(this), 200); 296 | }, 297 | // 刷新一下面包屑导航栏 298 | f5_breMenuList: function() { 299 | // 如果非单窗口模式, 则不刷新了, 节省cpu 300 | if(this.is_show_tabbar) { 301 | return; 302 | } 303 | // 304 | var menu = this.getMenuById(this.menuList, this.nativeTab.id); 305 | if(menu == null) { // 自定义tab这里会取不到值, 就造个假tab就好了 306 | this.breMenuList = [{name: this.nativeTab.name}]; 307 | } else { 308 | var breMenuList = [menu]; 309 | for (var i = 0; i < breMenuList.length; i+=0) { 310 | var parent_id = breMenuList[0].parent_id; 311 | if(parent_id == 0 || parent_id == undefined) { 312 | break; 313 | } 314 | let menu = this.getMenuById(this.menuList, parent_id); 315 | breMenuList.unshift(menu); 316 | } 317 | this.breMenuList = breMenuList; 318 | } 319 | }, 320 | // ------------------- tab 右键菜单相关 -------------------- 321 | // 显示右键菜单 322 | right_showMenu: function(tab, event) { 323 | this.rightTab = tab; // 绑定操作tab 324 | var e = event || window.event; 325 | this.rightStyle.left = (e.clientX + 1) + 'px'; // 设置给坐标x 326 | this.rightStyle.top = e.clientY + 'px'; // 设置给坐标y 327 | this.rightShow = true; // 显示右键菜单 328 | this.$nextTick(function() { 329 | var foxHeight = document.querySelector('.right-box-2').offsetHeight; // 应该展开多高 330 | this.rightStyle.maxHeight = foxHeight + 'px'; // 展开 331 | document.querySelector('.right-box').focus(); // 获得焦点,以被捕获失去焦点事件 332 | }); 333 | }, 334 | // 关闭右键菜单 - 立即关闭 335 | right_closeMenu: function() { 336 | this.rightStyle.maxHeight = '0px'; 337 | this.rightShow = false; 338 | }, 339 | // 关闭右键菜单 - 带动画折叠关闭 (失去焦点和点击取消时调用, 为什么不全部调用这个? 因为其它时候调用这个都太卡了) 340 | right_closeMenu2: function() { 341 | this.rightStyle.maxHeight = '0px'; 342 | // this.rightShow = false; 343 | }, 344 | // 右键 刷新 345 | right_f5: function() { 346 | this.showTab(this.rightTab); // 先转到 347 | this.rightTab.is_rend = false; 348 | this.$forceUpdate(); // 必须强制更新一下 349 | this.$nextTick(function() { 350 | this.rightTab.is_rend = true; 351 | }) 352 | 353 | }, 354 | // 右键 复制 355 | right_copy: function() { 356 | var tab = {id: new Date().getTime(), name: this.rightTab.name, view: this.rightTab.view}; 357 | this.showTab(tab); 358 | }, 359 | // 右键 悬浮 360 | right_xf: function() { 361 | if(this.rightTab.id == this.homeTab.id + ''){ 362 | this.$message({ 363 | message: '这个不能全屏哦,换个卡片试试吧', 364 | type: 'warning' 365 | }); 366 | return; 367 | } 368 | // 先关闭 369 | this.closeTab(this.rightTab, function() { 370 | this.f5_breMenuList(); 371 | }.bind(this)); 372 | // 再打开 373 | this.dialogTabShow(this.rightTab.name, this.rightTab.view, this.rightTab.params, 2); 374 | }, 375 | // 右键 - 关闭 376 | right_close: function() { 377 | if(this.rightTab.id == this.homeTab.id + ''){ 378 | this.$message({ 379 | message: '这个不能关闭哦', 380 | type: 'warning' 381 | }); 382 | return; // 隐藏右菜单 383 | } 384 | this.closeTab(this.rightTab, function() { 385 | this.f5_breMenuList(); 386 | }.bind(this)); 387 | }, 388 | // 右键 - 关闭其它 389 | right_close_other: function() { 390 | // 先滑到最左边 391 | this.scrollX = 0; 392 | // 递归删除 393 | var i = 0; 394 | var deleteFn = function() { 395 | // 如果已经遍历全部 396 | if(i >= this.tabList.length) { 397 | return; 398 | } 399 | // 如果在白名单,i++继续遍历, 如果不是,递归删除 400 | var tab = this.tabList[i]; 401 | if(tab.id + '' == this.homeTab.id + '' || tab.id + '' == this.rightTab.id){ 402 | i++; 403 | deleteFn(); 404 | } else { 405 | this.closeTab(tab, function() { 406 | deleteFn(); 407 | }); 408 | } 409 | }.bind(this); 410 | deleteFn(); 411 | }, 412 | // 右键 - 关闭所有 413 | right_close_all: function() { 414 | // 先滑到最左边 415 | this.scrollX = 0; 416 | // 递归删除 417 | var i = 0; 418 | var deleteFn = function() { 419 | // 如果已经遍历全部 420 | if(i >= this.tabList.length) { 421 | this.f5_breMenuList(); 422 | return; 423 | } 424 | // 如果在白名单,i++继续遍历, 如果不是,递归删除 425 | var tab = this.tabList[i]; 426 | if(tab.id + '' == this.homeTab.id + ''){ 427 | i++; 428 | deleteFn(); 429 | } else { 430 | this.closeTab(tab, function() { 431 | deleteFn(); 432 | }); 433 | } 434 | }.bind(this); 435 | deleteFn(); 436 | }, 437 | // 右键 - 全屏打开 438 | right_full: function() { 439 | // 先关闭 440 | if(this.rightTab.id != this.homeTab.id + ''){ // 主页就不关了 441 | this.closeTab(this.rightTab, function() { 442 | this.f5_breMenuList(); 443 | }.bind(this)); 444 | } 445 | // 再打开 446 | this.dialogTabShow(this.rightTab.name, this.rightTab.view, this.rightTab.params, 1); 447 | }, 448 | // 右键 - 新窗口打开 449 | right_window_open: function() { 450 | // 先关闭 451 | if(this.rightTab.id != this.homeTab.id + ''){ // 主页就不关了 452 | this.closeTab(this.rightTab, function() { 453 | this.f5_breMenuList(); 454 | }.bind(this)); 455 | } 456 | open(this.rightTab.url); 457 | }, 458 | // 获取指定tab所代表iframe的url地址 (同域下可获取最新地址, 跨域时只能获取初始化时的地址) 459 | getTabUrl: function(tab) { 460 | this.sss(tab); 461 | // var cs = '#iframe-' + tab.id; 462 | // var iframe = document.querySelector(cs); 463 | // try{ 464 | // return iframe.contentWindow.location.href; 465 | // }catch(e){ 466 | // return iframe.getAttribute('src'); 467 | // } 468 | }, 469 | 470 | // ------------------- menu 相关 -------------------- 471 | // 点击子菜单时的回调, 472 | // 参数: 点击菜单index标识(不是下标), 所有已经打开的菜单 index 473 | selectMenu: function(index) { 474 | var menu = this.getMenuById(this.menuList, index); 475 | if(menu != null) { 476 | // 如果是click函数 477 | if(menu.click) { 478 | return menu.click(this, this.sa); 479 | } 480 | // 如果是外部链接 481 | if(menu.is_blank) { 482 | return open(menu.url); 483 | } 484 | this.showTab(menu); 485 | } 486 | }, 487 | // js显示某个菜单 488 | showMenuById: function(id) { 489 | var menu = this.getMenuById(this.menuList, id); 490 | if(menu) { 491 | this.showTab(menu); 492 | } 493 | }, 494 | // 返回指定 index 的menu 495 | getMenuById: function(menuList, id) { 496 | for (var i = 0; i < menuList.length; i++) { 497 | var menu = menuList[i]; 498 | if(menu.id + '' == id + '') { 499 | return menu; 500 | } 501 | // 如果是二级或多级 502 | if(menu.childList) { 503 | var menu2 = this.getMenuById(menu.childList, id); 504 | if(menu2 != null) { 505 | return menu2; 506 | } 507 | } 508 | } 509 | return null; 510 | }, 511 | // 显示homeTab 512 | showHome: function() { 513 | this.showTab(this.homeTab); 514 | }, 515 | 516 | // ------------------- tab title 相关 -------------------- 517 | // 在一个tab上, 初始化 view 518 | initTabView: function(tab) { 519 | // 如果已经初始化过了 520 | if(tab.is_init_view) { 521 | return; 522 | } 523 | // 开始初始化 524 | tab.params = tab.params || {}; // 给参数一个默认值 525 | tab.is_rend = true; // 是否显示, 利用此来强制刷新子组件 526 | 527 | // 如果是一个.html页面 528 | if(tab.url) { 529 | let template = '
'; 530 | tab.view = {template: template}; 531 | return tab.is_init_view = true; 532 | } 533 | 534 | // 如果是 535 | 536 | return tab.is_init_view = true; 537 | }, 538 | // 关闭tab - 无动画版本 539 | closeTab_not_an: function(tab) { 540 | this.sss(tab); 541 | // 根据没有地方调用这个方法, 所以先不写了嘻嘻 542 | }, 543 | // 关闭页面 544 | closeTab: function(tab, callFn) { 545 | 546 | // 执行关闭动画 547 | var div = document.querySelector('#tab-' + tab.id); 548 | div.style.width = div.offsetWidth + 'px'; 549 | setTimeout(function() { 550 | div.style.width = '0px'; 551 | }, 0); 552 | 553 | // 等待动画结束 554 | setTimeout(function() { 555 | 556 | // 如果tab为当前正在显示的tab, 则先不让它显示 557 | if(tab == this.nativeTab) { 558 | var index = this.tabList.indexOf(tab); 559 | var preTab = this.tabList[index - 1]; 560 | this.showTab(preTab); 561 | } 562 | // 开始从集合中移除 563 | sa_admin_code_util.arrayDelete(this.tabList, tab); 564 | // this.deleteSlide(tab.id); 565 | // 如果有回调 566 | if(callFn) { 567 | this.$nextTick(function() { 568 | callFn(); 569 | }) 570 | } 571 | }.bind(this), 150); 572 | }, 573 | // js关闭某个tab, 根据id 574 | closeTabById: function(id, callFn) { 575 | var tab = this.getTabById(id); 576 | if(tab) { 577 | this.closeTab(tab, callFn); 578 | } 579 | }, 580 | // 添加一个Tab {id,name,url} 581 | addTab: function(tab) { 582 | tab.is_have_en = this.is_have_en(tab.name); // 有英文字母的不能加字体加粗动画, 因为会影响tab选项卡的width尺寸, 造成动画混乱 583 | // tab.view = () => import('@/sa-view/HelloWorld.vue'); 584 | this.initTabView(tab); 585 | this.tabList.push(tab); 586 | // this.addSlide(tab); 587 | }, 588 | // 显示某个页面 (如果不存在, 则先添加) 589 | showTab: function(tab) { 590 | // 如果是当前正在显示的tab , 则直接 返回 591 | if(tab == this.nativeTab && tab != this.homeTab) { 592 | return; 593 | } 594 | // 如果没有先添加 595 | if(this.getTabById(tab.id) == null){ 596 | this.addTab(tab); 597 | } 598 | // 然后显示 599 | this.$nextTick(function() { 600 | 601 | this.gotoSlide(tab.id); 602 | // 如果是无tabbar模式 603 | if(!this.is_show_tabbar) { 604 | this.rightTab = tab; 605 | this.right_close_other(); 606 | this.f5_breMenuList(); 607 | } 608 | this.f5HashByNativeTab(); 609 | }) 610 | 611 | this.nativeTab = tab; 612 | this.default_active = tab.id + ''; // 左边自动关联, 如果左边没有,则无效果 613 | 614 | // 归位一下 615 | this.$nextTick(function() { 616 | this.scrollToAuto(); 617 | }.bind(this)) 618 | }, 619 | // 显示一个选项卡, 根据 id , 不存在则不显示 620 | showTabById: function(id) { 621 | var tab = this.getTabById(id); 622 | if(tab) { 623 | this.showTab(tab); 624 | } 625 | }, 626 | // 获取 Tab 根据 id 627 | getTabById: function(id) { 628 | for (var i = 0; i < this.tabList.length; i++) { 629 | if(this.tabList[i].id + '' == id + '') { 630 | return this.tabList[i]; 631 | } 632 | } 633 | return null; 634 | }, 635 | // 双击tab栏空白处, 打开弹窗添加窗口 636 | atOpen: function() { 637 | window.r_layer_12345 = this.layer.open({ 638 | type: 1, 639 | shade: 0.5, 640 | title: "添加新窗口", //不显示标题 641 | content: $('.at-form-dom'), //捕获的元素 642 | cancel: function(){ 643 | 644 | } 645 | }); 646 | }, 647 | // 根据表单添加新窗口 648 | atOk: function() { 649 | if(this.atTitle == '' || this.atUrl == '') { 650 | return; 651 | } 652 | // 创建tab 653 | var tab = {id: new Date().getTime(), name: this.atTitle, url: this.atUrl}; 654 | // 打开tab 655 | this.showTab(tab); 656 | // 关闭并清空 657 | this.layer.close(window.r_layer_12345); 658 | this.atTitle = ''; 659 | this.atUrl = ''; 660 | }, 661 | // 返回一个字符串中是否有英文字母 662 | is_have_en: function(str) { 663 | var reg = /[a-z]/i; 664 | return reg.test(str);//true,说明有英文字母 665 | }, 666 | // ------------------- tab左右滑动 -------------------- 667 | // 视角向左滑动一段距离 668 | scrollToLeft: function() { 669 | var width = document.querySelector('.nav-right-2').clientWidth; // 视角宽度 670 | this.scrollX += width / 2; // 视角向左滑动一段距离 671 | // 越界检查 672 | setTimeout(function() { 673 | if(this.scrollX > 0){ 674 | this.scrollX = 0; 675 | } 676 | }.bind(this), 200); 677 | }, 678 | // 视角向右滑动一段距离 679 | scrollToRight: function() { 680 | var width = document.querySelector('.nav-right-2').clientWidth; // 视角宽度 681 | var tabListWidth = document.querySelector('.tab-title-box').clientWidth; // title总盒子宽度 682 | var rightLimit = (0 - tabListWidth + width / 2); // 右滑的极限 683 | this.scrollX -= width / 2; // 视角向右滑动一段距离 684 | // 越界检查 685 | setTimeout(function() { 686 | if(this.scrollX < rightLimit){ 687 | this.scrollX = rightLimit; 688 | } 689 | // 同时防止左边越界 690 | if(this.scrollX > 0){ 691 | this.scrollX = 0; 692 | } 693 | }.bind(this), 200); 694 | }, 695 | // 自动归位 696 | scrollToAuto: function() { 697 | // console.log('自动归位========='); 698 | try{ 699 | // 最后一个不用归位了 700 | // if(this.nativeTab == this.tabList[this.tabList.length - 1]){ 701 | // return; 702 | // } 703 | var width = document.querySelector('.nav-right-2').clientWidth; // 视角宽度 704 | var left = document.querySelector('.tab-native').lastChild.offsetLeft; // 当前native-tilte下一个距离左边的距离 705 | // console.log(width, left, this.scrollX); 706 | // 如果在视图右边越界 707 | if(left + this.scrollX > (width - 100)){ 708 | return this.scrollToRight(); 709 | } 710 | // 如果在视图左边越界 711 | if(left + this.scrollX < 0) { 712 | return this.scrollToLeft(); 713 | } 714 | }catch(e){ 715 | // throw e; 716 | } 717 | }, 718 | // ------------------- tab拖拽相关 -------------------- 719 | // 在 某个tab上被松开 --> 重新排序 ( 函数未完成 ) 720 | tab_ondrop: function(tab) { 721 | this.sss(tab); 722 | /** 723 | * 写到一半发现,这看似简单的一个功能, 实则复杂无比 724 | * 首先tab卡交换顺序, 算法就已经比较复杂, 同时为了不显着生硬,还要加上: 725 | * tab被悬浮提示, 726 | * tab卡交换动画, 727 | * 避开在v-for下操作dom带来的一系列坑 728 | * 其次, 下面的iframe, 也要按照相应顺序进行交换, 729 | * 而swiper本身没有提供这样的api, 又要用js操作dom 730 | * 交换dom顺序, 同时又要保持iframe不被销毁(因为用户肯定不想看到交换一下tab 页面竟然初始化了) 731 | * 同时一些列操作后, 又要保证不和swiper本身产生冲突... 732 | * 脑供血不足了...... 让我缓缓... 733 | * 求前端大神提交pr, 跪谢!!! 734 | */ 735 | 736 | // // 如果没有交换 737 | // if(tab == this.dragTab) { 738 | // return; 739 | // } 740 | // // 删除这个 741 | // var dragIndex = this.tabList.indexOf(this.dragTab); 742 | // this.tabList.splice(dragIndex, 1); 743 | // // 重新添加到这个位置 744 | // this.$nextTick(function() { 745 | // var tabIndex = this.tabList.indexOf(tab); 746 | // this.tabList.splice(tabIndex + 1, 0, this.dragTab); 747 | // }) 748 | }, 749 | // ------------------- 锚链接路由相关 -------------------- 750 | // 根据锚链接, 打开窗口 751 | showTabByHash: function() { 752 | 753 | // 如果非记住模式 754 | if(this.is_reme_open == false) { 755 | return; 756 | } 757 | // 获取锚链接中的id 758 | var hash = location.hash; 759 | var id = hash.replace('#', ''); 760 | 761 | if(id == '') { 762 | this.showHome(); 763 | return; 764 | } 765 | // 如果已经存在与tabbar中 766 | var tab = this.getTabById(id); 767 | if(tab != null) { 768 | return this.showTab(tab); 769 | } 770 | // 否则从菜单中打开 771 | if(id == this.homeTab.id){ 772 | this.showHome(); 773 | } else { 774 | this.showMenuById(id); 775 | } 776 | // 此时, 仍有一种tab打不开, 那就是自定义tab然后还已经关闭的, 777 | // 预设 解决方案: 在localStor里存储所有打开过的tab, 778 | // 以后如果有强需求这个功能时, 再实现 779 | }, 780 | // 根据当前tab刷新一下锚链接 781 | f5HashByNativeTab: function() { 782 | // 如果非记住模式 783 | if(this.is_reme_open == false) { 784 | return; 785 | } 786 | location.hash = this.nativeTab.id; 787 | }, 788 | // ------------------- swiper相关 -------------------- 789 | // 初始化swiper 790 | initSwiper: function(switchV) { 791 | this.sss(switchV); 792 | this.mySwiper = this.$refs.mySwiper.swiper; 793 | // this.mySwiper = new Swiper('.swiper-container', { 794 | // autoplay: false, // 可选选项,自动滑动 795 | // effect: switchV 796 | // }) 797 | }, 798 | // 获取指定slide的索引, 根据id 799 | getSlideIndexById: function(id) { 800 | var tab = this.getTabById(id); 801 | return this.tabList.indexOf(tab); 802 | }, 803 | // 删除slide, 根据指定iframe的id 804 | deleteSlide: function(id) { 805 | this.sss(id); 806 | // var slideIndex = this.getSlideIndexById(id); 807 | // if(slideIndex != -1) { 808 | // this.mySwiper.removeSlide(slideIndex); 809 | // } 810 | }, 811 | // 切换到指定的slide, 根据id 812 | gotoSlide: function(id) { 813 | var slideIndex = this.getSlideIndexById(id); 814 | if(slideIndex != -1) { 815 | this.mySwiper.slideTo(slideIndex, 300); 816 | } 817 | }, 818 | // 更正slide大小 ms = 延时毫秒数 819 | updateSlideSize: function(ms) { 820 | ms = ms || 1; 821 | setTimeout(function() { 822 | this.mySwiper.update(); // swipre重新计算大小 823 | }.bind(this), ms); 824 | }, 825 | // ------------------- 登录 与鉴权 -------------------- 826 | // 打开登录页面 827 | openLogin: function() { 828 | this.$refs['sa-login'].isShow = true; 829 | }, 830 | // 关闭login页面 831 | closeLogin: function() { 832 | this.$refs['sa-login'].isShow = false; 833 | }, 834 | // 打开403页面 835 | open403: function() { 836 | this.$refs['sa403'].isShow = true; 837 | }, 838 | // 打开404页面 839 | open404: function() { 840 | this.$refs['sa404'].isShow = true; 841 | }, 842 | // 打开500页面 843 | open500: function() { 844 | this.$refs['sa500'].isShow = true; 845 | }, 846 | 847 | // ------------------- 杂七杂八 -------------------- 848 | // 什么也不做, 帮助一下不太规范的语法逃避检查 849 | sss: function() { 850 | 851 | }, 852 | // 获取指定视图的对象,用来跨视图通信 853 | getView: function(id) { 854 | var com = this.$refs['view-' + id]; 855 | if(com) { 856 | return com[0]; 857 | } 858 | }, 859 | // 悬浮或者, 全屏显示tab 860 | // title=标题, view=要显示的组件, params=参数 ,way= 方式(1=全屏,2=悬浮打开) 861 | dialogTabShow: function(title, view, params, way) { 862 | this.dialogTab = { // dialog信息 863 | isShow: true, // 是否显示 864 | isShow2: true, // 是否显示视图(先关闭视图,在关闭dialog,解决dialog关闭时视图重复刷新的莫名bug) 865 | title: title || '信息', // 标题 866 | view: view || {template: '
加载中
'}, // 显示的组件 867 | params: params || {}, // 参数 868 | way: way || 1, 869 | beforeClose: function(done) { 870 | this.dialogTab.isShow2 = false; 871 | done(); 872 | }.bind(this) 873 | }; 874 | }, 875 | closeDialog: function() { 876 | this.dialogTab.isShow = false; 877 | }, 878 | // 打开便签 879 | openNote: function() { 880 | var w = (document.body.clientWidth * 0.4) + 'px'; 881 | var h = (document.body.clientHeight * 0.6) + 'px'; 882 | var default_content = '一个简单的小便签, 关闭浏览器后再次打开仍然可以加载到上一次的记录, 你可以用它来记录一些临时资料'; 883 | var value = localStorage.getItem('sa_admin_note') || default_content; 884 | var index = this.layer.prompt({ 885 | title: '一个小便签', 886 | value: value, 887 | formType: 2, 888 | area: [w, h], 889 | btn: ['保存'], 890 | maxlength: 99999999, 891 | }, function(pass, index){ 892 | this.layer.close(index) 893 | }.bind(this)); 894 | var se = '#layui-layer' + index + ' .layui-layer-input'; 895 | var d = document.querySelector(se); 896 | d.oninput = function() { 897 | localStorage.setItem('sa_admin_note', this.value); 898 | } 899 | }, 900 | // 弹窗 901 | msg: function(msg) { 902 | this.layer.msg(msg) 903 | }, 904 | // 打印版本 905 | printVesion: function() { 906 | console.log('欢迎使用sa-admin(vue单页版),当前版本:' + this.version + ",更新于:" + this.update_time + ",GitHub地址:" + this.github_url); 907 | console.log('如在使用中发现任何bug或者疑问,请加入QQ群交流:782974737,点击加入:' + 'https://jq.qq.com/?_wv=1027&k=5DHN5Ib'); 908 | }, 909 | // 初始化window相关配置 910 | initWindow: function() { 911 | 912 | // sa-admin对象 913 | var sa_admin = this; 914 | 915 | // 挂在到原型 916 | Vue.prototype.sa_admin = sa_admin; 917 | 918 | // 监听窗口大小变动 919 | window.onresize = function() { 920 | if(document.body.clientWidth < 800) { 921 | sa_admin.fold_start(); 922 | } else { 923 | sa_admin.fold_end(); 924 | } 925 | } 926 | 927 | // 监听锚链接变动 928 | window.onhashchange = function() { 929 | // console.log('锚链接变动了'); 930 | this.showTabByHash(); 931 | }.bind(this) 932 | 933 | // 一直更新时间 934 | if(window.abcdefghijklmn) { 935 | clearInterval(window.abcdefghijklmn); 936 | } 937 | window.abcdefghijklmn = setInterval(function() { 938 | var da = new Date(); 939 | var Y = da.getFullYear(); //年 940 | var M = da.getMonth() + 1; //月 941 | var D = da.getDate(); //日 942 | var h = da.getHours(); //小时 943 | var sx = "凌晨"; 944 | if (h >= 6) { 945 | sx = "上午" 946 | } 947 | if (h >= 12) { 948 | sx = "下午"; 949 | if (h >= 18) { 950 | sx = "晚上"; 951 | } 952 | h -= 12; 953 | } 954 | var m = da.getMinutes(); //分 955 | var s = da.getSeconds(); //秒 956 | var z = ['日', '一', '二', '三', '四', '五', '六'][da.getDay()] ; //周几 957 | // z = z == 0 ? '日' : z; 958 | var zong = ""; 959 | 960 | zong += Y + "-" + M + "-" + D + " " + sx + " " + h + ":" + m + ":" + s + " 周" + z; 961 | sa_admin.now_time = zong; 962 | }, 1000); 963 | 964 | } 965 | }, 966 | mounted: function(){ 967 | this.initWindow(); 968 | this.SaAdminInIt(this.sa_admin, this.sa); 969 | }, 970 | 971 | }; 972 | 973 | 974 | 975 | 976 | --------------------------------------------------------------------------------