Browse Source

完成基于vue的前端页面基本功能

szjcomo 3 years ago
parent
commit
2cebbc1674

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 18 - 2
README.md

@@ -1,3 +1,19 @@
-# vue-como-web
+# vue-como-admin
 
-基于vue的前端系统
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).

+ 5 - 0
babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}

+ 35 - 0
package.json

@@ -0,0 +1,35 @@
+{
+  "name": "vue-kefu-admin",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build"
+  },
+  "dependencies": {
+    "core-js": "^3.6.5",
+    "szjcomo-utils": "^1.0.1",
+    "vue-como-image": "^1.0.2",
+    "vue-como-layer": "^1.0.6",
+    "vue-img-cutter": "^2.1.9"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "~4.5.0",
+    "@vue/cli-plugin-router": "~4.5.0",
+    "@vue/cli-plugin-vuex": "~4.5.0",
+    "@vue/cli-service": "~4.5.0",
+    "axios": "^0.20.0",
+    "echarts": "^4.9.0",
+    "element-ui": "^2.13.2",
+    "qs": "^6.9.4",
+    "vue": "^2.6.11",
+    "vue-router": "^3.2.0",
+    "vue-template-compiler": "^2.6.11",
+    "vuex": "^3.5.1"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ]
+}

BIN
public/favicon.ico


+ 37 - 0
public/index.html

@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+    <head>
+        <meta charset="utf-8">
+        <meta name=renderer content=webkit>
+        <meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1">
+        <meta name=referrer content=never>
+        <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1">
+        <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+        <title><%= htmlWebpackPlugin.options.title %></title>
+        <link rel="stylesheet" href="<%= BASE_URL %>init_loading.css">
+        <link rel="stylesheet" type="text/css" href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/theme-chalk/index.css">
+        <!-- 使用CDN的CSS文件 -->
+        <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
+            <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
+        <% } %>
+
+        <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
+            <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
+        <% } %>
+    </head>
+    <body>
+        <noscript>
+            <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+        </noscript>
+        <div id="app">
+            <div class="app-szjcomo-loading">
+                <div class="app-init-loading" style="margin: 0 auto;">
+                    <div class="app-init-loading-item app-init-loading-a"></div>
+                    <div class="app-init-loading-item app-init-loading-b"></div>
+                    <div class="app-init-loading-item app-init-loading-c"></div>
+                </div>
+                <p>玩命加载中</p>
+            </div>
+        </div>
+    </body>
+</html>

+ 38 - 0
public/init_loading.css

@@ -0,0 +1,38 @@
+/*防止首屏加载白版处理*/
+.app-szjcomo-loading{
+    position: fixed;width: 100%;
+    font-family: "黑体";
+    overflow: hidden;left: 0;top: 35%;
+}
+.app-init-loading {
+    text-align: center;
+    width: 100px;margin: 0 auto;
+}
+.app-szjcomo-loading p{
+    font-size: 14px;margin-top: 30px;
+    text-align: center;color:#999;
+}
+.app-init-loading-item {
+    display: inline-block;
+    height: 15px;
+    width: 15px;
+    border-radius: 50%;
+    background: #c00;;
+    animation: item 1s ease-in-out infinite;
+}
+.app-init-loading-b {animation-delay: .2s;}
+.app-init-loading-c {animation-delay: .4s;}
+@keyframes item {
+    0% {
+        transform: translateY(0);
+        opacity: 1;
+    }
+    50% {
+        transform: translateY(20px);
+        opacity: 0;
+    }
+    100% {
+        transform: translateY(0);
+        opacity: 1;
+    }
+}

+ 15 - 0
src/App.vue

@@ -0,0 +1,15 @@
+<template>
+    <div id="app"><router-view/></div>
+</template>
+<style>
+@import './assets/css/reset.css';
+@import './assets/css/szjcomo.css';
+@import './assets/css/iconfont.css';
+body,html{height:100%;overflow: hidden;}
+#app {
+    font-family: 黑体,Avenir, Helvetica, Arial, sans-serif;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+    color: #2c3e50;font-size:15px;height:100%;
+}
+</style>

+ 19 - 0
src/assets/css/iconfont.css

@@ -0,0 +1,19 @@
+
+@font-face {
+  font-family: 'iconfont';  /* project id 2103455 */
+  src: url('//at.alicdn.com/t/font_2103455_2qk36q348t1.eot');
+  src: url('//at.alicdn.com/t/font_2103455_2qk36q348t1.eot?#iefix') format('embedded-opentype'),
+  url('//at.alicdn.com/t/font_2103455_2qk36q348t1.woff2') format('woff2'),
+  url('//at.alicdn.com/t/font_2103455_2qk36q348t1.woff') format('woff'),
+  url('//at.alicdn.com/t/font_2103455_2qk36q348t1.ttf') format('truetype'),
+  url('//at.alicdn.com/t/font_2103455_2qk36q348t1.svg#iconfont') format('svg');
+}
+
+.iconfont {
+	font-family: "iconfont" !important;
+	font-size: 15px;
+	font-style: normal;
+	-webkit-font-smoothing: antialiased;
+	-moz-osx-font-smoothing: grayscale;
+	position:relative;
+}

+ 36 - 0
src/assets/css/reset.css

@@ -0,0 +1,36 @@
+/* 初始化CSS */
+html, body, ul, li, ol, dl, dd, dt, p, h1, h2, h3, h4, h5, h6, form, fieldset, legend, img { margin:0; padding:0; font-size: 15px;}
+fieldset, img { border:none; }
+img{display: inline-block;vertical-align: middle;max-width: 100%;max-height: 100%;}
+address, caption, cite, code, dfn, th, var { font-style:normal; font-weight:normal; }
+ul, ol { list-style:none; }
+input { padding-top:0; padding-bottom:0; font-family: "SimSun","黑体";}
+input::-moz-focus-inner { border:none; padding:0; }
+select, input { vertical-align:middle; }
+select, input, textarea { font-size:12px; margin:0; }
+input[type="text"], input[type="password"], textarea { outline-style:none; -webkit-appearance:none; }
+textarea { resize:none; }
+table { border-collapse:collapse; }
+body { color:#333; padding:0px 0; font:14px/20px "SimSun","黑体","Arial Narrow",HELVETICA; background:#fff;/* overflow-y:scroll;*/ }
+.clearfix:after { content:"."; display:block; height:0; visibility:hidden; clear:both; }
+.clearfix { zoom:1; }
+.clearit { clear:both; height:0; font-size:0; overflow:hidden; }
+a { color:#666; text-decoration:none; }
+a:visited { color:#666; }
+a:hover, a:active, a:focus { color:#ff8400; text-decoration:underline; }
+body blockquote {
+    display: block;
+    border-left: 8px solid #d0e5f2;
+    padding: 5px 10px;
+    margin: 10px 0;
+    line-height: 1.4;
+    font-size: 100%;
+    background-color: #f1f1f1;
+}
+body code{
+    display: inline-block;
+    background-color: #f1f1f1;
+    border-radius: 3px;
+    padding:3px 5px;
+    margin: 0 3px;
+}

+ 128 - 0
src/assets/css/szjcomo.css

@@ -0,0 +1,128 @@
+/*公共样式*/
+.el-button--info,body .el-button--danger{
+    background:#1E9FFF;
+    color:#fff;
+    border-color:#1E9FFF;
+    font-family: '黑体';
+}
+.el-button--success,body .el-button--danger{
+    background:#39B449;
+    color:#fff;
+    border-color:#39B449;
+    font-family: '黑体';
+}
+.el-button--warning,body .el-button--danger{
+    background:#FFB800;
+    color:#fff;
+    border-color:#FFB800;
+    font-family: '黑体';
+}
+.el-button--danger,body .el-button--danger{
+    background:#FF5722;
+    color:#fff;
+    border-color:#FF5722;
+    font-family: '黑体';
+}
+.el-button--danger.is-disabled, 
+.el-button--danger.is-disabled:active, 
+.el-button--danger.is-disabled:focus, 
+.el-button--danger.is-disabled:hover{
+    color: #fff;
+    background-color: #1E9FFF;
+    border-color: #1E9FFF;
+}
+.el-button--warning.is-disabled, .el-button--warning.is-disabled:active, .el-button--warning.is-disabled:focus, .el-button--warning.is-disabled:hover{
+    color: #fff;
+    background-color: #f3d19e;
+    border-color: #f3d19e88;
+}
+.el-button--success.is-disabled, .el-button--success.is-disabled:active, .el-button--success.is-disabled:focus, .el-button--success.is-disabled:hover{
+    color: #fff;
+    background-color:#39B449;
+    border-color: #39B449;
+}
+.el-button--info.is-disabled, .el-button--info.is-disabled:active, .el-button--info.is-disabled:focus, .el-button--info.is-disabled:hover{
+    color: #fff;
+    background-color: #a0cfff;
+    border-color: #a0cfff;
+}
+
+.el-button{border-radius:0;cursor:pointer;}
+.el-input__inner,body .el-input__inner{border-radius:0;}
+
+.clearfix{clear:both;}
+
+.szjcomo-text-green{color:#39B449;}
+.szjcomo-text-red{color:#ff0000;}
+.szjcomo-text-gray{color:#F0F0F0;}
+.szjcomo-text-orange{color:#F37A1F;}
+.szjcomo-text-center{text-align:center;}
+.szjcomo-text-over{
+    word-break:keep-all;/* 不换行 */
+    white-space:nowrap;/* 不换行 */
+    overflow:hidden;/* 内容超出宽度时隐藏超出部分的内容 */
+    text-overflow:ellipsis;
+}
+.szjcomo-not-select{
+    -moz-user-select:none; /*火狐*/
+    -webkit-user-select:none; /*webkit浏览器*/
+    -ms-user-select:none; /*IE10*/
+    -khtml-user-select:none; /*早期浏览器*/
+    user-select:none;
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.szjcomo-body-content{
+    overflow-y: auto;
+    overflow-x: hidden;
+    width: 100%;height: 99%;
+    margin: 0 auto;
+}
+
+.szjcomo-body-content::-webkit-scrollbar {/*滚动条整体样式*/
+    width:5px;/*高宽分别对应横竖滚动条的尺寸*/
+    height:5px;
+}
+.szjcomo-body-content::-webkit-scrollbar-thumb {/*滚动条里面小方块*/
+    border-radius:5px;
+    -webkit-box-shadow: inset 5px rgba(0,0,0,0.2);
+    -moz-box-shadow: inset 5px rgba(0,0,0,0.2);
+    -ms-box-shadow: inset 5px rgba(0,0,0,0.2);
+    -o-box-shadow: inset 5px rgba(0,0,0,0.2);
+    background:rgba(0,0,0,0.2);
+}
+.szjcomo-body-content::-webkit-scrollbar-track {/*滚动条里面轨道*/
+    -webkit-box-shadow: inset 5px rgba(0,0,0,0.2);
+    -moz-box-shadow: inset 5px rgba(0,0,0,0.2);
+    -ms-box-shadow: inset 5px rgba(0,0,0,0.2);
+    -o-box-shadow: inset 5px rgba(0,0,0,0.2);
+    border-radius:0;
+    background:rgba(0,0,0,0.1);
+}
+
+#app code {
+    display: inline-block;
+    background-color: #f1f1f1;
+    border-radius: 3px;
+    padding: 3px 5px;
+    margin: 0 3px;
+}
+
+#app blockquote {
+    display: block;
+    border-left: 8px solid #d0e5f2;
+    padding: 5px 10px;
+    margin: 5px 0;
+    line-height: 1.4rem;
+    font-size: 100%;
+    background-color: #f1f1f1;
+}
+
+#app .el-input__inner{
+    border-radius: 0px;
+}

BIN
src/assets/images/logo.png


+ 112 - 0
src/components/basics/form_dialog.vue

@@ -0,0 +1,112 @@
+<template>
+	<div class="basics-form-dialog">
+		<vue-como-form :config="formOptions" :attr="attr" @submit="handleFormSubmit" v-if="formOptions.length > 0" ref="form" :submit-name="submitName" :submit-type="submitType"></vue-como-form>
+	</div>
+</template>
+<script>
+export default {
+	name: 'basics-form-dialog',
+	components: {},
+	data() {
+		let that = this;
+		return {
+			/**
+			 * [formOptions 表单参数]
+			 * @type {Array}
+			 */
+			formOptions:[],
+			/**
+			 * [attr 表单属性参数]
+			 * @type {Object}
+			 */
+			attr:{labelWidth:'100px'},
+			/**
+			 * [submitName 提交名称]
+			 * @type {String}
+			 */
+			submitName:'立即提交',
+			/**
+			 * [submitType 按钮类型]
+			 * @type {String}
+			 */
+			submitType:'danger'
+		};
+	},
+	props:{
+		params:{
+			type:Object,
+			default:() =>{return {};}
+		}
+	},
+	methods:{
+		/**
+		 * [initialize 初始化函数]
+		 * @author    szjcomo
+		 * @date   		2020-10-22
+		 * @return {[type]}     [description]
+		 */
+		initialize:function() {
+			let that = this;
+			if(that.params.config) that.formOptions = that.params.config;
+			if(that.params.attr) that.attr = Object.assign(that.attr,that.params.attr);
+			if(that.params.submitName) that.submitName = that.params.submitName;
+			if(that.params.submitType) that.submitType = that.params.submitType;
+		},
+		/**
+		 * [handleFormSubmit 表单提交]
+		 * @author    szjcomo
+		 * @date   		2020-10-22
+		 * @param  {[type]}     data [description]
+		 * @return {[type]}          [description]
+		 */
+		handleFormSubmit:function(data) {
+			let that = this;
+			if(that.params.callback) return that.params.callback.call(that,data);
+		}
+	},
+	//计算属性
+	computed:{
+
+	},
+	//侦听属性
+	watch:{
+
+	},
+	//实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
+	beforeCreate:function(){
+
+	},
+	//实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板
+	created:function(){
+
+	},
+	//此时已经完成了模板的编译,但是还没有挂载到页面中
+	beforeMount:function(){
+
+	},
+	//此时,已经将编译好的模板,挂载到了页面指定的容器中显示
+	mounted:function(){
+		let that = this;
+		that.initialize();
+	},
+	//状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
+	beforeUpdate:function(){
+
+	},
+	//实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
+	updated:function(){
+
+	},
+	//实例销毁之前调用。在这一步,实例仍然完全可用。
+	beforeDestroy:function(){
+
+	},
+	//Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
+	destroyed:function(){
+
+	}
+}
+</script>
+<style scoped>
+/*pass*/
+</style>

+ 12 - 0
src/main.js

@@ -0,0 +1,12 @@
+import Vue 				from 'vue';
+import App 				from './App.vue';
+import router 			from './router';
+import store 			from './store';
+import ElementUI 		from 'element-ui';
+Vue.use(ElementUI);
+import vueComoLayer 	from 'vue-como-layer';
+import 'vue-como-layer/vue-como-layer.css';
+Vue.use(vueComoLayer);
+
+Vue.config.productionTip = false
+new Vue({router,store,render: h => h(App)}).$mount('#app')

+ 19 - 0
src/router/common/load.js

@@ -0,0 +1,19 @@
+'use strict';
+
+export default {
+	/**
+	 * [web 首页客服]
+	 * @type {Object}
+	 */
+	web:{
+		/**
+		 * [index 首页客服系统]
+		 * @author    szjcomo
+		 * @date   		2020-12-04
+		 * @return {[type]}     [description]
+		 */
+		index:function() {
+			return import('@/views/web/index.vue');
+		}
+	}
+};

+ 15 - 0
src/router/common/routes.js

@@ -0,0 +1,15 @@
+'use strict';
+import load 	from '@/router/common/load.js';
+/**
+ * [routes 动态路由]
+ * @type {Array}
+ */
+const routes = [
+	/**
+	 * [path 客服系统首页]
+	 * @type {String}
+	 */
+	{path:'/',name:'web_index',component:load.web.index}
+];
+
+export default routes;

+ 23 - 0
src/router/index.js

@@ -0,0 +1,23 @@
+import Vue 				from 'vue';
+import VueRouter 		from 'vue-router';
+import routes 			from '@/router/common/routes.js';
+import { globalConfig }	from '@/service/common/config.js';
+import cache 			from '@/service/common/cache';
+import store 			from '@/store';
+import comolayer 		from 'vue-como-layer';
+Vue.use(VueRouter)
+const router = new VueRouter({
+    mode: 'hash',
+    base: process.env.BASE_URL,
+    routes
+})
+
+router.beforeEach(async (to, from, next) => {
+	try {
+		next();
+	} catch(err) {
+		return comolayer.alert(err.message,{title:'权限拦截提醒',icon:2,shade:true});
+	}
+})
+
+export default router;

+ 10 - 0
src/service/common/api.config.js

@@ -0,0 +1,10 @@
+'use strict';
+
+import { globalConfig } 	from './config.js';
+
+
+export const basics = {
+
+};
+
+export default basics;

+ 175 - 0
src/service/common/cache.js

@@ -0,0 +1,175 @@
+'use strict';
+
+/**
+ * [cacheValue 缓存对象]
+ * @author        szjcomo
+ * @createTime 2020-09-29
+ * @param      {[type]}   key    [description]
+ * @param      {[type]}   value  [description]
+ * @param      {[type]}   expire [description]
+ * @return     {[type]}          [description]
+ */
+const Cache = function(key,value,expire) {
+    this.szjcomo_key = key;
+    this.szjcomo_value = value;
+    this.expire_time = expire;
+}
+
+/**
+ * [instanceof 判断是否是自身的对象]
+ * @author 	   szjcomo
+ * @createTime 2020-09-29
+ * @param      {[type]}   obj [description]
+ * @return     {[type]}       [description]
+ */
+Cache.instanceof = function(obj) {
+    if(obj instanceof Object) {
+        if(obj.hasOwnProperty('szjcomo_key') && obj.hasOwnProperty('szjcomo_value')) return true;
+        return false;
+    }
+    return false;
+}
+
+
+export const localStore = {
+    /**
+     * [get 获取本地存储]
+     * @author        szjcomo
+     * @createTime 2020-09-29
+     * @param      {[type]}   key [description]
+     * @return     {[type]}       [description]
+     */
+    get:function(key) {
+    	let that = this;
+        let result = window.localStorage.getItem(key);
+        if(!result) return false;
+        try {
+            let obj = JSON.parse(result);
+            if(Cache.instanceof(obj)) {
+                let curTime = new Date().getTime();
+                if(obj.expire_time == 0) return obj.szjcomo_value;
+                if(obj.expire_time >= curTime) return obj.szjcomo_value;
+                that.del(obj.szjcomo_key);
+                return false;
+            } else {
+                return obj;
+            }
+        } catch(err) {
+            console.error(err);
+            return false;
+        }
+    },
+    /**
+     * [set 设置缓存]
+     * @author        szjcomo
+     * @createTime 2020-09-29
+     * @param      {[type]}   key     [description]
+     * @param      {[type]}   value   [description]
+     * @param      {[type]}   seconds [description]
+     */
+    set:function(key,value,seconds = 0) {
+        try {
+        	let tmp_expire = 0;
+        	if(seconds > 0) tmp_expire = (new Date().getTime() + seconds * 1000);
+        	let tmpObj = new Cache(key,value,tmp_expire);
+        	window.localStorage.setItem(key,JSON.stringify(tmpObj));
+        	return true;
+        } catch(err) {
+            console.error(err);
+            return false;
+        }
+    },
+    /**
+     * [del 删除缓存]
+     * @author 	   szjcomo
+     * @createTime 2020-09-29
+     * @param      {[type]}   key [description]
+     * @return     {[type]}       [description]
+     */
+    del:function(key) {
+    	return window.localStorage.removeItem(key);
+    }
+};
+
+/**
+ * [sessStore session存储]
+ * @type {Object}
+ */
+export const sessStore = {
+    /**
+     * [get 获取本地存储]
+     * @author        szjcomo
+     * @createTime 2020-09-29
+     * @param      {[type]}   key [description]
+     * @return     {[type]}       [description]
+     */
+    get:function(key) {
+    	let that = this;
+        let result = window.sessionStorage.getItem(key);
+        if(!result) return false;
+        try {
+            let obj = JSON.parse(result);
+            if(Cache.instanceof(obj)) {
+                let curTime = new Date().getTime();
+                if(obj.expire_time == 0) return obj.szjcomo_value;
+                if(obj.expire_time >= curTime) return obj.szjcomo_value;
+                that.del(obj.szjcomo_key);
+                return false;
+            } else {
+                return obj;
+            }
+        } catch(err) {
+            console.error(err);
+            return false;
+        }
+    },
+    /**
+     * [set 设置缓存]
+     * @author        szjcomo
+     * @createTime 2020-09-29
+     * @param      {[type]}   key     [description]
+     * @param      {[type]}   value   [description]
+     * @param      {[type]}   seconds [description]
+     */
+    set:function(key,value,seconds = 0) {
+        try {
+        	let tmp_expire = 0;
+        	if(seconds > 0) tmp_expire = (new Date().getTime() + seconds * 1000);
+        	let tmpObj = new Cache(key,value,tmp_expire);
+        	window.sessionStorage.setItem(key,JSON.stringify(tmpObj));
+        	return true;
+        } catch(err) {
+            console.error(err);
+            return false;
+        }
+    },
+    /**
+     * [del 删除缓存]
+     * @author 	   szjcomo
+     * @createTime 2020-09-29
+     * @param      {[type]}   key [description]
+     * @return     {[type]}       [description]
+     */
+    del:function(key) {
+    	return window.sessionStorage.removeItem(key);
+    }
+};
+
+/**
+ * [szjcomoCache 导出缓存类]
+ * @author 	   szjcomo
+ * @createTime 2020-09-29
+ * @param      {String}   type [description]
+ * @return     {[type]}        [description]
+ */
+const szjcomoCache = function(type = 'sess') {
+	if(type == 'sess') {
+		if(!window.sessionStorage) throw new Error('您的浏览器不支持本地sessionStorage存储');
+		return sessStore;
+	}
+	if(!window.localStorage) throw new Error('您的浏览器不支持本地localStorage存储');
+	return localStore;
+}
+
+export default szjcomoCache;
+

+ 53 - 0
src/service/common/config.js

@@ -0,0 +1,53 @@
+'use strict';
+/**
+ * [global_config 全局配置]
+ * @type {Object}
+ */
+const global_config = {
+	/**
+	 * [app_domain 全局域名]
+	 * @type {String}
+	 */
+	app_domain:'http://192.168.1.222:8105',
+	/**
+	 * [app_project_name 项目名称]
+	 * @type {String}
+	 */
+	app_project_name:'思智捷科技客服管理系统',
+	/**
+	 * [app_upload_image_width 上传图片的最大宽度]
+	 * @type {Number}
+	 */
+	app_upload_image_width:1000,
+	/**
+	 * [app_upload_image_height 上传图片的最大高度]
+	 * @type {Number}
+	 */
+	app_upload_image_height:1000,
+	/**
+	 * [app_upload_image_quality 上传图片的质量处量]
+	 * @type {Number}
+	 */
+	app_upload_image_quality:0.5,
+	/**
+	 * [app_prefix 项目前缀]
+	 * @type {String}
+	 */
+	app_prefix:'kefu'
+};
+/**
+ * [config 导出全局配置]
+ * @type {[type]}
+ */
+export let globalConfig = global_config;
+
+/**
+ * 项目配置
+ */
+export default {
+	/**
+	 * [config 全局配置]
+	 * @type {[type]}
+	 */
+	global:global_config
+};

+ 225 - 0
src/service/common/request.js

@@ -0,0 +1,225 @@
+'use strict';
+import axios 								from 'axios';
+import { Loading }  						from 'element-ui';
+import qs 									from 'qs';
+import szjcomo 								from 'szjcomo-utils';
+import { globalConfig } 					from '@/service/common/config';
+import store 								from '@/store';
+import router 								from '@/router';
+import cache 								from '@/service/common/cache';
+/**
+ * [reqConf 全局通用配置]
+ * @type {Object}
+ */
+const reqConf = {
+	/**
+	 * [maxContentLength `定义http内容中允许的最大内容长度]
+	 * @type {[type]}
+	 */
+	maxContentLength:5 * 1024 * 1024,
+	/**
+	 * [timeout指定请求超时前的毫秒数。如果请求所用时间超过timeout,则请求将被中止请求。]
+	 * @type {Number}
+	 */
+	timeout:20 * 1000,
+	/**
+	 * 除非url是绝对的,否则baseURL将加在url前面。可以方便地为axios实例设置baseURL以传递相对url到该实例的方法。
+	 * @type {String}
+	 */
+	baseURL:process.env.NODE_ENV == 'development' ? '/apis' : globalConfig.app_domain,
+	/**
+	 * `transformResponse` allows changes to the response data to be made before it is passed to then/catch
+	 * @author 	   szjcomo
+	 * @createTime 2020-09-12
+	 * @param      {[type]}   data [description]
+	 * @return     {[type]}        [description]
+	 */
+	transformResponse:function(data) {
+		try {
+			return JSON.parse(data);
+		} catch(err) {
+			return data;
+		}
+	}
+};
+/**
+ * [openLoading 打开加载框]
+ * @author 	   szjcomo
+ * @createTime 2020-09-12
+ * @param      {Object}   options [description]
+ * @return     {[type]}           [description]
+ */
+const openLoading = function(options = {}) {
+	let defaults = {text:'加载中',background:'#000000cc'};
+	return Loading.service(Object.assign(defaults,options));
+}
+/**
+ * [closeLoading 关闭加载框]
+ * @author 	   szjcomo
+ * @createTime 2020-09-12
+ * @param      {Object}   options [description]
+ * @return     {[type]}           [description]
+ */
+const closeLoading = function(loadingInstance) {
+	if(loadingInstance) loadingInstance.close();
+}
+/**
+ * [getAxiosInstance 创建一个请求实例]
+ * @author 	   szjcomo
+ * @createTime 2020-09-12
+ * @param      {Object}   options [description]
+ * @return     {[type]}           [description]
+ */
+const getAxiosInstance = function(options = {}) {
+	let instance =  axios.create(Object.assign(reqConf,options));
+	instance.interceptors.request.use(async (config) => {
+		return config;
+	}, (error) => {return Promise.reject(error);});
+	instance.interceptors.response.use((response)=> {
+		if(response && response.data && response.data.result_code == 10001) {
+			return Promise.reject(new Error(response.data.message));
+		} else {
+			return Promise.resolve(response.data);
+		}
+	}, (error) => {
+		return Promise.reject(error);
+	});
+	return instance;
+}
+/**
+ * [szjcomoAxios 自定义请求对象]
+ * @type {Object}
+ */
+const szjcomoAxios = {
+	/**
+	 * [get 发送get请求]
+	 * @author 	   szjcomo
+	 * @createTime 2020-09-12
+	 * @param      {[type]}   url             [description]
+	 * @param      {Object}   params          [description]
+	 * @param      {Object}   instanceOptions [description]
+	 * @param      {Boolean}  loading         [description]
+	 * @return     {[type]}                   [description]
+	 */
+	get:async function(url,params = {},instanceOptions = {},loading = true) {
+		let loadingInstance,result;
+		if(loading) loadingInstance = openLoading({text:'加载中'});
+		try {
+			let tmpUrl = url;
+			if(params &&  Object.keys(params).length > 0) tmpUrl += ((tmpUrl.indexOf('?') > -1?'&':'?') + qs.stringify(params));
+			result = await getAxiosInstance(instanceOptions).get(tmpUrl);
+		} catch(err) {
+			result = szjcomo.appResult(err.message,err.data,true,err.status);
+		} finally {
+			if(loadingInstance) closeLoading(loadingInstance);
+			return result;
+		}
+	},
+	/**
+	 * [post 发送post请求]
+	 * @author 	   szjcomo
+	 * @createTime 2020-09-23
+	 * @param      {[type]}   url             [description]
+	 * @param      {Object}   params          [description]
+	 * @param      {Object}   instanceOptions [description]
+	 * @param      {Boolean}  loading         [description]
+	 * @return     {[type]}                   [description]
+	 */
+	post:async function(url,params = {},instanceOptions = {},loading = true) {
+		let loadingInstance,result;
+		if(loading) loadingInstance = openLoading({text:'提交中'});
+		try {
+			result = await getAxiosInstance(instanceOptions).post(url,params);
+		} catch(err) {
+			result = szjcomo.appResult(err.message,err.data,true,err.status);
+		} finally {
+			if(loadingInstance) closeLoading(loadingInstance);
+			return result;
+		}
+	},
+	/**
+	 * [put 发送put请求]
+	 * @author    szjcomo
+	 * @date   		2020-10-22
+	 * @param  {[type]}     url             [description]
+	 * @param  {Object}     params          [description]
+	 * @param  {Object}     instanceOptions [description]
+	 * @param  {Boolean}    loading         [description]
+	 * @return {[type]}                     [description]
+	 */
+	put:async function(url,params = {},instanceOptions = {},loading = true){
+		let loadingInstance,result;
+		if(loading) loadingInstance = openLoading({text:'提交中'});
+		try {
+			result = await getAxiosInstance(instanceOptions).put(url,params);
+		} catch(err) {
+			result = szjcomo.appResult(err.message,err.data,true,err.status);
+		} finally {
+			if(loadingInstance) closeLoading(loadingInstance);
+			return result;
+		}
+	},
+	/**
+	 * [delete 发送delete请求]
+	 * @author    szjcomo
+	 * @date   		2020-10-22
+	 * @param  {[type]}     url             [description]
+	 * @param  {Object}     params          [description]
+	 * @param  {Object}     instanceOptions [description]
+	 * @param  {Boolean}    loading         [description]
+	 * @return {[type]}                     [description]
+	 */
+	delete:async function(url,params = {},instanceOptions = {},loading = true) {
+		let loadingInstance,result;
+		if(loading) loadingInstance = openLoading({text:'提交中'});
+		try {
+			let tmpUrl = url;
+			if(params &&  Object.keys(params).length > 0) tmpUrl += ((tmpUrl.indexOf('?') > -1?'&':'?') + qs.stringify(params));
+			result = await getAxiosInstance(instanceOptions).delete(tmpUrl);
+		} catch(err) {
+			result = szjcomo.appResult(err.message,err.data,true,err.status);
+		} finally {
+			if(loadingInstance) closeLoading(loadingInstance);
+			return result;
+		}
+	},
+	/**
+	 * [upload 实现文件上传功能]
+	 * @author 	   szjcomo
+	 * @createTime 2020-10-06
+	 * @param      {[type]}   url             [description]
+	 * @param      {Object}   params          [description]
+	 * @param      {Object}   instanceOptions [description]
+	 * @param      {Boolean}  loading         [description]
+	 * @return     {[type]}                   [description]
+	 */
+	upload:async function(url,params = {},filename = 'filename',instanceOptions = {},loading = true) {
+		let loadingInstance,result;
+		if(loading) loadingInstance = openLoading({text:'上传中'});
+		try {
+			let formData = new FormData();
+			Object.keys(params).forEach(key => {
+				if(key == filename) {
+					formData.append(key,params[key],params[key].name);
+				} else if(key != 'headers') {
+					formData.append(key,params[key]);
+				}
+			})
+			let headers = {'Content-Type': 'multipart/form-data'};
+			if(params.headers) {
+				Object.keys(params.headers).forEach(key => {
+					headers[key] = params.headers[key];
+				})
+			}
+			result = await getAxiosInstance(instanceOptions).request({
+				method:'post',url:`${globalConfig.app_upload_domain}${url}`,data:formData,headers:headers
+			});
+		} catch(err) {
+			result = szjcomo.appResult(err.message,err.data,true,err.status);
+		} finally {
+			if(loadingInstance) closeLoading(loadingInstance);
+			return result;
+		}
+	}
+};
+export default szjcomoAxios;

+ 73 - 0
src/service/index.js

@@ -0,0 +1,73 @@
+'use strict';
+import request 		from '@/service/common/request';
+import cache 		from '@/service/common/cache';
+import config 		from '@/service/common/config';
+import image 		from 'vue-como-image';
+import APIURL 		from '@/service/common/api.config';
+import echarts 		from 'echarts';
+import basics_home 	from '@/service/basics/index';
+import adminAuth 	from '@/service/common/auth';
+import store 		from '@/store';
+import formDialog 	from '@/components/basics/form_dialog.vue';
+import comoLayer 	from 'vue-como-layer';
+
+export let Basics = Object.assign({},basics_home);
+/**
+ * [获取窗口最大宽高]
+ * @author    szjcomo
+ * @date   		2020-10-17
+ * @return {[type]}     [description]
+ */
+export const layerDialogMaxWH = (wRadio = 0.8,hRadio = 0.8) => {
+	let windowHeight = document.body.clientHeight;
+	let windowWidth  = document.body.clientWidth;
+	return {width:windowWidth * wRadio,height:windowHeight * hRadio};
+} 
+
+/**
+ * [authCheck 权限检测函数]
+ * @author    szjcomo
+ * @date   		2020-10-22
+ * @param  {[type]}     router_name [description]
+ * @return {[type]}                 [description]
+ */
+export const authCheck = async function(router_name) {
+	return await store.dispatch('authRouters',router_name);
+}
+/**
+ * [Dialog description]
+ * @author    szjcomo
+ * @date   		2020-10-22
+ * @param  {Object}     params [description]
+ */
+export const Dialog = function(dialog_options = {title:'数据操作',shade:true},options = {}) {
+	return comoLayer.iframe(formDialog,dialog_options,options);
+}
+/**
+ * [ActionException 展示提示信息展示]
+ * @author    szjcomo
+ * @date   		2020-10-31
+ * @param  {[type]}     message [description]
+ * @param  {Object}     options [description]
+ */
+export const ActionException = function(message,options = {}) {
+	let default_options = Object.assign({title:'信息操作失败提醒',shade:true,icon:2},options);
+	return comoLayer.alert(message,default_options);
+}
+
+
+export default {
+	basics:Basics,
+	upload:request.upload,
+	cache:cache,
+	config:config,
+	image:image,
+	apiurl:APIURL,
+	request:request,
+	echarts:echarts,
+	dialogMax:layerDialogMaxWH,
+	auth:adminAuth,
+	authCheck:authCheck,
+	dialog:Dialog,
+	exception:ActionException
+};

+ 20 - 0
src/store/index.js

@@ -0,0 +1,20 @@
+import Vue 				from 'vue';
+import Vuex 			from 'vuex';
+import szjcomo 			from 'szjcomo-utils';
+
+Vue.use(Vuex);
+
+export default new Vuex.Store({
+	state: {
+
+	},
+	mutations: {
+
+	},
+	actions: {
+
+	},
+	modules: {
+
+	}
+})

+ 64 - 0
src/views/web/index.vue

@@ -0,0 +1,64 @@
+<template>
+	<div class="web-index">
+		前端首页
+	</div>
+</template>
+<script>
+export default {
+	name: 'web-index',
+	components: {},
+	data() {
+		return {
+		};
+	},
+	methods:{
+		initialize:function() {
+			let that = this;
+		}
+	},
+	//计算属性
+	computed:{
+
+	},
+	//侦听属性
+	watch:{
+
+	},
+	//实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
+	beforeCreate:function(){
+
+	},
+	//实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板
+	created:function(){
+
+	},
+	//此时已经完成了模板的编译,但是还没有挂载到页面中
+	beforeMount:function(){
+
+	},
+	//此时,已经将编译好的模板,挂载到了页面指定的容器中显示
+	mounted:function(){
+		let that = this;
+		that.initialize();
+	},
+	//状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
+	beforeUpdate:function(){
+
+	},
+	//实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
+	updated:function(){
+
+	},
+	//实例销毁之前调用。在这一步,实例仍然完全可用。
+	beforeDestroy:function(){
+
+	},
+	//Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
+	destroyed:function(){
+
+	}
+}
+</script>
+<style scoped>
+/*pass*/
+</style>

+ 81 - 0
vue.config.js

@@ -0,0 +1,81 @@
+'use strict';
+/*
+ * @Description: In User Settings Edit
+ * @Author: your name
+ * @Date: 2019-09-15 19:43:39
+ * @LastEditTime: 2019-09-15 19:43:39
+ * @LastEditors: your name
+ */
+/**
+ * 自定义输入方式
+ * @type {String}
+ */
+const outputDir = 'web';
+const isProduction = process.env.NODE_ENV === 'production';
+const cdn = {
+    css: [],
+    js: [
+        'https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js',
+        'https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.js',
+        'https://cdn.bootcdn.net/ajax/libs/vuex/3.5.1/vuex.min.js',
+        'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/index.js',
+        'https://cdn.bootcdn.net/ajax/libs/qs/6.9.4/qs.js',
+        'https://cdn.bootcdn.net/ajax/libs/axios/0.20.0/axios.js',
+        'https://cdn.jsdelivr.net/npm/echarts@4.9.0/dist/echarts.min.js'
+    ],
+    externals:{
+        vue:'Vue',
+        'vue-router':'VueRouter',
+        'element-ui':'ELEMENT',
+        axios:'axios',
+        vuex:'Vuex',
+        echarts:'echarts',
+        qs:'Qs'
+    }
+}
+module.exports = {
+	publicPath: isProduction ? './': '/',
+    devServer: {
+        port: 8080,
+        proxy: {
+            '/apis': {
+                target: isProduction ?'http://192.168.1.222:8105':'http://192.168.1.222:8105',
+                ws: true,  // proxy websockets 
+                changeOrigin: true,  // needed for virtual hosted sites
+                pathRewrite: {
+                    '^/apis': '/'  // rewrite path
+                }
+            }
+        },
+		disableHostCheck: true
+    },
+    //关闭语法检查
+    lintOnSave:false,
+    //生产环境是否生成 sourceMap 文件
+    productionSourceMap:false,
+    //输出文件目录
+    outputDir:outputDir,
+    // 指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径
+    configureWebpack: config => {
+        if(isProduction) config.externals = cdn.externals;
+    },
+    chainWebpack: config => {
+        // 生产环境配置
+        if (isProduction) {
+            // 删除预加载
+            config.plugins.delete('preload');
+            config.plugins.delete('prefetch');
+            //压缩代码
+            config.optimization.minimize(true);
+            // 分割代码
+            config.optimization.splitChunks({
+                chunks: 'all'
+            })
+            //生产环境注入cdn
+            config.plugin('html').tap(args => {
+                args[0].cdn = cdn;
+                return args;
+            });
+        }
+    },
+};