Quellcode durchsuchen

feat: 初始化项目组件拆分,及简单布局

zhangyuhan vor 3 Jahren
Ursprung
Commit
070469deb3

+ 1 - 1
.browserslistrc

@@ -1,3 +1,3 @@
 > 1%
 last 2 versions
-not dead
+ie >= 11

+ 4 - 0
.env.development

@@ -0,0 +1,4 @@
+NODE_ENV=development
+VUE_APP_BASE_API='/api'
+VUE_APP_BASE_URL='/'
+VUE_APP_BASE_PUBLIC='/'

+ 4 - 0
.env.production

@@ -0,0 +1,4 @@
+NODE_ENV=production
+VUE_APP_BASE_API='/api'
+VUE_APP_BASE_URL='/'
+VUE_APP_BASE_PUBLIC='/page/'

+ 9 - 0
babel.config.js

@@ -1,5 +1,14 @@
 module.exports = {
   presets: [
     '@vue/cli-plugin-babel/preset'
+  ],
+  plugins: [
+    [
+      'component',
+      {
+        libraryName: 'element-ui',
+        styleLibraryName: 'theme-chalk'
+      }
+    ]
   ]
 }

+ 7 - 1
package.json

@@ -5,10 +5,15 @@
   "scripts": {
     "serve": "vue-cli-service serve",
     "build": "vue-cli-service build",
-    "lint": "vue-cli-service lint"
+    "lint": "vue-cli-service lint",
+    "lint:fix": "vue-cli-service lint --fix"
   },
   "dependencies": {
+    "axios": "^0.27.2",
     "core-js": "^3.8.3",
+    "dayjs": "^1.11.3",
+    "element-ui": "2.15.7",
+    "lodash": "^4.17.21",
     "register-service-worker": "^1.7.2",
     "vue": "^2.6.14",
     "vue-router": "^3.5.1",
@@ -24,6 +29,7 @@
     "@vue/cli-plugin-vuex": "~5.0.0",
     "@vue/cli-service": "~5.0.0",
     "@vue/eslint-config-standard": "^6.1.0",
+    "babel-plugin-component": "^1.1.1",
     "eslint": "^7.32.0",
     "eslint-plugin-import": "^2.25.3",
     "eslint-plugin-node": "^11.1.0",

+ 6 - 20
src/App.vue

@@ -1,32 +1,18 @@
 <template>
   <div id="app">
-    <nav>
-      <router-link to="/">Home</router-link> |
-      <router-link to="/about">About</router-link>
-    </nav>
     <router-view/>
   </div>
 </template>
 
 <style lang="scss">
+@import "~@/assets/style/index.scss";
 #app {
-  font-family: Avenir, Helvetica, Arial, sans-serif;
+  font-family: -apple-system,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Microsoft YaHei,Source Han Sans SC,Noto Sans CJK SC,WenQuanYi Micro Hei,sans-serif;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-  color: #2c3e50;
-}
-
-nav {
-  padding: 30px;
-
-  a {
-    font-weight: bold;
-    color: #2c3e50;
-
-    &.router-link-exact-active {
-      color: #42b983;
-    }
-  }
+  color: #171826;
+  width: 100%;
+  height: 100%;
+  position: relative;
 }
 </style>

BIN
src/assets/logo.png


+ 9 - 0
src/assets/style/_mixin.scss

@@ -0,0 +1,9 @@
+@mixin diy-icon ($name, $width: 24, $height: 24) {
+  ::v-deep .el-icon-jy-#{$name} {
+    background: url("~@/assets/images/icon/#{$name}.png") no-repeat;
+    background-size: cover;
+    display: inline-block;
+    width: #{$width}px;
+    height: #{$height}px;
+  }
+}

+ 21 - 0
src/assets/style/_variables.scss

@@ -0,0 +1,21 @@
+$color_main: #2CB7CA;
+
+$bg-retrieve: #010C28;
+$bg-button--default: linear-gradient(84deg, #AF9552 0%, #EFDA98 100%);
+$bg-card--default: linear-gradient(#031242 0%, #010E36 100%);
+$bg-button--tran: linear-gradient(#53F1DD 0%, #07907E 100%);
+$bg-color-1: linear-gradient(90deg, #9F1B89 0%, #BB36A5 100%);
+$bg-color-2: linear-gradient(90deg, #46B5D1 0%, #8CD5E4 100%);
+$bg-color-3: linear-gradient(90deg, #F5AB48 0%, #F4CE8F 100%);
+$bg-color-4: linear-gradient(90deg, #F83F4F 0%, #F38797 100%);
+$bg-color-5: linear-gradient(90deg, #41AF92 0%, #84CEB7 100%);
+$bg-color-6: linear-gradient(90deg, #7446A0 0%, #A380C4 100%);
+
+$font-text--title: 17px;
+
+$color-text--default: #1D1D1D;
+$color-text--active: $color_main;
+$color-input--default: #1D1D1D;
+$color-text--highlight: $color_main;
+
+$bg-main-color: #fff;

+ 79 - 0
src/assets/style/common.scss

@@ -0,0 +1,79 @@
+html,body {
+  margin: 0;
+  height: 100%;
+}
+
+body {
+  background-color: #F2F2F4;
+}
+
+.highlight-text {
+  color: $color-text--highlight;
+}
+
+.ellipsis {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  text-align: justify
+}
+
+/* 超过2行省略号显示 */
+.ellipsis-2 {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  text-align: justify
+}
+
+/* 超过3行省略号显示 */
+.ellipsis-3 {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+}
+
+::-webkit-scrollbar {
+  /*滚动条整体样式*/
+  width: 8px;
+}
+::-webkit-scrollbar-thumb {
+  /*滚动条里面小方块*/
+  border-radius: 3px;
+  background-color: #ECECEC;
+  opacity: 0.15;
+}
+.scrollbar {
+  &::-webkit-scrollbar {
+    /*滚动条整体样式*/
+    width: 8px;
+  }
+  &::-webkit-scrollbar-thumb {
+    /*滚动条里面小方块*/
+    border-radius: 3px;
+    background-color: #ECECEC;
+    opacity: 0.15;
+  }
+}
+
+// 清除 input type=number 默认样式
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+  -webkit-appearance: none;
+}
+input[type='number'] {
+  -moz-appearance: textfield;
+}
+
+.f-share {
+  padding: 6px 4px!important;
+  box-shadow: 0px 0px 28px 0px #999;
+  border: none!important;
+  .popper__arrow {
+    border: none!important;
+  }
+}

+ 2 - 0
src/assets/style/index.scss

@@ -0,0 +1,2 @@
+@import "./common";
+@import "./reset-ele";

+ 81 - 0
src/assets/style/reset-ele.scss

@@ -0,0 +1,81 @@
+@import './_mixin';
+@import './_variables';
+
+.reset-page {
+  // 分页样式重置
+  .el-pagination-container {
+    position: relative;
+    margin-top: 28px;
+    padding-bottom: 60px;
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+    .el-pagination {
+      position: absolute;
+      right: 0;
+    }
+  }
+  .el-pagination.is-background .el-pager {
+    li {
+      background-color: #fff;
+      border: 1px solid rgba($color: #000, $alpha: 0.05);
+    }
+
+    li:not(.disabled).active,
+    li:not(.disabled):hover {
+      color: #fff;
+      background-color: $color-text--highlight;
+    }
+  }
+
+  // 修改输入框默认focus边框颜色
+  .el-input.is-active .el-input__inner,
+  .el-input__inner:focus {
+    border-color: $color-text--highlight;
+  }
+
+  .el-checkbox__inner {
+    width: 16px;
+    height: 16px;
+    border-radius: 3px;
+    &::after {
+      border-width: 2px;
+      left: 5px;
+      top: 1px;
+    }
+  }
+
+
+
+  .el-button--main {
+    font-family: Microsoft YaHei, Microsoft YaHei-Regular;
+    border-color: $color-text--highlight;
+    background: $color-text--highlight;
+    border-radius: 6px;
+    padding: 8px 16px;
+    box-sizing: border-box;
+    font-size: 14px;
+    font-weight: 400;
+    color: #fff;
+    line-height: 24px;
+    &:hover, &:focus {
+      border-color: $color-text--highlight;
+      background: $color-text--highlight;
+      color: #fff;
+    }
+  }
+
+  .el-link {
+    &.el-link--default{
+      &:hover {
+        color: $color-text--highlight;
+      }
+    }
+  }
+}
+
+.el-popper {
+  li{
+    float: none;
+  }
+}

+ 13 - 0
src/components/CustomerInfo.vue

@@ -0,0 +1,13 @@
+<template>
+  <h3>客户信息</h3>
+</template>
+
+<script>
+export default {
+  name: 'CustomerInfo'
+}
+</script>
+
+<style scoped>
+
+</style>

+ 0 - 61
src/components/HelloWorld.vue

@@ -1,61 +0,0 @@
-<template>
-  <div class="hello">
-    <h1>{{ msg }}</h1>
-    <p>
-      For a guide and recipes on how to configure / customize this project,<br>
-      check out the
-      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
-    </p>
-    <h3>Installed CLI Plugins</h3>
-    <ul>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa" target="_blank" rel="noopener">pwa</a></li>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
-    </ul>
-    <h3>Essential Links</h3>
-    <ul>
-      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
-      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
-      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
-      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
-      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
-    </ul>
-    <h3>Ecosystem</h3>
-    <ul>
-      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
-      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
-      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
-      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
-      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
-    </ul>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'HelloWorld',
-  props: {
-    msg: String
-  }
-}
-</script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-h3 {
-  margin: 40px 0 0;
-}
-ul {
-  list-style-type: none;
-  padding: 0;
-}
-li {
-  display: inline-block;
-  margin: 0 10px;
-}
-a {
-  color: #42b983;
-}
-</style>

+ 110 - 0
src/components/MessageItem.vue

@@ -0,0 +1,110 @@
+<template>
+<div class="message--item">
+  <div class="head">
+    <el-badge :value="badge" :hidden="!showBadge">
+      <img :src="headImg" alt="头像">
+    </el-badge>
+  </div>
+  <div class="content">
+    <span class="name">{{nickName}}</span>
+    <div class="ellipsis">
+      <span class="badge-tip" v-if="showBadge">{{badgeTip}}</span>
+      <span>{{message}}</span>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+import { Badge } from 'element-ui'
+
+export default {
+  name: 'MessageItem',
+  components: {
+    [Badge.name]: Badge
+  },
+  props: {
+    headImg: {
+      type: String,
+      default: 'https://www.jianyu360.cn/common-module/public/image/auto.png'
+    },
+    nickName: {
+      type: String,
+      default: ''
+    },
+    badge: {
+      type: [String, Number],
+      default: 0
+    },
+    badgeTip: {
+      type: String,
+      default: '[新消息]'
+    },
+    message: {
+      type: String,
+      default: ''
+    }
+  },
+  computed: {
+    showBadge () {
+      return this.badge > 0
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.message--item {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  width: 100%;
+  height: 62px;
+  padding: 10px;
+  border-radius: 8px;
+  box-sizing: border-box;
+  font-weight: 400;
+  cursor: pointer;
+  transition: background-color 0.3s;
+  &.active,
+  &:active,
+  &:hover {
+    background-color: rgba(44, 183, 202, 0.2);
+  }
+  .head {
+    flex-shrink: 0;
+    width: 42px;
+    height: 42px;
+    border-radius: 50%;
+    border: 1px solid #E0E0E0;
+    ::v-deep {
+      .el-badge__content {
+        right: 15px;
+        top: 5px;
+      }
+    }
+    img {
+      width: 100%;
+    }
+  }
+  .content {
+    max-width: 170px;
+    flex-shrink: 0;
+    font-size: 12px;
+    font-weight: 400;
+    line-height: 18px;
+    color: #686868;
+    margin-left: 8px;
+  }
+  .badge-tip {
+    color: #FF3A20;
+    margin-right: 2px;
+  }
+  .name {
+    font-size: 14px;
+    color: #1D1D1D;
+    line-height: 22px;
+    margin-bottom: 2px;
+  }
+}
+</style>

+ 52 - 0
src/components/MessageList.vue

@@ -0,0 +1,52 @@
+<template>
+  <div class="message--list">
+    <h3 v-if="title">{{title}}</h3>
+    <div class="list">
+      <message-item
+        v-for="(item, index) in list"
+        :key="index"
+        v-bind="item"
+        @click="$emit('select', item)"
+      ></message-item>
+    </div>
+  </div>
+</template>
+
+<script>
+import MessageItem from '@/components/MessageItem'
+export default {
+  name: 'MessageList',
+  components: { MessageItem },
+  props: {
+    title: {
+      type: String,
+      default: '会话列表'
+    },
+    /**
+     * 消息列表
+     */
+    list: {
+      type: Array,
+      default: () => []
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.message--list {
+  h3 {
+    font-size: 18px;
+    font-weight: 400;
+    color: #1D1D1D;
+    line-height: 28px;
+    padding: 0 16px;
+  }
+  .list {
+    padding: 0 8px;
+    .message--item {
+      margin: 8px 0;
+    }
+  }
+}
+</style>

+ 5 - 0
src/main.js

@@ -3,7 +3,12 @@ import App from './App.vue'
 import './registerServiceWorker'
 import router from './router'
 import store from './store'
+import { Message, MessageBox } from 'element-ui'
+import '@/utils'
 
+Vue.prototype.$message = Message
+Vue.prototype.$alert = MessageBox.alert
+Vue.prototype.$confirm = MessageBox.confirm
 Vue.config.productionTip = false
 
 new Vue({

+ 7 - 9
src/router/index.js

@@ -1,22 +1,20 @@
 import Vue from 'vue'
 import VueRouter from 'vue-router'
-import HomeView from '../views/HomeView.vue'
+import CustomerServiceView from '../views/CustomerServiceView.vue'
+import CustomerView from '../views/CustomerView.vue'
 
 Vue.use(VueRouter)
 
 const routes = [
   {
     path: '/',
-    name: 'home',
-    component: HomeView
+    name: 'CustomerView',
+    component: CustomerView
   },
   {
-    path: '/about',
-    name: 'about',
-    // route level code-splitting
-    // this generates a separate chunk (about.[hash].js) for this route
-    // which is lazy-loaded when the route is visited.
-    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
+    path: '/service',
+    name: 'CustomerServiceView',
+    component: CustomerServiceView
   }
 ]
 

+ 19 - 6
src/store/index.js

@@ -5,13 +5,26 @@ Vue.use(Vuex)
 
 export default new Vuex.Store({
   state: {
+    CommunicateList: [
+      {
+        nickName: '北京客户',
+        badge: 2,
+        message: '大会员里边大会员里边的分析功能咋用?'
+      },
+      {
+        nickName: '河南客户',
+        message: '大会员里边的分析功能咋用?'
+      }
+    ]
   },
   getters: {
+    unReadCount (state) {
+      return state.CommunicateList.reduce((a, b) => {
+        return a + Number(b?.badge || 0)
+      }, 0)
+    }
   },
-  mutations: {
-  },
-  actions: {
-  },
-  modules: {
-  }
+  mutations: {},
+  actions: {},
+  modules: {}
 })

+ 3 - 0
src/utils/bus.js

@@ -0,0 +1,3 @@
+import Vue from 'vue'
+const $bus = new Vue()
+export default $bus

+ 14 - 0
src/utils/format.js

@@ -0,0 +1,14 @@
+// 处理关键词数据 转化成美元分类的关键词数组
+export function formatKeywordsList (res) {
+  if (!res || !res.member_jy.a_items) return
+  const data = res.member_jy.a_items
+  const newArr = []
+  data.forEach((v) => {
+    if (v.a_key) {
+      v.a_key.forEach((s) => {
+        newArr.push(s)
+      })
+    }
+  })
+  return newArr
+}

+ 50 - 0
src/utils/functions/limit-input-numbers.js

@@ -0,0 +1,50 @@
+// FROM: https://juejin.im/post/5b6b9308f265da0f4e62fecc
+// demo: http://192.168.3.207:10080/jianyu/page_h5/src/master/src/components/screen_price.vue
+// limit-input-number.js
+
+// 设置组件中的指定属性的值
+const setValue = function (exp, value, context) {
+  value = isNaN(value) ? '' : value
+  // eslint-disable-next-line
+  new Function ('context', 'value', `context.${exp} = value`)(context, value)
+}
+
+// 自定义指令
+export default {
+  bind (el, { expression }, { context }) {
+    // 初始化lastValue
+    el.lastValue = el.value
+    // 通过dataset 判断是否允许小数点
+    const allowDot = !!el.dataset.dotrange
+    const dotRange = el.dataset.dotrange || '{0,2}' // 默认
+    const pattern = `^[0-9]+${allowDot ? `(.[0-9]${dotRange})?` : ''}$`
+
+    if (!expression) {
+      throw new TypeError('请绑定expression')
+    }
+
+    el.handleInputEvent = function (e) {
+      setTimeout(() => {
+        if (e.target.value === '') {
+          setValue(expression, '', context)
+          // 遇到非法数值,则重置
+          e.target.value = ''
+        } else if (e.target.value !== '' && !new RegExp(pattern).test(e.target.value)) {
+          setValue(expression, parseFloat(e.target.lastValue), context)
+          // 遇到非法数值,则重置为lastValue
+          e.target.value = e.target.lastValue
+          if (allowDot) {
+            // 这个时候,根据业务逻辑,可以收起键盘,给出吐司提示
+            e.target.blur()
+            // context.showToast(`小数点后最多${dotRange.replace(/[}{]/g, '').split(',')[1]}位`)
+          }
+        }
+        e.target.lastValue = e.target.value
+      }, 0)
+    }
+    el.addEventListener('input', el.handleInputEvent, false)
+  },
+  unbind (el) {
+    el && el.removeEventListener('input', el.handleInputEvent, false)
+  }
+}

+ 28 - 0
src/utils/functions/syncSessionStorage.js

@@ -0,0 +1,28 @@
+const syncKey = 'syncSessionStorage'
+const sessKey = 'sessionStorage'
+// 实时同步 sessionStorage 的 key
+const syncKeyList = ['visited-path-list']
+
+// 新打开一个tab标签页并通知其他标签页同步sessionStorage的数据到本标签页
+if (!sessionStorage.length) {
+  // 这个调用能触发storage事件,从而达到共享数据的目的
+  localStorage.setItem(syncKey, Date.now())
+}
+
+window.addEventListener('storage', function (e) {
+  if (e.key === syncKey) {
+    // 已存在的标签页会收到这个事件
+    localStorage.setItem(sessKey, JSON.stringify(sessionStorage))
+    localStorage.removeItem(sessKey)
+  } else if (e.key === sessKey && !sessionStorage.length) {
+    // 新开启的标签页会收到这个事件
+    const data = JSON.parse(e.newValue)
+    for (const k in data) {
+      sessionStorage.setItem(k, data[k])
+    }
+  } else if (syncKeyList.indexOf(e.key) !== -1) {
+    // if (e.newValue) {
+    //   sessionStorage.setItem(e.key, e.newValue)
+    // }
+  }
+})

+ 26 - 0
src/utils/globalDirectives.js

@@ -0,0 +1,26 @@
+import Vue from 'vue'
+import limitInputNumbers from '@/utils/functions/limit-input-numbers'
+
+// 输入数字
+Vue.directive('limit-input-numbers', limitInputNumbers)
+
+// Vue自定义指令文档:https://cn.vuejs.org/v2/guide/custom-directive.html
+Vue.directive('auto-focus', {
+  // 当被绑定的元素插入到 DOM 中时……
+  inserted: function (el) {
+    const autoFocusArr = ['input', 'textarea']
+    const tagName = el.nodeName.toLowerCase()
+    const isTag = autoFocusArr.find((item) => {
+      return item === tagName
+    })
+
+    setTimeout(() => {
+      if (isTag) {
+        el.focus()
+      } else {
+        const dom = el.querySelector('.van-field__control')
+        dom.focus()
+      }
+    }, 30)
+  }
+})

+ 33 - 0
src/utils/globalFilters.js

@@ -0,0 +1,33 @@
+import Vue from 'vue'
+import {
+  dateFormatter,
+  dateFromNow,
+  addSpaceForTel,
+  upPrice,
+  formatPrice,
+  fen2Yuan,
+  addSpaceForBank,
+  addConfusionForBank,
+  formatSize
+} from './globalFunctions'
+
+// 注册全局过滤器
+Vue.filter('sizeFormatter', formatSize)
+// 时间格式化(同时间格式化函数)
+Vue.filter('dateFormatter', dateFormatter)
+// 时间戳转换 多少秒、多少分、多少小时前、多少天前  超出10天显示年月日
+Vue.filter('dateFromNow', dateFromNow)
+// 手机号加空格
+Vue.filter('addSpaceForTel', addSpaceForTel)
+
+// 分转元
+Vue.filter('fen2Yuan', fen2Yuan)
+// 金额大写
+Vue.filter('upPrice', upPrice)
+// 金额3位逗号分隔
+Vue.filter('formatPrice', formatPrice)
+
+// 银行卡号字符串加空格
+Vue.filter('addSpaceForBank', addSpaceForBank)
+// 银行卡加*
+Vue.filter('addConfusionForBank', addConfusionForBank)

+ 590 - 0
src/utils/globalFunctions.js

@@ -0,0 +1,590 @@
+// 字符串处理相关函数
+// 手机号中间4位加* ------------>
+export function addConfusionForTel (tel) {
+  const reg = /^(\d{3})\d{4}(\d{4})$/
+  return tel.replace(reg, '$1****$2')
+}
+// 手机号加空格 ------------>
+export function addSpaceForTel (tel) {
+  const regMap = {
+    isConfuse: /^(\d{3})\*{4}(\d{4})$/,
+    addSpace: /^(\d{3})(\d{4})(\d{4})$/
+  }
+  const confusion = regMap.isConfuse.test(tel)
+  if (confusion) {
+    return tel.replace(regMap.isConfuse, '$1 **** $2')
+  } else {
+    return tel.replace(regMap.addSpace, '$1 $2 $3')
+  }
+}
+// 银行卡加空格
+export function addSpaceForBank (v, hasConfusion = false) {
+  // 无*银行卡号加空格
+  if (hasConfusion) {
+    // 带有*的银行卡号字符串加空格
+    return v.replace(/\s/g, '').replace(/(.{4})/g, '$1 ')
+  } else {
+    // 纯数字银行卡号字符串加空格
+    return v.replace(/\s/g, '').replace(/(\d{4})(?=\d)/g, '$1 ')
+  }
+}
+// 银行卡加 **
+export function addConfusionForBank (v) {
+  if (String(v).length < 12) {
+    return v.replace(/\s/g, '').replace(/^(\d{2})\d+(\d{2})$/, '$1 **** **** $2')
+  } else {
+    return v.replace(/\s/g, '').replace(/^(\d{4})\d+(\d{4})$/, '$1 **** **** $2')
+  }
+}
+
+// 金额处理
+// 分转元
+export function fen2Yuan (v) {
+  return v / 100
+}
+
+// 金额大写,链接:https://juejin.im/post/5a2a7a5051882535cd4abfce
+// upDigit(1682) result:"人民币壹仟陆佰捌拾贰元整"
+// upDigit(-1693) result:"欠壹仟陆佰玖拾叁元整"
+export function upPrice (n) {
+  const fraction = ['角', '分', '厘']
+  const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
+  const unit = [
+    ['元', '万', '亿'],
+    ['', '拾', '佰', '仟']
+  ]
+  // const head = n < 0 ? '欠人民币' : '人民币'
+  const head = ''
+  n = Math.abs(n)
+  let s = ''
+  for (let i = 0; i < fraction.length; i++) {
+    s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '')
+  }
+  s = s || '整'
+  n = Math.floor(n)
+  for (let i = 0; i < unit[0].length && n > 0; i++) {
+    let p = ''
+    for (let j = 0; j < unit[1].length && n > 0; j++) {
+      p = digit[n % 10] + unit[1][j] + p
+      n = Math.floor(n / 10)
+    }
+    s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s
+    // s = p + unit[0][i] + s;
+  }
+  return head + s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整')
+}
+
+// 金额3位逗号分隔  ------------>
+/**
+ * @param s 要格式化的数字(四舍五入)
+ * @param n 保留几位小数(不传或者传-1 --> 如果为整数,则不保留小数。如果为浮点数,则保留两位小数)
+ * @param comma 是否小数点前每3位添加逗号
+ */
+export function newFormat (s = 0, n = -1, comma = false) {
+  n = n === -1 ? 0 : n
+  if (n > 20 || n < -1) {
+    n = 2
+  }
+  s = Number(s)
+  return s.toLocaleString('zh-CN', {
+    style: 'decimal',
+    useGrouping: comma,
+    minimumFractionDigits: n,
+    maximumFractionDigits: n
+  })
+}
+export function formatPrice (s, n = -1, comma = false) {
+  // 如果不传s或者s为空,则直接返回0
+  if (!s) return 0
+
+  if (n !== -1) n = n > 0 && n <= 20 ? n : 2
+  const intS = parseInt(String(s))
+  let point = '.'
+  let left = []
+  let right = ''
+  s = parseFloat((s + '').replace(/[^\d.-]/g, ''))
+  // 没传n或者n为-1,默认(如果为整数,则不保留小数。如果为浮点数,则保留两位小数)
+  if (n === -1) {
+    if (s === intS) {
+      n = 0
+      right = ''
+      point = ''
+    } else {
+      n = 2
+      s = s.toFixed(n)
+      right = s.split('.')[1]
+    }
+    s = s + ''
+    left = s.split('.')[0].split('').reverse()
+  } else {
+    s = parseFloat((s + '').replace(/[^\d.-]/g, '')).toFixed(n) + ''
+    left = s.split('.')[0].split('').reverse()
+    right = s.split('.')[1]
+  }
+
+  if (comma) {
+    let t = ''
+    for (let i = 0; i < left.length; i++) {
+      t += left[i] + ((i + 1) % 3 === 0 && (i + 1) !== left.length ? ',' : '')
+    }
+    return t.split('').reverse().join('') + point + right
+  }
+
+  return left.reverse().join('') + point + right
+}
+
+// 时间格式化相关函数
+/*
+* 时间格式化函数(将时间格式化为,2019年08月12日,2019-08-12,2019/08/12的形式)
+*   pattern参数(想要什么格式的数据就传入什么格式的数据)
+*     · 'yyyy-MM-dd'  ---> 输出如2019-09-20
+*     · 'yyyy-MM-dd HH:mm'  --->  输出如2019-09-20 18:20
+*     · 'yyyy-MM-dd HH:mm:ss'  --->  输出如2019-09-20 06:20:23
+*     · 'yyyy/MM/dd'  --->  输出如2019/09/20
+*     · 'yyyy年MM月dd日'  --->  输出如2019年09月20日
+*     · 'yyyy年MM月dd日 HH时mm分'  --->  输出如2019年09月20日 18时20分
+*     · 'yyyy年MM月dd日 HH时mm分ss秒'  --->  输出如2019年09月20日 18时20分23秒
+*     · 'yyyy年MM月dd日 HH时mm分ss秒 EE'  --->  输出如2019年09月20日 18时20分23秒 周二
+*     · 'yyyy年MM月dd日 HH时mm分ss秒 EEE'  --->  输出如2019年09月20日 18时20分23秒 星期二
+*  参考: https://www.cnblogs.com/mr-wuxiansheng/p/6296646.html
+*/
+export function dateFormatter (date, fmt = 'yyyy-MM-dd HH:mm:ss') {
+  // 将传入的date转为时间对象
+  if (!date) return ''
+  date = new Date(date)
+  const o = {
+    'y+': date.getFullYear(),
+    'M+': date.getMonth() + 1, // 月份
+    'd+': date.getDate(), // 日
+    // 12小时制
+    'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, // 小时
+    // 24小时制
+    'H+': date.getHours(), // 小时
+    'm+': date.getMinutes(), // 分
+    's+': date.getSeconds(), // 秒
+    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
+    S: date.getMilliseconds(), // 毫秒
+    'E+': date.getDay() // 周
+  }
+  const week = ['日', '一', '二', '三', '四', '五', '六']
+
+  if (/(y+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
+  }
+  if (/(E+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '星期' : '周') : '') + week[date.getDay()])
+  }
+  for (const k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) {
+      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
+    }
+  }
+  return fmt
+}
+
+export const debounce = (func, delay = 200, immediate) => {
+  let timer = null
+  return function () {
+    const context = this
+    const args = arguments
+    if (timer) clearTimeout(timer)
+    if (immediate) {
+      const doNow = !timer
+      timer = setTimeout(function () {
+        timer = null
+      }, delay)
+      if (doNow) {
+        func.apply(context, args)
+      }
+    } else {
+      timer = setTimeout(function () {
+        func.apply(context, args)
+      }, delay)
+    }
+  }
+}
+
+// 时间戳转换 多少秒、多少分、多少小时前、多少天前  超出10天显示年月日
+// 传入一个时间戳
+export function dateFromNow (originTime, useOld = false) {
+  if (!originTime) return
+  // 原始时间 - 传入的时间戳
+  const originTimeStamp = +new Date(originTime)
+  // 当前时间戳
+  const nowTimeStamp = +new Date()
+  // 时间戳相差多少
+  const diffTimeStamp = nowTimeStamp - originTimeStamp
+
+  const postfix = diffTimeStamp > 0 ? '前' : '后'
+  // 求绝对值 ms(毫秒)
+  const diffTimeStampAbsMs = Math.abs(diffTimeStamp)
+  const diffTimeStampAbsS = Math.round(diffTimeStampAbsMs / 1000)
+
+  // 10天的秒数
+  const days11 = 11 * 24 * 60 * 60
+
+  const dataMap = {
+    zh: ['天', '小时', '分钟', '秒'],
+    number: [24 * 60 * 60, 60 * 60, 60, 1]
+  }
+
+  let timeString = ''
+  // 10天前
+  const tenDaysAgo = diffTimeStampAbsS > days11
+  // 是否是当天
+  const isCurrentDay = dateFormatter(originTimeStamp, 'yyyy.MM.dd') === dateFormatter(nowTimeStamp, 'yyyy.MM.dd')
+
+  let condition = !isCurrentDay
+  if (useOld) {
+    condition = tenDaysAgo
+  }
+
+  if (condition) {
+    // 不是当天,则使用正常日期显示
+    const originDate = new Date(originTimeStamp)
+    const nowDate = new Date()
+    // 是否同年
+    const sameYear = originDate.getFullYear() === nowDate.getFullYear()
+    // 如果是当年,则不显示年
+    const patternString = sameYear ? 'MM-dd' : 'yyyy-MM-dd'
+    timeString = dateFormatter(originDate, patternString)
+  } else {
+    for (let i = 0; i < dataMap.number.length; i++) {
+      const inm = Math.floor(diffTimeStampAbsS / dataMap.number[i])
+      if (inm !== 0) {
+        timeString = inm + dataMap.zh[i] + postfix
+        break
+      }
+    }
+  }
+  return timeString
+}
+
+// 文件大小格式化
+export function formatSize (size, pointLength, units) {
+  size = Number(size)
+  let unit = ''
+  units = units || ['B', 'K', 'M', 'G', 'TB']
+  while ((unit = units.shift()) && size > 1024) {
+    size = size / 1024
+  }
+  return (unit === 'B' ? size : size.toFixed(pointLength === undefined ? 2 : pointLength)) + (unit || '')
+}
+
+// 金额类型转换
+export function moneyUnit (m, type = 'string', lv = 0) {
+  const mUnit = {
+    levelArr: ['元', '万', '亿', '万亿'],
+    test (num, type, lv) {
+      if (num === 0) {
+        if (type === 'string') {
+          return '0元'
+        }
+        if (type === 'lv') {
+          return this.levelArr[lv]
+        }
+        if (type === 'number') {
+          return 0
+        }
+        if (type === 'index') {
+          return lv
+        }
+        if (type === 'transfer') {
+          return 0
+        }
+      }
+
+      const result = num / Math.pow(10000, lv)
+
+      if (result > 10000 && lv < 2) {
+        return this.test(num, type, lv + 1)
+      } else {
+        if (type === 'string') {
+          return String(Math.floor(result * 100) / 100).replace('.00', '') + this.levelArr[lv]
+        }
+        if (type === 'lv') {
+          return this.levelArr[lv]
+        }
+        if (type === 'number') {
+          return String(Math.floor(result * 100) / 100).replace('.00', '')
+        }
+        if (type === 'index') {
+          return lv
+        }
+      }
+    },
+    // 需要传入固定的lv(此时lv为 levelArr 中的一个)
+    transfer (num, lvString) {
+      const index = this.levelArr.indexOf(lvString)
+      if (index === -1 || index === 0) {
+        return num
+      } else {
+        return (num / Math.pow(10000, index)).toFixed(2) + lvString
+      }
+    }
+  }
+  if (m === undefined || m === null) {
+    return ''
+  } else {
+    if (type === 'transfer') {
+      return mUnit.transfer(m, lv)
+    } else {
+      return mUnit.test(m, type, lv)
+    }
+  }
+}
+
+/**
+ * 通用关键字高亮替换
+ * @param {String} value 要高亮的字符串
+ * @param {String|Array} oldChar 要被替换的字符串(或数组)
+ * @param {String|Array} newChar 要替换成的字符串(或数组)
+ *
+ * 比如:要将 - `剑鱼标讯工具函数` 字符串中的 `工具` 高亮
+ * 则此时 value -> `剑鱼标讯工具函数`
+ *        oldChar -> `工具`
+ *        newChar -> `<span class="highlight-text">工具</span>`
+ *
+ * 批量高亮-----
+ * 比如:要将 - `剑鱼标讯工具函数` 字符串中的 `工具` `剑鱼` 高亮
+ * 则此时 value -> `剑鱼标讯工具函数`批量高亮
+ *        oldChar -> ['工具', '剑鱼']
+ *        newChar -> ['<span class="highlight-text">', '</span>']
+ *
+ *   注意:此时newChar为一个长度为2的数组,数组中为高亮标签的起始标签和结束标签
+ *
+ */
+export function replaceKeyword (value, oldChar, newChar) {
+  if (!oldChar || !newChar) return value
+  // oldChar的字符串数组,用来循环替换
+  let oldCharArr = []
+
+  if (Array.isArray(oldChar)) {
+    oldCharArr = oldChar.concat()
+  } else {
+    oldCharArr.push(oldChar)
+  }
+
+  // 数组去重
+  oldCharArr = Array.from(new Set(oldCharArr))
+
+  try {
+    oldCharArr.forEach(function (item) {
+      // 去空格之后为空字符串,则直接跳过当前替换
+      if (item && item.replace(/\s+/g, '')) {
+        let oc = item
+        oc = oc.replace(/\$/g, '\\$')
+          .replace(/\(/g, '\\(')
+          .replace(/\)/g, '\\)')
+          .replace(/\*/g, '\\*')
+          .replace(/\+/g, '\\+')
+          .replace(/\./g, '\\.')
+          .replace(/\[/g, '\\[')
+          .replace(/\]/g, '\\]')
+          .replace(/\?/g, '\\?')
+          .replace(/\\/g, '\\')
+          .replace(/\//g, '\\/')
+          .replace(/\^/g, '\\^')
+          .replace(/\{/g, '\\{')
+          .replace(/\}/g, '\\}')
+          .replace(/\|/g, '\\|')
+
+        if (Array.isArray(newChar)) {
+          // 批量高亮
+          const tempV = value
+          value = value.replace(new RegExp('(' + oc + ')', 'gmi'), newChar[0] + oc + newChar[1])
+          if (value === tempV && oc.indexOf('+') !== -1) {
+            const splitReg = oc.split('\\+')
+            splitReg.map((v) => {
+              value = value.replace(new RegExp('(' + v + ')', 'gmi'), newChar[0] + v + newChar[1])
+              return value
+            })
+          }
+        } else {
+          // 普通单个高亮
+          value = value.replace(new RegExp('(' + oc + ')', 'gmi'), newChar)
+        }
+      }
+    })
+  } catch (e) {
+    console.log(e)
+    return value
+  }
+  return value
+}
+
+// 获取随机字符串
+// 不传参数则获取长度不固定的字符串
+export const getRandomString = (len) => {
+  let randomString = ''
+  if (len) {
+    /** 默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1 **/
+    const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
+    const maxPos = $chars.length
+    for (let i = 0; i < len; i++) {
+      randomString += $chars.charAt(Math.floor(Math.random() * maxPos))
+    }
+  } else {
+    // Math.random()  生成随机数字, eg: 0.123456
+    // .toString(36)  转化成36进制 : "0.4fzyo82mvyr"
+    // .substring(2)  去掉前面两位 : "yo82mvyr"
+    // .slice(-8)  截取最后八位 : "yo82mvyr"
+    randomString = Math.random().toString(36).substring(2)
+  }
+  return randomString
+}
+
+export function getParam (name) {
+  let reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
+  let r = window.location.search.substr(1).match(reg)
+  let context = '' // eslint-disable-line no-unused-vars
+  if (r !== null) context = r[2]
+  // 释放变量
+  reg = null
+  r = null
+  return context === null || context === '' || context === 'undefined' ? '' : context
+}
+
+/* eslint-disable */
+Number.prototype.fixed = function (len) {
+  len = isNaN(len) ? 0 : len
+  const num = Math.pow(10, len)
+  return Math.round(this * num) / num
+}
+
+/* eslint-disable */
+Date.prototype.pattern = function (fmt) {
+  if (!fmt) return ''
+  const o = {
+    'y+': this.getFullYear(),
+    'M+': this.getMonth() + 1, // 月份
+    'd+': this.getDate(), // 日
+    // 12小时制
+    'h+': this.getHours() % 12 === 0 ? 12 : this.getHours() % 12, // 小时
+    // 24小时制
+    'H+': this.getHours(), // 小时
+    'm+': this.getMinutes(), // 分
+    's+': this.getSeconds(), // 秒
+    'q+': Math.floor((this.getMonth() + 3) / 3), // 季度
+    'S': this.getMilliseconds(), // 毫秒
+    'E+': this.getDay() // 周
+  }
+  const week = {
+    '0': '日',
+    '1': '一',
+    '2': '二',
+    '3': '三',
+    '4': '四',
+    '5': '五',
+    '6': '六'
+  }
+  if (/(y+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length))
+  }
+  if (/(E+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '星期' : '周') : '') + week[this.getDay() + ''])
+  }
+  for (var k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) {
+      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
+    }
+  }
+  return fmt
+}
+
+export function bSort (arr, value) {
+  const len = arr.length
+  for (let i = 0; i < len - 1; i++) {
+    for (let j = 0; j < len - 1 - i; j++) {
+      // 相邻元素两两对比,元素交换,大的元素交换到后面
+      if (arr[j][value] > arr[j + 1][value]) {
+        const temp = arr[j]
+        arr[j] = arr[j + 1]
+        arr[j + 1] = temp
+      }
+    }
+  }
+  return arr
+}
+
+export function recoveryPageData (key, defaultValues = {}) {
+  return JSON.parse(localStorage.getItem(key) || JSON.stringify(defaultValues))
+}
+export function defaultLocalPageData (key, defaultValues = {}) {
+  return JSON.parse(localStorage.getItem(key) || JSON.stringify(defaultValues))
+}
+
+function dateTime(date, fmt) {
+  if (fmt === void 0) {
+    fmt = 'yyyy年MM月dd日 hh:mm';
+  }
+
+  // 时间戳转换
+  if (!date) return '';
+  var d = new Date(date);
+  var o = {
+    "M+": d.getMonth() + 1,
+    //月份
+    "d+": d.getDate(),
+    //日
+    "h+": d.getHours(),
+    //小时
+    "m+": d.getMinutes(),
+    //分
+    "s+": d.getSeconds() //秒
+
+  }; // 根据y的长度来截取年
+
+  if (/(y+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, (d.getFullYear() + "").substr(4 - RegExp.$1.length));
+  }
+
+  for (var k in o) {
+    if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
+  }
+
+  return fmt;
+}
+
+function dateYear() {
+  // 获取当年的第一天
+  var date = new Date();
+  date.setDate(1);
+  date.setMonth(0);
+  return date.getTime();
+}
+
+function dateLast() {
+  // 获取昨天
+  var day = new Date();
+  day.setTime(day.getTime() - 24 * 60 * 60 * 1000);
+  return dateTime(day, 'yyyy/MM/dd');
+}
+
+export function dateMatter(time, drag = 'normal') {
+  // 时间转换
+  var timeStr = '';
+  var currentDay = dateTime(time, 'yyyy/MM/dd'); // 当前日期
+
+  var lingDay = new Date(new Date().toLocaleDateString()).getTime(); // 获取当天0点
+
+  var isTime = new Date(time) - lingDay;
+  var diff = isTime < 0 ? '昨天' : '';
+  var isDate = new Date(time) - dateYear();
+
+  if (isDate < 0) {
+    drag == 'normal' ? timeStr = dateTime(time) : timeStr = dateTime(time, 'yyyy年MM月dd日');
+  } else {
+    if (currentDay === dateLast()) {
+      drag == 'normal' ? timeStr = diff + dateTime(time, 'hh:mm') : timeStr = diff;
+    } else {
+      if (isTime < 0) {
+        drag == 'normal' ? timeStr = dateTime(time, 'MM月dd日 hh:mm') : timeStr = dateTime(time, 'MM月dd日');
+      } else {
+        timeStr = dateTime(time, 'hh:mm');
+      }
+    }
+  }
+
+  return timeStr;
+}

+ 5 - 0
src/utils/index.js

@@ -0,0 +1,5 @@
+import './globalDirectives'
+import './globalFilters'
+import './functions/syncSessionStorage'
+
+export * from './globalFunctions'

+ 0 - 5
src/views/AboutView.vue

@@ -1,5 +0,0 @@
-<template>
-  <div class="about">
-    <h1>This is an about page</h1>
-  </div>
-</template>

+ 16 - 0
src/views/ChatView.vue

@@ -0,0 +1,16 @@
+<template>
+  <div>
+    <h1>聊天内容中间区域</h1>
+    <div>底部输入框</div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ChatView'
+}
+</script>
+
+<style scoped>
+
+</style>

+ 15 - 0
src/views/CustomerServiceView.vue

@@ -0,0 +1,15 @@
+<template>
+  <chat-view></chat-view>
+</template>
+
+<script>
+import ChatView from '@/views/ChatView'
+export default {
+  name: 'CustomerServiceView',
+  components: { ChatView }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 117 - 0
src/views/CustomerView.vue

@@ -0,0 +1,117 @@
+<template>
+  <el-container class="message--layout">
+    <el-aside class="message--aside-group" width="64px">
+      <el-menu
+        :collapse="true"
+        default-active="2"
+        class="el-menu-vertical-demo"
+      >
+        <el-menu-item index="1">
+          <img class="logo" src="https://www.jianyu360.cn/common-module/public/image/auto.png">
+        </el-menu-item>
+        <el-menu-item index="1">
+          <el-badge :value="unReadCount">
+            <i class="el-icon-chat-dot-square"></i>
+          </el-badge>
+        </el-menu-item>
+      </el-menu>
+    </el-aside>
+    <el-main class="message--main">
+      <el-container class="message--list-group">
+        <message-list title="会话中" :list="CommunicateList"></message-list>
+        <message-list title="排队中(2)" :list="CommunicateList"></message-list>
+      </el-container>
+      <el-container class="message--chat-group">
+        <chat-view></chat-view>
+      </el-container>
+      <el-container class="message--info-group">
+        <customer-info></customer-info>
+      </el-container>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { Aside, Container, Main, Menu, MenuItem, Avatar, Badge } from 'element-ui'
+import ChatView from '@/views/ChatView'
+import CustomerInfo from '@/components/CustomerInfo'
+import MessageList from '@/components/MessageList'
+import { mapState, mapGetters } from 'vuex'
+export default {
+  name: 'CustomerView',
+  components: {
+    MessageList,
+    CustomerInfo,
+    ChatView,
+    [Container.name]: Container,
+    [Main.name]: Main,
+    [Aside.name]: Aside,
+    [Menu.name]: Menu,
+    [MenuItem.name]: MenuItem,
+    [Avatar.name]: Avatar,
+    [Badge.name]: Badge
+  },
+  data () {
+    return {}
+  },
+  computed: {
+    ...mapState([
+      'CommunicateList'
+    ]),
+    ...mapGetters([
+      'unReadCount'
+    ])
+  }
+}
+</script>
+
+<style scoped lang="scss">
+
+$bg-color: #F2F2F4;
+$bg-white-color: #FFFFFF;
+$bg-chat-color: #F6F6F6;
+
+.message-- {
+  &layout {
+    background-color: $bg-color;
+    width: 100%;
+    height: 100%;
+  }
+  &main {
+    display: flex;
+    flex-direction: row;
+    padding: 0;
+  }
+  &aside-group {
+    .el-menu-item {
+      padding-left: 0 !important;
+      padding: 0;
+      text-align: center;
+    }
+    .el-badge {
+      display: inline;
+    }
+    .logo {
+      width: 36px;
+      height: 36px;
+    }
+  }
+  &list-group {
+    flex-direction: column;
+    flex: unset;
+    flex-shrink: 0;
+    width: 256px;
+    background-color: $bg-white-color;
+  }
+  &chat-group {
+    min-width: 560px;
+    background-color: $bg-chat-color;
+  }
+  &info-group {
+    flex: unset;
+    flex-shrink: 0;
+    width: 320px;
+    background-color: $bg-white-color;
+  }
+}
+</style>

+ 0 - 18
src/views/HomeView.vue

@@ -1,18 +0,0 @@
-<template>
-  <div class="home">
-    <img alt="Vue logo" src="../assets/logo.png">
-    <HelloWorld msg="Welcome to Your Vue.js App"/>
-  </div>
-</template>
-
-<script>
-// @ is an alias to /src
-import HelloWorld from '@/components/HelloWorld.vue'
-
-export default {
-  name: 'HomeView',
-  components: {
-    HelloWorld
-  }
-}
-</script>

+ 10 - 1
vue.config.js

@@ -1,4 +1,13 @@
 const { defineConfig } = require('@vue/cli-service')
 module.exports = defineConfig({
-  transpileDependencies: true
+  publicPath: process.env.VUE_APP_BASE_PUBLIC,
+  transpileDependencies: true,
+  lintOnSave: true,
+  css: {
+    loaderOptions: {
+      sass: {
+        additionalData: '@import "@/assets/style/_mixin.scss";@import "@/assets/style/_variables.scss";'
+      }
+    }
+  }
 })

Datei-Diff unterdrückt, da er zu groß ist
+ 661 - 7
yarn.lock


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.