朱鑫波 4 gadi atpakaļ
vecāks
revīzija
124f8a8a19
100 mainītis faili ar 20993 papildinājumiem un 20403 dzēšanām
  1. 1 0
      .env
  2. 0 25
      debug.log
  3. 0 18943
      package-lock.json
  4. 6 2
      package.json
  5. 3 1
      public/color.less
  6. 16 5
      public/index.html
  7. BIN
      public/noImg.png
  8. 6953 0
      public/polyfill.js
  9. 62 46
      src/App.vue
  10. 28 0
      src/api/actor.js
  11. 1 1
      src/api/api.js
  12. 117 0
      src/api/music.js
  13. 7 0
      src/api/yard.js
  14. BIN
      src/assets/background/bg-1.jpg
  15. BIN
      src/assets/background/bg-2.jpg
  16. BIN
      src/assets/img/album_cover_player.png
  17. BIN
      src/assets/img/default.png
  18. BIN
      src/assets/img/player_cover.png
  19. BIN
      src/assets/img/wave.gif
  20. BIN
      src/assets/logoNew.png
  21. BIN
      src/assets/logoNew1.png
  22. BIN
      src/assets/noImg.png
  23. BIN
      src/assets/orderIcon.png
  24. BIN
      src/assets/success.png
  25. BIN
      src/assets/touxiang.png
  26. BIN
      src/assets/work.png
  27. 230 0
      src/base/mm-dialog/mm-dialog.vue
  28. 53 0
      src/base/mm-icon/mm-icon.vue
  29. 86 0
      src/base/mm-loading/mm-loading.vue
  30. 34 0
      src/base/mm-no-result/mm-no-result.vue
  31. 178 0
      src/base/mm-progress/mm-progress.vue
  32. 39 0
      src/base/mm-toast/index.js
  33. 73 0
      src/base/mm-toast/mm-toast.vue
  34. 3 1
      src/components/DatePicker.vue
  35. 36 0
      src/components/bigImg.vue
  36. 524 0
      src/components/calendar/calendar.js
  37. 809 0
      src/components/calendar/calendar.vue
  38. 7 4
      src/components/ctop/ActorModal.vue
  39. 186 0
      src/components/ctop/propModal.vue
  40. 553 0
      src/components/ctop/vm-card-prop.vue
  41. 120 19
      src/components/ctop/vm-card.vue
  42. 21 9
      src/components/ctop/vm-image-list.vue
  43. 323 0
      src/components/editableTree.vue
  44. 326 0
      src/components/formComponents/Cascader.vue
  45. 330 0
      src/components/formComponents/checkBoxGroup.vue
  46. 237 0
      src/components/formComponents/radioCheck.vue
  47. 412 0
      src/components/formComponents/selectGroup.vue
  48. 427 0
      src/components/formComponents/selectRegion.vue
  49. 75 0
      src/components/formComponents/timeRange.vue
  50. 314 0
      src/components/formComponents/toutiaoTime.vue
  51. 21 4
      src/components/layouts/TabLayout.vue
  52. 4 5
      src/components/layouts/UserLayout.vue
  53. 174 0
      src/components/lyric/lyric.vue
  54. 58 26
      src/components/menu/SideMenu.vue
  55. 229 0
      src/components/mm-header/mm-header.vue
  56. 81 0
      src/components/music-btn/music-btn.vue
  57. 377 0
      src/components/music-list/music-list.vue
  58. 1 1
      src/components/page/GlobalFooter.vue
  59. 12 6
      src/components/page/GlobalHeader.vue
  60. 24 8
      src/components/page/GlobalLayout.vue
  61. 2 2
      src/components/table/index.js
  62. 1 1
      src/components/tools/Breadcrumb.vue
  63. 2 2
      src/components/tools/HeaderNotice.vue
  64. 27 4
      src/components/tools/Logo.vue
  65. 1 1
      src/components/tools/UserPassword.vue
  66. 226 204
      src/components/uploadFile.vue
  67. 3735 0
      src/components/vend-ztree.all.js
  68. 82 0
      src/components/volume/volume.vue
  69. 36 0
      src/config.js
  70. 98 0
      src/directive.js
  71. 301 0
      src/filters.js
  72. 20 8
      src/main.js
  73. 48 15
      src/mixins/JeecgListMixin.js
  74. 0 2
      src/permission.js
  75. 102 0
      src/store/actions.js
  76. 27 6
      src/store/getters.js
  77. 8 11
      src/store/index.js
  78. 2 2
      src/store/modules/app.js
  79. 15 0
      src/store/modules/music.js
  80. 3 3
      src/store/modules/permission.js
  81. 12 1
      src/store/mutation-types.js
  82. 38 0
      src/store/mutations.js
  83. 98 0
      src/styles/index.less
  84. 13 0
      src/styles/mixin.less
  85. 141 0
      src/styles/reset.less
  86. 52 0
      src/styles/var.less
  87. 342 0
      src/utils/Tools.js
  88. 3 3
      src/utils/authFilter.js
  89. 23 0
      src/utils/axiosMusic.js
  90. 1 1
      src/utils/getDay.js
  91. 53 1
      src/utils/mixin.js
  92. 13 9
      src/utils/request.js
  93. 70 0
      src/utils/song.js
  94. 136 0
      src/utils/storage.js
  95. 110 1
      src/utils/util.js
  96. 589 484
      src/views/activiti/ProcessModelList.vue
  97. 220 149
      src/views/activiti/applyHome.vue
  98. 504 387
      src/views/activiti/applyList.vue
  99. 268 0
      src/views/activiti/customerForm.vue
  100. 0 0
      src/views/activiti/doneManage.vue

+ 1 - 0
.env

@@ -0,0 +1 @@
+VUE_APP_BASE_API_URL = http://118.24.244.213:3030

+ 0 - 25
debug.log

@@ -1,25 +0,0 @@
-[0525/133323.308:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0
-[0529/110148.397:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0
-[0529/112937.608:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0
-[0619/110835.729:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0
-[0701/153438.192:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0
-[0708/055752.148:ERROR:process_info.cc(375)] NtQuerySystemInformation SystemExtendedHandleInformation: {没锟斤拷锟姐够锟斤拷锟絵 没锟斤拷锟姐够锟斤拷锟斤拷锟斤拷锟节达拷锟斤拷页锟侥硷拷锟斤拷锟斤拷锟斤拷锟斤拷指锟斤拷锟侥诧拷锟斤拷锟斤拷 (0xc0000017)
-[0708/055807.088:ERROR:process_info.cc(359)] UncheckedAllocate
-[0713/163350.208:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0
-[0716/000711.554:ERROR:process_reader_win.cc(123)] NtOpenThread: {无法访问} 过程已请求访问一对象,但未给访问权限。 (0xc0000022)
-[0716/000711.558:ERROR:exception_snapshot_win.cc(98)] thread ID 12948 not found in process
-[0716/000711.559:ERROR:process_reader_win.cc(123)] NtOpenThread: {无法访问} 过程已请求访问一对象,但未给访问权限。 (0xc0000022)
-[0716/000711.560:ERROR:exception_snapshot_win.cc(98)] thread ID 15736 not found in process
-[0716/000711.577:ERROR:process_reader_win.cc(123)] NtOpenThread: {无法访问} 过程已请求访问一对象,但未给访问权限。 (0xc0000022)
-[0716/000711.577:ERROR:exception_snapshot_win.cc(98)] thread ID 2072 not found in process
-[0716/000711.592:ERROR:process_reader_win.cc(123)] NtOpenThread: {无法访问} 过程已请求访问一对象,但未给访问权限。 (0xc0000022)
-[0716/000711.593:ERROR:exception_snapshot_win.cc(98)] thread ID 12744 not found in process
-[0716/000711.593:ERROR:process_reader_win.cc(123)] NtOpenThread: {无法访问} 过程已请求访问一对象,但未给访问权限。 (0xc0000022)
-[0716/000711.593:ERROR:exception_snapshot_win.cc(98)] thread ID 17220 not found in process
-[0716/000711.615:ERROR:process_reader_win.cc(123)] NtOpenThread: {无法访问} 过程已请求访问一对象,但未给访问权限。 (0xc0000022)
-[0716/000711.615:ERROR:exception_snapshot_win.cc(98)] thread ID 18896 not found in process
-[0716/000711.616:ERROR:process_reader_win.cc(123)] NtOpenThread: {无法访问} 过程已请求访问一对象,但未给访问权限。 (0xc0000022)
-[0716/000711.617:ERROR:exception_snapshot_win.cc(98)] thread ID 15368 not found in process
-[0727/154120.269:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0
-[0727/173939.302:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0
-[0729/144506.169:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 18943
package-lock.json


+ 6 - 2
package.json

@@ -48,6 +48,7 @@
     "vue-count-to": "^1.0.13",
     "vue-cropper": "^0.4.8",
     "vue-i18n": "^8.7.0",
+    "vue-infinite-scroll": "^2.0.2",
     "vue-layer": "^1.0.2",
     "vue-loader": "^15.7.0",
     "vue-ls": "^3.2.0",
@@ -62,7 +63,8 @@
     "vue-waterfall-easy": "^2.4.3",
     "vuedraggable": "^2.20.0",
     "vuex": "^3.0.1",
-    "vuex-class": "^0.3.1"
+    "vuex-class": "^0.3.1",
+    "vue-lazyload": "^1.3.3"
   },
   "devDependencies": {
     "@babel/polyfill": "^7.2.5",
@@ -79,7 +81,9 @@
     "less-loader": "^4.1.0",
     "node-sass": "^4.11.0",
     "sass-loader": "^7.0.1",
-    "vue-template-compiler": "^2.6.10"
+    "vue-template-compiler": "^2.6.10",
+    "style-resources-loader": "^1.3.2",
+    "vue-cli-plugin-style-resources-loader": "^0.1.3"
   },
   "eslintConfig": {
     "root": true,

+ 3 - 1
public/color.less

@@ -2216,7 +2216,9 @@ this.tinycolor = tinycolor;
 }
 
 .layout .header {
-  background-color: @primary-color !important;
+  // background-color:#0B2040  !important;
+  background:#0B2040  !important;
+  
 }
 
 i.trigger:hover {

+ 16 - 5
public/index.html

@@ -6,11 +6,19 @@
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="referrer" content="never">
   <meta name="viewport" content="width=device-width,initial-scale=1.0">
-  <title>智能投放平台</title>
+  <title>有腾投放助手</title>
   <link rel="icon" href="<%= BASE_URL %>logo.png">
   <link href="<%= BASE_URL %>color.less">
-  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.2.5/polyfill.js"></script>
+  <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.2.5/polyfill.js"></script> -->
+  <script src="<%= BASE_URL %>polyfill.js"></script>
   <script src="<%= BASE_URL %>web-office-sdk-v1.1.1.umd.js"></script>
+  <link href="https://fonts.googleapis.com/css?family=Lexend+Deca&display=swap" rel="stylesheet">
+  <link
+  rel="stylesheet"
+  href="//at.alicdn.com/t/font_1367495_eza6utwbiqn.css"
+/>
+
+
   <style>
     html,
     body,
@@ -271,10 +279,13 @@
   <!-- 全局配置 -->
   <script>
     window._CONFIG = {};
-    window._CONFIG['domianURL'] = 'http://39.97.120.42:8080/jeecg-boot';
-    window._CONFIG['imgDomainURL'] = 'http://39.97.120.42:8080/jeecg-boot/sys/common/view';
-    window._CONFIG['pdfDomainURL'] = 'http://39.97.120.42:8080/jeecg-boot/sys/common/pdf/pdfPreviewIframe';
+    // window._CONFIG['domianURL'] = 'http://192.168.1.72:8088/jeecg-boot';
+    window._CONFIG['domianURL'] = 'http://139.186.31.213:8804/jeecg-boot';
+    window._CONFIG['imgDomainURL'] = 'http://139.186.31.213:8804/jeecg-boot/sys/common/view';
+    window._CONFIG['pdfDomainURL'] = 'http://139.186.31.213:8804/jeecg-boot/sys/common/pdf/pdfPreviewIframe';
   </script>
+  <script src="https://unpkg.com/splitting/dist/splitting.min.js"></script>
+
 </head>
 
 <body>

BIN
public/noImg.png


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 6953 - 0
public/polyfill.js


+ 62 - 46
src/App.vue

@@ -2,62 +2,78 @@
   <a-locale-provider :locale="locale">
     <div id="app">
       <router-view />
+      <audio ref="mmPlayer"></audio>
     </div>
   </a-locale-provider>
 </template>
 <script>
-  import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
-  import enquireScreen from '@/utils/device'
-
-  export default {
-    data() {
-      return {
-        locale: zhCN,
+import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
+import enquireScreen from '@/utils/device'
+import { mapGetters, mapMutations, mapActions } from 'vuex'
+export default {
+  data() {
+    return {
+      locale: zhCN,
+    }
+  },
+  created() {
+    let that = this
+    enquireScreen((deviceType) => {
+      // tablet
+      if (deviceType === 0) {
+        that.$store.commit('TOGGLE_DEVICE', 'mobile')
+        that.$store.dispatch('setSidebar', true)
       }
-    },
-    created() {
-      let that = this
-      enquireScreen(deviceType => {
-        // tablet
-        if (deviceType === 0) {
-          that.$store.commit('TOGGLE_DEVICE', 'mobile')
-          that.$store.dispatch('setSidebar', false)
-        }
-        // mobile
-        else if (deviceType === 1) {
-          that.$store.commit('TOGGLE_DEVICE', 'mobile')
-          that.$store.dispatch('setSidebar', false)
-        } else {
-          that.$store.commit('TOGGLE_DEVICE', 'desktop')
-          that.$store.dispatch('setSidebar', true)
-        }
+      // mobile
+      else if (deviceType === 1) {
+        that.$store.commit('TOGGLE_DEVICE', 'mobile')
+        that.$store.dispatch('setSidebar', false)
+      } else {
+        that.$store.commit('TOGGLE_DEVICE', 'desktop')
+        that.$store.dispatch('setSidebar', true)
+      }
+    })
 
-      })
-    }
-  }
+    this.$nextTick(() => {
+      this.setAudioele(this.$refs.mmPlayer)
+    })
+  },
+  methods: {
+    ...mapMutations({
+      setPlaying: 'SET_PLAYING',
+      setPlay: 'SET_PLAYLIST',
+      setCurrentIndex: 'SET_CURRENTINDEX',
+      setAudioele: 'SET_AUDIOELE',
+    }),
+  },
+}
 </script>
 <style>
-  #app {
-    height: 100%;
-  }
+#app {
+  height: 100%;
+}
 
-  body {
-    -webkit-user-select: text !important;
-  }
+body {
+  -webkit-user-select: text !important;
+}
 
-  * {
-    -webkit-user-select: text !important;
-    -moz-user-select: text !important;
-    -ms-user-select: text !important;
-    user-select: text !important;
-  }
+* {
+  -webkit-user-select: text !important;
+  -moz-user-select: text !important;
+  -ms-user-select: text !important;
+  user-select: text !important;
+}
 
-  .ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab {
-    height: 39px !important;
-  }
+.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab {
+  height: 39px !important;
+}
 
-  .ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab-active {
-    height: 39px !important;
-    border-bottom:1px solid #1890ff !important;
-  }
+.ant-tabs.ant-tabs-card .ant-tabs-card-bar .ant-tabs-tab-active {
+  height: 39px !important;
+  border-bottom: 1px solid #1890ff !important;
+}
+.layout .header {
+  background-color: #0b2040 !important;
+  background: #0b2040 !important;
+}
 </style>

+ 28 - 0
src/api/actor.js

@@ -32,6 +32,20 @@ export function actorPhotoDelete(parameter) {
       params: parameter
     })
 }
+export function actorListDelete(parameter) {
+    return axios({
+      url: "/ctop/actor/delete",
+      method: 'delete',
+      params: parameter
+    })
+}
+export function propListDelete(parameter) {
+    return axios({
+      url: "/ctop/prop/delete",
+      method: 'delete',
+      params: parameter
+    })
+}
 
 export function actorPhotoAdd(parameter) {
     return axios({
@@ -64,6 +78,13 @@ export function actorDetail(parameter) {
         params: parameter
     })
 }
+export function propDetail(parameter) {
+    return axios({
+        url: "/ctop/prop/detail",
+        method: 'get',
+        params: parameter
+    })
+}
 
 
 export function actorCommentList(parameter) {
@@ -132,6 +153,13 @@ export function fileInsertV2(parameter) {
         data: parameter
     })
 }
+export function orderFileInsert(parameter) {
+    return axios({
+        url: "/platform/file/uploadVideo",
+        method: 'post',
+        data: parameter
+    })
+}
 
 
 export function fileEdit(parameter) {

+ 1 - 1
src/api/api.js

@@ -76,7 +76,7 @@ export const ajaxGetDictItems = (code, params)=>getAction(`/sys/dict/getDictItem
 function getDictItemsFromCache(dictCode) {
   if (Vue.ls.get(UI_CACHE_DB_DICT_DATA) && Vue.ls.get(UI_CACHE_DB_DICT_DATA)[dictCode]) {
     let dictItems = Vue.ls.get(UI_CACHE_DB_DICT_DATA)[dictCode];
-    console.log("-----------getDictItemsFromCache----------dictCode="+dictCode+"---- dictItems=",dictItems)
+    // console.log("-----------getDictItemsFromCache----------dictCode="+dictCode+"---- dictItems=",dictItems)
     return dictItems;
   }
 }

+ 117 - 0
src/api/music.js

@@ -0,0 +1,117 @@
+import axios from '@/utils/axiosMusic'
+import { defaultLimit } from '@/config'
+import { formatTopSongs } from '@/utils/song'
+
+axios.defaults.baseURL = process.env.VUE_APP_BASE_API_URL
+
+// 排行榜列表
+export function getToplistDetail() {
+  return axios.get('/toplist/detail')
+}
+
+// 推荐歌单
+export function getPersonalized() {
+  return axios.get('/personalized')
+}
+
+// 歌单详情
+export function getPlaylistDetail(id) {
+  return new Promise((resolve, reject) => {
+    axios
+      .get('/playlist/detail', {
+        params: { id }
+      })
+      .then(({ playlist }) => playlist)
+      .then(playlist => {
+        const { trackIds, tracks } = playlist
+        // 过滤完整歌单 如排行榜
+        if (tracks.length === trackIds.length) {
+          playlist.tracks = formatTopSongs(playlist.tracks)
+          resolve(playlist)
+          return
+        }
+        // 限制歌单详情最大 500
+        const ids = trackIds
+          .slice(0, 500)
+          .map(v => v.id)
+          .toString()
+        getMusicDetail(ids).then(({ songs }) => {
+          playlist.tracks = formatTopSongs(songs)
+          resolve(playlist)
+        })
+      })
+  })
+}
+
+// 搜索
+export function search(keywords, page = 0, limit = defaultLimit) {
+  return axios.get('/search', {
+    params: {
+      offset: page * limit,
+      limit: limit,
+      keywords
+    }
+  })
+}
+
+// 热搜
+export function searchHot() {
+  return axios.get('/search/hot')
+}
+
+// 获取用户歌单详情
+export function getUserPlaylist(uid) {
+  return axios.get('/user/playlist', {
+    params: {
+      uid
+    }
+  })
+}
+
+// 获取歌曲详情
+export function getMusicDetail(ids) {
+  return axios.get('/song/detail', {
+    params: {
+      ids
+    }
+  })
+}
+
+// 获取音乐是否可以用
+export function getCheckMusic(id) {
+  return axios.get('/check/music', {
+    params: {
+      id
+    }
+  })
+}
+
+// 获取音乐地址
+export function getMusicUrl(id) {
+  return axios.get('/song/url', {
+    params: {
+      id
+    }
+  })
+}
+
+// 获取歌词
+export function getLyric(id) {
+  const url = '/lyric'
+  return axios.get(url, {
+    params: {
+      id
+    }
+  })
+}
+
+// 获取音乐评论
+export function getComment(id, page, limit = defaultLimit) {
+  return axios.get('/comment/music', {
+    params: {
+      offset: page * limit,
+      limit: limit,
+      id
+    }
+  })
+}

+ 7 - 0
src/api/yard.js

@@ -8,6 +8,13 @@ export function yardPhotoList(parameter) {
       params: parameter
     })
 }
+export function yardListDelete(parameter) {
+    return axios({
+      url: "/ctop/yard/delete",
+      method: 'delete',
+      params: parameter
+    })
+}
 
 export function yardVideoList(parameter) {
     return axios({

BIN
src/assets/background/bg-1.jpg


BIN
src/assets/background/bg-2.jpg


BIN
src/assets/img/album_cover_player.png


BIN
src/assets/img/default.png


BIN
src/assets/img/player_cover.png


BIN
src/assets/img/wave.gif


BIN
src/assets/logoNew.png


BIN
src/assets/logoNew1.png


BIN
src/assets/noImg.png


BIN
src/assets/orderIcon.png


BIN
src/assets/success.png


BIN
src/assets/touxiang.png


BIN
src/assets/work.png


+ 230 - 0
src/base/mm-dialog/mm-dialog.vue

@@ -0,0 +1,230 @@
+<template>
+  <!--对话框-->
+  <transition name="mm-dialog-fade">
+    <div v-show="dialogShow" class="mm-dialog-box">
+      <div class="mm-dialog-wrapper">
+        <div class="mm-dialog-content">
+          <div class="mm-dialog-head" v-text="headText"></div>
+          <slot>
+            <!-- eslint-disable-next-line vue/no-v-html -->
+            <div class="mm-dialog-text" v-html="bodyText"></div>
+          </slot>
+          <div class="mm-dialog-btns">
+            <div
+              v-if="dialogType!=='alert'"
+              class="mm-btn-cancel"
+              @click="cancel"
+              v-text="cancelBtnText"
+            ></div>
+            <slot name="btn"></slot>
+            <div
+              class="mm-btn-confirm"
+              @click="confirm"
+              v-text="confirmBtnText"
+            ></div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </transition>
+</template>
+
+<script>
+export default {
+  name: 'MmDialog',
+  props: {
+    // type:confirm、alert
+    type: {
+      type: String,
+      default: 'confirm'
+    },
+    // 标题文本
+    headText: {
+      type: String,
+      default: '提示'
+    },
+    // 内容文本(可以是html)
+    bodyText: {
+      type: String,
+      default: ''
+    },
+    // 取消按钮文本
+    cancelBtnText: {
+      type: String,
+      default: '取消'
+    },
+    // 确定按钮文本
+    confirmBtnText: {
+      type: String,
+      default: '确定'
+    },
+    // Dialog 是否插入至 body 元素下
+    appendToBody: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data() {
+    return {
+      dialogShow: false // 是否显示对话框
+    }
+  },
+  computed: {
+    dialogType() {
+      return this.type.toLowerCase()
+    }
+  },
+  watch: {
+    dialogShow(val) {
+      if (val && this.appendToBody) {
+        document.body.appendChild(this.$el)
+      }
+    }
+  },
+  mounted() {
+    if (this.dialogShow && this.appendToBody) {
+      document.body.appendChild(this.$el)
+    }
+  },
+  destroyed() {
+    if (this.appendToBody && this.$el && this.$el.parentNode) {
+      this.$el.parentNode.removeChild(this.$el)
+    }
+  },
+  methods: {
+    // 显示事件
+    show() {
+      this.dialogShow = true
+    },
+    // 隐藏事件
+    hide() {
+      this.dialogShow = false
+    },
+    // 取消事件
+    cancel() {
+      this.hide()
+      this.$emit('cancel')
+    },
+    // 确定事件
+    confirm() {
+      this.hide()
+      this.$emit('confirm')
+    }
+  }
+}
+</script>
+
+<style lang="less">
+@dialog-prefix-cls: mm-dialog;
+
+.@{dialog-prefix-cls}-box {
+  position: fixed;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0;
+  z-index: 1996;
+  background-color: @dialog_bg_color;
+  user-select: none;
+  &.@{dialog-prefix-cls}-fade-enter-active {
+    animation: mm-dialog-fadein 0.3s;
+    .@{dialog-prefix-cls}-content {
+      animation: mm-dialog-zoom 0.3s;
+    }
+  }
+  .@{dialog-prefix-cls}-wrapper {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    z-index: 1996;
+    .@{dialog-prefix-cls}-content {
+      width: 420px;
+      border-radius: 5px;
+      background: @dialog_content_bg_color;
+      @media (max-width: 767px) {
+        width: 270px;
+        border-radius: 10px;
+        text-align: center;
+      }
+      .@{dialog-prefix-cls}-head {
+        padding: 15px;
+        padding-bottom: 0;
+        font-size: @font_size_large;
+        color: @text_color_active;
+      }
+      .@{dialog-prefix-cls}-text {
+        padding: 20px 15px;
+        line-height: 22px;
+        font-size: @font_size_medium;
+        color: @dialog_text_color;
+      }
+      .@{dialog-prefix-cls}-btns {
+        display: flex;
+        align-items: center;
+        padding: 0 15px 10px;
+        text-align: center;
+        color: @dialog_text_color;
+        @media (min-width: 768px) {
+          justify-content: flex-end;
+          div {
+            display: block;
+            padding: 8px 15px;
+            border-radius: 3px;
+            border: 1px solid @btn_color;
+            font-size: @font_size_medium;
+            cursor: pointer;
+            &:not(:nth-of-type(1)) {
+              margin-left: 10px;
+            }
+            &.mm-btn-confirm {
+              border-style: @btn_color_active;
+            }
+            &:hover {
+              color: @text_color_active;
+              border: 1px solid @btn_color_active;
+            }
+          }
+        }
+        @media (max-width: 767px) {
+          & {
+            padding: 0;
+            justify-content: center;
+            div {
+              flex: 1;
+              line-height: 22px;
+              padding: 10px 0;
+              border-top: 1px solid @dialog_line_color;
+              font-size: @font_size_large;
+              &:not(:nth-of-type(1)) {
+                border-left: 1px solid @dialog_line_color;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+@keyframes mm-dialog-fadein {
+  0% {
+    opacity: 0;
+  }
+  100% {
+    opacity: 1;
+  }
+}
+
+@keyframes mm-dialog-zoom {
+  0% {
+    transform: scale(0);
+  }
+  50% {
+    transform: scale(1.1);
+  }
+  100% {
+    transform: scale(1);
+  }
+}
+</style>

+ 53 - 0
src/base/mm-icon/mm-icon.vue

@@ -0,0 +1,53 @@
+<!-- icon 组件 -->
+<script>
+export default {
+  name: 'MmIcon',
+  props: {
+    type: {
+      type: String,
+      required: true
+    },
+    size: {
+      type: Number,
+      default: 16
+    }
+  },
+  methods: {
+    getIconCls() {
+      return `icon-${this.type}`
+    },
+    getIconStyle() {
+      return { fontSize: this.size + 'px' }
+    },
+    onClick(e) {
+      this.$emit('click', e)
+    }
+  },
+  render() {
+    const Icon = (
+      <i
+        onClick={this.onClick}
+        class={`iconfont ${this.getIconCls()}`}
+        style={this.getIconStyle()}
+      />
+    )
+    return Icon
+  }
+}
+</script>
+
+<style lang="less">
+.iconfont {
+  display: inline-block;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  line-height: 1;
+  vertical-align: baseline;
+  text-transform: none;
+  speak: none;
+  /* Better Font Rendering =========== */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+</style>

+ 86 - 0
src/base/mm-loading/mm-loading.vue

@@ -0,0 +1,86 @@
+<template>
+  <!--加载动画-->
+  <div
+    v-show="value"
+    class="mm-loading"
+    :style="{backgroundColor:loadingBgColor}"
+  >
+    <div class="mm-loading-content">
+      <svg class="circular" viewBox="25 25 50 50">
+        <circle class="path" cx="50" cy="50" r="20" fill="none" />
+      </svg>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'MmLoading',
+  props: {
+    // 是否显示
+    value: {
+      type: Boolean,
+      default: true
+    },
+    // 加载动画背景颜色
+    loadingBgColor: {
+      type: String,
+      default: ''
+    }
+  }
+}
+</script>
+
+<style lang="less">
+.mm-loading {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 1996;
+  background: @load_bg_color;
+  .mm-loading-content {
+    position: absolute;
+    top: 50%;
+    width: 100%;
+    transform: translateY(-50%);
+    text-align: center;
+    .circular {
+      height: 50px;
+      width: 50px;
+      animation: loading-rotate 2s linear infinite;
+      .path {
+        animation: loading-dash 1.5s ease-in-out infinite;
+        stroke-dasharray: 90, 150;
+        stroke-dashoffset: 0;
+        stroke-width: 2;
+        stroke: @text_color;
+        stroke-linecap: round;
+      }
+    }
+  }
+}
+
+//动画函数
+@keyframes loading-rotate {
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes loading-dash {
+  0% {
+    stroke-dasharray: 1, 200;
+    stroke-dashoffset: 0;
+  }
+  50% {
+    stroke-dasharray: 90, 150;
+    stroke-dashoffset: -40px;
+  }
+  100% {
+    stroke-dasharray: 90, 150;
+    stroke-dashoffset: -120px;
+  }
+}
+</style>

+ 34 - 0
src/base/mm-no-result/mm-no-result.vue

@@ -0,0 +1,34 @@
+<template>
+  <!--暂无数据提示-->
+  <div class="mm-no-result">
+    <p class="mm-no-result-text">{{ title }}</p>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'MmNoResult',
+  props: {
+    // 无数据提示文本
+    title: {
+      type: String,
+      default: ''
+    }
+  }
+}
+</script>
+
+<style lang="less">
+.mm-no-result {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  &-text {
+    margin-top: 30px;
+    font-size: @font_size_medium;
+    color: @text_color;
+  }
+}
+</style>

+ 178 - 0
src/base/mm-progress/mm-progress.vue

@@ -0,0 +1,178 @@
+<template>
+  <!--进度条拖动-->
+  <div ref="mmProgress" class="mmProgress" @click="barClick">
+    <div class="mmProgress-bar"></div>
+    <div ref="mmPercentProgress" class="mmProgress-outer"></div>
+    <div ref="mmProgressInner" class="mmProgress-inner">
+      <div
+        class="mmProgress-dot"
+        @mousedown="barDown"
+        @touchstart.prevent="barDown"
+      ></div>
+    </div>
+  </div>
+</template>
+
+<script>
+const dotWidth = 10
+export default {
+  name: 'MmProgress',
+  props: {
+    // 进度值一
+    percent: {
+      type: [Number],
+      default: 0
+    },
+    // 进度值二(歌曲缓冲进度用)
+    percentProgress: {
+      type: [Number],
+      default: 0
+    }
+  },
+  data() {
+    return {
+      move: {
+        status: false, // 是否可拖动
+        startX: 0, // 记录最开始点击的X坐标
+        left: 0 // 记录当前已经移动的距离
+      }
+    }
+  },
+  watch: {
+    percent(newPercent) {
+      if (newPercent >= 0 && !this.move.status) {
+        const barWidth = this.$refs.mmProgress.clientWidth - dotWidth
+        const offsetWidth = newPercent * barWidth
+        this.moveSilde(offsetWidth)
+      }
+    },
+    percentProgress(newValue) {
+      let offsetWidth = this.$refs.mmProgress.clientWidth * newValue
+      this.$refs.mmPercentProgress.style.width = `${offsetWidth}px`
+    }
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.bindEvents()
+      const barWidth = this.$refs.mmProgress.clientWidth - dotWidth
+      const offsetWidth = this.percent * barWidth
+      this.moveSilde(offsetWidth)
+    })
+  },
+  beforeDestroy() {
+    this.unbindEvents()
+  },
+  methods: {
+    // 添加绑定事件
+    bindEvents() {
+      document.addEventListener('mousemove', this.barMove)
+      document.addEventListener('mouseup', this.barUp)
+
+      document.addEventListener('touchmove', this.barMove)
+      document.addEventListener('touchend', this.barUp)
+    },
+    // 移除绑定事件
+    unbindEvents() {
+      document.removeEventListener('mousemove', this.barMove)
+      document.removeEventListener('mouseup', this.barUp)
+
+      document.removeEventListener('touchmove', this.barMove)
+      document.removeEventListener('touchend', this.barUp)
+    },
+    // 点击事件
+    barClick(e) {
+      let rect = this.$refs.mmProgress.getBoundingClientRect()
+      let offsetWidth = Math.min(
+        this.$refs.mmProgress.clientWidth - dotWidth,
+        Math.max(0, e.clientX - rect.left)
+      )
+      this.moveSilde(offsetWidth)
+      this.commitPercent(true)
+    },
+    // 鼠标按下事件
+    barDown(e) {
+      this.move.status = true
+      this.move.startX = e.clientX || e.touches[0].pageX
+      this.move.left = this.$refs.mmProgressInner.clientWidth
+    },
+    // 鼠标/触摸移动事件
+    barMove(e) {
+      if (!this.move.status) {
+        return false
+      }
+      e.preventDefault()
+      let endX = e.clientX || e.touches[0].pageX
+      let dist = endX - this.move.startX
+      let offsetWidth = Math.min(
+        this.$refs.mmProgress.clientWidth - dotWidth,
+        Math.max(0, this.move.left + dist)
+      )
+      this.moveSilde(offsetWidth)
+      this.commitPercent()
+    },
+    // 鼠标/触摸释放事件
+    barUp(e) {
+      if (this.move.status) {
+        this.commitPercent(true)
+        this.move.status = false
+      }
+    },
+    // 移动滑块
+    moveSilde(offsetWidth) {
+      this.$refs.mmProgressInner.style.width = `${offsetWidth}px`
+    },
+    // 修改 percent
+    commitPercent(isEnd = false) {
+      const { mmProgress, mmProgressInner } = this.$refs
+      const lineWidth = mmProgress.clientWidth - dotWidth
+      const percent = mmProgressInner.clientWidth / lineWidth
+      this.$emit(isEnd ? 'percentChangeEnd' : 'percentChange', percent)
+    }
+  }
+}
+</script>
+
+<style lang="less">
+.mmProgress {
+  position: relative;
+  padding: 5px;
+  user-select: none;
+  cursor: pointer;
+  overflow: hidden;
+  .mmProgress-bar {
+    height: 2px;
+    width: 100%;
+    background: @bar_color;
+  }
+  .mmProgress-outer {
+    position: absolute;
+    top: 50%;
+    left: 5px;
+    display: inline-block;
+    width: 0;
+    height: 2px;
+    margin-top: -1px;
+    background: rgba(255, 255, 255, 0.2);
+  }
+  .mmProgress-inner {
+    position: absolute;
+    top: 50%;
+    left: 5px;
+    display: inline-block;
+    width: 0;
+    height: 2px;
+    margin-top: -1px;
+    background: @line_color;
+    .mmProgress-dot {
+      position: absolute;
+      top: 50%;
+      right: -5px;
+      width: 10px;
+      height: 10px;
+      border-radius: 50%;
+      background-color: @dot_color;
+      transform: translateY(-50%);
+    }
+  }
+}
+</style>

+ 39 - 0
src/base/mm-toast/index.js

@@ -0,0 +1,39 @@
+import TempToast from './mm-toast.vue'
+
+let instance
+let showToast = false
+let time // 存储toast显示状态
+const mmToast = {
+  install(Vue, options = {}) {
+    let opt = TempToast.data() // 获取组件中的默认配置
+    Object.assign(opt, options) // 合并配置
+    Vue.prototype.$mmToast = (message, position) => {
+      if (showToast) {
+        clearTimeout(time)
+        instance.vm.visible = showToast = false
+        document.body.removeChild(instance.vm.$el)
+        // return;// 如果toast还在,则不再执行
+      }
+      if (message) {
+        opt.message = message // 如果有传message,则使用所传的message
+      }
+      if (position) {
+        opt.position = position // 如果有传type,则使用所传的type
+      }
+      let TempToastConstructor = Vue.extend(TempToast)
+      instance = new TempToastConstructor({
+        data: opt
+      })
+      instance.vm = instance.$mount()
+      document.body.appendChild(instance.vm.$el)
+      instance.vm.visible = showToast = true
+
+      time = setTimeout(function() {
+        instance.vm.visible = showToast = false
+        document.body.removeChild(instance.vm.$el)
+      }, opt.duration)
+    }
+  }
+}
+
+export default mmToast

+ 73 - 0
src/base/mm-toast/mm-toast.vue

@@ -0,0 +1,73 @@
+<template>
+  <!--弹出层提示-->
+  <transition name="toast-fade">
+    <div v-show="visible" class="mm-toast" :class="positionClasss">{{ message }}</div>
+  </transition>
+</template>
+
+<script>
+export default {
+  name: 'MmToast',
+  data() {
+    return {
+      position: 'center', // 默认显示位置
+      message: '', // 默认显示文本
+      duration: 1500, // 显示时间, 毫秒
+      visible: false // 是否显示
+    }
+  },
+  computed: {
+    positionClasss() {
+      return 'mm-toast-' + this.position
+    }
+  }
+}
+</script>
+
+<style lang="less">
+@prefix-cls: mm-toast;
+
+.@{prefix-cls} {
+  position: fixed;
+  left: 50%;
+  z-index: 1996;
+  max-width: 80%;
+  box-sizing: border-box;
+  border-radius: 5px;
+  padding: 10px 20px;
+  overflow: hidden;
+  text-align: center;
+  min-height: 40px;
+  line-height: 20px;
+  font-size: 14px;
+  color: #fff;
+  background: rgba(0, 0, 0, 0.5);
+  user-select: none;
+  transform: translateX(-50%);
+  &&-top {
+    top: 10%;
+  }
+  &&-center {
+    top: 50%;
+    margin-top: -20px;
+  }
+  &&-bottom {
+    bottom: 10%;
+  }
+}
+
+.toast-fade-enter {
+  opacity: 0;
+  transform: translate3d(-50%, -10px, 0);
+}
+
+.toast-fade-enter-active {
+  will-change: transform;
+  transition: all 0.2s;
+}
+
+.toast-fade-enter-to {
+  opacity: 1;
+  transform: translate3d(-50%, 0, 0);
+}
+</style>

+ 3 - 1
src/components/DatePicker.vue

@@ -171,7 +171,9 @@ export default {
     if (this.left) {
       this.$refs.pickOptions.style.left = this.left
     }
-
+    if(this.parentDate){
+      this.dateValue=this.parentDate
+    }
     // console.log(this.$refs.pickOptions.style.left)
   }
 }

+ 36 - 0
src/components/bigImg.vue

@@ -0,0 +1,36 @@
+<template>
+  <!-- 过渡动画 -->
+  <a-modal title="查看封面" v-model="showImageChouzhen" :width="400" :footer="null">
+    <img :src="showImageStr" style="width: 100%" v-if="showType == 'inage'" />
+    <video class="video" :src="showImageStr" controls="controls" style="width: 100%" v-else>
+      您的浏览器不支持 video 标签。
+    </video>
+  </a-modal>
+</template>
+<script>
+export default {
+  props: {
+    imgSrc: {
+      type: String,
+      default: '',
+    },
+  },
+  data() {
+    return {
+      scale: 1,
+      showType: 'image',
+      showImageChouzhen: false,
+      showImageStr:''
+    }
+  },
+  methods: {
+    showImg(type,url) {
+      this.showType = type
+      this.showImageStr = url
+      this.showImageChouzhen = true
+    },
+  },
+}
+</script>
+
+

+ 524 - 0
src/components/calendar/calendar.js

@@ -0,0 +1,524 @@
+/**
+* @1900-2100区间内的公历、农历互转
+* @charset UTF-8
+* @Author  Jea杨(JJonline@JJonline.Cn) 
+* @Time    2014-7-21
+* @Time    2016-8-13 Fixed 2033hex、Attribution Annals
+* @Time    2016-9-25 Fixed lunar LeapMonth Param Bug
+* @Version 1.0.2
+* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+*/
+var calendar = {
+
+    /**
+      * 农历1900-2100的润大小信息表
+      * @Array Of Property
+      * @return Hex 
+      */
+    lunarInfo:[0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,//1900-1909
+            0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,//1910-1919
+            0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,//1920-1929
+            0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,//1930-1939
+            0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,//1940-1949
+            0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,//1950-1959
+            0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,//1960-1969
+            0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,//1970-1979
+            0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,//1980-1989
+            0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,//1990-1999
+            0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,//2000-2009
+            0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,//2010-2019
+            0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,//2020-2029
+            0x05aa0,0x076a3,0x096d0,0x04afb,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,//2030-2039
+            0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,//2040-2049
+            /**Add By JJonline@JJonline.Cn**/
+            0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50, 0x06b20,0x1a6c4,0x0aae0,//2050-2059
+            0x0a2e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,//2060-2069
+            0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,//2070-2079
+            0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,//2080-2089
+            0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a2d0,0x0d150,0x0f252,//2090-2099
+            0x0d520],//2100
+
+    /**
+      * 公历每个月份的天数普通表
+      * @Array Of Property
+      * @return Number 
+      */
+    solarMonth:[31,28,31,30,31,30,31,31,30,31,30,31],
+
+    /**
+      * 天干地支之天干速查表
+      * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+      * @return Cn string 
+      */
+    Gan:["\u7532","\u4e59","\u4e19","\u4e01","\u620a","\u5df1","\u5e9a","\u8f9b","\u58ec","\u7678"],
+
+    /**
+      * 天干地支之地支速查表
+      * @Array Of Property 
+      * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+      * @return Cn string 
+      */
+    Zhi:["\u5b50","\u4e11","\u5bc5","\u536f","\u8fb0","\u5df3","\u5348","\u672a","\u7533","\u9149","\u620c","\u4ea5"],
+
+    /**
+      * 天干地支之地支速查表<=>生肖
+      * @Array Of Property 
+      * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+      * @return Cn string 
+      */
+    Animals:["\u9f20","\u725b","\u864e","\u5154","\u9f99","\u86c7","\u9a6c","\u7f8a","\u7334","\u9e21","\u72d7","\u732a"],
+
+    /**
+      * 24节气速查表
+      * @Array Of Property 
+      * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+      * @return Cn string 
+      */
+    solarTerm:["\u5c0f\u5bd2","\u5927\u5bd2","\u7acb\u6625","\u96e8\u6c34","\u60ca\u86f0","\u6625\u5206","\u6e05\u660e","\u8c37\u96e8","\u7acb\u590f","\u5c0f\u6ee1","\u8292\u79cd","\u590f\u81f3","\u5c0f\u6691","\u5927\u6691","\u7acb\u79cb","\u5904\u6691","\u767d\u9732","\u79cb\u5206","\u5bd2\u9732","\u971c\u964d","\u7acb\u51ac","\u5c0f\u96ea","\u5927\u96ea","\u51ac\u81f3"],
+
+    /**
+      * 1900-2100各年的24节气日期速查表
+      * @Array Of Property 
+      * @return 0x string For splice
+      */
+    sTermInfo:['9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf97c3598082c95f8c965cc920f',
+              '97bd0b06bdb0722c965ce1cfcc920f','b027097bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+              '97bcf97c359801ec95f8c965cc920f','97bd0b06bdb0722c965ce1cfcc920f','b027097bd097c36b0b6fc9274c91aa',
+              '97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f','97bd0b06bdb0722c965ce1cfcc920f',
+              'b027097bd097c36b0b6fc9274c91aa','9778397bd19801ec9210c965cc920e','97b6b97bd19801ec95f8c965cc920f',
+              '97bd09801d98082c95f8e1cfcc920f','97bd097bd097c36b0b6fc9210c8dc2','9778397bd197c36c9210c9274c91aa',
+              '97b6b97bd19801ec95f8c965cc920e','97bd09801d98082c95f8e1cfcc920f','97bd097bd097c36b0b6fc9210c8dc2',
+              '9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec95f8c965cc920e','97bcf97c3598082c95f8e1cfcc920f',
+              '97bd097bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec9210c965cc920e',
+              '97bcf97c3598082c95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+              '97b6b97bd19801ec9210c965cc920e','97bcf97c3598082c95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722',
+              '9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f',
+              '97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+              '97bcf97c359801ec95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+              '97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f','97bd097bd07f595b0b6fc920fb0722',
+              '9778397bd097c36b0b6fc9210c8dc2','9778397bd19801ec9210c9274c920e','97b6b97bd19801ec95f8c965cc920f',
+              '97bd07f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c920e',
+              '97b6b97bd19801ec95f8c965cc920f','97bd07f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2',
+              '9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec9210c965cc920e','97bd07f1487f595b0b0bc920fb0722',
+              '7f0e397bd097c36b0b6fc9210c8dc2','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+              '97bcf7f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+              '97b6b97bd19801ec9210c965cc920e','97bcf7f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
+              '9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf7f1487f531b0b0bb0b6fb0722',
+              '7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+              '97bcf7f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+              '97b6b97bd19801ec9210c9274c920e','97bcf7f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
+              '9778397bd097c36b0b6fc9210c91aa','97b6b97bd197c36c9210c9274c920e','97bcf7f0e47f531b0b0bb0b6fb0722',
+              '7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c920e',
+              '97b6b7f0e47f531b0723b0b6fb0722','7f0e37f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2',
+              '9778397bd097c36b0b70c9274c91aa','97b6b7f0e47f531b0723b0b6fb0721','7f0e37f1487f595b0b0bb0b6fb0722',
+              '7f0e397bd097c35b0b6fc9210c8dc2','9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721',
+              '7f0e27f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+              '97b6b7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
+              '9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
+              '7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721',
+              '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+              '97b6b7f0e47f531b0723b0787b0721','7f0e27f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
+              '9778397bd097c36b0b6fc9210c91aa','97b6b7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
+              '7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9210c8dc2','977837f0e37f149b0723b0787b0721',
+              '7f07e7f0e47f531b0723b0b6fb0722','7f0e37f5307f595b0b0bc920fb0722','7f0e397bd097c35b0b6fc9210c8dc2',
+              '977837f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0721','7f0e37f1487f595b0b0bb0b6fb0722',
+              '7f0e397bd097c35b0b6fc9210c8dc2','977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+              '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','977837f0e37f14998082b0787b06bd',
+              '7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
+              '977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
+              '7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+              '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14998082b0787b06bd',
+              '7f07e7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
+              '977837f0e37f14998082b0723b06bd','7f07e7f0e37f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
+              '7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b0721',
+              '7f07e7f0e47f531b0723b0b6fb0722','7f0e37f1487f595b0b0bb0b6fb0722','7f0e37f0e37f14898082b0723b02d5',
+              '7ec967f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0722','7f0e37f1487f531b0b0bb0b6fb0722',
+              '7f0e37f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+              '7f0e37f1487f531b0b0bb0b6fb0722','7f0e37f0e37f14898082b072297c35','7ec967f0e37f14998082b0787b06bd',
+              '7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e37f0e37f14898082b072297c35',
+              '7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
+              '7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f149b0723b0787b0721',
+              '7f0e27f1487f531b0b0bb0b6fb0722','7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14998082b0723b06bd',
+              '7f07e7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722','7f0e37f0e366aa89801eb072297c35',
+              '7ec967f0e37f14998082b0723b06bd','7f07e7f0e37f14998083b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
+              '7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14898082b0723b02d5','7f07e7f0e37f14998082b0787b0721',
+              '7f07e7f0e47f531b0723b0b6fb0722','7f0e36665b66aa89801e9808297c35','665f67f0e37f14898082b0723b02d5',
+              '7ec967f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0722','7f0e36665b66a449801e9808297c35',
+              '665f67f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+              '7f0e36665b66a449801e9808297c35','665f67f0e37f14898082b072297c35','7ec967f0e37f14998082b0787b06bd',
+              '7f07e7f0e47f531b0723b0b6fb0721','7f0e26665b66a449801e9808297c35','665f67f0e37f1489801eb072297c35',
+              '7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722'],
+
+    /**
+      * 数字转中文速查表
+      * @Array Of Property 
+      * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+      * @return Cn string 
+      */
+    nStr1:["\u65e5","\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341"],
+
+    /**
+      * 日期转农历称呼速查表
+      * @Array Of Property 
+      * @trans ['初','十','廿','卅']
+      * @return Cn string 
+      */
+    nStr2:["\u521d","\u5341","\u5eff","\u5345"],
+
+    /**
+      * 月份转农历称呼速查表
+      * @Array Of Property 
+      * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+      * @return Cn string 
+      */
+    nStr3:["\u6b63","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341","\u51ac","\u814a"],
+
+    /**
+      * 返回农历y年一整年的总天数
+      * @param lunar Year
+      * @return Number
+      * @eg:var count = calendar.lYearDays(1987) ;//count=387
+      */
+    lYearDays:function(y) {
+        var i, sum = 348;
+        for(i=0x8000; i>0x8; i>>=1) { sum += (calendar.lunarInfo[y-1900] & i)? 1: 0; }
+        return(sum+calendar.leapDays(y));
+    },
+
+    /**
+      * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+      * @param lunar Year
+      * @return Number (0-12)
+      * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+      */
+    leapMonth:function(y) { //闰字编码 \u95f0
+        return(calendar.lunarInfo[y-1900] & 0xf);
+    },
+
+    /**
+      * 返回农历y年闰月的天数 若该年没有闰月则返回0
+      * @param lunar Year
+      * @return Number (0、29、30)
+      * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+      */
+    leapDays:function(y) {
+        if(calendar.leapMonth(y))  { 
+            return((calendar.lunarInfo[y-1900] & 0x10000)? 30: 29); 
+        }
+        return(0);
+    },
+
+    /**
+      * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+      * @param lunar Year
+      * @return Number (-1、29、30)
+      * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+      */
+    monthDays:function(y,m) {
+        if(m>12 || m<1) {return -1}//月份参数从1至12,参数错误返回-1
+        return( (calendar.lunarInfo[y-1900] & (0x10000>>m))? 30: 29 );
+    },
+
+    /**
+      * 返回公历(!)y年m月的天数
+      * @param solar Year
+      * @return Number (-1、28、29、30、31)
+      * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+      */
+    solarDays:function(y,m) {
+        if(m>12 || m<1) {return -1} //若参数错误 返回-1
+        var ms = m-1;
+        if(ms==1) { //2月份的闰平规律测算后确认返回28或29
+            return(((y%4 == 0) && (y%100 != 0) || (y%400 == 0))? 29: 28);
+        }else {
+            return(calendar.solarMonth[ms]);
+        }
+    },
+
+    /**
+     * 农历年份转换为干支纪年
+     * @param  lYear 农历年的年份数
+     * @return Cn string
+     */
+    toGanZhiYear:function(lYear) {
+        var ganKey = (lYear - 3) % 10;
+        var zhiKey = (lYear - 3) % 12;
+        if(ganKey == 0) ganKey = 10;//如果余数为0则为最后一个天干
+        if(zhiKey == 0) zhiKey = 12;//如果余数为0则为最后一个地支
+        return calendar.Gan[ganKey-1] + calendar.Zhi[zhiKey-1];
+        
+    },
+
+    /**
+     * 公历月、日判断所属星座
+     * @param  cMonth [description]
+     * @param  cDay [description]
+     * @return Cn string
+     */
+    toAstro:function(cMonth,cDay) {
+        var s   = "\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf";
+        var arr = [20,19,21,21,21,22,23,23,23,23,22,22];
+        return s.substr(cMonth*2 - (cDay < arr[cMonth-1] ? 2 : 0),2) + "\u5ea7";//座
+    },
+
+    /**
+      * 传入offset偏移量返回干支
+      * @param offset 相对甲子的偏移量
+      * @return Cn string
+      */
+    toGanZhi:function(offset) {
+        return calendar.Gan[offset%10] + calendar.Zhi[offset%12];
+    },
+
+    /**
+      * 传入公历(!)y年获得该年第n个节气的公历日期
+      * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 
+      * @return day Number
+      * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+      */
+    getTerm:function(y,n) {
+        if(y<1900 || y>2100) {return -1;}
+        if(n<1 || n>24) {return -1;}
+        var _table = calendar.sTermInfo[y-1900];
+        var _info = [
+            parseInt('0x'+_table.substr(0,5)).toString() ,
+            parseInt('0x'+_table.substr(5,5)).toString(),
+            parseInt('0x'+_table.substr(10,5)).toString(),
+            parseInt('0x'+_table.substr(15,5)).toString(),
+            parseInt('0x'+_table.substr(20,5)).toString(),
+            parseInt('0x'+_table.substr(25,5)).toString()
+        ];
+        var _calday = [
+            _info[0].substr(0,1),
+            _info[0].substr(1,2),
+            _info[0].substr(3,1),
+            _info[0].substr(4,2),
+            
+            _info[1].substr(0,1),
+            _info[1].substr(1,2),
+            _info[1].substr(3,1),
+            _info[1].substr(4,2),
+            
+            _info[2].substr(0,1),
+            _info[2].substr(1,2),
+            _info[2].substr(3,1),
+            _info[2].substr(4,2),
+            
+            _info[3].substr(0,1),
+            _info[3].substr(1,2),
+            _info[3].substr(3,1),
+            _info[3].substr(4,2),
+            
+            _info[4].substr(0,1),
+            _info[4].substr(1,2),
+            _info[4].substr(3,1),
+            _info[4].substr(4,2),
+            
+            _info[5].substr(0,1),
+            _info[5].substr(1,2),
+            _info[5].substr(3,1),
+            _info[5].substr(4,2),
+        ];
+        return parseInt(_calday[n-1]);
+    },
+
+    /**
+      * 传入农历数字月份返回汉语通俗表示法
+      * @param lunar month
+      * @return Cn string
+      * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+      */
+    toChinaMonth:function(m) { // 月 => \u6708
+        if(m>12 || m<1) {return -1} //若参数错误 返回-1
+        var s = calendar.nStr3[m-1];
+        s+= "\u6708";//加上月字
+        return s;
+    },
+
+    /**
+      * 传入农历日期数字返回汉字表示法
+      * @param lunar day
+      * @return Cn string
+      * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+      */
+    toChinaDay:function(d){ //日 => \u65e5
+        var s;
+        switch (d) {
+            case 10:
+            s = '\u521d\u5341'; break;
+        case 20:
+            s = '\u4e8c\u5341'; break;
+            break;
+        case 30:
+            s = '\u4e09\u5341'; break;
+            break;
+        default :
+            s = calendar.nStr2[Math.floor(d/10)];
+            s += calendar.nStr1[d%10];
+        }
+        return(s);
+    },
+
+    /**
+      * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+      * @param y year
+      * @return Cn string
+      * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+      */
+    getAnimal: function(y) {
+        return calendar.Animals[(y - 4) % 12]
+    },
+
+    /**
+      * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+      * @param y  solar year
+      * @param m  solar month
+      * @param d  solar day
+      * @return JSON object
+      * @eg:console.log(calendar.solar2lunar(1987,11,01));
+      */
+    solar2lunar:function (y,m,d) { //参数区间1900.1.31~2100.12.31
+        if(y<1900 || y>2100) {return -1;}//年份限定、上限
+        if(y==1900&&m==1&&d<31) {return -1;}//下限
+        if(!y) { //未传参  获得当天
+            var objDate = new Date();
+        }else {
+            var objDate = new Date(y,parseInt(m)-1,d)
+        }
+        var i, leap=0, temp=0;
+        //修正ymd参数
+        var y = objDate.getFullYear(),m = objDate.getMonth()+1,d = objDate.getDate();
+        var offset   = (Date.UTC(objDate.getFullYear(),objDate.getMonth(),objDate.getDate()) - Date.UTC(1900,0,31))/86400000;
+        for(i=1900; i<2101 && offset>0; i++) { temp=calendar.lYearDays(i); offset-=temp; }
+        if(offset<0) { offset+=temp; i--; }
+        
+        //是否今天
+        var isTodayObj = new Date(),isToday=false;
+        if(isTodayObj.getFullYear()==y && isTodayObj.getMonth()+1==m && isTodayObj.getDate()==d) {
+            isToday = true;
+        }
+        //星期几
+        var nWeek = objDate.getDay(),cWeek = calendar.nStr1[nWeek];
+        if(nWeek==0) {nWeek =7;}//数字表示周几顺应天朝周一开始的惯例
+        //农历年
+        var year = i;
+        
+        var leap = calendar.leapMonth(i); //闰哪个月
+        var isLeap = false;
+        
+        //效验闰月
+        for(i=1; i<13 && offset>0; i++) {
+            //闰月
+            if(leap>0 && i==(leap+1) && isLeap==false){ 
+                --i;
+                isLeap = true; temp = calendar.leapDays(year); //计算农历闰月天数
+            }
+            else{
+                temp = calendar.monthDays(year, i);//计算农历普通月天数
+            }
+            //解除闰月
+            if(isLeap==true && i==(leap+1)) { isLeap = false; }
+            offset -= temp;
+        }
+        
+        if(offset==0 && leap>0 && i==leap+1)
+        if(isLeap){
+            isLeap = false;
+        }else{ 
+            isLeap = true; --i;
+        }
+        if(offset<0){ offset += temp; --i; }
+        //农历月
+        var month   = i;
+        //农历日
+        var day     = offset + 1;
+        
+        //天干地支处理
+        var sm      =   m-1;
+        var gzY     =   calendar.toGanZhiYear(year);
+        
+        //月柱 1900年1月小寒以前为 丙子月(60进制12)
+        var firstNode   = calendar.getTerm(year,(m*2-1));//返回当月「节」为几日开始
+        var secondNode  = calendar.getTerm(year,(m*2));//返回当月「节」为几日开始
+        
+        //依据12节气修正干支月
+        var gzM     =   calendar.toGanZhi((y-1900)*12+m+11);
+        if(d>=firstNode) {
+            gzM     =   calendar.toGanZhi((y-1900)*12+m+12);
+        }
+        
+        //传入的日期的节气与否
+        var isTerm = false;
+        var Term   = null;
+        if(firstNode==d) {
+            isTerm  = true;
+            Term    = calendar.solarTerm[m*2-2];
+        }
+        if(secondNode==d) {
+            isTerm  = true;
+            Term    = calendar.solarTerm[m*2-1];
+        }
+        //日柱 当月一日与 1900/1/1 相差天数
+        var dayCyclical = Date.UTC(y,sm,1,0,0,0,0)/86400000+25567+10;
+        var gzD = calendar.toGanZhi(dayCyclical+d-1);
+        //该日期所属的星座
+        var astro = calendar.toAstro(m,d);
+        
+        return {'lYear':year,'lMonth':month,'lDay':day,'Animal':calendar.getAnimal(year),'IMonthCn':(isLeap?"\u95f0":'')+calendar.toChinaMonth(month),'IDayCn':calendar.toChinaDay(day),'cYear':y,'cMonth':m,'cDay':d,'gzYear':gzY,'gzMonth':gzM,'gzDay':gzD,'isToday':isToday,'isLeap':isLeap,'nWeek':nWeek,'ncWeek':"\u661f\u671f"+cWeek,'isTerm':isTerm,'Term':Term,'astro':astro};
+    },
+
+    /**
+      * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+      * @param y  lunar year
+      * @param m  lunar month
+      * @param d  lunar day
+      * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+      * @return JSON object
+      * @eg:console.log(calendar.lunar2solar(1987,9,10));
+      */
+    lunar2solar:function(y,m,d,isLeapMonth) {   //参数区间1900.1.31~2100.12.1
+        var isLeapMonth = !!isLeapMonth;
+        var leapOffset  = 0;
+        var leapMonth   = calendar.leapMonth(y);
+        var leapDay     = calendar.leapDays(y);
+        if(isLeapMonth&&(leapMonth!=m)) {return -1;}//传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+        if(y==2100&&m==12&&d>1 || y==1900&&m==1&&d<31) {return -1;}//超出了最大极限值 
+        var day  = calendar.monthDays(y,m); 
+        var _day = day;
+        //bugFix 2016-9-25 
+        //if month is leap, _day use leapDays method 
+        if(isLeapMonth) {
+            _day = calendar.leapDays(y,m);
+        }
+        if(y < 1900 || y > 2100 || d > _day) {return -1;}//参数合法性效验
+        
+        //计算农历的时间差
+        var offset = 0;
+        for(var i=1900;i<y;i++) {
+            offset+=calendar.lYearDays(i);
+        }
+        var leap = 0,isAdd= false;
+        for(var i=1;i<m;i++) {
+            leap = calendar.leapMonth(y);
+            if(!isAdd) {//处理闰月
+                if(leap<=i && leap>0) {
+                    offset+=calendar.leapDays(y);isAdd = true;
+                }
+            }
+            offset+=calendar.monthDays(y,i);
+        }
+        //转换闰月农历 需补充该年闰月的前一个月的时差
+        if(isLeapMonth) {offset+=day;}
+        //1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+        var stmap   =   Date.UTC(1900,1,30,0,0,0);
+        var calObj  =   new Date((offset+d-31)*86400000+stmap);
+        var cY      =   calObj.getUTCFullYear();
+        var cM      =   calObj.getUTCMonth()+1;
+        var cD      =   calObj.getUTCDate();
+
+        return calendar.solar2lunar(cY,cM,cD);
+    }
+};
+
+export default calendar

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 809 - 0
src/components/calendar/calendar.vue


+ 7 - 4
src/components/ctop/ActorModal.vue

@@ -21,16 +21,16 @@
             :accessKeySecret="accessKeySecret"
           ></upload-to-ali> -->
 
-          <uploadFile :value.sync="coverUrl" :fileCount="1" uploadType="image" :multiple="false" />
+          <uploadFile :value.sync="coverUrl" :fileCount="1" uploadType="image" :multiple="false" :sizeCheck="false"/>
         </a-form-item>
         <a-form-item label="照片" :labelCol="labelCol" :wrapperCol="wrapperCol" v-if="!model.name">
-          <uploadFile :value.sync="imageUrl" :fileCount="10" uploadType="image"  />
+          <uploadFile :value.sync="imageUrl" :fileCount="10" uploadType="image"  :sizeCheck="false"/>
           <!-- <upload-to-ali v-model="imageUrl" :customDomain="customDomain" multiple preview :region="region"
             :bucket="bucket" :accept="acceptImage" :max="10" :size="1024000" :accessKeyId="accessKeyId"
             :accessKeySecret="accessKeySecret"></upload-to-ali> -->
         </a-form-item>
         <a-form-item label="视频作品" :labelCol="labelCol" :wrapperCol="wrapperCol" v-if="!model.name">
-          <uploadFile :value.sync="videoUrl" :fileCount="1" uploadType="video" />
+          <uploadFile :value.sync="videoUrl" :fileCount="10" uploadType="video" :sizeCheck="false"/>
           <!-- <upload-to-ali v-model="videoUrl" :customDomain="customDomain" multiple preview :region="region"
             :bucket="bucket" :accept="acceptVideo" :max="10" :size="1024000" :accessKeyId="accessKeyId"
             :accessKeySecret="accessKeySecret"></upload-to-ali> -->
@@ -130,6 +130,9 @@
         })
       },
       add() {
+        this.imageUrl = []
+        this.videoUrl = []
+        this.coverUrl = ""
         this.edit({});
       },
       edit(record) {
@@ -138,7 +141,7 @@
         that.form.resetFields();
         that.model = Object.assign({}, record);
         that.visible = true;
-        // that.coverUrl = ""
+        that.coverUrl = record.coverUrl?record.coverUrl:''
         // that.imageUrl = ""
         // that.videoUrl = ""
         that.$nextTick(() => {

+ 186 - 0
src/components/ctop/propModal.vue

@@ -0,0 +1,186 @@
+<template>
+  <a-modal :title="title" :width="800" :visible="visible" :confirmLoading="confirmLoading" @ok="handleOk"
+    @cancel="handleCancel" cancelText="关闭">
+
+    <a-spin :spinning="confirmLoading">
+      <a-form :form="form">
+
+        <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="道具名称">
+          <a-input placeholder="请输入道具名称" v-decorator="['name', validatorRules.name ]" />
+        </a-form-item>
+        <a-form-item label="照片" :labelCol="labelCol" :wrapperCol="wrapperCol">
+          <uploadFile  v-if="visible" :value.sync="imageUrl" :fileCount="10"
+            uploadType="image" :sizeCheck="false"/>
+          <!-- <upload-to-ali v-model="imageUrl" :customDomain="customDomain" multiple preview :region="region"
+            :bucket="bucket" :accept="acceptImage" :max="10" :size="1024000" :accessKeyId="accessKeyId"
+            :accessKeySecret="accessKeySecret"></upload-to-ali> -->
+        </a-form-item>
+        <a-form-item label="道具类型" :labelCol="labelCol" :wrapperCol="wrapperCol">
+          <a-input placeholder="请输入道具类型" v-decorator="['type']" />
+        </a-form-item>
+        <!-- <a-form-item label="生日" :labelCol="labelCol" :wrapperCol="wrapperCol">
+          <a-date-picker
+            style="width: 100%"
+            placeholder="请选择生日"
+            v-decorator="['birthday', {initialValue:!model.birthday?null:moment(model.birthday,dateFormat)}]"/>
+        </a-form-item>
+
+        <a-form-item label="性别" :labelCol="labelCol" :wrapperCol="wrapperCol">
+          <a-select v-decorator="[ 'sex', {}]" placeholder="请选择性别">
+            <a-select-option :value="1">男</a-select-option>
+            <a-select-option :value="2">女</a-select-option>
+          </a-select>
+        </a-form-item> -->
+        <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="描述">
+          <a-input placeholder="请输入道具描述" v-decorator="['desc']" />
+        </a-form-item>
+
+
+      </a-form>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script>
+  import {
+    httpAction
+  } from '@/api/manage'
+  import pick from 'lodash.pick'
+  import JTreeSelect from '@/components/jeecg/JTreeSelect'
+  import moment from 'moment'
+  import UploadToAli from '@femessage/upload-to-ali'
+  import uploadFile from '@/components/uploadFile.vue'
+  export default {
+    name: "ActorModal",
+    components: {
+      UploadToAli,
+      JTreeSelect,
+      uploadFile
+    },
+    data() {
+      return {
+        title: "操作",
+        visible: false,
+        model: {},
+        videoUrl: [],
+        imageUrl: [],
+        customDomain: '',
+        region: 'oss-cn-beijing',
+        bucket: 'ctop-media',
+        acceptVideo: 'video/mpeg,video/mp4',
+        acceptImage: 'image/jpeg,image/jpg,image/png',
+        accessKeyId: 'LTAIbNbqWzSOklQV',
+        accessKeySecret: '1rkPz7JNoXk8sJevPaeYHWqfkQXBGh',
+        dateFormat: "YYYY-MM-DD",
+        labelCol: {
+          xs: {
+            span: 24
+          },
+          sm: {
+            span: 5
+          },
+        },
+        wrapperCol: {
+          xs: {
+            span: 24
+          },
+          sm: {
+            span: 16
+          },
+        },
+
+        confirmLoading: false,
+        form: this.$form.createForm(this),
+        validatorRules: {
+          name: {
+            rules: [{
+              required: true,
+              message: '请输入演员姓名!'
+            }]
+          },
+          // sex:{initialValue:((!this.model.sex)?"": (this.model.sex+""))},
+        },
+        url: {
+          add: "/ctop/prop/add",
+          edit: "/ctop/prop/edit",
+        },
+      }
+    },
+    created() {},
+    methods: {
+      add() {
+        this.edit({});
+      },
+      edit(record) {
+        console.log(record)
+        let that = this;
+        that.form.resetFields();
+        that.model = Object.assign({}, record);
+        that.visible = true;
+        
+        that.$nextTick(() => {
+          that.imageUrl = record.coverUrl?[record.coverUrl]:[]
+          that.form.setFieldsValue(pick(this.model, 'name', 'desc'))
+          //时间格式化
+        });
+
+      },
+      close() {
+        this.$emit('close');
+        this.visible = false;
+      },
+      moment,
+      handleOk() {
+        const that = this;
+        // 触发表单验证
+        this.form.validateFields((err, values) => {
+          if (!err) {
+            that.confirmLoading = true;
+            let httpurl = '';
+            let method = '';
+            if (!this.model.id) {
+              httpurl += this.url.add;
+              method = 'post';
+            } else {
+              httpurl += this.url.edit;
+              method = 'put';
+            }
+            // if (!values.birthday) {
+            //   values.birthday = '';
+            // } else {
+            //   values.birthday = values.birthday.format(this.dateFormat);
+            // }
+            values.coverUrl = this.imageUrl[0]
+            let formData = Object.assign(this.model, values);
+            //时间格式化
+
+            console.log(formData)
+            httpAction(httpurl, formData, method).then((res) => {
+              if (res.success) {
+                that.$message.success(res.message);
+                that.$emit('ok');
+                that.$emit("updateList")
+              } else {
+                that.$message.warning(res.message);
+              }
+            }).finally(() => {
+              that.confirmLoading = false;
+              that.close();
+            })
+
+
+          }
+        })
+      },
+      handleCancel() {
+        this.close()
+      },
+
+
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+
+</style>

+ 553 - 0
src/components/ctop/vm-card-prop.vue

@@ -0,0 +1,553 @@
+<style>
+.ping .ant-modal-content .ant-modal-footer {
+  display: none !important;
+}
+.ping .ant-list-item-content {
+  justify-content: space-between;
+  cursor: pointer;
+}
+.ping-list .detele {
+  display: none;
+}
+/* .ping-list:hover span {
+  display: block;
+} */
+li {
+  list-style-type: none;
+}
+</style>
+<style lang="scss" scoped>
+.actor-photo-list {
+  display: flex;
+  padding-left: 0;
+  li {
+    width: 25%;
+    margin: 10px;
+    position: relative;
+    height: 250px;
+    border: 1px solid #f2f2f2;
+    img,
+    video {
+      width: 100%;
+      margin-top: auto;
+      margin-bottom: auto;
+      top: 0;
+      bottom: 0;
+      position: absolute;
+      max-height: 250px;
+    }
+  }
+}
+</style>
+<template>
+  <div :class="[type === 'horizantal' ? 'vm-card-horizantal' : 'vm-card-vertical' , 'vm-panel']" style="border:1px solid black">
+    <div
+      class="card-img"
+      style="height:250px;border-bottom:1px solid #f2f2f2"
+      :style="{backgroundImage: img?'url(' +img+')':'url('+noImgUrl+')', backgroundSize:'contain', backgroundRepeat: 'no-repeat',backgroundPosition:'center center'}"
+    >
+      <!-- <img :src="img" alt="" height="250"> -->
+      <div v-if="editable && type == 'vertical'" class="control">
+        <span class="edit">
+          <a href="javascript:;">
+            <a-icon type="edit" @click="handleDetail(record)"></a-icon>
+          </a>
+        </span>
+        <span class="delete">
+          <a-icon type="message" @click="getComment"></a-icon>
+        </span>
+      </div>
+    </div>
+    <div class="card-desc panel-body">
+      <h2>{{ title }}</h2>
+      <p>{{ desc }}</p>
+      <a :href="detailUrl" @click="getDetail">详情 ></a>
+      <a :href="detailUrl" @click="deleteItemCard" style="float:right">删除 </a>
+    </div>
+    <a-modal title="评论" v-model="visible" class="ping">
+      <div>
+        <div style="height:300px;overflow:auto">
+          <a-list
+            v-if="comments.length"
+            :dataSource="comments"
+            :header="`${comments.length} ${comments.length > 1 ? 'replies' : 'reply'}`"
+            itemLayout="horizontal"
+          >
+            <a-list-item slot="renderItem" slot-scope="item, index" class="ping-list">
+              <a-comment
+                :author="item.createBy"
+                :avatar="item.avatar"
+                :content="item.comment"
+                :datetime="item.createTime"
+              ></a-comment>
+              <span class="detele" style="margin:20px;color:red;" @click="detele(item,index)">删除</span>
+            </a-list-item>
+          </a-list>
+          <div v-else style="height:300px;text-align:center;line-height:300px">暂无评论</div>
+        </div>
+        <a-comment>
+          <!-- <a-avatar
+            slot="avatar"
+            :src="loginInfo.value.avatar"
+            alt="Han Solo"
+          />-->
+          <img :src="loginInfo.value.avatar" alt slot="avatar" />
+          <div slot="content">
+            <a-form-item>
+              <a-textarea :rows="4" @change="handleChange" :value="value"></a-textarea>
+            </a-form-item>
+            <a-form-item>
+              <a-button
+                htmlType="submit"
+                :loading="submitting"
+                @click="handleSubmit"
+                type="primary"
+              >添加</a-button>
+            </a-form-item>
+          </div>
+        </a-comment>
+      </div>
+    </a-modal>
+
+    <a-modal title="详情" v-model="detail" class="ping" :width="800">
+      <a-tabs defaultActiveKey="1" @change="callback" v-model="indexDetail">
+        <a-tab-pane tab="演员信息" key="1">
+          <div style="display:flex;flex-direction:row;height:200px;">
+            <div
+              style="height:200px;width:30%;border:1px solid #f2f2f2;"
+              :style="{backgroundImage: img?'url(' +img+')':'url('+noImgUrl+')', backgroundSize:'contain', backgroundRepeat: 'no-repeat',backgroundPosition:'center center'}"
+            ></div>
+            <div style="flex:1;margin:15px">
+              <a-form :form="form">
+                <a-form-item label="姓名" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
+                  <span>{{form.name}}</span>
+                </a-form-item>
+                <a-form-item label="性别" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
+                  <span>{{form.sex==1?"男":"女"}}</span>
+                </a-form-item>
+                <a-form-item label="出生年月" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
+                  <span>{{form.birthday}}</span>
+                </a-form-item>
+              </a-form>
+            </div>
+          </div>
+          <div style="margin-top:20px">
+            <a-collapse accordion>
+              <a-collapse-panel header="个人信息" key="1">
+                <p>{{form.speciality}}</p>
+              </a-collapse-panel>
+            </a-collapse>
+          </div>
+        </a-tab-pane>
+        <a-tab-pane tab="演员图集" key="2" forceRender>
+          <div>
+            <a type="primary" style="margin-left:10px" @click="addPhoto=true">新增图集</a>
+            <ul class="actor-photo-list">
+              <li v-for="(item,index) of photoList" :key="index">
+                <a-icon
+                  type="close-circle"
+                  style="position:absolute;top:-5px;right:-5px;color:red;z-index:10;cursor: pointer;"
+                  @click="deteleImg(item.id)"
+                />
+                <img :src="item.photoUrl" alt />
+              </li>
+            </ul>
+          </div>
+          <div style="text-align:right">
+            <a-pagination
+              showQuickJumper
+              :total="total"
+              @change="pageChange"
+              :pageSize.sync="pageSize"
+            />
+          </div>
+        </a-tab-pane>
+        <a-tab-pane tab="演员作品" key="3">
+          <div>
+            <a type="primary" style="margin-left:10px" @click="addVideo=true">新增作品</a>
+            <ul class="actor-photo-list">
+              <li v-for="(item,index) of videoList" :key="index">
+                <a-icon
+                  type="close-circle"
+                  style="position:absolute;top:-5px;right:-5px;color:red;z-index:10;cursor: pointer;"
+                  @click="deteleVideo(item.id)"
+                />
+                <video
+                  class="video"
+                  :src="item.videoUrl"
+                  controls="controls"
+                  style="min-height:160px"
+                >您的浏览器不支持 video 标签。</video>
+              </li>
+            </ul>
+          </div>
+          <div style="text-align:right">
+            <a-pagination
+              showQuickJumper
+              :total="videoTotal"
+              @change="pageChange"
+              :pageSize.sync="pageSize"
+            />
+          </div>
+        </a-tab-pane>
+      </a-tabs>
+    </a-modal>
+    <!-- 新增图集 -->
+    <a-modal
+      v-model="addPhoto"
+      title="新增图集"
+      @ok="handleOk('photo')"
+      @cancel="handleCancel('photo')"
+    >
+    <uploadFile :value.sync="imageUrl" :fileCount="1" uploadType="image" :multiple="false" :sizeCheck="false"/>
+      <!-- <upload-to-ali
+        style="margin:10px"
+        v-model="imageUrl"
+        :customDomain="customDomain"
+        preview
+        :region="region"
+        :bucket="bucket"
+        :accept="acceptImage"
+        :max="1"
+        :size="1024000"
+        :accessKeyId="accessKeyId"
+        :accessKeySecret="accessKeySecret"
+      ></upload-to-ali> -->
+    </a-modal>
+    <!-- 新增作品 -->
+    <a-modal
+      v-model="addVideo"
+      title="新增作品"
+      @ok="handleOk('video')"
+      @cancel="handleCancel('video')"
+    >
+    <uploadFile :value.sync="videoUrl" :fileCount="1" uploadType="video" :multiple="false" :sizeCheck="false"/>
+      <!-- <upload-to-ali
+        style="margin:10px"
+        v-model="videoUrl"
+        :customDomain="customDomain"
+        preview
+        :region="region"
+        :bucket="bucket"
+        :accept="acceptVideo"
+        :max="1"
+        :size="1024000"
+        :accessKeyId="accessKeyId"
+        :accessKeySecret="accessKeySecret"
+      ></upload-to-ali> -->
+    </a-modal>
+    <actor-modal ref="modalForm" @ok="modalFormOk" @updateList="updateList" @close="closeVideo"></actor-modal>
+  </div>
+</template>
+<script>
+import moment from 'moment'
+import {
+  actorDetail,
+  actorCommentList,
+  actorCommentAdd,
+  actorCommentDetele,
+  actorPhotoList,
+  actorVideoList,
+  actorPhotoDelete,
+  actorVideoDelete,
+  actorPhotoAdd,
+  actorVideoAdd
+} from '@/api/actor'
+import actorModal from './ActorModal'
+import UploadToAli from '@femessage/upload-to-ali'
+  import uploadFile from '@/components/uploadFile.vue'
+import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+import { closeAllVideoFun } from '@/utils/videoControl'
+export default {
+  name: 'VmCard',
+  mixins: [JeecgListMixin],
+  components: {
+    actorModal,
+    UploadToAli,
+    uploadFile
+  },
+  props: {
+    type: {
+      type: String,
+      default: 'vertical'
+    },
+    editable: {
+      type: Boolean,
+      default: false
+    },
+    title: {
+      type: String,
+      default: 'Title'
+    },
+    img: {
+      type: String,
+      default: require('@/assets/img/noImg.png')
+    },
+    desc: {
+      type: String,
+      default:
+        "Lorem Ipsum is simply dummy text of the printing and typesetting industry,Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"
+    },
+    detailUrl: {
+      type: String,
+      default: '#'
+    },
+    editUrl: {
+      type: String,
+      default: '#'
+    },
+    deleteUrl: {
+      type: String,
+      default: '#'
+    },
+    id: {
+      default() {
+        return 0
+      }
+    },
+    record: {
+      default() {
+        return {}
+      }
+    }
+  },
+  data: function() {
+    return {
+      addPhoto: false,
+      addVideo: false,
+      indexDetail: '1',
+      to: null,
+      comments: [],
+      submitting: false,
+      value: '',
+      moment,
+      visible: false,
+      detail: false,
+      customDomain: '',
+      region: 'oss-cn-beijing',
+      bucket: 'ctop-media',
+      acceptVideo: 'video/mpeg,video/mp4',
+      acceptImage: 'image/jpeg,image/jpg,image/png',
+      accessKeyId: 'LTAIbNbqWzSOklQV',
+      accessKeySecret: '1rkPz7JNoXk8sJevPaeYHWqfkQXBGh',
+      form: {
+        name: '',
+        birthday: '',
+        sex: null,
+        speciality: ''
+      },
+      noImgUrl: require('@/assets/img/noImg.png'),
+      loginInfo: JSON.parse(localStorage.getItem('pro__Login_Userinfo')),
+      photoList: [],
+      imageUrl: '',
+      videoUrl: '',
+      total: 1,
+      videoTotal: 1,
+      pageSize: 4,
+      url: {
+        list: '/ctop/actor/list'
+      },
+      columns: [
+        {
+          title: 'ID',
+          align: 'center',
+          dataIndex: 'id'
+        },
+        {
+          title: '姓名',
+          align: 'center',
+          dataIndex: 'name'
+        },
+        {
+          title: '生日',
+          align: 'center',
+          dataIndex: 'birthday'
+        },
+        {
+          title: '性别',
+          align: 'center',
+          dataIndex: 'sex_dictText'
+        },
+        {
+          title: '特长',
+          align: 'center',
+          dataIndex: 'speciality'
+        },
+        {
+          title: '创建时间',
+          align: 'center',
+          dataIndex: 'createTime'
+        },
+        {
+          title: '操作',
+          dataIndex: 'action',
+          align: 'center',
+          scopedSlots: { customRender: 'action' }
+        }
+      ],
+      currentPage: 1,
+      videoList: []
+    }
+  },
+  methods: {
+    updateList() {
+      this.$emit('updateList')
+    },
+    pageChange: function(page) {
+      console.log(page)
+      this.currentPage = page
+      if (this.indexDetail == '2') {
+        this.getPhoto()
+      } else if (this.indexDetail == '3') {
+        this.getVideo()
+      }
+    },
+    handleSubmit() {
+      if (!this.value) {
+        return
+      }
+
+      this.submitting = true
+
+      actorCommentAdd({ actorId: this.id, comment: this.value }).then(res => {
+        this.submitting = false
+        if (res.code == 200) {
+          this.getComment()
+          this.value = ''
+        }
+      })
+      //   setTimeout(() => {}, 1000)
+    },
+    handleChange(e) {
+      this.value = e.target.value
+    },
+    // 删除
+    deleteItemCard(){
+      this.$emit('delete-ok', {id:this.id})
+    },
+    getDetail() {
+      this.indexDetail = '1'
+      this.detail = true
+      actorDetail({ actorId: this.id }).then(res => {
+        console.log(res)
+        if (res.code == 0) {
+          this.form.name = res.result.name
+          this.form.birthday = res.result.birthday
+          this.form.sex = res.result.sex
+          this.form.speciality = res.result.speciality
+        }
+      })
+    },
+    getComment() {
+      this.visible = true
+      this.comments = []
+      actorCommentList({ actorId: this.id }).then(res => {
+        if (res.code == 0) {
+          console.log(res)
+          this.comments = res.result.records.reverse()
+        }
+      })
+    },
+    detele(item, index) {
+      console.log(item, index)
+
+      actorCommentDetele({ id: item.id }).then(res => {
+        if (res.code == 200) {
+          this.comments.splice(index, 1)
+        }
+      })
+    },
+    closeVideo() {
+      closeAllVideoFun()
+    },
+    callback(key) {
+      console.log(key)
+      if (key == '2') {
+        this.closeVideo()
+        this.currentPage = 1
+        this.getPhoto()
+      } else if (key == '3') {
+        this.currentPage = 1
+        this.getVideo()
+      } else if (key == '1') {
+        this.closeVideo()
+      }
+    },
+    getPhoto() {
+      actorPhotoList({ pageNo: this.currentPage, pageSize: 4, actorId: this.id }).then(res => {
+        if (res.code == 0) {
+          console.log(res)
+          this.photoList = res.result.records
+          this.total = res.result.total
+        }
+      })
+    },
+    getVideo() {
+      actorVideoList({ pageNo: this.currentPage, pageSize: 4, actorId: this.id }).then(res => {
+        if (res.code == 0) {
+          console.log(res)
+          this.videoList = res.result.records
+          this.videoTotal = res.result.total
+        }
+      })
+    },
+    deteleImg(id) {
+      actorPhotoDelete({ id: id }).then(res => {
+        if (res.code == 200) {
+          console.log(res)
+          this.getPhoto()
+        }
+      })
+    },
+    deteleVideo(id) {
+      actorPhotoDelete({ id: id }).then(res => {
+        if (res.code == 200) {
+          console.log(res)
+          this.getPhoto()
+        }
+      })
+    },
+    handleOk(type) {
+      const that = this
+      if (type == 'photo') {
+        actorPhotoAdd({ actorId: that.id, photoUrl: that.imageUrl })
+          .then(res => {
+            if (res.success) {
+              this.getPhoto()
+              this.handleCancel('photo')
+            } else {
+              that.$message.warning(res.message)
+            }
+          })
+          .finally(() => {
+            that.confirmLoading = false
+            that.handleCancel('photo')
+          })
+      } else {
+        actorVideoAdd({ actorId: that.id, videoUrl: that.videoUrl })
+          .then(res => {
+            if (res.success) {
+              this.getVideo()
+              this.handleCancel('video')
+            } else {
+              that.$message.warning(res.message)
+            }
+          })
+          .finally(() => {
+            that.confirmLoading = false
+            that.handleCancel('video')
+          })
+      }
+    },
+    handleCancel(type) {
+      if (type == 'photo') {
+        this.imageUrl = ''
+        this.addPhoto = false
+      } else {
+        this.videoUrl = ''
+        this.addVideo = false
+      }
+    }
+  }
+}
+</script>

+ 120 - 19
src/components/ctop/vm-card.vue

@@ -12,9 +12,6 @@
 /* .ping-list:hover span {
   display: block;
 } */
-/* .ant-modal-body {
-  padding-top: 0px;
-} */
 li {
   list-style-type: none;
 }
@@ -53,7 +50,7 @@ li {
       <div v-if="editable && type == 'vertical'" class="control">
         <span class="edit">
           <a href="javascript:;">
-            <a-icon type="edit" @click="handleDetail(record)"></a-icon>
+            <a-icon type="edit" @click="handleDetails(record)"></a-icon>
           </a>
         </span>
         <span class="delete">
@@ -65,6 +62,7 @@ li {
       <h2>{{ title }}</h2>
       <p>{{ desc }}</p>
       <a :href="detailUrl" @click="getDetail">详情 ></a>
+      <a :href="detailUrl" @click="deleteItemCard" style="float:right">删除 </a>
     </div>
     <a-modal title="评论" v-model="visible" class="ping">
       <div>
@@ -112,7 +110,8 @@ li {
     </a-modal>
 
     <a-modal title="详情" v-model="detail" class="ping" :width="800">
-      <a-tabs defaultActiveKey="1" @change="callback" v-model="indexDetail">
+    <!-- {{pageTitle}} -->
+      <a-tabs defaultActiveKey="1" @change="callback" v-model="indexDetail" v-if="pageTitle=='演员列表'">
         <a-tab-pane tab="演员信息" key="1">
           <div style="display:flex;flex-direction:row;height:200px;">
             <div
@@ -193,6 +192,28 @@ li {
           </div>
         </a-tab-pane>
       </a-tabs>
+      <div v-if="pageTitle=='道具列表'">
+        <div style="display:flex;flex-direction:row;height:200px;">
+            <div
+              style="height:200px;width:30%;border:1px solid #f2f2f2;"
+              :style="{backgroundImage: img?'url(' +img+')':'url('+noImgUrl+')', backgroundSize:'contain', backgroundRepeat: 'no-repeat',backgroundPosition:'center center'}"
+            ></div>
+            <div style="flex:1;margin:15px">
+              <a-form :form="form">
+                <a-form-item label="道具名称" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
+                  <span>{{form.name}}</span>
+                </a-form-item>
+                <a-form-item label="道具类型" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
+                  <span>{{form.type|| '暂无类型'}}</span>
+                </a-form-item>
+                <a-form-item label="道具描述" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
+                  <span>{{form.decs|| '暂无描述'}}</span>
+                </a-form-item>
+              </a-form>
+            </div>
+          </div>
+      </div>
+      
     </a-modal>
     <!-- 新增图集 -->
     <a-modal
@@ -201,7 +222,7 @@ li {
       @ok="handleOk('photo')"
       @cancel="handleCancel('photo')"
     >
-    <uploadFile :value.sync="imageUrl" :fileCount="1" uploadType="image" :multiple="false" />
+    <uploadFile :value.sync="imageUrl" :fileCount="10" uploadType="image" :sizeCheck="false"/>
       <!-- <upload-to-ali
         style="margin:10px"
         v-model="imageUrl"
@@ -223,7 +244,7 @@ li {
       @ok="handleOk('video')"
       @cancel="handleCancel('video')"
     >
-    <uploadFile :value.sync="videoUrl" :fileCount="1" uploadType="video" :multiple="false" />
+    <uploadFile :value.sync="videoUrl" :fileCount="10" uploadType="video" :sizeCheck="false"/>
       <!-- <upload-to-ali
         style="margin:10px"
         v-model="videoUrl"
@@ -238,7 +259,9 @@ li {
         :accessKeySecret="accessKeySecret"
       ></upload-to-ali> -->
     </a-modal>
-    <actor-modal ref="modalForm" @ok="modalFormOk" @updateList="updateList" @close="closeVideo"></actor-modal>
+    <actor-modal ref="modalFormActor" @ok="modalFormOks" @updateList="updateList" @close="closeVideo"  ></actor-modal>
+    <propModal ref="modalFormProp" @ok="modalFormOks" @updateList="updateList" @close="closeVideo"  ></propModal>
+    <yardModal ref="modalFormYard" @ok="modalFormOks" @updateList="updateList" @close="closeVideo" ></yardModal>
   </div>
 </template>
 <script>
@@ -253,20 +276,25 @@ import {
   actorPhotoDelete,
   actorVideoDelete,
   actorPhotoAdd,
-  actorVideoAdd
+  actorVideoAdd,
+  propDetail,
 } from '@/api/actor'
 import actorModal from './ActorModal'
+import yardModal from '@/views/modules/yard/modules/YardModal'
 import UploadToAli from '@femessage/upload-to-ali'
   import uploadFile from '@/components/uploadFile.vue'
 import { JeecgListMixin } from '@/mixins/JeecgListMixin'
 import { closeAllVideoFun } from '@/utils/videoControl'
+
+import propModal from './propModal'
 export default {
   name: 'VmCard',
-  mixins: [JeecgListMixin],
   components: {
     actorModal,
     UploadToAli,
-    uploadFile
+    uploadFile,
+    propModal,
+    yardModal
   },
   props: {
     type: {
@@ -281,14 +309,19 @@ export default {
       type: String,
       default: 'Title'
     },
+    pageTitle: {
+      type: String,
+    },
     img: {
       type: String,
       default: require('@/assets/img/noImg.png')
     },
     desc: {
       type: String,
-      default:
-        "Lorem Ipsum is simply dummy text of the printing and typesetting industry,Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"
+      default(){
+        return ""
+      }
+       
     },
     detailUrl: {
       type: String,
@@ -298,6 +331,10 @@ export default {
       type: String,
       default: '#'
     },
+    deleteUrl: {
+      type: String,
+      default: '#'
+    },
     id: {
       default() {
         return 0
@@ -332,13 +369,15 @@ export default {
         name: '',
         birthday: '',
         sex: null,
-        speciality: ''
+        speciality: '',
+        desc:'',
+        type:'',
       },
       noImgUrl: require('@/assets/img/noImg.png'),
       loginInfo: JSON.parse(localStorage.getItem('pro__Login_Userinfo')),
       photoList: [],
-      imageUrl: '',
-      videoUrl: '',
+      imageUrl: [],
+      videoUrl: [],
       total: 1,
       videoTotal: 1,
       pageSize: 4,
@@ -390,6 +429,9 @@ export default {
   methods: {
     updateList() {
       this.$emit('updateList')
+      if(this.pageTitle=='场地列表'){
+        this.loadData(1)
+      }
     },
     pageChange: function(page) {
       console.log(page)
@@ -419,10 +461,16 @@ export default {
     handleChange(e) {
       this.value = e.target.value
     },
+    // 删除
+    deleteItemCard(){
+  
+      this.$emit('delete-ok', {id:this.id})
+    },
     getDetail() {
       this.indexDetail = '1'
       this.detail = true
-      actorDetail({ actorId: this.id }).then(res => {
+      if(this.pageTitle=='演员列表'){
+        actorDetail({ actorId: this.id }).then(res => {
         console.log(res)
         if (res.code == 0) {
           this.form.name = res.result.name
@@ -431,6 +479,23 @@ export default {
           this.form.speciality = res.result.speciality
         }
       })
+      }else if(this.pageTitle=='道具列表'){
+        propDetail({ propId: this.id }).then(res => {
+            console.log(res)
+          if (res.code == 0) {
+            this.form.name = res.result.name
+            this.form.type = res.result.type
+            this.form.desc = res.result.desc
+            
+          }
+        })
+      }else{
+        console.log(1)
+        this.indexDetail = '1'
+        this.detail = false
+        this.$emit('showDetail')
+      }
+      
     },
     getComment() {
       this.visible = true
@@ -541,7 +606,43 @@ export default {
         this.videoUrl = ''
         this.addVideo = false
       }
-    }
-  }
+    },
+
+    handleDetails(record) {
+      console.log(record) 
+      if(this.pageTitle=='演员列表'){
+        console.log(this.$refs.modalFormActor)
+        this.$refs.modalFormActor.edit(record);
+        this.$refs.modalFormActor.title = "详情";
+        this.$refs.modalFormActor.disableSubmit = true;
+      }else if(this.pageTitle=='道具列表'){
+        this.$refs.modalFormProp.edit(record);
+        this.$refs.modalFormProp.title = "详情";
+        this.$refs.modalFormProp.disableSubmit = true;
+      }else if(this.pageTitle=='场地列表'){
+        this.$refs.modalFormYard.edit(record);
+        this.$refs.modalFormYard.title = "详情";
+        this.$refs.modalFormYard.disableSubmit = true;
+      }
+      
+    },
+
+    modalFormOks() {
+      // 新增/修改 成功时,重载列表
+      if(this.pageTitle=='演员列表'){
+        this.url.list='/ctop/actor/list'
+      }else if(this.pageTitle=='道具列表'){
+        this.url.list='/ctop/prop/list'
+      }else if(this.pageTitle=='场地列表'){
+        this.url.list='/ctop/yard/list'
+      }
+      // this.loadData(1);
+      this.updateList()
+    },
+  },
+
+  mounted() {
+    console.log(this.pageTitle)
+  },
 }
 </script>

+ 21 - 9
src/components/ctop/vm-image-list.vue

@@ -68,23 +68,25 @@
       </a-row>
     </a-row>
     <a-row class="image-list" :gutter="16" style="padding:0 8px">
-      <a-col :lg="6" :sm="12" class="vm-margin" v-for="(item, index) of dataShow" :key="index" style="padding:0">
-        <vm-card
+      <a-col :lg="6" :sm="12" class="vm-margin" style="padding:0"  v-for="(item, index) of dataShow" :key="index" >
+          <vm-card
           style="margin: 0 15px;"
           :editable="true"
           :title="item.name"
           :img="item.coverUrl"
           :desc="item.speciality"
           :detailUrl="item.videoUrl"
-          :editUrl="item.editUrl"
+          :editUrl="item.editUrl"      
+          :pageTitle='title'   
           :id="item.id"
-          :record="item"
-          @delete-ok="deleteOk(item)"
+          :record="item"          
+          @delete-ok="deleteOk({...item,'page':currentPage})"
           @updateList="updateList"
         ></vm-card>
       </a-col>
     </a-row>
-    <actor-modal ref="modalForm" @ok="modalFormOk" @updateList="updateList"></actor-modal>
+    <actor-modal ref="modalForm" @ok="modalFormOk" @updateList="updateList" v-if="title=='演员列表'"></actor-modal>
+    <propModal ref="modalForm" @ok="modalFormOk" @updateList="updateList" v-if="title=='道具列表'"></propModal>
   </div>
 </template>
 
@@ -93,6 +95,7 @@ import VmCard from '@/components/ctop/vm-card'
 import ARow from 'ant-design-vue/es/grid/Row'
 import ACol from 'ant-design-vue/es/grid/Col'
 import actorModal from './ActorModal'
+import propModal from './propModal'
 import { JeecgListMixin } from '@/mixins/JeecgListMixin'
 export default {
   name: 'VmImageList',
@@ -100,13 +103,15 @@ export default {
     ACol,
     ARow,
     VmCard,
-    actorModal
+    actorModal,
+    propModal
   },
   mixins: [JeecgListMixin],
+  
   props: {
     title: {
       type: String,
-      default: '演员列表'
+      // default: '演员列表'
     },
     // origin data
     data: {
@@ -180,6 +185,9 @@ export default {
       ]
     }
   },
+  // provide:{
+  //   page:this.currentPage,
+  // },
   methods: {
     updateList() {
       this.$emit('updateList')
@@ -209,8 +217,12 @@ export default {
     //     }
     //   })
     // },
+    // deleteItem(data){
+    //     console.log(data,1)
+    //     this.$emit('delete-ok', {page:this.currentPage,...data})
+    // },
     deleteOk: function(data) {
-      this.$emit('delete-ok', data)
+      this.$emit('delete-ok', {page:this.currentPage,...data})
     },
     search() {
       if (this.keyword) {

+ 323 - 0
src/components/editableTree.vue

@@ -0,0 +1,323 @@
+<template>
+  <div id="areaTree" class="addrTree">
+    <ul :id="treeId" class="ztree"></ul>
+  </div>
+</template>
+
+<script>
+import $ from 'jquery'
+export default {
+  name: 'editableTree',
+  components: {},
+  props: {
+    treeData: {
+      // 树 data
+      type: Array,
+      default() {
+        return []
+      },
+    },
+    editable: {
+      // 是否可编辑
+      type: Boolean,
+      default: true,
+    },
+    btn: {
+      // 按钮集合
+      type: Array,
+      default() {
+        return ['add', 'del']
+      },
+    },
+    selectFirstNode: {
+      // 是否默认选中第一个节点
+      type: Boolean,
+      default: false,
+    },
+    pIdKey: {
+      type: String,
+      default: 'parentId',
+    },
+  },
+  data() {
+    return {
+      treeId: 'treeDemo' + new Date().getTime(),
+      setting: {
+        view: {
+          addHoverDom: this.editable && this.showBtn('add') && this.addHoverDom,
+          showIcon: false,
+          showLine: true,
+          selectedMulti: false,
+        },
+        edit: {
+          enable: this.editable,
+          showRemoveBtn: this.showBtn('del') && this.showRemoveBtn,
+          showRenameBtn: this.showBtn('edit') && this.showRenameBtn,
+          removeTitle: '删除',
+          renameTitle: '修改',
+        },
+        data: {
+          simpleData: {
+            enable: true,
+            idKey: 'id',
+            pIdKey: this.pIdKey,
+            rootPId: 0,
+          },
+        },
+        callback: {
+          onClick: this.zTreeOnClick,
+          beforeDrag: this.beforeDrag,
+          onExpand: this.onExpand,
+          beforeRemove: this.beforeRemove,
+          beforeEditName: this.beforeEditName,
+          beforeRename: this.beforeRename,
+          onRename: this.onRename,
+          onRemove: this.onRemove,
+        },
+      },
+      zNodes: [],
+      log: '',
+      className: 'dark',
+      newCount: 1,
+      curDragNodes: '',
+      autoExpandNode: '',
+      action: 'add',
+    }
+  },
+  methods: {
+    zTreeOnClick(event, treeId, treeNode) {
+      this.$emit('treeNodeClick', treeNode)
+    },
+    beforeDrag(treeId, treeNodes) {
+      return false
+    },
+    onExpand(event, treeId, treeNode) {
+      if (treeNode === this.autoExpandNode) {
+        this.className = this.className === 'dark' ? '' : 'dark'
+        this.showLog('[ ' + this.getTime() + ' onExpand ]&nbsp;&nbsp;&nbsp;&nbsp;' + treeNode.name)
+      }
+    },
+    remove() {
+      // 移除节点
+      var zTree = $.fn.zTree.getZTreeObj(this.treeId),
+        nodes = zTree.getSelectedNodes(),
+        treeNode = nodes[0]
+      this.$emit('delTreeNode', treeNode)
+    },
+    beforeRemove(treeId, treeNode) {
+      if (treeNode.id) {
+        console.log('delete:', treeNode)
+        this.$emit('deleteNode', treeNode) // 触发删除
+      }
+      return false
+    },
+    onRemove(e, treeId, treeNode) {
+      this.showLog('[ ' + this.getTime() + ' onRemove ]&nbsp;&nbsp;&nbsp;&nbsp; ' + treeNode.name)
+    },
+    onRename(event, treeId, treeNode, isCancel) {
+      console.log('rename')
+      this.$emit('treeInfoEvent', treeNode, event)
+    },
+    beforeRename(treeId, treeNode, newName) {
+      if (newName.length == 0) {
+        alert('节点名称不能为空.')
+        var zTree = $.fn.zTree.getZTreeObj(this.treeId)
+        setTimeout(function () {
+          zTree.editName(treeNode)
+        }, 10)
+        return false
+      } else if (newName.length > 20) {
+        alert('节点名称不能大于20个字符.')
+        var zTree = $.fn.zTree.getZTreeObj(this.treeId)
+        setTimeout(function () {
+          zTree.editName(treeNode)
+        }, 10)
+        return false
+      }
+      return true
+    },
+    showLog(str) {
+      if (!this.log) this.log = $('#log')
+      this.log.append("<li class='" + this.className + "'>" + str + '</li>')
+      if (this.log.children('li').length > 8) {
+        this.log.get(0).removeChild(this.log.children('li')[0])
+      }
+    },
+    getTime() {
+      var now = new Date(),
+        h = now.getHours(),
+        m = now.getMinutes(),
+        s = now.getSeconds(),
+        ms = now.getMilliseconds()
+      return h + ':' + m + ':' + s + ' ' + ms
+    },
+    addHoverDom(treeId, treeNode) {
+      if (treeNode.addDis != true) {
+        let _this = this
+        var sObj = $('#' + treeNode.tId + '_span')
+        if (treeNode.editNameFlag || $('#addBtn_' + treeNode.tId).length > 0) return
+        var addStr =
+          "<span class='button add' id='addBtn_" + treeNode.tId + "' title='新增' onfocus='this.blur();'>新增</span>"
+        sObj.after(addStr)
+        var btn = $('#addBtn_' + treeNode.tId)
+        if (btn)
+          btn.bind('click', function () {
+            _this.add(treeNode)
+          })
+      }
+    },
+    add(treeNode) {
+      // 增加子节点
+      this.action = 'add' // 切换为add
+      var zTree = $.fn.zTree.getZTreeObj(this.treeId)
+      var isParent = false
+      var nodes = zTree.getSelectedNodes()
+      var treeNode2 = nodes[0]
+      if (treeNode) {
+        treeNode2 = zTree.addNodes(treeNode, {
+          id: '',
+          pId: treeNode.id,
+          sysCode: treeNode.sysCode || treeNode.valStr,
+          isParent: isParent,
+          name: '新子节点' + this.newCount++,
+        })
+      } else {
+        treeNode2 = zTree.addNodes(null, { id: '', pId: 0, isParent: isParent, name: '新节点' + this.newCount++ })
+      }
+      if (treeNode2) {
+        zTree.editName(treeNode2[0])
+      } else {
+        alert('叶子节点被锁定,无法增加子节点')
+      }
+    },
+    addTopNodes() {
+      this.action = 'add'
+      var zTree = $.fn.zTree.getZTreeObj(this.treeId)
+      var treeNode = zTree.addNodes(null, { id: '', pId: 0, isParent: true, name: '新节点' + this.newCount++ })
+      zTree.editName(treeNode[0])
+      this.treeNodeMessage = zTree.getNodes() //Root节点信息
+      this.$emit('RootNodes', this.treeNodeMessage)
+    },
+    showRemoveBtn(treeId, treeNode) {
+      if (treeNode.removeDis == true) return false
+      return true
+    },
+    showRenameBtn(treeId, treeNode) {
+      if (treeNode.renameDis == true) return true
+      return false
+    },
+    showBtn(button) {
+      if (this.btn && this.btn.length > 0) {
+        if (this.btn.findIndex((v) => v == button) > -1) {
+          return true
+        } else {
+          return false
+        }
+      } else {
+        return false
+      }
+    },
+    selectFirstNodeFun() {
+      // 选中第一个节点
+      if (this.selectFirstNode) {
+        const treeObj = $.fn.zTree.getZTreeObj(this.treeId)
+        console.log(treeObj)
+        let nodes = treeObj.getNodes()
+        if (nodes.length > 0) {
+          treeObj.selectNode(nodes[0])
+        }
+      }
+    },
+    selectNode(node) {
+      const treeObj = $.fn.zTree.getZTreeObj(this.treeId)
+      treeObj.selectNode(node)
+    },
+    queryTreeFun(data) {
+      this.searchTree(data)
+    },
+    searchTree(info) {
+      var treeObj = $.fn.zTree.getZTreeObj(this.treeId)
+      if (info.val == '') {
+        $.fn.zTree.init($('#' + this.treeId), this.setting, this.treeData)
+      } else {
+        function filter(node) {
+          return node.name.indexOf(info.val) > -1
+        }
+        let nodes = treeObj.transformToArray(this.treeData)
+        nodes = JSON.parse(JSON.stringify(nodes))
+        nodes.map((item) => {
+          if (item.hasOwnProperty('children')) {
+            delete item.children
+          }
+          return item
+        })
+
+        nodes = nodes.filter((v) => v.name.indexOf(info.val) > -1)
+        nodes = treeObj.transformTozTreeNodes(nodes)
+        $.fn.zTree.init($('#' + this.treeId), this.setting, nodes)
+        treeObj.expandAll(true)
+      }
+    },
+  },
+  watch: {
+    treeData(val) {
+      $.fn.zTree.init($('#' + this.treeId), this.setting, val)
+      this.selectFirstNodeFun()
+    },
+  },
+  mounted() {
+    this.$nextTick(() => {
+      $.fn.zTree.init($('#' + this.treeId), this.setting, this.treeData)
+      this.selectFirstNodeFun()
+    })
+  },
+}
+</script>
+<style lang="scss" scope>
+.addrTree {
+  position: relative;
+  height: 100%;
+  background: #fff;
+  .tree_title {
+    height: 30px;
+    line-height: 30px;
+    padding: 0 10px;
+    background: #fafafa;
+    button {
+      float: right;
+      margin: 4px 4px 0 0;
+    }
+  }
+  .ztree {
+    height: 100%;
+    overflow: auto;
+    padding: 5px;
+    li span.button.add {
+      background-position: -143px 0;
+    }
+  }
+  [treenode_a] {
+    vertical-align: middle;
+    .button.add {
+      margin-right: 2px;
+      display: none;
+    }
+    &:hover {
+      .button.add {
+        display: inline-block;
+      }
+    }
+    span {
+      vertical-align: middle !important;
+    }
+  }
+  [treenode_a].curSelectedNode {
+    .button.add {
+      display: inline-block;
+    }
+  }
+  [treenode_switch] {
+    vertical-align: middle;
+  }
+}
+</style>

+ 326 - 0
src/components/formComponents/Cascader.vue

@@ -0,0 +1,326 @@
+<style lang="scss">
+.hcst-cascader{
+    .el-cascader {
+        display: inline-block;
+        position: relative;
+        font-size: 14px;
+        height: 32px;
+    }
+    .el-input__inner{
+        height: 32px  !important;
+    }
+    .el-cascader__tags {
+        top: 64%;
+    }
+}
+
+</style>
+<template>
+  <div class="hcst-cascader">
+    <!-- <a-checkbox-group v-model="selectedVal" :disabled="disabled" style="width: 100%">
+      <a-checkbox
+        v-for="item of selectOption"
+        :label="item.label"
+        :key="item.value"
+        :value="item.value"
+        :class="{
+          item: true && userButton,
+          selection: selectedVal && selectedVal.indexOf(item.value) > -1 && userButton,
+          bgcolor: item.disabled,
+        }"
+        >{{ item.label }}</a-checkbox
+      >
+    </a-checkbox-group> -->
+
+    <!-- :show-all-levels="false"  :clearCheckedNodes='clearCheckedNodes'-->
+        <el-cascader
+        v-model='selectedVal'
+        :disabled="disabled"        
+        :props="props"
+        clearable
+        collapse-tags
+        
+        ></el-cascader>
+        <!-- {{selectedVal}} -->
+  </div>
+</template>
+
+<script>
+// import { dictionary } from '@/components/service/core/dictionary'
+import { isArray } from '@/utils/Tools'
+import { httpAction, getAction } from '@/api/manage'
+
+let id = 0;
+
+export default {
+  name: 'Cascader', // Cascader组件二次封装
+  props: {
+    userButton: {
+      type: Boolean,
+      default() {
+        return true
+      },
+    },
+    code: {
+      // 参数code
+      type: String,
+      default: '',
+    },
+    allValue: {
+      type: Boolean,
+      default() {
+        return false
+      },
+    },
+    options: {
+      // 外部传入的option 可替代请求项
+      type: Array,
+      default() {
+        return []
+      },
+    },
+    value: {
+      // v-model绑定值
+      type: [Number, Array, String],
+      default() {
+        return []
+      },
+    },
+    cascaderValue: {
+      // v-model绑定值
+      type: [Number, Array, String],
+      default() {
+        return []
+      },
+    },
+    label: {
+      // label 绑定值
+      type: [Array, String],
+      default: '',
+    },
+    radioStyle: {
+      // 是否复选框模拟单选效果
+      type: Boolean,
+      default: false,
+    },
+    disabled: {
+      // 是否禁用
+      type: Boolean,
+      default: false,
+    },
+    isLazy: {
+      // 是否禁用
+      type: Boolean,
+      default: false,
+    },
+  },
+  components: {},
+  data() {
+    return {
+        // selectOption: [],
+        totalData:[],
+        selectedVal:[],
+        props: {
+            multiple: true ,
+            // checkStrictly: true ,
+            emitPath:false,
+            lazy: this.isLazy,
+            lazyLoad :(node, resolve) =>{
+                // console.log(node,resolve)
+                const { level } = node;
+               
+                // const nodes = Array.from({ length: level + 1 })
+                //     .map(item => ({
+                //     value: ++id,
+                //     label: `选项${id}`,
+                //     leaf: level >= 2
+                //     }));
+                let params={}
+                if(node.root){
+                    params.level=1;
+                }else{
+                    params.parent=node.root?0:node.value
+                }
+                
+                getAction('/kuaishou/batch/getRegion',params).then(res=>{
+                    // console.log(res)
+                    
+                    const nodes =res.result.map(item => ({
+                        value: item.regionId,
+                        label: item.name,
+                        level:item.level,
+                        parent:item.parent,                        
+                        leaf: item.level >= 2
+                    }));
+                    if(!node.root&&node.level==1){
+                      if(this.totalData.length){
+                        let dataArr=[];
+                        dataArr=this.totalData.filter(item=>{
+                            return item.value==node.value
+                        });
+                        if(!dataArr.length){
+                          this.totalData.push({
+                            label:node.label,
+                            value:node.value,
+                            children:res.result.length
+                          })
+                        }
+                      }else{
+                        this.totalData.push({
+                          label:node.label,
+                          value:node.value,
+                          children:res.result.length
+                        })
+                      }
+                    }
+                    // console.log(this.totalData)
+                    // 通过调用resolve将子节点数据返回,通知组件数据加载完成
+                    resolve(nodes);
+                })
+                
+               
+            }
+        },
+    }
+  },
+  watch: {
+    
+    selectedVal(n, o) {
+      console.log(n,o)
+      if(n.length){
+          var retrunData = []
+          
+          if(n.length){
+             retrunData=JSON.parse(JSON.stringify(n))
+            this.totalData.forEach(element => {
+                let arr=[];
+                arr=n.filter(item=>{
+                  let number=(item+'').substr(0,2)
+                  // console.log(number)
+                  return element.value==number
+                })
+                if(arr.length==element.children ){
+                  
+                  retrunData=retrunData.filter(ele=>{
+                    return element.value!=(ele+'').substr(0,2)
+                  })
+                  console.log(retrunData)
+                  // this.selectedVal=[];
+                  retrunData=[
+                    ...retrunData,
+                    element.value,
+                    
+                  ]
+                }
+                
+              
+            });
+          }
+          console.log('retrunData',retrunData)
+          // this.$emit('input', val)
+          // this.$emit('change', val)
+          this.$emit('update:cascaderValue',retrunData)
+          // this.consoleFun('VALUE: ', val, 'LABEL:', label)
+      
+      }else{
+        this.$emit('update:cascaderValue',n)
+      }
+      
+      
+    },
+  },
+  // 
+  mounted() {
+    this.$nextTick(() => {
+      
+    })
+  },
+  methods: {
+    getOptions() {
+      // 获取字典项
+      getAction('/sys/dictItem/list', { dictId: this.code, pageSize: 1000, pageNo: 1 }).then((res) => {
+        if (res.success && isArray(res.result.records)) {
+          var data = res.result.records
+          data = data.map((item) => {
+            return {
+              label: item.itemText,
+              value: item.itemValue,
+              ...item,
+            }
+          })
+          if (this.allValue) {
+            data.unshift({ label: '不检查', value: 'unlimited' })
+            if (this.value == '' || this.value == '' || this.value == null || this.value.length == 0) {
+              this.selectedVal = ['unlimited']
+            }
+          } else {
+            if (this.value == '' || this.value == '' || this.value == null || this.value.length == 0) {
+              this.selectedVal = []
+            }
+          }
+          this.selectOption.push(...data)
+        }
+      })
+    },
+    selectChange(val) {
+      
+        // 多选
+        var retrunData = []
+        
+        console.log(val)
+        
+        if(val.length){
+          this.totalData.forEach(element => {
+              let arr=[];
+              arr=val.filter(item=>{
+                let number=(item+'').substr(0,2)
+                // console.log(number)
+                return element.value==number
+              })
+              if(arr.length==element.children ){
+                let otherArr=[]
+                otherArr=this.selectedVal.filter(ele=>{
+                  return element.value!=(ele+'').substr(0,2)
+                })
+                console.log(otherArr)
+                // this.selectedVal=[];
+                retrunData=[
+                  ...retrunData,
+                  element.value,
+                  ...otherArr,
+                ]
+                
+              }else{
+                retrunData=[
+                  ...retrunData,
+                  ...arr
+                ]
+              }
+             
+          });
+        }
+        console.log('retrunData',retrunData)
+        // this.$emit('input', val)
+        // this.$emit('change', val)
+        this.$emit('update:cascaderValue',retrunData)
+        // this.consoleFun('VALUE: ', val, 'LABEL:', label)
+      
+    },
+    emitChange(...args) {
+      // emit change事件
+      this.$emit('change', ...args)
+    },
+    consoleFun(...args) {
+      // 打印信息
+      if (process.env.NODE_ENV == 'development') {
+        console.log(...args)
+      }
+    },
+
+    // clearCheckedNodes(){
+
+    // },
+  },
+}
+</script>
+

+ 330 - 0
src/components/formComponents/checkBoxGroup.vue

@@ -0,0 +1,330 @@
+<style lang="scss">
+.ul-radio-group {
+  border-radius: 5px;
+  height: 32px;
+  padding: 0;
+  margin: 0;
+  border: none;
+  background: transparent;
+
+  .btn {
+    background: transparent;
+    padding: 0;
+    margin: 0;
+    border: none;
+  }
+  .item {
+    min-width: 80px;
+    padding: 0px 15px;
+    text-align: center;
+    display: inline-block;
+    border: 1px solid #ccc;
+    border-left: 0;
+    height: 32px;
+    line-height: 32px;
+    cursor: pointer;
+    .checkbox-button-input {
+      width: 0;
+      height: 0;
+      opacity: 0;
+    }
+    .ant-checkbox {
+      display: none;
+    }
+  }
+  .bgcolor {
+    color: rgba(0, 0, 0, 0.25);
+    background-color: #f5f5f5;
+    border-color: #d9d9d9;
+    cursor: not-allowed;
+  }
+  .item::after {
+    content: '';
+    position: absolute;
+    right: 1px;
+    top: -2px;
+    width: 0;
+    height: 0;
+    border-top: 6px solid transparent;
+    border-bottom: 6px solid transparent;
+    border-left: 6px solid;
+    border-left-color: #e0e0e0;
+    -webkit-transform: rotate(-45deg);
+    -ms-transform: rotate(-45deg);
+    transform: rotate(-45deg);
+    border-radius: 2px;
+  }
+  .item:hover {
+    color: #1890ff;
+  }
+  .selection {
+    color: #fff;
+    background: #1890ff;
+    // border-color: #1890ff;
+  }
+  .item.selection:hover {
+    color: #fff;
+    background: #40a9ff;
+  }
+  .item.selection:after {
+    border-left: 6px solid #1890ff;
+  }
+  .item:hover::after {
+    // border-color: #40a9ff;
+    border-left: 6px solid #40a9ff;
+  }
+  .bgcolor:hover {
+    color: rgba(0, 0, 0, 0.25);
+    background-color: #f5f5f5;
+    border-color: #d9d9d9;
+    cursor: not-allowed;
+  }
+  .bgcolor:hover::after {
+    // border-color: #40a9ff;
+    border-left: 6px solid #e0e0e0;
+  }
+  .bgcolor.selection:after {
+    border-left: 6px solid #e0e0e0;
+  }
+  .bgcolor.selection {
+    color: rgba(0, 0, 0, 0.25);
+    background-color: #f5f5f5;
+    border-color: #d9d9d9;
+    cursor: not-allowed;
+  }
+  .bgcolor.selection:hover {
+    // color: #1890ff;
+    color: rgba(0, 0, 0, 0.25);
+    background-color: #f5f5f5;
+    border-color: #d9d9d9;
+    cursor: not-allowed;
+  }
+  .bgcolor.selection:after {
+    border-left: 6px solid #e0e0e0;
+  }
+  .ant-checkbox-wrapper + .ant-checkbox-wrapper {
+    margin-left: 0;
+  }
+}
+.ul-radio-group .item:first-child {
+  border-radius: 5px 0 0 5px;
+  border-left: 1px solid #ccc;
+}
+.ul-radio-group .item.selection:first-child {
+  border-radius: 5px 0 0 5px;
+  border-left: 1px solid #1890ff;
+}
+.ul-radio-group .item.selection:nth-child(2) {
+  border-left: 1px solid #1890ff;
+}
+.ul-radio-group .item:first-child:after {
+  border-left: 0;
+}
+.ul-radio-group .item:last-child {
+  border-radius: 0px 5px 5px 0px;
+}
+</style>
+<template>
+  <div class="ul-radio-group">
+    <a-checkbox-group v-model="selectedVal" :disabled="disabled" style="width: 100%">
+      <a-checkbox
+        v-for="item of selectOption"
+        :label="item.label"
+        :key="item.value"
+        :value="item.value"
+        :class="{
+          item: true && userButton,
+          selection: selectedVal && selectedVal.indexOf(item.value) > -1 && userButton,
+          bgcolor: item.disabled,
+        }"
+        >{{ item.label }}</a-checkbox
+      >
+    </a-checkbox-group>
+  </div>
+</template>
+
+<script>
+// import { dictionary } from '@/components/service/core/dictionary'
+import { isArray } from '@/utils/Tools'
+import { httpAction, getAction } from '@/api/manage'
+export default {
+  name: 'CheckboxGroup', // checkbox组件二次封装
+  props: {
+    userButton: {
+      type: Boolean,
+      default() {
+        return true
+      },
+    },
+    code: {
+      // 参数code
+      type: String,
+      default: '',
+    },
+    allValue: {
+      type: Boolean,
+      default() {
+        return false
+      },
+    },
+    options: {
+      // 外部传入的option 可替代请求项
+      type: Array,
+      default() {
+        return []
+      },
+    },
+    value: {
+      // v-model绑定值
+      type: [Number, Array, String],
+      default() {
+        return []
+      },
+    },
+    label: {
+      // label 绑定值
+      type: [Array, String],
+      default: '',
+    },
+    radioStyle: {
+      // 是否复选框模拟单选效果
+      type: Boolean,
+      default: false,
+    },
+    disabled: {
+      // 是否禁用
+      type: Boolean,
+      default: false,
+    },
+  },
+  components: {},
+  data() {
+    return {
+      selectOption: [],
+    }
+  },
+  watch: {
+    options(val) {
+      if (isArray(val)) {
+        this.selectOption.splice(0, this.selectOption.length)
+        this.selectOption.push(...val)
+      }
+    },
+    selectedVal(n, o) {
+      if (this.radioStyle && !this.allValue) {
+        if (n.length == 0) {
+          this.selectedVal = o
+        }
+      }
+    },
+  },
+  computed: {
+    selectedVal: {
+      get: function () {
+        return this.value
+      },
+      set: function (val) {
+        this.selectChange(val)
+      },
+    },
+  },
+  mounted() {
+    this.$nextTick(() => {
+      if (this.code && this.options.length == 0) {
+        // 如果code值不为空且options.lengt==0, 则去请求字典项
+        this.getOptions()
+      } else {
+        // 否则直接获取外部传入的options
+        this.selectOption.splice(0, this.selectOption.length)
+        this.selectOption.push(...this.options)
+      }
+    })
+  },
+  methods: {
+    getOptions() {
+      // 获取字典项
+      getAction('/sys/dictItem/list', { dictId: this.code, pageSize: 1000, pageNo: 1 }).then((res) => {
+        if (res.success && isArray(res.result.records)) {
+          var data = res.result.records
+          data = data.map((item) => {
+            return {
+              label: item.itemText,
+              value: item.itemValue,
+              ...item,
+            }
+          })
+          if (this.allValue) {
+            data.unshift({ label: '不检查', value: 'unlimited' })
+            if (this.value == '' || this.value == '' || this.value == null || this.value.length == 0) {
+              this.selectedVal = ['unlimited']
+            }
+          } else {
+            if (this.value == '' || this.value == '' || this.value == null || this.value.length == 0) {
+              this.selectedVal = []
+            }
+          }
+          this.selectOption.push(...data)
+        }
+      })
+    },
+    selectChange(val) {
+      // 选择事件
+      if (!this.radioStyle) {
+        // 多选
+        var retrunData = []
+        let label = this.selectOption.filter((v) => val.findIndex((item) => item == v.value) > -1).map((v) => v.label)
+        if (this.allValue) {
+          if (val.findIndex((item) => item == 'unlimited') == 0) {
+            retrunData = val.slice(-1)
+          } else if (val.findIndex((item) => item == 'unlimited') == val.length - 1) {
+            retrunData = ['unlimited']
+          } else {
+            retrunData = val
+          }
+        } else {
+          retrunData = val
+        }
+
+        this.$emit('input', retrunData)
+        this.$emit('update:label', label)
+        this.$emit('change', val)
+        // this.consoleFun('VALUE: ', val, 'LABEL:', label)
+      } else {
+        // 模拟单选radio效果
+        let selectVal = []
+        if (this.allValue) {
+          if (val.length == 0) {
+            selectVal = ['unlimited']
+          }
+        } else {
+          if (val.length == 0) {
+            selectVal = []
+          }
+        }
+
+        if (val.length > 0) {
+          selectVal = val.slice(-1)
+        }
+        let label = this.selectOption
+          .filter((v) => selectVal.findIndex((item) => item == v.value) > -1)
+          .map((v) => v.label)
+        this.$emit('input', selectVal)
+        this.$emit('update:label', label)
+        this.$emit('change', selectVal)
+        // this.consoleFun('VALUE: ', selectVal, 'LABEL:', label)
+      }
+    },
+    emitChange(...args) {
+      // emit change事件
+      this.$emit('change', ...args)
+    },
+    consoleFun(...args) {
+      // 打印信息
+      if (process.env.NODE_ENV == 'development') {
+        console.log(...args)
+      }
+    },
+  },
+}
+</script>
+

+ 237 - 0
src/components/formComponents/radioCheck.vue

@@ -0,0 +1,237 @@
+<template>
+    <div class="radioCheck" >
+        
+            <label :class="{'item':true,'selection': selectValue.indexOf(item.value)>-1,'bgcolor':item.disabled}" v-for="(item,index) in checkArr" :key="index">
+                
+                <span>
+                    <input type="checkbox" :value="item.value"  class="checkbox-button-input" @click="radioCheckChange($event,item)">
+                </span>
+                <span>{{item.title}}</span>
+                
+            </label>
+            <!-- <label :class="{'item':true,'selection': selectValue.indexOf(item.value)>-1}" v-for="(item,index) in checkArr" :key="index" :disabled='item.disabled'>               
+                <span>
+                    <input type="checkbox" :value="item.value"  class="checkbox-button-input" @click="radioCheckChange">
+                </span>
+                <span>{{item.title}}</span>               
+            </label>        -->
+    </div>
+</template>
+
+<script>
+export default {
+    data() {
+        return {
+            selectValue:[''],
+            currentSelect:'',
+        }
+    },
+   
+    props:['checkArr','selectArr'],
+    watch:{
+        checkArr:{
+            handler(n,o){
+                // console.log(n,o)
+                this.selectValue=[];
+                let i=0;
+                this.checkArr.forEach((item,index)=>{
+                    // console.log(item)
+                    if(item.disabled==false){
+                        i++;
+                        this.selectValue[0]=item.value;
+                        // console.log(this.selectValue)
+                    }else if(item.disabled==undefined){
+                        if(item.checked){
+                            this.selectValue.push(item.value)
+                        }
+                        
+                    }
+                })
+                if(this.selectValue.length==0){
+                    this.selectValue=[''];
+                }
+                if(i==3){
+                    this.selectValue[0]='';
+                }
+                // console.log(this.selectValue)
+                this.$emit('getSonValue',this.selectValue)
+            },
+            deep:true,
+            // immediate: true
+
+        },
+        
+    },
+    methods: {
+        //单选多选的事件
+        radioCheckChange(e,item){
+            console.log(e.target,item,!item.disabled);
+            
+            this.currentSelect=e.target.value;
+            if(!item.disabled){
+
+                if(e.target.value=='' || this.selectValue.length==0 ){
+                    this.selectValue=[''];
+                }else{
+                    if(this.selectValue.indexOf('')>-1){
+                        let i=this.selectValue.indexOf('');
+                        this.selectValue.splice(0,1)
+                    }
+                    
+                    if(this.selectValue.indexOf(e.target.value)==-1){
+                        this.selectValue.push(e.target.value)
+                    }else if(this.selectValue.indexOf(e.target.value)>-1){
+                        this.selectValue=this.selectValue.filter((item)=>{
+                            return item!=e.target.value
+                        })  
+                    }
+
+                    if(this.selectValue.length==0){
+                        this.selectValue=[''];
+                    }
+                    
+                }
+            // console.log(this.currentSelect,this.selectValue);
+            }
+            this.$emit('getSonValue',this.selectValue)
+        },
+    },
+    mounted() {
+        // this.selectValue=[]
+        // console.log(this.checkArr)
+        this.checkArr.forEach((item,index)=>{
+            
+            if(item.disabled&&item.disabled!=undefined){
+                this.selectValue[0]=item.value
+            }else{
+                this.selectValue=['']
+            }
+        })
+        this.$emit('getSonValue',this.selectValue)
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+.radioCheck{        
+    border-radius: 5px;
+    height: 32px;
+    padding: 0;
+    margin: 0;
+    border: none;
+    background: transparent;
+    .btn{
+        background: transparent;
+        padding: 0;
+        margin: 0;
+        border: none;
+    }
+    .item{
+        min-width: 80px;
+        padding: 0px 15px;
+        text-align: center;
+        display: inline-block;
+        border: 1px solid #ccc;
+        border-left: 0;
+        height: 32px;
+        line-height: 32px;
+        cursor: pointer;
+        .checkbox-button-input{
+            width: 0;
+            height: 0;
+            opacity: 0;
+        }
+
+    }
+    .bgcolor{
+        color:rgba(0, 0, 0, 0.25);
+        background-color: #f5f5f5;
+        border-color: #d9d9d9;
+        cursor: not-allowed;
+    }
+    .item::after{
+        content: "";
+        position: absolute;
+        right: 1px;
+        top: -2px;
+        width: 0;
+        height: 0;
+        border-top: 6px solid transparent;
+        border-bottom: 6px solid transparent;
+        border-left: 6px solid;
+        border-left-color: #e0e0e0;
+        -webkit-transform: rotate(-45deg);
+        -ms-transform: rotate(-45deg);
+        transform: rotate(-45deg);
+        border-radius: 2px;
+    }
+    .item:hover{
+        color: #1890ff;
+    }
+    .selection{
+        color: #1890ff;
+        background: #fff;
+        border-color: #1890ff;
+    }
+    .item.selection:hover{
+        // color: #1890ff;
+        color: #40a9ff;
+        background: #fff;
+        border-color: #40a9ff;
+    }
+    .item.selection:after{
+        border-left: 6px solid #1890ff;
+    }
+    .item:hover::after{
+        // border-color: #40a9ff;
+        border-left: 6px solid #40a9ff;
+    }
+    .bgcolor:hover{
+        color:rgba(0, 0, 0, 0.25);
+        background-color: #f5f5f5;
+        border-color: #d9d9d9;
+        cursor: not-allowed;
+    }
+    .bgcolor:hover::after{
+        // border-color: #40a9ff;
+        border-left: 6px solid #e0e0e0;
+    }
+    .bgcolor.selection:after{
+        border-left: 6px solid #e0e0e0;
+    }
+    .bgcolor.selection{
+        color:rgba(0, 0, 0, 0.25);
+        background-color: #f5f5f5;
+        border-color: #d9d9d9;
+        cursor: not-allowed;
+    }
+    .bgcolor.selection:hover{
+        // color: #1890ff;
+        color:rgba(0, 0, 0, 0.25);
+        background-color: #f5f5f5;
+        border-color: #d9d9d9;
+        cursor: not-allowed;
+    }
+    .bgcolor.selection:after{
+        border-left: 6px solid #e0e0e0;
+    }
+}
+.radioCheck .item:first-child {
+    border-radius: 5px 0 0 5px;
+    border-left: 1px solid #ccc;
+}
+.radioCheck .item.selection:first-child {
+    border-radius: 5px 0 0 5px;
+    border-left: 1px solid #1890ff;
+}
+.radioCheck .item.selection:nth-child(2) {
+    
+    border-left: 1px solid #1890ff;
+}
+.radioCheck .item:first-child:after{
+    border-left: 0;
+}
+.radioCheck .item:last-child {
+    border-radius: 0px 5px 5px 0px;
+}
+</style>

+ 412 - 0
src/components/formComponents/selectGroup.vue

@@ -0,0 +1,412 @@
+<template>
+  <a-select
+    :title="title"
+    v-model="selectedVal"
+    :collapse-tags="collapseTags"
+    :placeholder="placeholder"
+    :allow-create="allowCreate"
+    :allowClear="allowClear"
+    :mode="multiple ? 'multiple' : 'default'"
+    :disabled="disabled"
+    :filterOption="filterOptionAll"
+    :remote="remote"
+    :remote-method="remoteMethod"
+    :maxTagCount="maxTagCount"
+  >
+    <a-select-option v-for="(item, index) in filterOption" :value="item.value" :key="'select' + item.value + index">
+      {{ item.label }}
+    </a-select-option>
+  </a-select>
+</template>
+<script>
+// import { dictionary } from '@/components/service/core/dictionary'
+// import { queryParaCascByIdAndParamType } from '@/components/service/core/sysConfig'
+import { isArray } from '@/utils/Tools'
+import { httpAction, getAction } from '@/api/manage'
+export default {
+  name: 'selectPara',
+  props: {
+    code: {
+      // 字典代码
+      type: String,
+      default: '',
+    },
+    requestUrl: {
+      // 字典代码
+      type: String,
+      default: '',
+    },
+    require: {
+      type: Boolean,
+      default() {
+        return false
+      },
+    },
+    value: {
+      // v-model绑定值
+      type: [String, Array, Number, Boolean],
+      default() {
+        if (this.multiple) return []
+        else return ''
+      },
+    },
+    label: {
+      // label值
+      type: [String, Array, Number],
+      default() {
+        if (this.multiple) {
+          return []
+        }
+        if (!this.multiple) {
+          return ''
+        }
+      },
+    },
+    collapseTags: {
+      // 多选时是否将选中值按文字的形式展示
+      type: Boolean,
+      default: false,
+    },
+    placeholder: {
+      type: String,
+      default: '请选择',
+    },
+    options: {
+      // 外部传入的options 可代替自带的请求项
+      type: Array,
+      default() {
+        return []
+      },
+    },
+    allowCreate: {
+      // 是否允许创建新条目
+      type: Boolean,
+      default: false,
+    },
+    defaultFirstOption: {
+      // 按回车键 选择第一项
+      type: Boolean,
+      default: false,
+    },
+    allowClear: {
+      // 选项是否可清除
+      type: Boolean,
+      default: true,
+    },
+    disabled: {
+      // 是否禁用
+      type: Boolean,
+      default: false,
+    },
+    multiple: {
+      // 是否多选
+      type: Boolean,
+      default: false,
+    },
+    filterable: {
+      // 模糊搜索
+      type: Boolean,
+      default: false,
+    },
+    remote: {
+      // 远程搜索
+      type: Boolean,
+      default: false,
+    },
+    remoteMethod: {
+      // 远程搜索方法
+      type: Function,
+      default: () => {
+        console.error('没有配置远程搜索方法!')
+      },
+    },
+    disabledChange: {
+      // 禁用change事件
+      type: Boolean,
+      default: false,
+    },
+    enable: {
+      // 允许选择的项   为空数组全显示,不为空数组只显示规定的选项
+      type: Array,
+      default() {
+        return []
+      },
+    },
+    casCode: {
+      // 下级级联参数的code
+      type: String,
+      default: '',
+    },
+    casVal: {
+      // 下级参数绑定值
+      type: [String, Number],
+      default: '',
+    },
+    casLabel: {
+      // 下级参数绑定label
+      type: String,
+      default: '',
+    },
+    casOpt: {
+      // 级联参数options
+      type: Array,
+      default() {
+        return []
+      },
+    },
+    maxTagCount: {
+      type: Number,
+      default() {
+        return 2
+      },
+    },
+  },
+  data() {
+    return {
+      selectOption: [],
+      visible: false,
+      label_tmp: null,
+    }
+  },
+  watch: {
+    options(val) {
+      if (isArray(val)) {
+        this.selectOption.splice(0, this.selectOption.length)
+        this.selectOption.push(...val)
+      }
+    },
+    value(val, oldVal) {
+      if (!this.valueEquals(val, oldVal)) {
+        this.selectChange(val)
+      }
+    },
+  },
+  computed: {
+    selectedVal: {
+      get: function () {
+
+        return this.value
+      },
+      set: function (val) {
+        this.$emit('input', val)
+      },
+    },
+    filterOption() {
+      // 过滤后的下拉项
+      if (this.enable.length == 0) {
+        return this.selectOption
+      } else {
+        return this.selectOption.filter((v) => this.enable.findIndex((item) => item == v.value) > -1)
+      }
+    },
+    title() {
+      // title提示文字
+      // if (!this.multiple) {
+      //   return this.filterOption.filter((v) => v.value == this.value).length == 1
+      //     ? this.filterOption.filter((v) => v.value == this.value)[0].label
+      //     : ''
+      // } else if (this.multiple) {
+      //   return this.filterOption
+      //     .filter((item) => {
+      //       return this.value.findIndex((v) => v == item.value) > -1
+      //     })
+      //     .map((item2) => item2.label)
+      // }
+      // if (this.enable.length > 0) {
+      //   return this.filterOption.map((v) => v.label)
+      // }
+    },
+  },
+  mounted() {
+    this.$nextTick(() => {
+      if (this.code && this.options.length == 0) {
+        // 如果code值不为空且options.lengt==0, 则去请求字典项
+        this.getOptions(this.requestUrl)
+      } else if(this.requestUrl && this.options.length == 0){
+        this.getOptionsByURrl(this.requestUrl)
+        
+      }else {
+        // 否则将外部传入的options显示到下拉框中
+        this.selectOption.splice(0, this.selectOption.length)
+        this.selectOption.push(...this.options)
+        this.setDefaultVal(this.selectOption)
+      }
+    })
+  },
+  methods: {
+    filterOptionAll(input, option) {
+      return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+    },
+    getOptionsByURrl(url){
+      getAction(url,{}).then((res) => {
+        if (res.success && isArray(res.result)) {
+          let data = res.result
+          data = data.map((v) => {
+            return {
+              ...v,
+              label: v.tagName,
+              value: v.tagId+"",
+            }
+          })
+          if (!this.multiple && !this.require) {
+            data.unshift({ label: '不限', value: 'unlimited' })
+            if (this.value == ''|| this.value == null || this.value.length == 0 ) {
+              this.selectedVal = ['unlimited']
+            }
+          } else {
+            if (this.value == ''|| this.value == null || this.value.length == 0 ) {
+              this.selectedVal = []
+              this.$emit('clearValidate', this.selectedVal)
+            }
+          }
+          this.selectOption.splice(0, this.selectOption.length)
+          this.selectOption.push(...data)
+          this.setDefaultVal(this.selectOption)
+        }
+      })
+    },
+    getOptions() {
+      // 获取字典项
+
+      getAction('/sys/dictItem/list', { dictId: this.code, pageSize: 1000, pageNo: 1 }).then((res) => {
+        if (res.success && isArray(res.result.records)) {
+          let data = res.result.records
+          data = data.map((v) => {
+            return {
+              ...v,
+              label: v.itemText,
+              value: v.itemValue+"",
+            }
+          })
+          if (!this.multiple && !this.require) {
+            data.unshift({ label: '不限', value: 'unlimited' })
+            if (this.value == ''|| this.value == null || this.value.length == 0 ) {
+              this.selectedVal = ['unlimited']
+            }
+          } else {
+            if (this.value == ''|| this.value == null || this.value.length == 0 ) {
+              this.selectedVal = []
+              this.$emit('clearValidate', this.selectedVal)
+            }
+          }
+          this.selectOption.splice(0, this.selectOption.length)
+          this.selectOption.push(...data)
+          this.setDefaultVal(this.selectOption)
+        }
+      })
+    },
+    selectChange(val) {
+      // 选择change事件
+      if (this.multiple) {
+        // 多选
+        if (isArray(val) && val.length == 0) {
+          this.$emit('update:label', [])
+          this.emitChange([], [])
+          return
+        }
+        if (isArray(val) && val.length > 0) {
+          this.label_tmp = val.map((v) => {
+            let item = this.selectOption.find((n) => {
+              return n.value == v
+            })
+            if (item) {
+              return item.label
+            }
+          })
+          this.$emit('update:label', this.label_tmp)
+          this.emitChange(val, this.label_tmp)
+        }
+      }
+
+      if (!this.multiple) {
+        // 单选
+        if (!this.valueEquals(val, this.selectedVal)) return
+        if (!val) {
+          this.emitChange('', '', '')
+          this.$emit('update:label', '')
+          this.clearCascadeOpt()
+          return
+        }
+        let total = 0
+        let interval = window.setInterval(() => {
+          // 定时 解决下级级联参数没有触发change事件的问题
+          total++
+          let obj = this.selectOption.find((v) => v.value == val)
+          if (obj) {
+            this.$emit('update:label', obj.label)
+            this.emitChange(this.selectedVal, obj.id, obj.cId)
+            this.getCascadeOption(obj.id)
+            window.clearInterval(interval)
+          }
+          if (total >= 200) {
+            // 超过2秒 结束计数
+            window.clearInterval(interval)
+          }
+        }, 10)
+      }
+    },
+    valueEquals(a, b) {
+      // 比较两个值是否相同
+      if (a === b) return true
+      if (!(a instanceof Array)) return false
+      if (!(b instanceof Array)) return false
+      if (a.length !== b.length) return false
+      for (let i = 0; i !== a.length; ++i) {
+        if (a[i] !== b[i]) return false
+      }
+      return true
+    },
+    emitChange(...args) {
+      // 触发外部change方法
+      if (!this.disabledChange) {
+        setTimeout(() => {
+          this.$emit('change', ...args)
+        }, 1)
+      }
+    },
+    // 获取下级级联参数的options 开始==================
+    getCascadeOption(id) {
+      // 获取下级级联参数
+      if (!this.casCode) return
+      queryParaCascByIdAndParamType(id, this.casCode).then((res) => {
+        if (res.data.code == 1000) {
+          let data = res.data.data
+          if (isArray(data)) {
+            data = data.map((item) => {
+              return {
+                value: item.valStr,
+                label: item.name,
+                ...item,
+              }
+            })
+            this.$emit('update:casOpt', data)
+            this.clearCascadeVal()
+          }
+        }
+      })
+    },
+    clearCascadeOpt() {
+      // 清空下级级联参数
+      if (!this.casCode) return
+      this.$emit('update:casOpt', [])
+      this.clearCascadeVal()
+    },
+    clearCascadeVal() {
+      // 清空下级参数绑定值
+      this.$emit('update:casVal', '')
+      this.$emit('update:casLabel', '')
+    },
+    // 获取下级级联参数的options 结束==================
+    setDefaultVal(options = []) {
+      // 设置默认值(只有值为空的情况下可以设置默认值)
+      let def = options.find((v) => v.haveDefault == '1')
+      if (def) {
+        if (!this.value && this.value !== 0) {
+          this.$emit('input', def.value)
+          this.$emit('update:label', def.name)
+        }
+      }
+    },
+  },
+}
+</script>

+ 427 - 0
src/components/formComponents/selectRegion.vue

@@ -0,0 +1,427 @@
+<style scoped>
+  .transfer {
+    display: flex;
+  }
+
+  .transfer .transfer-box {
+    width: 350px;
+    height: 250px;
+    border: 1px solid #dadfe3;
+    display: flex;
+    overflow-x: auto;
+    overflow-y: hidden;
+    box-sizing: border-box;
+    margin-right: 25px;
+  }
+
+  .content-box {
+    min-width: 174px;
+    width: 100%;
+    height: 100%;
+    border-right: 1px solid #dadfe3;
+  }
+
+  .content-box .content-title {
+    width: 100%;
+    background: #f8f9fa;
+    border-bottom: 1px solid #dadfe3;
+    padding: 5px 15px;
+    font-size: 16px;
+    font-weight: 700;
+    line-height: 30px;
+  }
+
+  .content-box .content {
+    width: 100%;
+    height: 85%;
+    overflow: auto;
+  }
+
+  .content-box .content p:hover {
+    background: #edf1f5;
+    cursor: pointer;
+  }
+
+  .content-box .content p:hover span {
+    display: inline-block;
+  }
+
+  .content-box .content p span {
+    display: none;
+  }
+
+  p {
+    padding: 5px;
+    margin-bottom: 0;
+    border-bottom: 1px solid #f2f2f2;
+  }
+
+  .backgroundOnly {
+    background: #edf1f5;
+  }
+</style>
+<style>
+  .transfer .ant-checkbox-group {
+    width: 100px;
+    overflow-x: hidden;
+  }
+
+  .transfer .ant-checkbox-group-item {
+    display: inline-block;
+    margin-right: 8px;
+    width: 100%;
+  }
+</style>
+
+<template>
+  <div class="transfer">
+    <div class="transfer-box">
+      <div class="content-box">
+        <p class="content-title">省份</p>
+        <div class="content" style="position:relative">
+          <!-- <div style="position:absolute;top:0;left:0;width:100%;height:100%;background: rgba(0, 0, 0, 0.5)"
+            v-if="loading"></div> -->
+          <p><input type="checkbox" class="select_all" @change="checkAllBox" v-model="checkAll" /> 全选</p>
+
+          <p v-for="(item, index) of province" :key="item.value" :class="{ backgroundOnly: provinceIndex == index }">
+            <input class="checkItem" type="checkbox" :value="item.value" :id="item.value" v-model="checkMatched"
+              @change="checkOne(item, index)" />
+            <span @click="showCity(item, index)" style="color:black;display:inline-block;margin-left:5px;width:80%">{{
+              item.label
+            }}</span>
+          </p>
+        </div>
+      </div>
+      <div class="content-box" :style="{ borderRight: !showClass ? '0px' : '1px solid #dadfe3' }" v-show="showCityData">
+        <p class="content-title">城市</p>
+        <div class="content">
+          <!-- <p>
+            <input
+              type="checkbox"
+              class="select_all"
+              @change="checkAllBoxCity"
+              v-model="checkAllCity"
+            /> 全选
+          </p>-->
+          <p v-for="(item, index) of city" :key="item.id" :class="{ backgroundOnly: cityIndex == index }">
+            <input class="checkItem" type="checkbox" :value="item.value" :id="item.value" v-model="checkMatchedCity"
+              @change="checkOneCity(item, index)" />
+            {{ item.label }}
+          </p>
+        </div>
+      </div>
+    </div>
+    <div class="transfer-box" style="width:176px">
+      <div class="content-box">
+        <p class="content-title">
+          已选
+          <span style="float:right;font-weight:normal;color:blue;cursor: pointer;font-size:14px"
+            @click="clear">清空</span>
+        </p>
+        <div class="content">
+          <p v-for="(item, index) of checkData" :key="index">
+            {{ item.label }}
+            <!-- <span
+              style="float:right;font-weight:normal;color:blue;cursor: pointer;font-size:14px"
+              @click="detele(item,index)"
+            >删除</span>-->
+          </p>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import {
+    JeecgListMixin
+  } from '@/mixins/JeecgListMixin'
+  import {
+    getProvince,
+    postAction
+  } from '@/api/manage'
+  import $ from 'jquery'
+  export default {
+    name: 'ByteDanceUserOrientationTemplateList',
+    components: {},
+    props: ['dataValue'],
+    data() {
+      return {
+        showClass: false,
+        showCityData: false,
+        checkData: [],
+        checkMatched: [],
+        province: [],
+        city: [],
+        area: [],
+        CityData: '/kuaishou/batch/getRegion',
+        provinceIndex: null,
+        cityIndex: null,
+        checkAll: false,
+        checkMatchedCity: [],
+        checkAllCity: false,
+        nowProvince: null,
+        loading: false
+      }
+    },
+    computed: {},
+    mounted() {
+      this.$nextTick(() => {
+        this.getData().then(() => {
+          if (this.dataValue&&this.dataValue.length>0&&this.dataValue[0].value) {
+            return
+          } else {
+            postAction('/kuaishou/batch/getRegionDetail', {
+              regionArr: this.dataValue
+            }).then(res => {
+              if (res.success) {
+                //   this.populationData.allForm.region = res.result
+                this.checkData = res.result
+                this.checkMatched = res.result.map(item => {
+                  return item.value
+                })
+                this.checkMatchedCity = res.result.map(item => {
+                  return item.value
+                })
+                this.$emit('update:checkData', this.checkData)
+              }
+            })
+          }
+
+          //   if (this.dataValue.length > 0) {
+
+          //   }
+        })
+
+      })
+    },
+    watch: {
+      dataValue: {
+        deep: true,
+        immediate: true, // immediate选项可以开启首次赋值监听
+        handler(n, o) {
+          if (n&&n.length > 0 && n[0].value) {
+            // this.getData().then(() => {
+            //   if (n.length > 0) {
+            this.$nextTick(() => {
+              this.checkData = n
+              this.checkMatched = n.map(item => {
+                return item.value
+              })
+              this.checkMatchedCity = n.map(item => {
+                return item.value
+              })
+              this.$emit('update:checkData', this.checkData)
+            })
+            //   }
+            // })
+
+          }
+
+        }
+      }
+    },
+    methods: {
+      getData() {
+        this.loading = true
+        var that = this
+        return new Promise(function (resolve, reject) {
+          getProvince(that.CityData, {
+            level: 1
+          }).then(res => {
+            if (res.code == '0') {
+              //   this.province.push(...res.result.records)
+              that.province = res.result.map(item => {
+                return {
+                  label: item.name,
+                  value: item.regionId
+                }
+              })
+              that.province = that.province.filter(item => {
+                return item.label != '台湾' && item.label != '香港' && item.label != '澳门'
+              })
+              that.loading = false
+              resolve()
+            }
+          })
+        })
+
+      },
+      showCity(item, index) {
+        this.city = []
+        this.showCityData = false
+        this.showClass = false
+        this.provinceIndex = index
+        this.cityIndex = null
+        this.nowProvince = item
+        this.checkMatchedCity = []
+        getProvince(this.CityData, {
+          parent: item.value
+        }).then(res => {
+          if (res.code == '0') {
+            this.city = res.result.map(item => {
+              return {
+                label: item.name,
+                value: item.regionId
+              }
+            })
+            if (this.checkMatched.indexOf(item.value) != -1) {
+              this.checkAllCity = true
+              this.checkMatchedCity = this.city.map(item => {
+                return item.value
+              })
+            } else {
+              this.checkMatchedCity = this.checkData.map(item => {
+                return item.value
+              })
+            }
+            //   this.city.push(...res.result.records)
+            this.showCityData = true
+          }
+        })
+      },
+      checkAllBox() {
+        this.checkMatched = []
+        this.checkData = []
+        if (this.checkAll) {
+          this.checkMatched = this.province.map(item => {
+            return item.value
+          })
+          this.checkData.push(...this.province)
+        } else {
+          this.checkMatched = []
+          this.checkData = []
+          this.checkMatchedCity = []
+        }
+        this.$emit('update:checkData', this.checkData)
+      },
+      checkOne(item, index) {
+        if (this.checkMatched.indexOf(item.value) == -1) {
+          var index1 = this.checkData.findIndex(v => v.value === item.value)
+          this.checkData.splice(index1, 1)
+          if (this.nowProvince) {
+            if (this.nowProvince.value == item.value) {
+              this.checkMatchedCity = []
+            }
+          }
+        } else {
+          this.checkData.push(item)
+          var data = []
+          getProvince(this.CityData, {
+            parent: item.value
+          }).then(res => {
+            if (res.code == '0') {
+              data = res.result.map(item => {
+                return {
+                  label: item.name,
+                  value: item.regionId
+                }
+              })
+              for (var i = 0; i < this.checkData.length; i++) {
+                for (let j = 0; j < data.length; j++) {
+                  if (this.checkData[i].value == data[j].value) {
+                    this.checkData.splice(i, 1)
+                  }
+                }
+              }
+            }
+          })
+
+          if (this.nowProvince) {
+            if (item.value == this.nowProvince.value) {
+              this.checkMatchedCity = this.city.map(item => {
+                return item.value
+              })
+            }
+          }
+        }
+        if (this.checkMatched.length == this.province.length) {
+          this.checkAll = true
+        } else {
+          this.checkAll = false
+        }
+        this.$emit('update:checkData', this.checkData)
+      },
+      checkAllBoxCity() {
+        this.checkMatchedCity = []
+        if (this.checkAllCity) {
+          this.checkMatchedCity = this.city.map(item => {
+            return item.value
+          })
+          this.checkMatched.push(this.nowProvince.value)
+          this.checkData.push(this.nowProvince)
+        } else {
+          this.checkMatchedCity = []
+          var index = this.checkData.findIndex(v => v.value === this.nowProvince.value)
+          var index1 = this.checkMatched.findIndex(v => v === this.nowProvince.value)
+          this.checkData.splice(index, 1)
+          this.checkMatched.splice(index1, 1)
+        }
+        this.$emit('update:checkData', this.checkData)
+      },
+      checkOneCity(item, index) {
+        var allCity = this.checkMatchedCity.filter(v => {
+          return v.toString().slice(0, 2) == this.nowProvince.value
+        })
+
+        if (allCity.length == this.city.length) {
+          this.checkMatched.push(this.nowProvince.value)
+          this.checkData.push(this.nowProvince)
+          for (var i = 0; i < allCity.length; i++) {
+            for (var j = 0; j < this.checkData.length; j++) {
+              if (allCity[i] == this.checkData[j].value) {
+                this.checkData.splice(j, 1)
+                j--
+              }
+            }
+          }
+        }
+        if (this.checkMatchedCity.indexOf(item.value) != -1) {
+          if (allCity.length != this.city.length) {
+            this.checkData.push(item)
+          }
+          if (this.checkMatched.length == this.province.length) {
+            this.checkAll = true
+          } else {
+            this.checkAll = false
+          }
+        } else {
+          if (this.checkMatched.indexOf(this.nowProvince.value) != -1) {
+            var index1 = this.checkData.findIndex(v => v.value === this.nowProvince.value)
+            var index2 = this.checkMatched.findIndex(v => v === this.nowProvince.value)
+            this.checkData.splice(index1, 1)
+            this.checkMatched.splice(index2, 1)
+            this.checkAll = false
+            var data = this.city.filter(v => {
+              return v.value != item.value
+            })
+            this.checkData.push(...data)
+          } else {
+            var index1 = this.checkData.findIndex(v => v.value === item.value)
+            this.checkData.splice(index1, 1)
+          }
+        }
+        this.$emit('update:checkData', this.checkData)
+      },
+      clear() {
+        this.checkData = []
+        this.checkMatched = []
+        this.checkMatchedCity = []
+        this.checkAll = false
+        this.$emit('update:checkData', this.checkData)
+      },
+      detele(item, index) {
+        //   if (this.checkMatched.indexOf(this.nowProvince.value) != -1) {
+        var index1 = this.checkMatched.findIndex(v => v === item.value)
+        var index2 = this.checkData.findIndex(v => v.value === item.value)
+        this.checkData.splice(index2, 1)
+        this.checkMatched.splice(index1, 1)
+        this.checkAll = false
+        //   } else {
+        //   }
+
+        this.$emit('update:checkData', this.checkData)
+      }
+    }
+  }
+</script>

+ 75 - 0
src/components/formComponents/timeRange.vue

@@ -0,0 +1,75 @@
+
+<template>
+  <div class="rang-picker">
+    <a-range-picker v-model="timeRange" :format="format" :allowClear="allowClear" :disabled-date="disabledDate" />
+  </div>
+</template>
+
+<script>
+// import { dictionary } from '@/components/service/core/dictionary'
+import { isArray } from '@/utils/Tools'
+import { httpAction, getAction } from '@/api/manage'
+import moment from 'moment'
+export default {
+  name: 'rang-picker', // 日期选择器组件二次封装
+  props: {
+    format: {
+      type: String,
+      default() {
+        return 'YYYY-MM-DD'
+      },
+    },
+    allowClear: {
+      type: Boolean,
+      default() {
+        return false
+      },
+    },
+    disabledDate: {
+      type: Function,
+      default(current) {
+        return current && current < moment().subtract(1, 'days')
+      },
+    },
+    value: {
+      // v-model绑定值
+      type: [Array, String],
+      default() {
+        return []
+      },
+    },
+    disabled: {
+      // 是否禁用
+      type: Boolean,
+      default: false,
+    },
+  },
+  components: {},
+  data() {
+    return {
+      selectOption: [],
+    }
+  },
+  watch: {},
+  computed: {
+    timeRange: {
+      get: function () {
+        return this.value
+      },
+      set: function (val) {
+        this.$emit('input', val)
+      },
+    },
+  },
+  mounted() {
+    this.$nextTick(() => {})
+  },
+  methods: {
+    emitChange(...args) {
+      // emit change事件
+      this.$emit('change', ...args)
+    },
+  },
+}
+</script>
+

+ 314 - 0
src/components/formComponents/toutiaoTime.vue

@@ -0,0 +1,314 @@
+<template>
+  <div class="calendar">
+    <table class="time_table table" v-on:mouseleave="mouseleave">
+      <thead>
+        <tr>
+          <th rowspan="2" style="width: 120px">日期/时间</th>
+        </tr>
+        <tr>
+          <th class="unselectable" v-for="(num, index) in numData" :key="index" style="width: 30px">{{ num }}</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr v-for="(options, index) in timeData" :key="index">
+          <th scope="row">{{ options.date }}</th>
+          <td
+            v-for="(timeItem, key) in options.time"
+            :key="key"
+            :class="[{ active_select: timeItem.isSelected, active_select_move: timeItem.show }]"
+            v-on:mousedown="mousedown(index, key)"
+            v-on:mouseover="mouseover(index, key)"
+            v-on:mouseup="mouseup(index, key)"
+            @click="showOne(index, key)"
+          ></td>
+        </tr>
+      </tbody>
+    </table>
+    <!-- <div style="height:200px;overflow:auto">
+      <span v-for="(item, index) of dataString" :key="index">
+        <a-tag v-if="item" closable :afterClose="() => handleClose(index)">
+          {{ setItemDate(index) }}
+        </a-tag>
+      </span>
+    </div> -->
+  </div>
+</template>
+<script>
+export default {
+  name: 'calendar-table',
+  props: {
+    scheduleTime: {
+      type: String,
+      default() {
+        return '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+      },
+    },
+  },
+  data() {
+    return {
+      changeDataElse: ['1'],
+      show: false,
+      numData: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
+      dateTime: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
+      timeData: [
+        {
+          date: '周一',
+          time: [],
+        },
+        {
+          date: '周二',
+          time: [],
+        },
+        {
+          date: '周三',
+          time: [],
+        },
+        {
+          date: '周四',
+          time: [],
+        },
+        {
+          date: '周五',
+          time: [],
+        },
+        {
+          date: '周六',
+          time: [],
+        },
+        {
+          date: '周日',
+          time: [],
+        },
+      ],
+      selectFlag: false,
+      max: {
+        x: 0,
+        y: 0,
+      },
+      dataString: '',
+    }
+  },
+  methods: {
+    // handleClose(item) {
+    //   var index = Math.floor(item / 24)
+    //   var key = item % 7
+    //   if (this.timeData[index].time[key].value == false) {
+    //     if (this.timeData[index].time[key].isSelected) {
+    //       this.timeData[index].time[key].isSelected = false
+    //     } else {
+    //       this.$set(this.timeData[index].time[key], 'isSelected', true)
+    //     }
+    //   }
+    // },
+    showOne(index, key) {
+      //   this.timeData[index].time[key].isSelected = !this.timeData[index].time[key].isSelected
+    },
+    mousedown: function (index, key) {
+      this.selectStart = []
+      this.selectFlag = true
+      if (this.timeData[index].time[key].value == false) {
+        if (this.timeData[index].time[key].isSelected) {
+          this.timeData[index].time[key].isSelected = false
+        } else {
+          this.$set(this.timeData[index].time[key], 'isSelected', true)
+        }
+      }
+      this.selectStart.push({
+        index: index,
+        key: key,
+      })
+    },
+    mouseover: function (index, key) {
+      // x 垂直方向  y 水平方向
+      if (this.selectFlag) {
+        if (index >= this.max.x) {
+          this.max.x = index
+        }
+        if (key >= this.max.y) {
+          this.max.y = key
+        }
+        var start = {
+          x: this.selectStart[0].index,
+          y: this.selectStart[0].key,
+        }
+        var end = {
+          x: index,
+          y: key,
+        }
+        for (var i = end.x; i <= this.max.x; i++) {
+          for (var j = end.y; j <= this.max.y; j++) {
+            this.$set(this.timeData[i].time[j], 'isSelected', false)
+          }
+        }
+        /* 渲染选中的区域 */
+        // for (var i = start.x; i <= end.x; i++) {
+        //   for (var j = start.y; j <= end.y; j++) {
+        //     // this.$set(this.timeData[i].time[j], 'isSelected', true)
+        //     this.$set(this.timeData[i].time[j], 'show', true)
+        //   }
+        // }
+        for (var i = start.x; i <= end.x; i++) {
+          for (var j = start.y; j <= end.y; j++) {
+            this.$set(this.timeData[i].time[j], 'isSelected', true)
+          }
+        }
+      }
+    },
+    mouseup: function (index, key) {
+      var start = {
+        x: this.selectStart[0].index,
+        y: this.selectStart[0].key,
+      }
+      var end = {
+        x: index,
+        y: key,
+      }
+      /* 渲染选中的区域 */
+      var data = []
+      var arr = []
+      //   for (let i = 0; i < this.timeData.length; i++) {
+      //     for (let j = 0; j < this.timeData[i].time.length; j++) {
+      //       this.timeData[i].time[j].show = false
+      //     }
+      //   }
+      arr = data.concat(
+        this.timeData[0].time,
+        this.timeData[1].time,
+        this.timeData[2].time,
+        this.timeData[3].time,
+        this.timeData[4].time,
+        this.timeData[5].time,
+        this.timeData[6].time
+      )
+      this.dataString = arr.map((item) => {
+        if (item.isSelected) {
+          return 1
+        } else {
+          return 0
+        }
+      })
+      console.log(this.dataString.join(''))
+      this.$emit('update:scheduleTime', this.dataString.join(''))
+      this.selectFlag = false
+      this.max = {
+        x: 0,
+        y: 0,
+      }
+    },
+    mouseleave: function () {
+      this.selectFlag = false
+    },
+    setItemDate(sum) {
+      let i = sum
+      if (i < 24) {
+        return '周一' + i + ':00~' + (i + 1) + ':00'
+      } else if (i < 48 && i >= 24) {
+        return '周二' + (i - 24) + ':00~' + (i - 23) + ':00'
+      } else if (i < 72 && i >= 48) {
+        return '周三' + (i - 48) + ':00~' + (i - 47) + ':00'
+      } else if (i < 96 && i >= 72) {
+        return '周四' + (i - 72) + ':00~' + (i - 71) + ':00'
+      } else if (i < 120 && i >= 96) {
+        return '周五' + (i - 96) + ':00~' + (i - 95) + ':00'
+      } else if (i < 144 && i >= 120) {
+        return '周六' + (i - 120) + ':00~' + (i - 119) + ':00'
+      } else {
+        return '周日' + (i - 144) + ':00~' + (i - 143) + ':00'
+      }
+    },
+    init() {
+      var str = this.scheduleTime
+      var arr = str.split('')
+      this.timeData = [
+        {
+          date: '周一',
+          time: [],
+        },
+        {
+          date: '周二',
+          time: [],
+        },
+        {
+          date: '周三',
+          time: [],
+        },
+        {
+          date: '周四',
+          time: [],
+        },
+        {
+          date: '周五',
+          time: [],
+        },
+        {
+          date: '周六',
+          time: [],
+        },
+        {
+          date: '周日',
+          time: [],
+        },
+      ]
+      for (let i = 0; i < arr.length; i++) {
+        if (i < 24) {
+          this.timeData[0].time.push({ isSelected: arr[i] == 1 ? true : false, move: false, value: false })
+        } else if (i < 48 && i >= 24) {
+          this.timeData[1].time.push({ isSelected: arr[i] == 1 ? true : false, move: false, value: false })
+        } else if (i < 72 && i >= 48) {
+          this.timeData[2].time.push({ isSelected: arr[i] == 1 ? true : false, move: false, value: false })
+        } else if (i < 96 && i >= 72) {
+          this.timeData[3].time.push({ isSelected: arr[i] == 1 ? true : false, move: false, value: false })
+        } else if (i < 120 && i >= 96) {
+          this.timeData[4].time.push({ isSelected: arr[i] == 1 ? true : false, move: false, value: false })
+        } else if (i < 144 && i >= 120) {
+          this.timeData[5].time.push({ isSelected: arr[i] == 1 ? true : false, move: false, value: false })
+        } else {
+          this.timeData[6].time.push({ isSelected: arr[i] == 1 ? true : false, move: false, value: false })
+        }
+      }
+    },
+  },
+  watch: {
+    scheduleTime: {
+      immediate: true, // immediate选项可以开启首次赋值监听
+      handler(newVal, oldVal) {
+        if (newVal == '' || newVal == null || newVal == undefined) {
+          // this.scheduleTime=oldVal
+          this.$emit('update:scheduleTime', oldVal)
+        } else {
+          this.init()
+        }
+      },
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.time_table {
+  border: 1px solid #ccc;
+  border-collapse: collapse;
+}
+.time_table th,
+.time_table td {
+  border: 1px solid #ccc;
+  text-align: center;
+}
+.time_table .unselectable {
+  //   width: 30px !important;
+  height: 20px;
+}
+.active_select {
+  background: cornflowerblue;
+}
+.active_select_move {
+  background: aqua;
+}
+* {
+  -webkit-touch-callout: none !important;
+  -webkit-user-select: none !important;
+  -moz-user-select: none !important;
+  -ms-user-select: none !important;
+  user-select: none !important;
+}
+</style>

+ 21 - 4
src/components/layouts/TabLayout.vue

@@ -24,6 +24,7 @@
 </template>
 
 <script>
+  import { mapActions } from 'vuex'
   import GlobalLayout from '@/components/page/GlobalLayout'
   import Contextmenu from '@/components/menu/Contextmenu'
   import {
@@ -118,6 +119,12 @@
         localStorage.removeItem("advertisingGroup")
         localStorage.removeItem("advertisingGroupKey")
       }
+      if (!this.linkList.includes("/crossAccount/createCreative")) {
+        localStorage.removeItem("crossAccountUnit")
+      }
+      if (!this.linkList.includes("/crossAccount/createUnit")) {
+        localStorage.removeItem("crossAccountCampaign")
+      }
     },
     watch: {
       '$route': function (newRoute) {
@@ -163,12 +170,21 @@
           localStorage.removeItem("originality")
           localStorage.removeItem("originalityKey")
         }
+        if (!this.linkList.includes("/crossAccount/createCreative")) {
+        localStorage.removeItem("crossAccountUnit")
+        }
+        if (!this.linkList.includes("/crossAccount/createUnit")) {
+          localStorage.removeItem("crossAccountCampaign")
+        }
       },
       'activePage': function (key) {
         let index = this.linkList.lastIndexOf(key)
         let waitRouter = this.pageList[index]
         this.$router.push(Object.assign({}, waitRouter));
         // this.changeTitle(waitRouter.meta.title)
+        if(key!='/material/musicMaterial'){
+          this.clearPlayList()
+        }
       },
       'multipage': function (newVal) {
         if (this.reloadFlag) {
@@ -187,6 +203,7 @@
       // update-end-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
     },
     methods: {
+      ...mapActions([ 'clearPlayList']),
       // update-begin-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
       // 将首页添加到第一位
       addIndexToFirst() {
@@ -235,7 +252,7 @@
           this.$message.warning('这是最后一页,不能再关闭了啦')
           return
         }
-        console.log("this.pageList ", this.pageList);
+        // console.log("this.pageList ", this.pageList);
         // this.pageList = this.pageList.filter(item => item.fullPath !== key)
         // let index = this.linkList.indexOf(key)
         // this.linkList = this.linkList.filter(item => item !== key)
@@ -287,14 +304,14 @@
         if (key == '/modules/BatchCreate/newGroup' && !(this.linkList.includes('/modules/BatchCreate/newCreate') || this
             .linkList.includes('/modules/BatchCreate/newPlan') || this.linkList.includes(
               '/modules/BatchCreate/TouTiaoBatch'))) {
-          console.log('newGroup')
+          // console.log('newGroup')
           localStorage.setItem('accountInfo', '')
           localStorage.setItem("groupInfo", '')
         }
         if (key == '/modules/BatchCreate/newPlan' && !(this.linkList.includes('/modules/BatchCreate/newCreate') || this
             .linkList.includes('/modules/BatchCreate/newGroup') || this.linkList.includes(
               '/modules/BatchCreate/TouTiaoBatch'))) {
-          console.log('newPlan')
+          // console.log('newPlan')
           localStorage.setItem('accountInfo', '')
           localStorage.setItem("groupInfo", '')
           localStorage.setItem("planInfo", '')
@@ -302,7 +319,7 @@
         if (key == '/modules/BatchCreate/newCreate' && !(this.linkList.includes('/modules/BatchCreate/TouTiaoBatch') ||
             this.linkList.includes('/modules/BatchCreate/newPlan') || this.linkList.includes(
               '/modules/BatchCreate/newGroup'))) {
-          console.log('newCreate')
+          // console.log('newCreate')
           localStorage.setItem('accountInfo', '')
           localStorage.setItem("groupInfo", '')
           localStorage.setItem("planInfo", '')

+ 4 - 5
src/components/layouts/UserLayout.vue

@@ -1,10 +1,10 @@
 <template>
   <div id="userLayout" :class="['user-layout-wrapper', device]">
     <div class="container">
-      <div class="top" style="margin-bottom:15px">
+      <div class="top" style="margin-bottom:30px">
         <div class="header">
           <a href="/">
-            <img src="~@/assets/logo.png" class="logo" alt="logo" style="height: 66px" />
+            <img src="~@/assets/logo.png" class="logo" alt="logo" style="height: 50px" />
             <!--            <span class="title">Jeecg Boot</span>-->
           </a>
         </div>
@@ -21,7 +21,7 @@
           <a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">隐私</a>
           <a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">条款</a>
         </div>-->
-        <div class="copyright">Copyright &copy; 上海有腾文化传播有限公司</div>
+        <div class="copyright">Copyright &copy; 2019 上海有腾</div>
       </div>
     </div>
   </div>
@@ -65,7 +65,7 @@ export default {
     min-height: 100%;
     background: #f0f2f5 url(~@/assets/background.svg) no-repeat 50%;
     background-size: 100%;
-    padding: 110px 0 144px;
+    padding: 160px 0 144px;
     position: relative;
 
     a {
@@ -92,7 +92,6 @@ export default {
         .logo {
           height: 44px;
           vertical-align: top;
-        //   margin-right: 16px;
           border-style: none;
         }
 

+ 174 - 0
src/components/lyric/lyric.vue

@@ -0,0 +1,174 @@
+<template>
+  <div>
+    <!--封面-->
+    <dl class="music-info">
+      <dt>
+        <img :src="musicPicUrl" />
+      </dt>
+      <template v-if="currentMusic.id">
+        <dd>歌曲名:{{ currentMusic.name }}</dd>
+        <dd>歌手名:{{ currentMusic.singer }}</dd>
+        <dd>专辑名:{{ currentMusic.album }}</dd>
+      </template>
+      <!-- <template v-else>
+        <dd>mmPlayer在线音乐播放器</dd>
+        <dd>
+          <a class="hover" target="_blank" href="https://github.com/maomao1996">
+            <mm-icon type="github" :size="14" />&nbsp;茂茂
+          </a>
+        </dd>
+      </template> -->
+    </dl>
+    <!--歌词-->
+    <div ref="musicLyric" class="music-lyric">
+      <div class="music-lyric-items" :style="lyricTop">
+        <p v-if="!currentMusic.id">还没有播放音乐哦!</p>
+        <p v-else-if="nolyric">暂无歌词!</p>
+        <template v-else-if="lyric.length > 0">
+          <p v-for="(item, index) in lyric" :key="index" :class="{ on: lyricIndex === index }">
+            {{ item.text }}
+          </p>
+        </template>
+        <p v-else>歌词加载失败!</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+
+export default {
+  name: 'Lyric',
+  props: {
+    // 歌词数据
+    lyric: {
+      type: Array,
+      default: () => [],
+    },
+    // 是否无歌词
+    nolyric: {
+      type: Boolean,
+      default: false,
+    },
+    // 当前歌词下标
+    lyricIndex: {
+      type: Number,
+      default: 0,
+    },
+  },
+  data() {
+    return {
+      top: 0, // 歌词居中
+    }
+  },
+  computed: {
+    musicPicUrl() {
+      return this.currentMusic.id
+        ? `${this.currentMusic.image}?param=300y300`
+        : require('../../assets/img/player_cover.png')
+    },
+    lyricTop() {
+      return `transform :translate3d(0, ${-34 * (this.lyricIndex - this.top)}px, 0)`
+    },
+    ...mapGetters(['currentMusic']),
+  },
+  mounted() {
+    window.addEventListener('resize', () => {
+      clearTimeout(this.resizeTimer)
+      this.resizeTimer = setTimeout(() => this.clacTop(), 60)
+    })
+    this.$nextTick(() => this.clacTop())
+  },
+  methods: {
+    // 计算歌词居中的 top值
+    clacTop() {
+      this.$nextTick(() => {
+        const dom = this.$refs.musicLyric
+        if (dom) {
+          const { display = '' } = window.getComputedStyle(dom)
+          if (display === 'none') {
+            return
+          }
+          const height = dom.offsetHeight
+          this.top = Math.floor(height / 34 / 2)
+        }
+      })
+    },
+  },
+}
+</script>
+
+<style lang="less" scoped>
+.music-info {
+  padding-bottom: 20px;
+  text-align: center;
+  font-size: @font_size_medium;
+  dt {
+    position: relative;
+    width: 186px;
+    height: 186px;
+    margin: 0 auto 15px;
+    &:after {
+      content: '';
+      position: absolute;
+      left: 9px;
+      top: 0;
+      width: 201px;
+      height: 180px;
+      background: url('~@/assets/img/album_cover_player.png') 0 0 no-repeat;
+    }
+    img {
+      vertical-align: middle;
+      width: 186px;
+      height: 186px;
+    }
+  }
+  dd {
+    height: 30px;
+    line-height: 30px;
+    .no-wrap();
+  }
+}
+
+/*歌词部分*/
+.music-lyric {
+  position: absolute;
+  top: 315px;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  overflow: hidden;
+  text-align: center;
+  mask-image: linear-gradient(
+    to bottom,
+    rgba(255, 255, 255, 0) 0,
+    rgba(255, 255, 255, 0.6) 15%,
+    rgba(255, 255, 255, 1) 25%,
+    rgba(255, 255, 255, 1) 75%,
+    rgba(255, 255, 255, 0.6) 85%,
+    rgba(255, 255, 255, 0) 100%
+  );
+  .music-lyric-items {
+    text-align: center;
+    line-height: 34px;
+    font-size: @font_size_small;
+    transform: translate3d(0, 0, 0);
+    transition: transform 0.6s ease-out;
+    .no-wrap();
+    .on {
+      color: @lyric_color_active;
+    }
+  }
+}
+
+// 当屏幕小于 960 时
+@media (max-width: 960px) {
+  .music-info {
+    display: none;
+  }
+  .music-lyric {
+    top: 0;
+  }
+}
+</style>

+ 58 - 26
src/components/menu/SideMenu.vue

@@ -146,32 +146,64 @@
 
 <!-- update_begin author:sunjianlei date:20190530 for: 选中首页的时候不显示背景颜色 -->
 <style lang="less">
-  .ant-menu.ant-menu-root {
-    & > .ant-menu-item:first-child {
-      background-color: transparent;
-
-    //   & > a, & > a:hover {
-    //     color: rgba(0, 0, 0, 0.65);
-    //   }
-
-    //   &.ant-menu-item-selected {
-    //     & > a, & > a:hover {
-    //       color: @primary-color;
-    //     }
-    //   }
-    }
+  // .ant-menu.ant-menu-root {
+  //   & > .ant-menu-item:first-child {
+  //     background-color: transparent;
+
+  //   //   & > a, & > a:hover {
+  //   //     color: rgba(0, 0, 0, 0.65);
+  //   //   }
+
+  //   //   &.ant-menu-item-selected {
+  //   //     & > a, & > a:hover {
+  //   //       color: @primary-color;
+  //   //     }
+  //   //   }
+  //   }
+
+  //   &.ant-menu-dark > .ant-menu-item:first-child {
+  //     & > a, & > a:hover {
+  //       color: rgba(255, 255, 255, 0.65);
+  //     }
+
+  //     &.ant-menu-item-selected {
+  //       & > a, & > a:hover {
+  //         color: rgba(255, 255, 255, 1);
+  //       }
+  //     }
+  //   }
+  // }
+</style>
+<!-- update_end author:sunjianlei date:20190530 for: 选中首页的时候不显示背景颜色 -->
 
-    &.ant-menu-dark > .ant-menu-item:first-child {
-      & > a, & > a:hover {
-        color: rgba(255, 255, 255, 0.65);
-      }
+<!--侧边栏的选中after显示在左侧 -->
 
-      &.ant-menu-item-selected {
-        & > a, & > a:hover {
-          color: rgba(255, 255, 255, 1);
-        }
-      }
-    }
+<style lang="less">
+  ul.ant-menu-inline li.ant-menu-selected::after, ul.ant-menu-vertical li.ant-menu-selected::after,ul.ant-menu-vertical li.ant-menu-item-selected::after,ul.ant-menu-inline li.ant-menu-item-selected::after {
+      -webkit-transform: scaleY(1);
+      transform: scaleY(1);
+      opacity: 1;
+      -webkit-transition: opacity 0.15s cubic-bezier(0.645, 0.045, 0.355, 1), -webkit-transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1);
+      transition: opacity 0.15s cubic-bezier(0.645, 0.045, 0.355, 1), -webkit-transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1);
+      transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1), opacity 0.15s cubic-bezier(0.645, 0.045, 0.355, 1);
+      transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1), opacity 0.15s cubic-bezier(0.645, 0.045, 0.355, 1), -webkit-transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1);
   }
-</style>
-<!-- update_end author:sunjianlei date:20190530 for: 选中首页的时候不显示背景颜色 -->
+  .ant-menu-inline li.ant-menu-item::after ,.ant-menu-vertical li.ant-menu-item::after ,.ant-menu-vertical li.ant-menu-item::after{
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0px;
+    right: 0;
+    bottom: 0;
+    border-left: 3px solid #1890ff;
+    border-right: 0px ;
+    -webkit-transform: scaleY(0.0001);
+    transform: scaleY(0.0001);
+    opacity: 0;
+    -webkit-transition: opacity 0.15s cubic-bezier(0.215, 0.61, 0.355, 1), -webkit-transform 0.15s cubic-bezier(0.215, 0.61, 0.355, 1);
+    transition: opacity 0.15s cubic-bezier(0.215, 0.61, 0.355, 1), -webkit-transform 0.15s cubic-bezier(0.215, 0.61, 0.355, 1);
+    transition: transform 0.15s cubic-bezier(0.215, 0.61, 0.355, 1), opacity 0.15s cubic-bezier(0.215, 0.61, 0.355, 1);
+    transition: transform 0.15s cubic-bezier(0.215, 0.61, 0.355, 1), opacity 0.15s cubic-bezier(0.215, 0.61, 0.355, 1), -webkit-transform 0.15s cubic-bezier(0.215, 0.61, 0.355, 1);
+    content: '';
+}
+</style>

+ 229 - 0
src/components/mm-header/mm-header.vue

@@ -0,0 +1,229 @@
+<template>
+  <!--头部-->
+  <header class="mm-header">
+    <h1 class="header">
+      <a href="https://github.com/maomao1996/Vue-mmPlayer" target="_blank">
+        mmPlayer 在线音乐播放器
+      </a>
+    </h1>
+    <dl class="user">
+      <template v-if="user.userId">
+        <router-link class="user-info" to="/music/userlist" tag="dt">
+          <img class="avatar" :src="`${user.avatarUrl}?param=50y50`" />
+          <span>{{ user.nickname }}</span>
+        </router-link>
+        <dd class="user-btn" @click="openDialog(2)">退出</dd>
+      </template>
+      <dd v-else class="user-btn" @click="openDialog(0)">登录</dd>
+    </dl>
+    <!--登录-->
+    <mm-dialog
+      ref="loginDialog"
+      head-text="登录"
+      confirm-btn-text="登录"
+      cancel-btn-text="关闭"
+      @confirm="login"
+    >
+      <div class="mm-dialog-text">
+        <input
+          v-model.trim="uidValue"
+          class="mm-dialog-input"
+          type="number"
+          autofocus
+          placeholder="请输入您的网易云 UID"
+          @keyup.enter="login"
+        />
+      </div>
+      <div slot="btn" @click="openDialog(1)">帮助</div>
+    </mm-dialog>
+    <!--帮助-->
+    <mm-dialog
+      ref="helpDialog"
+      head-text="登录帮助"
+      confirm-btn-text="去登录"
+      cancel-btn-text="关闭"
+      @confirm="openDialog(0)"
+    >
+      <div class="mm-dialog-text">
+        <p>
+          1、
+          <a target="_blank" href="https://music.163.com">
+            点我(https://music.163.com)
+          </a>
+          打开网易云音乐官网
+        </p>
+        <p>2、点击页面右上角的“登录”</p>
+        <p>3、点击您的头像,进入我的主页</p>
+        <p>4、复制浏览器地址栏 /user/home?id= 后面的数字(网易云 UID)</p>
+      </div>
+    </mm-dialog>
+    <!--退出-->
+    <mm-dialog
+      ref="outDialog"
+      body-text="确定退出当前用户吗?"
+      @confirm="out"
+    />
+  </header>
+</template>
+
+<script>
+import { getUserPlaylist } from '@api/music.js'
+import { mapGetters, mapActions } from 'vuex'
+import MmDialog from '@base/mm-dialog/mm-dialog'
+import { toHttps } from '@/utils/util'
+
+export default {
+  name: 'MmHeader',
+  components: {
+    MmDialog
+  },
+  data() {
+    return {
+      user: {}, // 用户数据
+      uidValue: '' // 记录用户 UID
+    }
+  },
+  computed: {
+    ...mapGetters(['uid'])
+  },
+  created() {
+    this.uid && this._getUserPlaylist(this.uid)
+  },
+  methods: {
+    // 打开对话框
+    openDialog(key) {
+      switch (key) {
+        case 0:
+          this.$refs.loginDialog.show()
+          break
+        case 1:
+          this.$refs.loginDialog.hide()
+          this.$refs.helpDialog.show()
+          break
+        case 2:
+          this.$refs.outDialog.show()
+          break
+        case 3:
+          this.$refs.loginDialog.hide()
+          break
+      }
+    },
+    // 退出登录
+    out() {
+      this.user = {}
+      this.setUid(null)
+      this.$mmToast('退出成功!')
+    },
+    // 登录
+    login() {
+      if (this.uidValue === '') {
+        this.$mmToast('UID 不能为空')
+        this.openDialog(0)
+        return
+      }
+      this.openDialog(3)
+      this._getUserPlaylist(this.uidValue)
+    },
+    // 获取用户数据
+    _getUserPlaylist(uid) {
+      getUserPlaylist(uid).then(({ playlist = [] }) => {
+        this.uidValue = ''
+        if (playlist.length === 0 || !playlist[0].creator) {
+          this.$mmToast(`未查询找 UID 为 ${uid} 的用户信息`)
+          return
+        }
+        const creator = playlist[0].creator
+        this.setUid(uid)
+        creator.avatarUrl = toHttps(creator.avatarUrl)
+        this.user = creator
+        setTimeout(() => {
+          this.$mmToast(`${this.user.nickname} 欢迎使用 mmPlayer`)
+        }, 200)
+      })
+    },
+    ...mapActions(['setUid'])
+  }
+}
+</script>
+
+<style lang="less">
+.mm-header {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 60px;
+  @media (max-width: 768px) {
+    background: @header_bg_color;
+  }
+  .header {
+    text-align: center;
+    line-height: 60px;
+    color: @text_color_active;
+    font-size: @font_size_large;
+    @media (max-width: 768px) {
+      padding-left: 15px;
+      text-align: left;
+    }
+  }
+  .user {
+    position: absolute;
+    top: 50%;
+    right: 15px;
+    line-height: 30px;
+    text-align: right;
+    transform: translateY(-50%);
+    &-info {
+      float: left;
+      margin-right: 15px;
+      cursor: pointer;
+      .avatar {
+        width: 30px;
+        height: 30px;
+        border-radius: 90px;
+        vertical-align: middle;
+      }
+      span {
+        margin-left: 10px;
+        color: @text_color_active;
+      }
+    }
+    &-btn {
+      float: left;
+      cursor: pointer;
+      &:hover {
+        color: @text_color_active;
+      }
+    }
+    @media (max-width: 768px) {
+      &-info {
+        margin-right: 10px;
+        span {
+          display: none;
+        }
+      }
+    }
+  }
+}
+.mm-dialog-text {
+  text-align: left;
+  .mm-dialog-input {
+    width: 100%;
+    height: 40px;
+    box-sizing: border-box;
+    padding: 0 15px;
+    border: 1px solid @btn_color;
+    outline: 0;
+    background: transparent;
+    color: @text_color_active;
+    font-size: @font_size_medium;
+    box-shadow: 0 0 1px 0 #fff inset;
+    &::placeholder {
+      color: @text_color;
+    }
+  }
+  a:hover {
+    color: #d43c33;
+  }
+}
+</style>

+ 81 - 0
src/components/music-btn/music-btn.vue

@@ -0,0 +1,81 @@
+<template>
+  <!--选项-->
+  <div class="music-btn">
+    
+    <span @click="$emit('changeList', 'playList')" :class="{hoverSpan:formComponent=='playList'}">正在播放</span>
+    <span @click="$emit('changeList', 'topList')" :class="{hoverSpan:formComponent=='topList'}">推荐</span>
+    <span @click="$emit('changeList', 'search')" :class="{hoverSpan:formComponent=='search'}">搜索</span>
+    <!-- <span @click="$emit('changeList','userList')">
+      我的歌单
+    </span>
+    <span class="show-960" @click="$emit('onClickLyric')">歌词</span>
+    <span @click="$emit('changeList','historyList')">我听过的</span> -->
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'music-btn',
+  props:{
+    formComponent:{
+      type:String,
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.music-btn {
+  width: 100%;
+  height: 60px;
+  font-size: 0;
+  white-space: nowrap;
+  overflow-x: auto;
+  -webkit-overflow-scrolling: touch;
+  .hoverSpan{
+    border-color: #fff;
+    color: #fff;
+  }
+  span {
+    display: inline-block;
+    height: 40px;
+    box-sizing: border-box;
+    margin-right: 8px;
+    padding: 0 23px;
+    border: 1px solid @btn_color;
+    color: @btn_color;
+    border-radius: 2px;
+    font-size: 14px;
+    line-height: 40px;
+    overflow: hidden;
+    cursor: pointer;
+    &:nth-last-of-type(1) {
+      margin: 0;
+    }
+    &:hover,
+    &.active {
+      border-color: @btn_color_active;
+      color: @btn_color_active;
+    }
+  }
+  @media (min-width: 960px) {
+    span.show-960 {
+      display: none;
+    }
+  }
+  @media (max-width: 960px) {
+    span.show-960 {
+      display: inline-block;
+    }
+  }
+  @media (max-width: 768px) {
+    height: 50px;
+    span {
+      height: 35px;
+      padding: 0 10px;
+      margin-right: 6px;
+      line-height: 35px;
+    }
+  }
+}
+</style>

+ 377 - 0
src/components/music-list/music-list.vue

@@ -0,0 +1,377 @@
+<template>
+  <!--歌曲列表-->
+  <div class="musicList">
+    <template v-if="list.length > 0">
+      <div class="list-item list-header">
+        <span class="list-name">歌曲</span>
+        <span class="list-artist">歌手</span>
+        <span v-if="listType === 1" class="list-time">时长</span>
+        <span v-else class="list-album">专辑</span>
+      </div>
+      <div ref="listContent" class="list-content" @scroll="listScroll($event)">
+        <div
+          v-for="(item, index) in list"
+          :key="item.id"
+          class="list-item"
+          :class="{ on: playing && currentMusic.id === item.id }"
+          @dblclick="selectItem(item, index, $event)"
+        >
+          <span class="list-num" v-text="index + 1"></span>
+          <div class="list-name">
+            <span>{{ item.name }}</span>
+            <div class="list-menu">
+              <mm-icon class="hover" :type="getPlayIconType(item)" :size="40" @click.stop="selectItem(item, index)" />
+            </div>
+          </div>
+          <span class="list-artist">{{ item.singer }}</span>
+          <span v-if="listType === 1" class="list-time">
+            {{ item.duration % 3600 | format }}
+            <!-- <mm-icon
+              class="hover list-menu-icon-del"
+              type="delete-mini"
+              :size="40"
+              @click.stop="deleteItem(item, index)"
+            /> -->
+            <a class="hover list-menu-icon-del" style="font-size: 16px" @click.stop="deleteItem(item, index)">下载</a>
+          </span>
+          <span v-else class="list-album"
+            >{{ item.album }}
+            <a
+              class="hover list-menu-icon-del"
+              style="font-size: 16px; float: right; margin-right: 10px"
+              @click.stop="deleteItem(item, index)"
+              >下载</a
+            >
+          </span>
+        </div>
+        <slot name="listBtn"></slot>
+      </div>
+    </template>
+    <mm-no-result v-else title="弄啥呢,怎么啥也没有!!!" />
+  </div>
+</template>
+
+<script>
+// import {getCheckMusic} from '@api/music.js'
+import { mapGetters, mapMutations } from 'vuex'
+import { format } from '@/utils/util'
+import MmNoResult from '@base/mm-no-result/mm-no-result'
+import { postAction, downFilePost } from '../../api/manage'
+
+const musicUrl = 'https://music.163.com/song/media/outer/url'
+export default {
+  name: 'MusicList',
+  components: {
+    MmNoResult,
+  },
+  filters: {
+    format,
+  },
+  props: {
+    // 歌曲数据
+    list: {
+      type: Array,
+      default: () => [],
+    },
+    /**
+     *  0:显示专辑栏目(默认)
+     *  1:显示时长栏目
+     */
+    listType: {
+      type: Number,
+      default: 0,
+    },
+  },
+  data() {
+    return {
+      lockUp: true, // 是否锁定滚动加载事件,默认锁定
+    }
+  },
+  computed: {
+    ...mapGetters(['playing', 'currentMusic']),
+  },
+  watch: {
+    list(newList, oldList) {
+      if (this.listType !== 2) {
+        return
+      }
+      if (newList.length !== oldList.length) {
+        this.lockUp = false
+      } else if (newList[newList.length - 1].id !== oldList[oldList.length - 1].id) {
+        this.lockUp = false
+      }
+    },
+  },
+  activated() {
+    this.scrollTop && this.$refs.listContent && (this.$refs.listContent.scrollTop = this.scrollTop)
+  },
+  methods: {
+    // 滚动事件
+    listScroll(e) {
+      const scrollTop = e.target.scrollTop
+      this.scrollTop = scrollTop
+      if (this.listType !== 2 || this.lockUp) {
+        return
+      }
+      const { scrollHeight, offsetHeight } = e.target
+      if (scrollTop + offsetHeight >= scrollHeight - 50) {
+        this.lockUp = true // 锁定滚动加载
+        this.$emit('pullUp') // 触发滚动加载事件
+      }
+    },
+    // 回到顶部
+    scrollTo() {
+      this.$refs.listContent.scrollTop = 0
+    },
+    // 播放暂停事件
+    selectItem(item, index, e) {
+      if (e && /list-menu-icon-del/.test(e.target.className)) {
+        return
+      }
+      if (this.currentMusic.id && item.id === this.currentMusic.id) {
+        this.setPlaying(!this.playing)
+        return
+      }
+
+      /**
+       * 为了修复 safari、 ios 微信、安卓 UC 无法播放问题,暂时移除接口校验直接播放
+       */
+      this.$emit('select', item, index) // 触发点击播放事件
+
+      // getMusicUrl(item.id)
+      // .then(res => {
+      //     if (!res.data.data[0].url) {
+      //         this.$mmToast('当前音乐无法播放,请播放其他音乐')
+      //     } else {
+      //         this.$emit('select', item, index)//触发点击播放事件
+      //     }
+      // });
+      // getCheckMusic(item.id)
+      // .then(res => {
+      //     if (res.data.message !== 'ok') {
+      //         this.$mmToast('当前音乐无法播放,请播放其他音乐')
+      //     } else {
+      //         this.$emit('select', item, index)//触发点击播放事件
+      //     }
+      // }).catch(error => {
+      //     this.$mmToast(error.response.data.message)
+      // })
+    },
+    // 获取播放状态 type
+    getPlayIconType({ id: itemId }) {
+      const {
+        playing,
+        currentMusic: { id },
+      } = this
+      return playing && id === itemId ? 'pause-mini' : 'play-mini'
+    },
+    // 删除事件
+    deleteItem(item, index) {
+      // this.$emit('del', index) // 触发删除事件
+      // console.log(item, index)
+      let downloadElement = document.createElement('a')
+      downloadElement.href = musicUrl + '?id=' + item.id + '.mp3'
+      downloadElement.download = item.name
+      downloadElement.target = '_blank'
+      document.body.appendChild(downloadElement)
+      downloadElement.click()
+      document.body.removeChild(downloadElement)
+
+      // downFilePost('/ctop/bytedanceMusicInfo/netEaseDownLoad', {
+      //   musicName: item.name,
+      //   musicUrl: musicUrl + '?id=' + item.id + '.mp3',
+      // }).then((res) => {
+      //   const blob = new Blob([res], { type: 'audio/mpeg' })
+      //   const a = document.createElement('a')
+      //   const url = window.URL.createObjectURL(blob)
+      //   const filename = item.musicInfo.name
+      //   a.href = url
+      //   a.download = filename
+      //   a.click()
+      //   window.URL.revokeObjectURL(url)
+      // })
+    },
+    ...mapMutations({
+      setPlaying: 'SET_PLAYING',
+    }),
+  },
+}
+</script>
+
+<style lang="less" scoped>
+.list-header {
+  border-bottom: 1px solid @list_head_line_color;
+  color: @text_color_active;
+
+  .list-name {
+    padding-left: 40px;
+    user-select: none;
+  }
+}
+
+.list-content {
+  width: 100%;
+  height: calc(~'100% - 120px');
+  overflow-x: hidden;
+  overflow-y: auto;
+  -webkit-overflow-scrolling: touch;
+}
+
+.list-no {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  color: @text_color;
+}
+
+.list-item {
+  display: flex;
+  width: 100%;
+  height: 50px;
+  border-bottom: 1px solid @list_item_line_color;
+  line-height: 50px;
+  overflow: hidden;
+
+  &.list-item-no {
+    justify-content: center;
+    align-items: center;
+  }
+
+  &.on {
+    color: #fff;
+
+    .list-num {
+      font-size: 0;
+      background: url('~@/assets/img/wave.gif') no-repeat center center;
+    }
+  }
+
+  &:hover {
+    .list-name {
+      padding-right: 80px;
+
+      .list-menu {
+        display: block;
+      }
+    }
+  }
+
+  &:not([class*='list-header']):hover {
+    .list-name {
+      padding-right: 80px;
+
+      .list-menu {
+        display: block;
+      }
+    }
+
+    .list-time {
+      font-size: 0;
+
+      .list-menu-icon-del {
+        display: block;
+      }
+    }
+  }
+
+  .list-num {
+    display: block;
+    width: 30px;
+    margin-right: 10px;
+    text-align: center;
+  }
+
+  .list-name {
+    position: relative;
+    flex: 1;
+    box-sizing: border-box;
+
+    & > span {
+      text-overflow: ellipsis;
+      overflow: hidden;
+      display: -webkit-box;
+      -webkit-line-clamp: 1;
+      -webkit-box-orient: vertical;
+    }
+
+    small {
+      margin-left: 5px;
+      font-size: 12px;
+      color: rgba(255, 255, 255, 0.5);
+    }
+
+    /*hover菜单*/
+
+    .list-menu {
+      display: none;
+      position: absolute;
+      top: 50%;
+      right: 10px;
+      height: 40px;
+      font-size: 0;
+      transform: translateY(-50%);
+    }
+  }
+
+  .list-artist,
+  .list-album {
+    display: block;
+    width: 300px;
+    .no-wrap();
+    @media (max-width: 1440px) {
+      width: 200px;
+    }
+    @media (max-width: 1200px) {
+      width: 150px;
+    }
+  }
+
+  .list-time {
+    display: block;
+    width: 60px;
+    position: relative;
+
+    .list-menu-icon-del {
+      display: none;
+      position: absolute;
+      top: 50%;
+      left: 0;
+      transform: translateY(-50%);
+    }
+  }
+}
+
+@media (max-width: 960px) {
+  .list-item .list-name {
+    padding-right: 70px;
+  }
+}
+
+@media (max-width: 768px) {
+  .list-item {
+    .list-name .list-menu {
+      display: block;
+    }
+
+    .list-artist,
+    .list-album {
+      width: 20%;
+    }
+  }
+}
+
+@media (max-width: 640px) {
+  .list-item {
+    .list-artist {
+      width: 80px;
+    }
+
+    .list-album,
+    .list-time {
+      display: none;
+    }
+  }
+}
+</style>

+ 1 - 1
src/components/page/GlobalFooter.vue

@@ -3,7 +3,7 @@
     <div class="copyright">
       Copyright
       <a-icon type="copyright"/>
-      2019 <span>上海有腾文化传播有限公司</span>
+      2019 <span>上海有腾</span>
     </div>
   </div>
 </template>

+ 12 - 6
src/components/page/GlobalHeader.vue

@@ -17,8 +17,8 @@
         :type="collapsed ? 'menu-unfold' : 'menu-fold'"
         @click.native="toggle"/>
 
-      <span v-if="device === 'desktop'">欢迎使用智能投放平台</span>
-      <span v-else>智能投放平台</span>
+      <!-- <span v-if="device === 'desktop'">欢迎使用汇创投放助手</span> -->
+      <!-- <span v-else>汇创投放助手</span> -->
 
 
       <user-menu :theme="theme"/>
@@ -166,7 +166,12 @@
   /* update_begin author:scott date:20190220 for: 缩小首页布局顶部的高度*/
 
   $height: 59px;
-
+    .layout .header .user-wrapper .action .anticon, .layout .top-nav-header-index .user-wrapper .action .anticon {
+        color: #999;
+    }
+    .layout .header .user-wrapper .action .anticon, .layout .top-nav-header-index .user-wrapper .action .anticon {
+        color: #999;
+    }
   .layout {
 
     .top-nav-header-index {
@@ -181,8 +186,8 @@
       }
 
       .trigger {
-        line-height: 64px;
-
+        line-height: 48px;
+        font-size: 17px;
         &:hover {
           background: rgba(0, 0, 0, 0.05);
         }
@@ -191,7 +196,7 @@
 
     .header {
       z-index: 2;
-      color: white;
+      color: #999;
       height: $height;
       background-color: #1890ff;
       transition: background 300ms;
@@ -202,6 +207,7 @@
         box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
         background-color: white !important;
       }
+
     }
 
     .header, .top-nav-header-index {

+ 24 - 8
src/components/page/GlobalLayout.vue

@@ -1,7 +1,7 @@
 <template>
   <a-layout class="layout" :class="[device]">
-
     <template v-if="layoutMode === 'sidemenu'">
+
       <a-drawer
         v-if="device === 'mobile'"
         :wrapClassName="'drawer-sider ' + navTheme"
@@ -75,7 +75,9 @@
     </a-layout>
 
     <!-- <setting-drawer></setting-drawer> -->
+
   </a-layout>
+ 
 </template>
 
 <script>
@@ -122,17 +124,22 @@
     watch: {
       sidebarOpened(val) {
         this.collapsed = !val
+      },
+      device(n,o){
+        if(n=='desktop'){
+          this.menus = this.permissionMenuList
+        }
       }
     },
     created() {
       //--update-begin----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
-      //this.menus = this.mainRouters.find((item) => item.path === '/').children;
+      // this.menus = this.mainRouters.find((item) => item.path === '/').children;
       this.menus = this.permissionMenuList
       // 根据后台配置菜单,重新排序加载路由信息
-      console.log('----加载菜单逻辑----')
-      console.log(this.mainRouters)
-      console.log(this.permissionMenuList)
-      console.log('----navTheme------'+this.navTheme)
+      // console.log('----加载菜单逻辑----')
+      // console.log(this.mainRouters)
+      // console.log(this.permissionMenuList)
+      // console.log('----navTheme------'+this.navTheme)
       //--update-end----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
     },
     methods: {
@@ -181,7 +188,15 @@
       filter: invert(80%);
     }
   }
-
+  .layout .header .user-wrapper .action i.anticon, .layout .top-nav-header-index .user-wrapper .action i.anticon {
+        color: #999;
+  }
+  .layout .header .user-wrapper .action:hover i.anticon, .layout .top-nav-header-index .user-wrapper .action:hover i.anticon {
+        color: #fff;
+  }
+  .layout .header .user-wrapper .action:hover, .layout .top-nav-header-index .user-wrapper .action:hover {
+        color: #fff;
+  }
   .layout {
     min-height: 100vh !important;
     overflow-x: hidden;
@@ -239,7 +254,7 @@
     }
 
     .trigger {
-      font-size: 22px;
+      font-size: 17px;
       line-height: 42px;
       padding: 0 18px;
       cursor: pointer;
@@ -247,6 +262,7 @@
 
       &:hover {
         background: rgba(255, 255, 255, 0.3);
+        color:#fff
       }
     }
 

+ 2 - 2
src/components/table/index.js

@@ -57,13 +57,13 @@ export default {
       });
     },
     pageSize(val) {
-      console.log('pageSize:', val)
+      // console.log('pageSize:', val)
       Object.assign(this.localPagination, {
         pageSize: val
       });
     },
     showSizeChanger(val) {
-      console.log('showSizeChanger', val)
+      // console.log('showSizeChanger', val)
       Object.assign(this.localPagination, {
         showSizeChanger: val
       });

+ 1 - 1
src/components/tools/Breadcrumb.vue

@@ -23,7 +23,7 @@
     methods: {
       getBreadcrumb() {
 
-        console.log('this.$route.matched', this.$route.matched)
+        // console.log('this.$route.matched', this.$route.matched)
 
         this.breadList = []
         this.breadList.push({name: 'dashboard', path: '/dashboard/', meta: {title: '首页'}})

+ 2 - 2
src/components/tools/HeaderNotice.vue

@@ -151,9 +151,9 @@
               this.msg2Title = "系统消息(" + res.result.sysMsgTotal + ")";
             }
           }).catch(error => {
-            console.log("系统消息通知异常",error);//这行打印permissionName is undefined
+            // console.log("系统消息通知异常",error);//这行打印permissionName is undefined
             this.stopTimer = true;
-            console.log("清理timer");
+            // console.log("清理timer");
           });
         } catch (err) {
           this.stopTimer = true;

+ 27 - 4
src/components/tools/Logo.vue

@@ -1,8 +1,9 @@
 <template>
   <div class="logo">
     <router-link :to="{name:'dashboard'}">
-      <img src="~@/assets/logo.png" alt="logo">
-      <h1 v-if="showTitle">{{ title }}</h1>
+      <img src="~@/assets/logoNew1.png" alt="logo">
+      <span class="name">有腾投放管家</span>
+      <!-- <h1 v-if="showTitle">{{ 有腾管家 }}</h1> -->
     </router-link>
   </div>
 </template>
@@ -49,7 +50,29 @@
     }
 
     &.light .logo {
-      background-color: #1890ff;
+      background-color: #0B2040;
     }
   }
-</style>
+.logo{
+  .name{
+    font-size: 14px;
+    font-family: PingFangSC-Medium, PingFang SC;
+    font-weight: 500;
+    color: #CED2D9;
+    // line-height: 22px;
+    margin-left: 20px;
+    padding-left: 21px;
+    position: relative;
+  }
+  .name::before{
+    content: '';
+    width: 1px;
+    height: 18px;
+    background: rgba(255, 255, 255, 0.31);
+    position: absolute;
+    left: 0;
+    top: 50%;
+    margin-top: -9px;
+  }
+}
+</style>

+ 1 - 1
src/components/tools/UserPassword.vue

@@ -112,7 +112,7 @@
           if (!err) {
             that.confirmLoading = true;
             let params = Object.assign({username:this.username},values)
-            console.log("修改密码提交数据",params)
+            // console.log("修改密码提交数据",params)
             putAction(this.url,params).then((res)=>{
               if(res.success){
                 console.log(res)

+ 226 - 204
src/components/uploadFile.vue

@@ -4,95 +4,104 @@
 </style>
 <template>
   <div class="upload-file">
-    <a-upload name="file" :multiple="multiple" list-type="picture-card" class="avatar-uploader"
-      action="https://media-1301855440.cos.ap-chongqing.myqcloud.com/" :before-upload="beforeUpload"
-      @change="handleChange" :accept="uploadType+'/'+(uploadType=='image'?'jpeg,image/jpg':'mp4')" :file-list="fileList"
-      :remove="removeFile" @preview="handlePreview" :disabled="loadingElse||disabled">
-      <div v-if="uploadType=='image'?fileList.length < fileCount:fileList.length<1">
-        <a-progress v-if='loadingElse' :percent="percent" :show-info="false" />
-        <div class="ant-upload-text">
-          点击上传
-        </div>
+    <a-upload
+      name="file"
+      :multiple="multiple"
+      list-type="picture-card"
+      class="avatar-uploader"
+      action="https://media-1301855440.cos.ap-chongqing.myqcloud.com/"
+      :before-upload="beforeUpload"
+      @change="handleChange"
+      :accept="uploadType + '/' + (uploadType == 'image' ? 'jpeg,image/jpg' : 'mp4')"
+      :file-list="fileList"
+      :remove="removeFile"
+      @preview="handlePreview"
+      :disabled="loadingElse || disabled"
+    >
+      <div v-if="fileList.length < fileCount || fileList.length < 1">
+        <a-progress v-if="loadingElse" :percent="percent" :show-info="false" />
+        <div class="ant-upload-text">点击上传</div>
       </div>
     </a-upload>
-    <div style="color:red">{{uploadType=='image'?'请上传jpg格式的图片,并且大小在2m之内':'请上传mp4格式,大小在100m之内'}}</div>
+    <div style="color: red" v-if="sizeCheck">
+      {{ uploadType == 'image' ? '请上传jpg格式的图片,并且大小在2m之内' : '请上传mp4格式,大小在100m之内' }}
+    </div>
     <a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
-      <img alt="example" style="width: 100%" :src="previewImage" v-if="uploadType=='image'" />
+      <img alt="example" style="width: 100%" :src="previewImage" v-if="uploadType == 'image'" />
       <video :src="previewImage" v-else style="width: 100%" controls="controls">您的浏览器不支持 video 标签。</video>
     </a-modal>
   </div>
 </template>
 
 <script>
-  import {
-    stopOtherVideo,
-    closeAllVideoFun
-  } from '@/utils/videoControl' // 停止除当前外的其他视频播放,及停止所有视频播放的方法
-  import {
-    mapGetters
-  } from 'vuex'
-  import moment from 'moment'
+import { stopOtherVideo, closeAllVideoFun } from '@/utils/videoControl' // 停止除当前外的其他视频播放,及停止所有视频播放的方法
+import { mapGetters } from 'vuex'
+import moment from 'moment'
 
-  import BMF from 'browser-md5-file'
+import BMF from 'browser-md5-file'
 
-  import qs from 'qs'
-  var COS = require('cos-js-sdk-v5');
-  var cos = new COS({
-    SecretId: 'AKIDE6IpMi8fJQRCg1iuWzFajjRs43kbbets',
-    SecretKey: 'tXzuwMfplTTK3c9GFUyETilasvQfePu9',
-  });
-  var i = 0
-  const oneKB = 1024
+import qs from 'qs'
+var COS = require('cos-js-sdk-v5')
+var cos = new COS({
+  SecretId: 'AKIDE6IpMi8fJQRCg1iuWzFajjRs43kbbets',
+  SecretKey: 'tXzuwMfplTTK3c9GFUyETilasvQfePu9',
+})
+var i = 0
+const oneKB = 1024
 
-  function getBase64(file) {
-    return new Promise((resolve, reject) => {
-      const reader = new FileReader();
-      reader.readAsDataURL(file);
-      reader.onload = () => resolve(reader.result);
-      reader.onerror = error => reject(error);
-    });
-  }
-  export default {
-    name: 'upload-file',
-    components: {},
-    props: {
-      uploadType: { //上传类型  值为  image/video,同时目录名称也是这样
-        type: String,
-        default () {
-          return 'image'
-        }
-      },
-      multiple: { // true 可以批量上传  false 不可以
-        type: Boolean,
-        default () {
-          return true
-        }
+function getBase64(file) {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+    reader.readAsDataURL(file)
+    reader.onload = () => resolve(reader.result)
+    reader.onerror = (error) => reject(error)
+  })
+}
+export default {
+  name: 'upload-file',
+  components: {},
+  props: {
+    uploadType: {
+      //上传类型  值为  image/video,同时目录名称也是这样
+      type: String,
+      default() {
+        return 'image'
       },
-      fileCount: { //最大上传数量
-        type: Number,
-        default () {
-          return 10
-        }
+    },
+    multiple: {
+      // true 可以批量上传  false 不可以
+      type: Boolean,
+      default() {
+        return true
       },
-      checkFile: { //返回promise方法,判读文件是否上传过,进行上传拦截
-        type: Function,
-        default (file) {
-          return Promise.resolve()
-        }
+    },
+    fileCount: {
+      //最大上传数量
+      type: Number,
+      default() {
+        return 10
       },
-      disabled: {
-        type: Boolean,
-        default () {
-          return false
-        }
+    },
+    checkFile: {
+      //返回promise方法,判读文件是否上传过,进行上传拦截
+      type: Function,
+      default() {
+        return Promise.resolve()
       },
-      value: {
-        type: [String, Array],
-        required: true
+    },
+    disabled: {
+      type: Boolean,
+      default() {
+        return false
       },
-      size: {
-        type: Number,
-        default () {
+    },
+    value: {
+      required: true,
+    },
+    size: {
+      type: Number,
+      default() {
+        if (this.sizeCheck) {
           if (this.uploadType == 'image') {
             return 2048
           } else {
@@ -100,34 +109,40 @@
           }
         }
       },
-      onOversize: {
-        type: Function,
-        default () {
-          alert(`请选择${this.size}KB内的文件!`)
-        }
-      },
     },
-    data() {
-      return {
-        fileList: [],
-        loadingElse: false,
-        imageUrl: '',
-        percent: 0,
-        previewVisible: false,
-        previewImage: '',
-      }
+    sizeCheck: {
+      type: Boolean,
+      default() {
+        return true
+      },
     },
-    filters: {},
-    computed: {},
-    mounted() {
-
+    onOversize: {
+      type: Function,
+      default() {
+        alert(`请选择${this.size}KB内的文件!`)
+      },
     },
-    watch: {
-      //    fileListAll
-      value: {
-        immediate: true,
-        handler(n, o) {
-          if (this.fileList.length == 0) {
+  },
+  data() {
+    return {
+      fileList: [],
+      loadingElse: false,
+      imageUrl: '',
+      percent: 0,
+      previewVisible: false,
+      previewImage: '',
+    }
+  },
+  filters: {},
+  computed: {},
+  mounted() {},
+  watch: {
+    //    fileListAll
+    value: {
+      immediate: true,
+      handler(n, o) {
+        this.$nextTick(() => {
+          if (this.fileList && this.fileList.length == 0) {
             if (typeof n == 'object') {
               this.fileList = n.map((item, index) => {
                 return {
@@ -141,131 +156,133 @@
               if (n == '') {
                 this.fileList = []
               } else {
-                this.fileList = [{
-                  uid: 1,
-                  name: 1,
-                  status: 'done',
-                  url: n,
-                }]
-
+                this.fileList = [
+                  {
+                    uid: 1,
+                    name: 1,
+                    status: 'done',
+                    url: n,
+                  },
+                ]
               }
-
             }
           } else {
             if (n.length == 0 || n == '') {
               this.clearAll()
             }
           }
-
-        },
-        deep: true
+        })
+      },
+      deep: true,
+    },
+  },
+  methods: {
+    handleCancel() {
+      closeAllVideoFun()
+      this.previewVisible = false
+    },
+    handleChange(info) {
+      if (info.file.status === 'uploading') {
+        this.loading = true
+        return
+      }
+      if (info.file.status === 'done') {
+        // Get this url from response in real world.
+        getBase64(info.file.originFileObj, (imageUrl) => {
+          this.imageUrl = imageUrl
+          this.loading = false
+        })
       }
     },
-    methods: {
-      handleCancel() {
-        closeAllVideoFun()
-        this.previewVisible = false;
-      },
-      handleChange(info) {
-        if (info.file.status === 'uploading') {
-          this.loading = true;
-          return;
-        }
-        if (info.file.status === 'done') {
-          // Get this url from response in real world.
-          getBase64(info.file.originFileObj, imageUrl => {
-            this.imageUrl = imageUrl;
-            this.loading = false;
-          });
-        }
-      },
-      removeFile(file) {
-        console.log(file)
-        var index = this.fileList.findIndex(v => v.uid === file.uid)
-        this.fileList.splice(index, 1)
-        this.$emit('removeUpload', file)
-      },
-      async handlePreview(file) {
-        if (!file.url && !file.preview) {
-          file.preview = await getBase64(file.originFileObj);
-        }
-        this.previewImage = file.url || file.preview;
-        this.previewVisible = true;
-      },
-      beforeUpload(file) {
-        this.percent = 0
-        this.loadingElse = true
-        const fileOvesize = (file.size > this.size * oneKB)
-        console.log(fileOvesize)
-        if (fileOvesize) {
-          this.onOversize()
-          this.loadingElse = false
-          return
-        } else {
-          this.checkFile(file).then(res => {
+    removeFile(file) {
+      // console.log(file)
+      var index = this.fileList.findIndex((v) => v.uid === file.uid)
+      this.fileList.splice(index, 1)
+      this.$emit('removeUpload', file)
+    },
+    async handlePreview(file) {
+      if (!file.url && !file.preview) {
+        file.preview = await getBase64(file.originFileObj)
+      }
+      this.previewImage = file.url || file.preview
+      this.previewVisible = true
+    },
+    beforeUpload(file) {
+      this.percent = 0
+      this.loadingElse = true
+      const fileOvesize = file.size > this.size * oneKB
+      // console.log(fileOvesize)
+      if (fileOvesize && this.sizeCheck) {
+        this.onOversize()
+        this.loadingElse = false
+        return
+      } else {
+        this.checkFile(file)
+          .then((res) => {
             this.cosUpload(file)
-
-          }).catch(() => {
-            this.loadingElse = false
           })
-          return new Promise(function (resolve, reject) {
-            reject()
+          .catch(() => {
+            this.loadingElse = false
           })
+        return new Promise(function (resolve, reject) {
+          reject()
+        })
+      }
+    },
+    clearAll() {
+      this.fileList = []
+    },
+    cosUpload(file) {
+      var that = this
+      let date = new Date()
+      let y = date.getFullYear()
+      let MM = date.getMonth() + 1
+      MM = MM < 10 ? '0' + MM : MM
+      let d = date.getDate()
+      d = d < 10 ? '0' + d : d
+      var timeElse = new Date().getTime()
+      var arr = file.name.split('.')
 
-        }
-
-      },
-      clearAll() {
-        this.fileList = []
-      },
-      cosUpload(file) {
-        var that = this
-        let date = new Date()
-        let y = date.getFullYear()
-        let MM = date.getMonth() + 1
-        MM = MM < 10 ? '0' + MM : MM
-        let d = date.getDate()
-        d = d < 10 ? '0' + d : d
-        var timeElse = new Date().getTime()
-        var arr = file.name.split('.')
-
-        var str = ''
-        for (let i = 0; i < arr.length; i++) {
-          if (i == arr.length - 1) {} else {
-            if (i == arr.length - 2) {
-              str += arr[i]
-            } else {
-              str += (arr[i] + '.')
-            }
+      var str = ''
+      for (let i = 0; i < arr.length; i++) {
+        if (i == arr.length - 1) {
+        } else {
+          if (i == arr.length - 2) {
+            str += arr[i]
+          } else {
+            str += arr[i] + '.'
           }
-
         }
-        console.log(str + '-' + timeElse + '.' + arr[arr.length - 1])
-        cos.putObject({
-          Bucket: 'thirdpart-1301855440',
+      }
+      // console.log(str + '-' + timeElse + '.' + arr[arr.length - 1])
+      cos.putObject(
+        {
+          Bucket: 'media-1301855440',
           /* 必须 */
           Region: 'ap-chongqing',
           /* 存储桶所在地域,必须字段 */
-          Key: that.uploadType + '/' + y + '-' + MM + '-' + d + '/' + str + '-' + timeElse + '.' + arr[arr
-            .length - 1],
+          Key: that.uploadType + '/' + y + '-' + MM + '-' + d + '/' + str + '-' + timeElse + '.' + arr[arr.length - 1],
           /* 必须 */
           StorageClass: 'STANDARD',
           Body: file, // 上传文件对象
-          onProgress: function (progressData) { //进度条方法
+          onProgress: function (progressData) {
+            //进度条方法
 
-            that.percent = (progressData.percent * 100)
+            that.percent = progressData.percent * 100
           },
           onTaskReady: function (tid) {
             // console.log('onTaskReady', tid);
           },
-          onTaskStart: function (info) { //开始上传
+          onTaskStart: function (info) {
+            //开始上传
             // console.log('onTaskStart', info);
           },
-        }, function (err, data) {
+        },
+        function (err, data) {
           if (err) {
             that.loadingElse = false
             that.$message.error('上传失败!!!' + err)
-            return;
+            return
           }
           // alert('成功')
           that.loadingElse = false
@@ -283,24 +300,29 @@
             that.$emit('overUpload', that.fileList)
 
             if (that.multiple) {
-              that.$emit('update:value', that.fileList.map(item => {
-                return item.url
-              }))
+              that.$emit(
+                'update:value',
+                that.fileList.map((item) => {
+                  return item.url
+                })
+              )
             } else {
-              that.$emit('update:value', that.fileList.map(item => {
-                return item.url
-              })[0])
+              that.$emit(
+                'update:value',
+                that.fileList.map((item) => {
+                  return item.url
+                })[0]
+              )
             }
-
-
           } else {
             that.$message.error('上传已达上限' + that.fileCount + '张')
           }
-        });
-      }
-    }
-  }
+        }
+      )
+    },
+  },
+}
 </script>
 <style scoped>
-  @import '~@assets/less/common.less';
+@import '~@assets/less/common.less';
 </style>

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3735 - 0
src/components/vend-ztree.all.js


+ 82 - 0
src/components/volume/volume.vue

@@ -0,0 +1,82 @@
+<!-- 音量控制组件 -->
+<template>
+  <div class="volume">
+    <mm-icon
+      class="pointer volume-icon"
+      :type="getVolumeIconType()"
+      :size="30"
+      @click="handleToggleVolume"
+    />
+    <div class="volume-progress-wrapper">
+      <mm-progress
+        :percent="volumeProgress"
+        @percentChange="handleVolumeChange"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import MmProgress from '@base/mm-progress/mm-progress'
+
+export default {
+  name: 'Volume',
+  components: {
+    MmProgress
+  },
+  props: {
+    volume: {
+      type: Number,
+      required: true
+    }
+  },
+  computed: {
+    volumeProgress() {
+      return this.volume
+    },
+    isMute: {
+      get() {
+        return this.volumeProgress === 0
+      },
+      set(newMute) {
+        const volume = newMute ? 0 : this.lastVolume
+        if (newMute) {
+          this.lastVolume = this.volumeProgress
+        }
+        this.handleVolumeChange(volume)
+      }
+    }
+  },
+  methods: {
+    getVolumeIconType() {
+      return this.isMute ? 'volume-off' : 'volume'
+    },
+    // 是否静音
+    handleToggleVolume() {
+      this.isMute = !this.isMute
+    },
+    handleVolumeChange(percent) {
+      this.$emit('volumeChange', percent)
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.volume {
+  display: flex;
+  align-items: center;
+  width: 150px;
+  &-icon {
+    margin-right: 5px;
+    color: #fff;
+  }
+  &-progress-wrapper {
+    flex: 1;
+  }
+  @media (max-width: 768px) {
+    top: 2px;
+    width: 36px;
+  }
+}
+</style>

+ 36 - 0
src/config.js

@@ -0,0 +1,36 @@
+// 版本号
+export const VERSION = process.env.VUE_APP_VERSION
+
+/**
+ * 默认歌单ID (正在播放列表)
+ * 默认云音乐热歌榜 https://music.163.com/#/discover/toplist?id=3778678
+ * 如需要修改自定义歌单的请修改
+ * @type {number}
+ */
+export const defaultSheetId = 3778678
+
+// 默认分页数量
+export const defaultLimit = 30
+
+// 默认背景图(可引入网络图或本地静态图)
+const requireAll = requireContext => requireContext.keys().map(requireContext)
+const req = require.context('./assets/background', false)
+const BG_ARR = requireAll(req)
+export const defaultBG = BG_ARR[Math.floor(Math.random() * BG_ARR.length)]
+
+// 默认音量
+export const defaultVolume = 0.8
+
+/**
+ * 播放模式
+ * listLoop: 列表循环
+ * order:顺序
+ * loop: 单曲循环
+ * random: 随机
+ */
+export const playMode = {
+  listLoop: 0,
+  order: 1,
+  random: 2,
+  loop: 3
+}

+ 98 - 0
src/directive.js

@@ -0,0 +1,98 @@
+const directives = {
+    'card': {
+        inserted(el, binding, vnode, oldVnode) {
+            el.classList.add('page-card');
+            el.style.height = window.innerHeight - 125 + 'px';
+
+            function heightHandler(e) {
+                el.style.height = window.innerHeight - 125 + 'px';
+            }
+            // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
+            el.__page_card__ = heightHandler;
+            window.addEventListener('resize', heightHandler);
+        },
+        unbind(el, binding) {
+            // 解除事件监听
+            window.removeEventListener('resize', el.__page_card__);
+            delete el.__page_card__;
+        }
+    },
+    'dialog-drag': { // 弹窗拖拽
+        bind(el, binding, vnode, oldVnode) {
+            const dialogHeaderEl = el.querySelector('.ant-modal-header');
+            const dragDom = el.querySelector('.ant-modal-content');
+            dialogHeaderEl.style.cursor = 'move';
+    
+            // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
+            const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
+            
+            dialogHeaderEl.onmousedown = (e) => {
+                // 鼠标按下,计算当前元素距离可视区的距离
+                const disX = e.clientX - dialogHeaderEl.offsetLeft;
+                const disY = e.clientY - dialogHeaderEl.offsetTop;
+                
+                // 获取到的值带px 正则匹配替换
+                let styL, styT;
+    
+                // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
+                if(sty.left.includes('%')) {
+                    styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100);
+                    styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100);
+                }else {
+                    styL = +sty.left.replace(/\px/g, '');
+                    styT = +sty.top.replace(/\px/g, '');
+                };
+                
+                document.onmousemove = function (e) {
+                    // 通过事件委托,计算移动的距离 
+                    const l = e.clientX - disX;
+                    const t = e.clientY - disY;
+                    // 移动当前元素  
+                    dragDom.style.left = `${l + styL}px`;
+                    dragDom.style.top = `${t + styT}px`;
+    
+                    //将此时的位置传出去
+                    //binding.value({x:e.pageX,y:e.pageY})
+                };
+    
+                document.onmouseup = function (e) {
+                    document.onmousemove = null;
+                    document.onmouseup = null;
+                };
+            }  
+        }
+    },
+    'clickoutside': {
+        // 初始化指令
+        bind(el, binding, vnode) {
+            function documentHandler(e) {
+                // 这里判断点击的元素是否是本身,是本身,则返回
+                if (el.contains(e.target)) {
+                    return false;
+                }
+                // 判断指令中是否绑定了函数
+                if (binding.expression) {
+                    // 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
+                    binding.value(e);
+                }
+            }
+            // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
+            el.__vueClickOutside__ = documentHandler;
+            document.addEventListener('click', documentHandler);
+        },
+        update() {},
+        unbind(el, binding) {
+            // 解除事件监听
+            document.removeEventListener('click', el.__vueClickOutside__);
+            delete el.__vueClickOutside__;
+        },
+    }
+}
+
+export default {
+    install: function(Vue) {
+        Object.keys(directives).forEach(key => {
+            Vue.directive(key, directives[key]);
+        })
+    }
+}

+ 301 - 0
src/filters.js

@@ -0,0 +1,301 @@
+
+import moment from 'moment';
+function fixedZero(val) {
+    return val * 1 < 10 ? `0${val}` : val;
+}
+const filters = {
+    // 格式化时间展示多长时间之前
+    formatTime(time) {
+        let formatTime
+        const date = new Date(time)
+        const dateObj = {
+            year: date.getFullYear(),
+            month: date.getMonth(),
+            date: date.getDate(),
+            hours: date.getHours(),
+            minutes: date.getMinutes()
+        }
+        const newTime = new Date()
+        const diff = newTime.getTime() - time
+        if (newTime.getDate() === dateObj.date && diff < 60000) {
+            formatTime = '刚刚'
+        } else if (
+            newTime.getDate() === dateObj.date &&
+            diff > 60000 &&
+            diff < 3600000
+        ) {
+            formatTime = `${Math.floor(diff / 60000)}分钟前`
+        } else if (
+            newTime.getDate() === dateObj.date &&
+            diff > 3600000 &&
+            diff < 86400000
+        ) {
+            formatTime = `${addZero(dateObj.hours)}:${addZero(dateObj.minutes)}`
+        } else if (newTime.getDate() !== dateObj.date && diff < 86400000) {
+            formatTime = `昨天${addZero(dateObj.hours)}:${addZero(dateObj.minutes)}`
+        } else if (newTime.getFullYear() === dateObj.year) {
+            formatTime = `${dateObj.month + 1}月${dateObj.date}日`
+        } else {
+            formatTime = `${dateObj.year}年${dateObj.month + 1}月${dateObj.date}日`
+        }
+        return formatTime
+    },
+    //时间日期格式化
+    format(time) {
+        const hours = 60 * 60 * 1000;
+        const minutes = 60 * 1000;
+
+        const h = Math.floor(time / hours);
+        const m = Math.floor((time - h * hours) / minutes);
+        const s = Math.floor((time - h * hours - m * minutes) / 1000);
+        return `${fixedZero(h)}:${fixedZero(m)}:${fixedZero(s)}`
+    },
+    //年月日格式化
+    getDate(value) {
+        let date = new Date(value)
+        let y = date.getFullYear()
+        let MM = date.getMonth() + 1
+        MM = MM < 10 ? '0' + MM : MM
+        let d = date.getDate()
+        d = d < 10 ? '0' + d : d
+        return y + '-' + MM + '-' + d
+    },
+    //年月日时分秒格式化
+    formatDate: function (value) {
+        let date = new Date(value)
+        let y = date.getFullYear()
+        let MM = date.getMonth() + 1
+        MM = MM < 10 ? '0' + MM : MM
+        let d = date.getDate()
+        d = d < 10 ? '0' + d : d
+        let h = date.getHours()
+        h = h < 10 ? '0' + h : h
+        let m = date.getMinutes()
+        m = m < 10 ? '0' + m : m
+        let s = date.getSeconds()
+        s = s < 10 ? '0' + s : s
+        return y + '-' + MM + '-' + d + ' ' + h + ':' + m + ':' + s
+    },
+    //bidType类型
+    bidType(sta) {
+        var data = {
+            null: '-',
+            1: 'CPM',
+            2: 'CPC',
+            6: 'OCPC',
+            10: 'OCPM',
+        }
+        return data[sta]
+    },
+    //ocpxActionType类型
+    ocpxActionType(sta) {
+        var data = {
+            null: '-',
+            0: '封面点击',
+            2: '行为数',
+            10: '曝光',
+            11: '点击',
+            31: '下载完成',
+            53: '提交线索',
+            180: '激活',
+            190: '付费',
+            191: '首日ROI',
+            348: '有效线索',
+            383: '授信',
+            384: '完件',
+            394: '订单提交',
+        }
+        return data[sta]
+    },
+    //角色类型
+    roleCode(value) {
+        var state = {
+            clip: '剪辑',
+            shot: '拍摄',
+            plan: '策划',
+            plane: '平面',
+            admin: '管理员',
+            operator: '运营专员',
+            kuaishouOperationManager: '快手运营经理',
+            designTeamLeader: '设计组长',
+            planeLeader: '平面组长',
+            touTiaoOperationManager: '头条运营经理',
+            operationAssistant: '运营助理',
+        }
+
+        if (value) {
+            return state[value]
+        } else {
+            return '暂无'
+        }
+    },
+    //保留两位小数并且变成货币格式
+    decimalsHandle(val) {
+        if (val && typeof val != 'string') {
+            if (val >= 0) {
+                val = parseFloat(val).toFixed(2);
+                let numberStr = val.toString()
+                let str = numberStr.split('.')
+
+                let str0 = str[0].split('').reverse();
+                for (let i = 0; i < str0.length; i++) {
+                    if ((i + 1) % 4 === 0) {
+                        str0.splice(i, 0, ',')
+                    }
+                }
+                str0.reverse()
+                let handleResult = ''
+                for (let j = 0; j < str0.length; j++) {
+                    handleResult += str0[j]
+                }
+
+                return handleResult + '.' + str[1]
+            } else {
+                val = parseFloat(val).toFixed(2);
+                let numberStr = val.toString()
+                let str = numberStr.split('.')
+
+                let str0 = str[0].substr(1).split('').reverse();
+                for (let i = 0; i < str0.length; i++) {
+                    if ((i + 1) % 4 === 0) {
+                        str0.splice(i, 0, ',')
+                    }
+                }
+                str0.reverse()
+                let handleResult = ''
+                for (let j = 0; j < str0.length; j++) {
+                    handleResult += str0[j]
+                }
+
+                return '-' + handleResult + '.' + str[1]
+            }
+
+        } else if (typeof val == 'string') {
+            return val
+        }
+        else {
+            return 0
+        }
+    },
+    //变成货币格式
+    formatCurrency(val) {
+        if ((val || val == 0) && typeof val != 'string') {
+            let numberStr = val.toString()
+            let str = numberStr.split('').reverse()
+            for (let i = 0; i < str.length; i++) {
+                if ((i + 1) % 4 === 0) {
+                    str.splice(i, 0, ',')
+                }
+            }
+            str.reverse()
+            let handleResult = ''
+            for (let j = 0; j < str.length; j++) {
+                handleResult += str[j]
+            }
+            return handleResult
+        } else if (typeof val == 'string') {
+            return val
+        }
+        else {
+            return '-'
+        }
+
+    },
+    //加百分比
+    toPercentage(val) {
+        if (val) {
+            if (typeof val != 'string') {
+                return (val * 100).toFixed(2) + '%'
+            } else {
+                return val
+            }
+        } else if (typeof val == 'string') {
+            return val
+        }
+        else {
+            return 0
+        }
+    },
+    //媒体类型
+    handleMediaType(val) {
+        switch (val) {
+            case 1:
+                return '头条'
+                break
+
+            case 2:
+                return '快手'
+                break
+
+            case 3:
+                return '头条内广'
+                break
+
+            case 4:
+                return '快手内广'
+                break
+
+            default:
+                break
+        }
+    },
+    //文件大小
+    sizeShow(value) {
+        return (value / 1024).toFixed(2)
+    },
+    //预警计算符号
+    conditionName(val) {
+        var data = {
+            equal: '=',//'等于',
+            not_equal: '≠',//不等于,
+            greater: '>',//大于
+            less: '<',//小于
+            greater_equal: '≥',//大于等于
+            less_equal: '≤',//小于等于
+        }
+
+        return data[val]
+    },
+}
+
+// 获取当前月的第一天
+function getCurrentMonthFirst() {
+    let date = new Date();
+    date.setDate(1);
+    return date;
+}
+// 获取当前月的最后一天
+function getCurrentMonthLast() {
+    let date = new Date();
+    let currentMonth = date.getMonth();
+    let nextMonth = ++currentMonth;
+    let nextMonthFirstDay = new Date(date.getFullYear(), nextMonth, 1);
+    let oneDay = 1000 * 60 * 60 * 24;
+    return new Date(nextMonthFirstDay - oneDay);
+}
+// 今日日期小于15号时和今日日期大于等于15号时
+function getCurrentMonthFifty() {
+    let date = new Date();
+    if (date.getDate() < 15) {
+        let bb = date.setTime(new Date().getTime() - 3600 * 1000 * 24 * 15);
+        return bb;
+    } else if (date.getDate() >= 15) {
+        let year = date.getFullYear();
+        let month = date.getMonth();
+        let myDate = new Date(year, month, 0);
+        let lastDay = year + "-" + month + "-" + myDate.getDate();//上个月的最后一天
+        return lastDay;
+    }
+
+}
+
+export default {
+    install: function (Vue) {
+        Object.keys(filters).forEach(key => {
+            Vue.filter(key, filters[key])
+        })
+    }
+
+}
+
+

+ 20 - 8
src/main.js

@@ -28,7 +28,10 @@ import 'vue-photo-preview/dist/skin.css'
 import VuePreview from 'vue-preview';
 import promiseFinally from 'promise.prototype.finally';
 promiseFinally.shim();
-
+import directive from './directive'
+import filters from './filters'
+Vue.use(directive)
+Vue.use(filters)
 import md5 from 'blueimp-md5';
 
 // import KFormDesign from 'k-form-design'
@@ -37,7 +40,12 @@ import 'k-form-design/styles/k-form-design.less'
 // import 'k-form-design/lib/k-form-design.css'
 Vue.use(KFormDesign)
 import 'ant-design-vue/dist/antd.less';
-
+import '@/styles/index.less'
+import VueLazyload from 'vue-lazyload'
+Vue.use(VueLazyload, {
+  preLoad: 1,
+  loading: require('@assets/img/default.png')
+})
 Vue.prototype.$md5 = md5;
 import {
   ACCESS_TOKEN,
@@ -58,13 +66,15 @@ import JDictSelectTag from './components/dict/index.js'
 import hasPermission from '@/utils/hasPermission'
 import vueBus from '@/utils/vueBus';
 
+
+import '@/components/vend-ztree.all.js'
 import JeecgComponents from '@/components/jeecg/index'
 // import iView from 'iview'
 import '!style-loader!css-loader!less-loader!./theme/index.less'
 
-// import ElementUI from 'element-ui';
-// import 'element-ui/lib/theme-chalk/index.css';
-
+import ElementUI from 'element-ui';
+import 'element-ui/lib/theme-chalk/index.css';
+Vue.use(ElementUI);
 import {
   getDay,
   allWeeks
@@ -73,11 +83,13 @@ import {
 import * as WPS from './assets/jwps.es6'
 Vue.prototype.wps = WPS;
 
-
+import Icon from '@base/mm-icon/mm-icon'
+import mmToast from '@base/mm-toast'
+Vue.component(Icon.name, Icon)
+Vue.use(mmToast)
 
 Vue.prototype.$getDay = getDay
 Vue.prototype.allWeeks = allWeeks
-// Vue.use(ElementUI);
 
 Vue.config.productionTip = false
 // Vue.use(iView)
@@ -94,7 +106,7 @@ Vue.use(preview)
 Vue.use(vueBus);
 Vue.use(JeecgComponents);
 Vue.use(VuePreview);
-Vue.prototype.doMian = "/jeecg-boot/";
+Vue.prototype.doMian = "http://139.186.31.213:8804/jeecg-boot";
 Vue.prototype.postFormAction = postFormAction;
 Vue.prototype.postDataAction = postAction;
 Vue.prototype.getAction = getAction;

+ 48 - 15
src/mixins/JeecgListMixin.js

@@ -12,6 +12,7 @@ import { transformTozTreeFormat } from '@/utils/util.js'
 export const JeecgListMixin = {
   data() {
     return {
+      spinning:true,
       //token header
       tokenHeader: {'X-Access-Token': Vue.ls.get(ACCESS_TOKEN)},
       /* 查询条件-请不要在queryParam中声明非字符串值的属性 */
@@ -79,7 +80,7 @@ export const JeecgListMixin = {
                                 key:index
                             }
                         })
-                        console.log(record.innerData)
+                        // console.log(record.innerData)
                         record.innerData = transformTozTreeFormat( record.innerData)
                     }
                     if (res.code === 510) {
@@ -93,6 +94,7 @@ export const JeecgListMixin = {
         }
     },
     async loadData(arg) {
+      this.spinning= true
       this.dataSource = []
       if (!this.url.list) {
         this.$message.error("请设置url.list属性!")
@@ -109,9 +111,9 @@ export const JeecgListMixin = {
         params.endDate = moment(params.createTime[1]).format('YYYY-MM-DD')
         params.createTime = null
       }
-      // if(params.startDate){
-      //   params.startDate = moment(params.startDate).format('YYYY-MM-DD')
-      // }
+      if(params.stateDate){
+        params.stateDate = moment(params.stateDate).format('YYYY-MM-DD')
+      }
       // if(params.endDate){
       //   params.endDate = moment(params.endDate).format('YYYY-MM-DD')
       // }
@@ -119,19 +121,47 @@ export const JeecgListMixin = {
       this.loading = true;
       await getAction(this.url.list, params).then((res) => {
         if (res.success) {
-            this.dataSource = res.result.records.map(item=>{
+          if(res.result){
+            if(res.result.records){
+              this.dataSource = res.result.records.map(item=>{
+                return {
+                    ...item,
+                    innerData:[],
+                    showVideo:false
+                }
+              })
+              this.ipagination.total = res.result.total;
+            }else{
+              this.dataSource = res.result.map(item=>{
                 return {
                     ...item,
                     innerData:[],
                     showVideo:false
                 }
-            })
-            this.ipagination.total = res.result.total;
+              })
+              // this.ipagination = null;
+              this.ipagination.pageSize=res.result.length
+            }
+          }else if(res.data){
+            if(res.data.list){
+              this.dataSource = res.data.list.map(item=>{
+                return {
+                    ...item,
+                    innerData:[],
+                    showVideo:false
+                }
+              })
+              this.ipagination.total = res.data.total;
+            }
+          }
+          
+          this.spinning= false
         }
         if (res.code === 510) {
           this.$message.warning(res.message)
         }
         this.loading = false;
+        this.spinning= false
       })
     },
     initDictConfig() {
@@ -163,7 +193,7 @@ export const JeecgListMixin = {
       if (this.superQueryParams) {
         sqp['superQueryParams'] = encodeURI(this.superQueryParams)
       }
-      console.log(this.queryParam)
+      // console.log(this.queryParam)
       var param = {...this.queryParam,...this.isorter, ...this.filters}
       param.pageNo = this.ipagination.current;
       param.pageSize = this.ipagination.pageSize;
@@ -243,7 +273,7 @@ export const JeecgListMixin = {
       });
     },
     handleEdit: function (record,sign) {
-        console.log(record,sign)
+        // console.log(record,sign)
       this.$refs.modalForm.edit(record,sign);
       this.$refs.modalForm.title = "编辑";
       this.$refs.modalForm.disableSubmit = false;
@@ -281,12 +311,15 @@ export const JeecgListMixin = {
     },
     modalFormOk() {
       // 新增/修改 成功时,重载列表
-      this.loadData();
+      this.loadData(1);
     },
     handleDetail: function (record) {
-      this.$refs.modalForm.edit(record);
-      this.$refs.modalForm.title = "详情";
-      this.$refs.modalForm.disableSubmit = true;
+      this.$nextTick(()=>{
+        this.$refs.modalForm.edit(record);
+        this.$refs.modalForm.title = "详情";
+        this.$refs.modalForm.disableSubmit = true;
+      })
+      
     },
     /* 导出 */
     handleExportXls2() {
@@ -302,7 +335,7 @@ export const JeecgListMixin = {
       if (this.selectedRowKeys && this.selectedRowKeys.length > 0) {
         param['selections'] = this.selectedRowKeys.join(",")
       }
-      console.log("导出参数", param)
+      // console.log("导出参数", param)
       downFile(this.url.exportXlsUrl, param).then((data) => {
         if (!data) {
           this.$message.warning("文件下载失败")
@@ -326,7 +359,7 @@ export const JeecgListMixin = {
     /* 导入 */
     handleImportExcel(info) {
       if (info.file.status !== 'uploading') {
-        console.log(info.file, info.fileList);
+        // console.log(info.file, info.fileList);
       }
       if (info.file.status === 'done') {
         if (info.file.response.success) {

+ 0 - 2
src/permission.js

@@ -23,10 +23,8 @@ router.beforeEach((to, from, next) => {
     } else {
       if (store.getters.permissionList.length === 0) {
         store.dispatch('GetPermissionList').then(res => {
-          console.log(res,34567890)
           const menuData = res.result.menu;
           localStorage.setItem("roleCode",res.result.roleCode)
-          console.log(res.message)
           if (menuData === null || menuData === "" || menuData === undefined) {
             return;
           }

+ 102 - 0
src/store/actions.js

@@ -0,0 +1,102 @@
+import {
+  clearHistoryList,
+  setHistoryList,
+  removeHistoryList,
+  setMode,
+  setUserId
+} from '@/utils/storage'
+import * as types from './mutation-types'
+
+function findIndex(list, music) {
+  return list.findIndex(item => {
+    return item.id === music.id
+  })
+}
+
+
+export default {
+  // 设置播放列表
+  setPlaylist: function ({ commit }, { list }) {
+    commit(types.SET_PLAYLIST, list)
+    commit(types.SET_ORDERLIST, list)
+  },
+  // 选择播放(会更新整个播放列表)
+  selectPlay: function ({ commit }, { list, index }) {
+    commit(types.SET_PLAYLIST, list)
+    commit(types.SET_ORDERLIST, list)
+    commit(types.SET_CURRENTINDEX, index)
+    commit(types.SET_PLAYING, true)
+  },
+  // 选择播放(会插入一条到播放列表)
+  selectAddPlay: function ({ commit, state }, music) {
+    let list = [...state.playlist]
+    // 查询当前播放列表是否有代插入的音乐,并返回其索引值
+    let index = findIndex(list, music)
+    // 当前播放列表有待插入的音乐时,直接改变当前播放音乐的索引值
+    if (index > -1) {
+      commit(types.SET_CURRENTINDEX, index)
+    } else {
+      list.unshift(music)
+      commit(types.SET_PLAYLIST, list)
+      commit(types.SET_ORDERLIST, list)
+      commit(types.SET_CURRENTINDEX, 0)
+    }
+    commit(types.SET_PLAYING, true)
+  },
+  // 清空播放列表
+  clearPlayList: function ({ commit }) {
+    commit(types.SET_PLAYING, false)
+    commit(types.SET_CURRENTINDEX, -1)
+    commit(types.SET_PLAYLIST, [])
+    commit(types.SET_ORDERLIST, [])
+  },
+
+  // 删除正在播放列表中的歌曲
+  removerPlayListItem: function (
+    { commit, state },
+    { list, index }
+  ) {
+    let currentIndex = state.currentIndex
+    if (index < state.currentIndex || list.length === state.currentIndex) {
+      currentIndex--
+      commit(types.SET_CURRENTINDEX, currentIndex)
+    }
+    commit(types.SET_PLAYLIST, list)
+    commit(types.SET_ORDERLIST, list)
+    if (!list.length) {
+      commit(types.SET_PLAYING, false)
+    } else {
+      commit(types.SET_PLAYING, true)
+    }
+  },
+  // 设置播放历史
+  setHistory: function ({ commit }, music) {
+    commit(types.SET_HISTORYLIST, setHistoryList(music))
+  },
+
+  // 删除播放历史
+  removeHistory: function ({ commit }, music) {
+    commit(types.SET_HISTORYLIST, removeHistoryList(music))
+  },
+  // 清空播放历史
+  clearHistory: function ({ commit }) {
+    commit(types.SET_HISTORYLIST, clearHistoryList())
+  },
+  // 设置播放模式
+  setPlayMode: function ({ commit }, mode) {
+    commit(types.SET_PLAYMODE, setMode(mode))
+  },
+  // 设置网易云用户UID
+  setUid: function ({ commit }, uid) {
+    commit(types.SET_UID, setUserId(uid))
+  }
+}
+
+
+
+
+
+
+
+
+

+ 27 - 6
src/store/getters.js

@@ -5,17 +5,38 @@ const getters = {
   theme: state => state.app.theme,
   color: state => state.app.color,
   token: state => state.user.token,
-  avatar: state => {state.user.avatar = Vue.ls.get(USER_INFO).avatar; return state.user.avatar},
+  avatar: state => { state.user.avatar = Vue.ls.get(USER_INFO).avatar; return state.user.avatar },
   username: state => state.user.username,
-  nickname: state => {state.user.realname = Vue.ls.get(USER_INFO).realname; return state.user.realname},
+  nickname: state => { state.user.realname = Vue.ls.get(USER_INFO).realname; return state.user.realname },
   welcome: state => state.user.welcome,
   permissionList: state => state.user.permissionList,
-  userInfo: state => {state.user.info = Vue.ls.get(USER_INFO); return state.user.info},
+  userInfo: state => { state.user.info = Vue.ls.get(USER_INFO); return state.user.info },
   addRouters: state => state.permission.addRouters,
-  enhanceJs:(state) => (code) => {
-    state.enhance.enhanceJs[code] = Vue.ls.get(ENHANCE_PRE+code);
+  enhanceJs: (state) => (code) => {
+    state.enhance.enhanceJs[code] = Vue.ls.get(ENHANCE_PRE + code);
     return state.enhance.enhanceJs[code]
-  }
+  },
+
+  // audio元素
+  audioEle: state => state.audioEle,
+  // 播放模式
+  mode: state => state.mode,
+  // 播放状态
+  playing: state => state.playing,
+  // 播放列表
+  playlist: state => state.playlist,
+  // 顺序列表
+  orderList: state => state.orderList,
+  // 当前音乐索引
+  currentIndex: state => state.currentIndex,
+  // 当前音乐
+  currentMusic: state => {
+    return state.playlist[state.currentIndex] || {}
+  },
+  // 播放历史列表
+  historyList: state => state.historyList,
+  // 网易云用户UID
+  uid: state => state.uid
 
 }
 

+ 8 - 11
src/store/index.js

@@ -5,8 +5,10 @@ import app from './modules/app'
 import user from './modules/user'
 import permission from './modules/permission'
 import enhance from './modules/enhance'
+import music from './modules/music'
 import getters from './getters'
-
+import mutations from './mutations'
+import actions from './actions'
 Vue.use(Vuex)
 
 export default new Vuex.Store({
@@ -14,16 +16,11 @@ export default new Vuex.Store({
     app,
     user,
     permission,
-    enhance
-  },
-  state: {
-
-  },
-  mutations: {
-
-  },
-  actions: {
-
+    enhance,
   },
+  state: music,
+  mutations,
+  actions,
   getters
+  
 })

+ 2 - 2
src/store/modules/app.js

@@ -35,9 +35,9 @@ const app = {
       Vue.ls.set(SIDEBAR_TYPE, type)
     },
     CLOSE_SIDEBAR: (state, withoutAnimation) => {
-      Vue.ls.set(SIDEBAR_TYPE, true)
+      Vue.ls.set(SIDEBAR_TYPE, false)
       state.sidebar.opened = false
-      state.sidebar.withoutAnimation = withoutAnimation
+      state.sidebar.withoutAnimation = false
     },
     TOGGLE_DEVICE: (state, device) => {
       state.device = device

+ 15 - 0
src/store/modules/music.js

@@ -0,0 +1,15 @@
+import { playMode } from '@/config'
+import { getHistoryList, getMode, getUserId } from '@/utils/storage'
+
+const state = {
+  audioEle: null, // audio元素
+  mode: Number(getMode()) || playMode.listLoop, // 播放模式,默认列表循环
+  playing: false, // 播放状态
+  playlist: [], // 播放列表
+  orderList: [], // 顺序列表
+  currentIndex: -1, // 当前音乐索引
+  historyList: getHistoryList() || [], // 播放历史列表
+  uid: getUserId() || null // 网易云用户UID
+}
+
+export default state

+ 3 - 3
src/store/modules/permission.js

@@ -60,17 +60,17 @@ const permission = {
     SET_ROUTERS: (state, data) => {
       state.addRouters = data
       state.routers = constantRouterMap.concat(data)
-      console.log('-----mutations---SET_ROUTERS----', data)
+      // console.log('-----mutations---SET_ROUTERS----', data)
     }
   },
   actions: {
     GenerateRoutes({ commit }, data) {
       return new Promise(resolve => {
         const { roles } = data
-        console.log('-----mutations---data----', data)
+        // console.log('-----mutations---data----', data)
         let accessedRouters
         accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
-        console.log('-----mutations---accessedRouters----', accessedRouters)
+        // console.log('-----mutations---accessedRouters----', accessedRouters)
         commit('SET_ROUTERS', accessedRouters)
         resolve()
       })

+ 12 - 1
src/store/mutation-types.js

@@ -21,4 +21,15 @@ export const INDEX_MAIN_PAGE_PATH =  "/dashboard/analysis"
 export const CONTENT_WIDTH_TYPE = {
   Fluid: 'Fluid',
   Fixed: 'Fixed'
-}
+}
+
+
+
+export const SET_AUDIOELE = 'SET_AUDIOELE' // 修改audio元素
+export const SET_PLAYMODE = 'SET_PLAYMODE' // 修改播放模式
+export const SET_PLAYING = 'SET_PLAYING' // 修改播放状态
+export const SET_PLAYLIST = 'SET_PLAYLIST' // 修改播放列表
+export const SET_ORDERLIST = 'SET_ORDERLIST' // 修改顺序列表
+export const SET_CURRENTINDEX = 'SET_CURRENTINDEX' // 修改当前音乐索引
+export const SET_HISTORYLIST = 'SET_HISTORYLIST' // 修改播放历史列表
+export const SET_UID = 'SET_UID' // 修改网易云用户UID

+ 38 - 0
src/store/mutations.js

@@ -0,0 +1,38 @@
+import * as types from './mutation-types'
+
+const mutations = {
+  // 修改audio元素
+  [types.SET_AUDIOELE](state, audioEle) {
+    state.audioEle = audioEle
+  },
+  // 修改播放模式
+  [types.SET_PLAYMODE](state, mode) {
+    state.mode = mode
+  },
+  // 修改播放状态
+  [types.SET_PLAYING](state, playing) {
+    state.playing = playing
+  },
+  // 修改播放列表
+  [types.SET_PLAYLIST](state, playlist) {
+    state.playlist = playlist
+  },
+  // 修改顺序列表
+  [types.SET_ORDERLIST](state, orderList) {
+    state.orderList = orderList
+  },
+  // 修改当前音乐索引
+  [types.SET_CURRENTINDEX](state, currentIndex) {
+    state.currentIndex = currentIndex
+  },
+  // 修改播放历史列表
+  [types.SET_HISTORYLIST](state, historyList) {
+    state.historyList = historyList
+  },
+  // 修改网易云用户UID
+  [types.SET_UID](state, uid) {
+    state.uid = uid
+  }
+}
+
+export default mutations

+ 98 - 0
src/styles/index.less

@@ -0,0 +1,98 @@
+@import 'reset';
+@import 'var';
+html,
+body,
+#app {
+  width: 100%;
+  height: 100%;
+  // overflow: hidden;
+}
+
+body {
+  min-width: 320px;
+  // font-family: Arial;
+}
+
+// #app {
+//   position: relative;
+// }
+
+.cover-img {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+//浮动
+.fl {
+  float: left;
+}
+
+.fr {
+  float: right;
+}
+
+.pointer {
+  cursor: pointer;
+}
+
+.hover {
+  color: @text_color;
+  cursor: pointer;
+  &:hover {
+    color: @text_color_active;
+  }
+}
+
+.text-left {
+  text-align: left;
+}
+
+.clearfix {
+  &:after {
+    display: block;
+    content: '';
+    clear: both;
+  }
+}
+
+//滚动条
+::-webkit-scrollbar {
+  /*滚动条整体部分,其中的属性有width,height,background,border(就和一个块级元素一样)等*/
+  background-color: rgba(0, 0, 0, 0.3);
+  width: 5px; //纵向滚动条
+  border-radius: 10px;
+}
+
+::-webkit-scrollbar-button {
+  /*滚动条两端的按钮。可以用display:none让其不显示,也可以添加背景图片,颜色改变显示效果。*/
+  display: none;
+}
+
+::-webkit-scrollbar-track {
+  /*外层轨道。可以用display:none让其不显示,也可以添加背景图片,颜色改变显示效果。*/
+  display: none;
+  //background-color: rgba(255, 255, 255, 0.1);
+  border-radius: 10px;
+}
+
+::-webkit-scrollbar-track-piece {
+  /*内层轨道,滚动条中间部分(除去)。*/
+  //background-color: rgba(255, 255, 255, .1);
+  border-radius: 10px;
+}
+
+::-webkit-scrollbar-thumb {
+  /*滚动条里面可以拖动的那部分*/
+  background-color: rgba(255, 255, 255, 0.5);
+  border-radius: 10px;
+}
+
+::-webkit-scrollbar-corner {
+  border-radius: 10px;
+}
+
+::-webkit-resizer {
+  /*定义右下角拖动块的样式*/
+  border-radius: 10px;
+}

+ 13 - 0
src/styles/mixin.less

@@ -0,0 +1,13 @@
+// 显示省略号
+.no-wrap() {
+  text-overflow: ellipsis;
+  overflow: hidden;
+  white-space: nowrap;
+}
+
+.flex-center(@direction: row) {
+  display: flex;
+  flex-direction: @direction;
+  justify-content: center;
+  align-items: center;
+}

+ 141 - 0
src/styles/reset.less

@@ -0,0 +1,141 @@
+// html,
+// body,
+// div,
+// span,
+// applet,
+// object,
+// iframe,
+// h1,
+// h2,
+// h3,
+// h4,
+// h5,
+// h6,
+// p,
+// blockquote,
+// pre,
+// a,
+// abbr,
+// acronym,
+// address,
+// big,
+// cite,
+// code,
+// del,
+// dfn,
+// em,
+// img,
+// ins,
+// kbd,
+// q,
+// s,
+// samp,
+// small,
+// strike,
+// strong,
+// sub,
+// sup,
+// tt,
+// var,
+// b,
+// u,
+// i,
+// center,
+// dl,
+// dt,
+// dd,
+// ol,
+// ul,
+// li,
+// fieldset,
+// form,
+// label,
+// legend,
+// table,
+// caption,
+// tbody,
+// tfoot,
+// thead,
+// tr,
+// th,
+// td,
+// article,
+// aside,
+// canvas,
+// details,
+// embed,
+// figure,
+// figcaption,
+// footer,
+// header,
+// hgroup,
+// menu,
+// nav,
+// output,
+// ruby,
+// section,
+// summary,
+// time,
+// mark,
+// audio,
+// video {
+//   margin: 0;
+//   padding: 0;
+//   border: 0;
+//   font-size: 100%;
+//   font: inherit;
+//   vertical-align: baseline;
+//   -webkit-tap-highlight-color: transparent;
+// }
+
+/* HTML5 display-role reset for older browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+  display: block;
+}
+
+body {
+  line-height: 1;
+}
+
+ol,
+ul {
+  list-style: none;
+}
+
+blockquote,
+q {
+  quotes: none;
+}
+
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+  content: '';
+  content: none;
+}
+
+// table {
+//   border-collapse: collapse;
+//   border-spacing: 0;
+// }
+
+// a {
+//   text-decoration: none;
+//   color: inherit;
+// }
+
+// input[type='number']::-webkit-inner-spin-button,
+// input[type='number']::-webkit-outer-spin-button {
+//   -webkit-appearance: none;
+// }

+ 52 - 0
src/styles/var.less

@@ -0,0 +1,52 @@
+//字体颜色定义规范
+@text_color: rgba(255, 255, 255, 0.6);
+@text_color_active: #fff; //重点部分
+
+//active颜色
+@active_color: #fff;
+
+//遮罩层颜色
+@mask_color: rgba(0, 0, 0, 0.4);
+
+//loading背景颜色
+@load_bg_color: rgba(0, 0, 0, 0.2);
+
+//header背景颜色
+@header_bg_color: rgba(0, 0, 0, 0.3);
+
+//search-head
+@search_bg_coloe: rgba(0, 0, 0, 0.2);
+
+//dialog相关
+@dialog_bg_color: rgba(0, 0, 0, 0.5);
+@dialog_content_bg_color: rgba(0, 0, 0, 0.6);
+@dialog_text_color: rgba(255, 255, 255, 0.7);
+@dialog_line_color: rgba(0, 0, 0, 0.35);
+
+//btn相关
+@btn_color: rgba(255, 255, 255, 0.6);
+@btn_color_active: #fff;
+
+//歌词高亮颜色
+@lyric_color_active: #40ce8f;
+
+//进度条
+@bar_color: rgba(255, 255, 255, 0.15);
+@line_color: #fff;
+@dot_color: #fff;
+
+//列表
+@list_head_line_color: rgba(255, 255, 255, 0.8);
+@list_item_line_color: rgba(255, 255, 255, 0.1);
+
+//评论
+@comment_head_line_color: rgba(255, 255, 255, 0.8);
+@comment_item_line_color: rgba(255, 255, 255, 0.1);
+@comment_replied_line_color: rgba(255, 255, 255, 0.3);
+
+//字体大小定义规范
+@font_size_small: 12px;
+@font_size_medium: 14px;
+@font_size_medium_x: 16px;
+@font_size_large: 18px;
+@font_size_large_x: 22px;

+ 342 - 0
src/utils/Tools.js

@@ -0,0 +1,342 @@
+
+
+export const pickerOptions = [{
+        text: "今天",
+        onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime());
+            picker.$emit('pick', [start, end]);
+        }
+    },
+    {
+        text: "昨天",
+        onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setDate(start.getDate() - 1);
+            end.setDate(end.getDate() - 1);
+            picker.$emit('pick', [start, end]);
+        }
+    },
+    {
+        text: "最近七天",
+        onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+            picker.$emit('pick', [start, end]);
+        }
+    },
+    {
+        text: "最近30天",
+        onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+            picker.$emit('pick', [start, end]);
+        }
+    },
+    {
+        text: "本月",
+        onClick(picker) {
+            const start = new Date();
+            const end = new Date(start);
+            end.setMonth(start.getMonth() + 1);
+            end.setDate(0);
+            start.setDate(1);
+            picker.$emit('pick', [start, end]);
+        }
+    },
+    {
+        text: "上个月",
+        onClick(picker) {
+            const start = new Date();
+            const end = new Date(start);
+            end.setMonth(start.getMonth());
+            start.setMonth(start.getMonth() - 1);
+            end.setDate(0);
+            start.setDate(1);
+            picker.$emit('pick', [start, end]);
+        }
+    },
+    {
+        text: "去年",
+        onClick(picker) {
+            const endYear = new Date().getFullYear() - 1;
+            const startYear = new Date().getFullYear() - 1;
+            const start = new Date(startYear, 0, 1);
+            const end = new Date(endYear, 11, 31);
+            picker.$emit('pick', [start, end]);
+        }
+    },
+    {
+        text: "今年",
+        onClick(picker) {
+            const endYear = new Date().getFullYear();
+            const startYear = new Date().getFullYear();
+            const start = new Date(startYear, 0, 1);
+            const end = new Date(endYear, 11, 31);
+            picker.$emit('pick', [start, end]);
+        }
+    },
+    {
+        text: "最近一年",
+        onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setFullYear(start.getFullYear() - 1);
+            picker.$emit('pick', [start, end]);
+        }
+    },
+];
+
+/**
+ * tree 形结构格式化
+ * @param {any} data 
+ */
+export const initTree = {
+
+}
+
+/**
+ * 判断是否是数组
+ * @param {Array} arr 
+ */
+export const isArray = (arr) => {
+    return Object.prototype.toString.call(arr) == '[object Array]'
+}
+
+/**
+ * 手机号屏蔽
+ */
+export const hidePhone = (num) => {
+    if (num) {
+        let newNum = String(num);
+        return newNum.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2');
+    }
+}
+/**
+ * 时间格式转化
+ * @param {Array} arr 
+ */
+export const dateReturn1 = (date1) => {
+    date1.toLocaleString("en-US", {
+        hour12: false
+    }).replace(/\b\d\b/g, '0><').replace(new RegExp('/', 'gm'), '-');
+}
+/**
+ * 十五分钟倒计时
+ * @param {Array}string
+ */
+export const fiftyDate = () => {
+    let now = new Date();
+    var hour = now.getHours();
+    var minu = now.getMinutes();
+    var sec = now.getSeconds();
+    if (hour < 10) hour = "0" + hour;
+
+    if (minu < 10) minu = "0" + minu;
+
+    if (sec < 10) sec = "0" + sec;
+    var time = "";
+    time = hour + ":" + minu + ":" + sec
+    console.info('倒计时--->', time);
+}
+
+/**
+ * 输入框单字节计算
+ * @param string
+ */
+export const strSubleng = (str) => {
+    var len = 0;
+    for (var i = 0; i < str.length; i++) {
+        var c = str.charCodeAt(i);
+        if ((c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f)) { //单字节加1
+            len++;
+        } else {
+            len += 2;
+        }
+    }
+    return len;
+}
+/**
+ * 截取路由参数
+ * @param string
+ */
+export const subparams = (str) => {
+    //let search = location.search;
+    let params = {};
+    if (str != "") {
+        str.slice(1).split("&").forEach((v) => {
+            let arr = v.split("=");
+            params[arr[0]] = arr[1];
+        })
+    }
+    return params;
+}
+
+/**
+ * get url 对象序列化
+ * @param string
+ */
+export const http_builder_url = (url, data) => {
+    if (typeof (url) == 'undefined' || url == null) {
+        return '';
+    }
+    if (typeof (data) == 'undefined' || data == null || typeof (data) != 'object') {
+        return '';
+    }
+    url += (url.indexOf("?") != -1) ? "" : "?";
+    for (var k in data) {
+        url += ((url.indexOf("=") != -1) ? "&" : "") + k + "=" + encodeURI(data[k]);
+        console.log(url);
+    }
+    return url;
+}
+
+/**
+ * 存储localStorage
+ * @param {any} data 
+ */
+export const setStore = (name, content) => {
+    if (!name) return
+    if (typeof content !== 'string') {
+        content = JSON.stringify(content);
+    }
+    window.localStorage.setItem(name, content);
+}
+/**
+ * 获取localStorage
+ * @param {any} data 
+ */
+export const getStore = name => {
+    if (!name) return;
+    return window.localStorage.getItem(name);
+}
+/**
+ *删除localStorage
+ * @param {any} data 
+ */
+export const removeStore = name => {
+    if (!name) return;
+    window.localStorage.removeItem(name);
+}
+
+/**
+ *去除字符串中逗号
+ * @param {any} data 
+ */
+export const clear = str => {
+    str = str.replace(/,/g, '')
+    return str;
+}
+
+/**
+ * 全屏显示
+ * @param {any} data 
+ */
+export const requestFullScreen = () => {
+    let elem = document.documentElement;
+    if (elem.requestFullscreen) {
+        elem.requestFullscreen()
+    } else if (elem.mozRequestFullScreen) {
+        elem.mozRequestFullScreen()
+    } else if (elem.webkitRequestFullScreen) {
+        elem.webkitRequestFullScreen()
+    } else if (elem.msRequestFullscreen) {
+        document.body.msRequestFullscreen()
+    }
+}
+/**
+ * 退出全屏
+ * @param {any} data 
+ */
+export const exitFullscreen = () => {
+    let doc = document
+    if (doc.exitFullscreen) {
+        doc.exitFullscreen()
+    } else if (doc.mozCancelFullScreen) {
+        doc.mozCancelFullScreen()
+    } else if (doc.webkitCancelFullScreen) {
+        doc.webkitCancelFullScreen()
+    } else if (doc.msExitFullscreen) {
+        document.msExitFullscreen()
+    }
+}
+
+
+/**
+ * 将简单数据格式转换成嵌套的数据格式
+ * @param {Object} setting 
+ * @param {Array} sNodes 
+ */
+export const transformTozTreeFormat = (sNodes, setting={}) => { 
+    var i, l,
+        key = setting.idKey || 'id',
+        parentKey = setting.parentKey || 'parentId',
+        childKey = setting.childKey || 'children';
+    if (!key || key == "" || !sNodes) return [];
+
+    if (isArray(sNodes)) {
+        var r = [];
+        var tmpMap = {};
+        for (i = 0, l = sNodes.length; i < l; i++) {
+            tmpMap[sNodes[i][key]] = sNodes[i];
+        }
+        for (i = 0, l = sNodes.length; i < l; i++) {
+            if (tmpMap[sNodes[i][parentKey]] && sNodes[i][key] != sNodes[i][parentKey]) {
+                if (!tmpMap[sNodes[i][parentKey]][childKey])
+                    tmpMap[sNodes[i][parentKey]][childKey] = [];
+                tmpMap[sNodes[i][parentKey]][childKey].push(sNodes[i]);
+            } else {
+                r.push(sNodes[i]);
+            }
+        }
+        return r;
+    } else {
+        return [sNodes];
+    }
+}
+
+/**
+ * 将嵌套的数据格式转换成简单数据格式
+ * @param {Object} setting 
+ * @param {Array} nodes 
+ */
+export const transformToArrayFormat = (nodes, setting={}) => {
+    if (!nodes) return [];
+    var childKey = setting.childKey || 'children',
+        r = [];
+    if (isArray(nodes)) {
+        for (var i = 0, l = nodes.length; i < l; i++) {
+            r.push(nodes[i]);
+            if (nodes[i][childKey])
+                r = r.concat(transformToArrayFormat(nodes[i][childKey], setting));
+        }
+    } else {
+        r.push(nodes);
+        if (nodes[childKey])
+            r = r.concat(transformToArrayFormat(nodes[childKey], setting));
+    }
+    return r;
+}
+
+/**将对象转换成url查询格式
+ * 
+ * @param {} url 
+ * @param {*} data 
+ */
+export const changeParam = (url, data) => {
+    if(typeof(url) == 'undefined' || url == null) {
+        return '';
+    }
+    if(typeof(data) == 'undefined' || data == null || typeof(data) != 'object') {
+        return '';
+    }
+    url += (url.indexOf("?") != -1) ? "" : "?";
+    for(var k in data) {
+        url += ((url.indexOf("=") != -1) ? "&" : "") + k + "=" + encodeURI(data[k]);
+    }
+    return url;
+}

+ 3 - 3
src/utils/authFilter.js

@@ -9,7 +9,7 @@ export function disabledAuthFilter(code, formData) {
 }
 
 function nodeDisabledAuth(code, formData) {
-  console.log("页面权限禁用--NODE--开始");
+  // console.log("页面权限禁用--NODE--开始");
   var permissionList = [];
   try {
     var obj = formData;
@@ -40,7 +40,7 @@ function nodeDisabledAuth(code, formData) {
   } else {
     for (var item2 of permissionList) {
       if (code === item2.action) {
-        console.log("页面权限禁用--NODE--生效");
+        // console.log("页面权限禁用--NODE--生效");
         return true;
       }
     }
@@ -49,7 +49,7 @@ function nodeDisabledAuth(code, formData) {
 }
 
 function globalDisabledAuth(code) {
-  console.log("页面禁用权限--Global--开始");
+  // console.log("页面禁用权限--Global--开始");
 
   var permissionList = [];
   var allPermissionList = [];

+ 23 - 0
src/utils/axiosMusic.js

@@ -0,0 +1,23 @@
+import axios from 'axios'
+import Vue from 'vue'
+
+const request = axios.create({
+  baseURL: process.env.VUE_APP_BASE_API_URL
+})
+
+request.interceptors.response.use(
+  response => {
+    window.response = response
+
+    if (response.status === 200 && response.data.code === 200) {
+      return response.data
+    }
+    return Promise.reject(response)
+  },
+  e => {
+    Vue.prototype.$mmToast(e.message)
+    throw e
+  }
+)
+
+export default request

+ 1 - 1
src/utils/getDay.js

@@ -65,7 +65,7 @@ export  function allWeeks(now_month) {
     let start = new Date(year, month, 1); // 得到当月第一天
     let end = new Date(year, month + 1, 0); // 得到当月最后一天
     let start_day = start.getDay(); // 当月第一天是周几
-    console.log(start.format("yyyy-MM-dd"), end.format("yyyy-MM-dd")); // 每月的起始日期
+    // console.log(start.format("yyyy-MM-dd"), end.format("yyyy-MM-dd")); // 每月的起始日期
     switch (start_day) {
       case 0:
         i = 0 - 1;

+ 53 - 1
src/utils/mixin.js

@@ -37,4 +37,56 @@ const mixinDevice = {
   }
 }
 
-export {mixin, mixinDevice}
+export {mixin, mixinDevice}
+
+
+
+
+
+import { mapGetters, mapMutations, mapActions } from 'vuex'
+
+/**
+ * 歌曲列表
+ */
+export const listMixin = {
+  computed: {
+    ...mapGetters(['playing', 'currentMusic'])
+  },
+  methods: {
+    selectItem(item, index) {
+      if (item.id === this.currentMusic.id && this.playing) {
+        this.setPlaying(false)
+      } else {
+        this.selectPlay({
+          list: this.list,
+          index
+        })
+      }
+    },
+    ...mapMutations({
+      setPlaying: 'SET_PLAYING'
+    }),
+    ...mapActions(['selectPlay'])
+  }
+}
+
+/**
+ * loading状态
+ * @type {{data(): *, methods: {_hideLoad(): void}}}
+ */
+export const loadMixin = {
+  data() {
+    return {
+      mmLoadShow: true // loading状态
+    }
+  },
+  methods: {
+    _hideLoad() {
+      let timer
+      clearTimeout(timer)
+      timer = setTimeout(() => {
+        this.mmLoadShow = false
+      }, 200)
+    }
+  }
+}

+ 13 - 9
src/utils/request.js

@@ -4,7 +4,7 @@ import store from '@/store'
 import {VueAxios} from './axios'
 import {Modal, notification} from 'ant-design-vue'
 import {ACCESS_TOKEN} from "@/store/mutation-types"
-
+const key = "1"
 // 创建 axios 实例
 
 if (process.env.NODE_ENV == 'development') { 
@@ -13,8 +13,10 @@ if (process.env.NODE_ENV == 'development') {
 else if (process.env.NODE_ENV == 'debug') { 
 } 
 else if (process.env.NODE_ENV == 'production') { 
-    // axios.defaults.baseURL = 'http://192.168.1.251/jeecg-boot';
-axios.defaults.baseURL = 'http://api.shyouteng.com.cn/jeecg-boot';
+  
+  // axios.defaults.baseURL = 'http://192.168.1.251/jeecg-boot';
+  // axios.defaults.baseURL = 'http://139.186.151.174:8804/jeecg-boot';     
+  axios.defaults.baseURL = 'http://api.tjyourong.com.cn/jeecg-boot';
 }
 const service = axios.create({
 //   baseURL: '/jeecg-boot', // api base_url
@@ -28,11 +30,11 @@ const err = (error) => {
   if (error.esponse) {
     let data = error.response.data
     const token = Vue.ls.get(ACCESS_TOKEN)
-    console.log("------异常响应------", token)
-    console.log("------异常响应------", error.response.status)
+    // console.log("------异常响应------", token)
+    // console.log("------异常响应------", error.response.status)
     switch (error.response.status) {
       case 403:
-        notification.error({message: '系统提示', description: '拒绝访问', duration: 4})
+        notification.error({key,message: '系统提示', description: '拒绝访问', duration: 4})
         break
       case 500:
         //notification.error({ message: '系统提示', description:'Token失效,请重新登录!',duration: 4})
@@ -57,13 +59,13 @@ const err = (error) => {
         }
         break
       case 404:
-        notification.error({message: '系统提示', description: '很抱歉,资源未找到!', duration: 4})
+        notification.error({key,message: '系统提示', description: '很抱歉,资源未找到!', duration: 4})
         break
       case 504:
-        notification.error({message: '系统提示', description: '网络超时'})
+        notification.error({key,message: '系统提示', description: '网络超时'})
         break
       case 401:
-        notification.error({message: '系统提示', description: '未授权,请重新登录', duration: 4})
+        notification.error({key,message: '系统提示', description: '未授权,请重新登录', duration: 4})
         if (token) {
           store.dispatch('Logout').then(() => {
             setTimeout(() => {
@@ -74,6 +76,7 @@ const err = (error) => {
         break
       default:
         notification.error({
+          key,
           message: '系统提示',
           description: data.message,
           duration: 4
@@ -106,6 +109,7 @@ service.interceptors.response.use((response) => {
   return response.data
 }, (error) => {
     notification.error({
+        key,
         message: '系统提示',
         description: '网络超时,请刷新页面'
       })

+ 70 - 0
src/utils/song.js

@@ -0,0 +1,70 @@
+import { toHttps } from './util'
+
+function filterSinger(singers) {
+  let arr = []
+  singers.forEach(item => {
+    arr.push(item.name)
+  })
+  return arr.join('/')
+}
+
+export class Song {
+  constructor({ id, name, singer, album, image, duration, url }) {
+    this.id = id
+    this.name = name
+    this.singer = singer
+    this.album = album
+    this.image = image
+    this.duration = duration
+    this.url = url
+  }
+}
+
+export function createPlayList(music) {
+  return new Song({
+    id: music.id,
+    name: music.name,
+    singer: music.artists.length > 0 && filterSinger(music.artists),
+    album: music.album.name,
+    image: toHttps(music.album.picUrl) || null,
+    duration: music.duration / 1000,
+    url: `https://music.163.com/song/media/outer/url?id=${music.id}.mp3`
+  })
+}
+
+export function createTopList(music) {
+  return new Song({
+    id: music.id,
+    name: music.name,
+    singer: music.ar.length > 0 && filterSinger(music.ar),
+    album: music.al.name,
+    image: toHttps(music.al.picUrl),
+    duration: music.dt / 1000,
+    url: `https://music.163.com/song/media/outer/url?id=${music.id}.mp3`
+  })
+}
+
+// 歌曲数据格式化
+const formatSongs = function formatPlayList(list) {
+  let Songs = []
+  list.forEach(item => {
+    const musicData = item
+    if (musicData.id) {
+      Songs.push(createPlayList(musicData))
+    }
+  })
+  return Songs
+}
+
+export const formatTopSongs = function formatTopList(list) {
+  let Songs = []
+  list.forEach(item => {
+    const musicData = item
+    if (musicData.id) {
+      Songs.push(createTopList(musicData))
+    }
+  })
+  return Songs
+}
+
+export default formatSongs

+ 136 - 0
src/utils/storage.js

@@ -1,3 +1,5 @@
+
+import { defaultVolume } from '@/config'
 /**
  * Set storage
  *
@@ -76,3 +78,137 @@ export const clearAll = () => {
   window.localStorage.clear()
 }
 
+
+
+//音乐库方法
+const _storage = window.localStorage
+const storage = {
+  get(key, data = []) {
+    if (_storage) {
+      return _storage.getItem(key)
+        ? Array.isArray(data)
+          ? JSON.parse(_storage.getItem(key))
+          : _storage.getItem(key)
+        : data
+    }
+  },
+  set(key, val) {
+    if (_storage) {
+      _storage.setItem(key, val)
+    }
+  },
+  clear(key) {
+    if (_storage) {
+      _storage.removeItem(key)
+    }
+  }
+}
+
+/**
+ * 播放历史
+ * @type    HISTORYLIST_KEY:key值
+ *          HistoryListMAX:最大长度
+ */
+const HISTORYLIST_KEY = '__mmPlayer_historyList__'
+const HistoryListMAX = 200
+// 获取播放历史
+export function getHistoryList() {
+  return storage.get(HISTORYLIST_KEY)
+}
+
+// 更新播放历史
+export function setHistoryList(music) {
+  let list = storage.get(HISTORYLIST_KEY)
+  const index = list.findIndex(item => {
+    return item.id === music.id
+  })
+  if (index === 0) {
+    return list
+  }
+  if (index > 0) {
+    list.splice(index, 1)
+  }
+  list.unshift(music)
+  if (HistoryListMAX && list.length > HistoryListMAX) {
+    list.pop()
+  }
+  storage.set(HISTORYLIST_KEY, JSON.stringify(list))
+  return list
+}
+
+// 删除一条播放历史
+export function removeHistoryList(music) {
+  storage.set(HISTORYLIST_KEY, JSON.stringify(music))
+  return music
+}
+
+// 清空播放历史
+export function clearHistoryList() {
+  storage.clear(HISTORYLIST_KEY)
+  return []
+}
+
+/**
+ * 播放模式
+ * @type    MODE_KEY:key值
+ *          HistoryListMAX:最大长度
+ */
+const MODE_KEY = '__mmPlayer_mode__'
+// 获取播放模式
+export function getMode() {
+  return storage.get(MODE_KEY, null)
+}
+// 修改播放模式
+export function setMode(mode) {
+  storage.set(MODE_KEY, mode)
+  return mode
+}
+
+/**
+ * 网易云用户uid
+ * @type USERID_KEY:key值
+ */
+const USERID_KEY = '__mmPlayer_userID__'
+// 获取用户uid
+export function getUserId() {
+  return Number(storage.get(USERID_KEY, null))
+}
+// 修改用户uid
+export function setUserId(uid) {
+  storage.set(USERID_KEY, uid)
+  return uid
+}
+
+/**
+ * 版本号
+ * @type VERSION_KEY:key值
+ */
+const VERSION_KEY = '__mmPlayer_version__'
+// 获取版本号
+export function getVersion() {
+  let version = storage.get(VERSION_KEY, null)
+  return Array.isArray(version) ? null : version
+}
+// 修改版本号
+export function setVersion(version) {
+  storage.set(VERSION_KEY, version)
+  return version
+}
+
+/**
+ * 音量
+ * @type VOLUME_KEY:key值
+ */
+const VOLUME_KEY = '__mmPlayer_volume__'
+// 获取音量
+export function getVolume() {
+  const volume = storage.get(VOLUME_KEY, defaultVolume)
+  return Number(volume)
+}
+// 修改音量
+export function setVolume(volume) {
+  storage.set(VOLUME_KEY, volume)
+  return volume
+}
+
+

+ 110 - 1
src/utils/util.js

@@ -506,4 +506,113 @@ export const transformTozTreeFormat = (sNodes, setting={}) => {
     } else {
         return [sNodes];
     }
-}
+}
+
+//音乐库方法
+// 随机排序数组/洗牌函数 https://github.com/lodash/lodash/blob/master/shuffle.js
+function copyArray(source, array) {
+  let index = -1
+  const length = source.length
+  array || (array = new Array(length))
+  while (++index < length) {
+    array[index] = source[index]
+  }
+  return array
+}
+
+export const randomSortArray = function shuffle(array) {
+  const length = array == null ? 0 : array.length
+  if (!length) {
+    return []
+  }
+  let index = -1
+  const lastIndex = length - 1
+  const result = copyArray(array)
+  while (++index < length) {
+    const rand = index + Math.floor(Math.random() * (lastIndex - index + 1))
+    const value = result[rand]
+    result[rand] = result[index]
+    result[index] = value
+  }
+  return result
+}
+
+// 防抖函数
+export function debounce(func, delay) {
+  let timer
+  return function(...args) {
+    if (timer) {
+      clearTimeout(timer)
+    }
+    timer = setTimeout(() => {
+      func.apply(this, args)
+    }, delay)
+  }
+}
+
+// 补0函数
+export function addZero(s) {
+  return s < 10 ? '0' + s : s
+}
+
+// 歌词解析
+const timeExp = /\[(\d{2,}):(\d{2})(?:\.(\d{2,3}))?]/g
+export function parseLyric(lrc) {
+  const lines = lrc.split('\n')
+  const lyric = []
+  for (let i = 0; i < lines.length; i++) {
+    const line = lines[i]
+    const result = timeExp.exec(line)
+    if (!result) {
+      continue
+    }
+    const text = line.replace(timeExp, '').trim()
+    if (text) {
+      lyric.push({
+        time: (result[1] * 6e4 + result[2] * 1e3 + (result[3] || 0) * 1) / 1e3,
+        text
+      })
+    }
+  }
+  return lyric
+}
+
+// 时间格式化
+export function format(value) {
+  let minute = Math.floor(value / 60)
+  let second = Math.floor(value % 60)
+  return `${addZero(minute)}:${addZero(second)}`
+}
+
+/**
+ * https://github.com/videojs/video.js/blob/master/src/js/utils/promise.js
+ * Silence a Promise-like object.
+ *
+ * This is useful for avoiding non-harmful, but potentially confusing "uncaught
+ * play promise" rejection error messages.
+ *
+ * @param  {Object} value
+ *         An object that may or may not be `Promise`-like.
+ */
+export function isPromise(v) {
+  return v !== undefined && v !== null && typeof v.then === 'function'
+}
+
+export function silencePromise(value) {
+  if (isPromise(value)) {
+    value.then(null, () => {})
+  }
+}
+
+// 判断 string 类型
+export function isString(v) {
+  return typeof v === 'string'
+}
+
+// http 链接转化成 https
+export function toHttps(url) {
+  if (!isString(url)) {
+    return url
+  }
+  return url.replace('http://', 'https://')
+}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 589 - 484
src/views/activiti/ProcessModelList.vue


+ 220 - 149
src/views/activiti/applyHome.vue

@@ -1,204 +1,275 @@
 <template>
   <a-card :bordered="false">
     <!--流程申请选择-->
-    <a-input-search style="margin-bottom: 10px;margin-right:10px;width: 200px" v-model="searchProcessKey"
-      placeholder="输入流程名称" @search="onSearchProcess" />
+    <a-input-search
+      style="margin-bottom: 10px;margin-right:10px;width: 200px"
+      v-model="searchProcessKey"
+      placeholder="输入流程名称"
+      @search="onSearchProcess"
+    />
     <a-button @click="onSearchProcess(searchProcessKey)" type="primary">查询</a-button>
     <a-button @click="onSearchProcess('')">重置</a-button>
     <a-button @click="handleToApplyList" type="primary" style="float: right;">前往我的申请列表</a-button>
     <a-empty description="无流程可供选择" v-if="activeKeyAll.length==0" />
     <div v-else>
       <a-collapse v-model="activeKey">
-        <a-collapse-panel v-for="(value, index)  in activeKeyAll" :header="filterDictText(dictOptions,value)||'未分类'"
-          :key="value">
+        <a-collapse-panel
+          v-for="(value, index)  in activeKeyAll"
+          :header="filterDictText(dictOptions,value)||'未分类'"
+          :key="value"
+        >
           <a-list :grid="{ gutter: 10,column:4}" :dataSource="processDataMap[value]">
             <a-list-item slot="renderItem" slot-scope="item">
               <a-card>
                 <div slot="title">
                   <a-row>
-                    <a-col span="12" :title="item.name">{{item.name}} </a-col>
+                    <a-col span="12" :title="item.name">{{item.name}}</a-col>
                     <a-col span="12" style="text-align: right;">
                       <a href="javascript:void (0)" @click="chooseProcess(item)">发起申请</a>
                     </a-col>
                   </a-row>
                 </div>
-                <b>版本:</b>v.{{item.version}}
+                <b>版本:</b>
+                v.{{item.version}}
                 <br />
-                <b>说明:</b>{{item.description}}
+                <b>说明:</b>
+                {{item.description}}
               </a-card>
             </a-list-item>
           </a-list>
         </a-collapse-panel>
       </a-collapse>
-      <k-form-design style="border:2px solid #ccc" :fields="typeList" :toolbars="['save', 'preview', 'reset', 'close' ]"
-        :showHead="false" toolbarsTop showToolbarsText @save="saveJson" />
     </div>
     <!--流程表单-->
-    <a-modal :title="lcModa.title" v-model="lcModa.visible" :footer="null" :maskClosable="false" width="80%">
-      <component :disabled="lcModa.disabled" v-if="lcModa.visible" :is="lcModa.formComponent"
-        :processData="lcModa.processData" :isNew="lcModa.isNew" @afterSubmit="afterSub"
-        @close="lcModa.visible=false,lcModa.disabled = false"></component>
+    <a-modal :title="lcModa.title" v-model="lcModa.visible" :maskClosable="false" width="80%">
+      <!-- <component
+        :disabled="lcModa.disabled"
+        v-if="lcModa.visible"
+        :is="lcModa.formComponent"
+        :processData="lcModa.processData"
+        :isNew="lcModa.isNew"
+        @afterSubmit="afterSub"
+        @close="lcModa.visible=false,lcModa.disabled = false"
+      ></component>-->
+      <template slot="footer">
+        <a-button @click="lcModa.visible=false">关闭</a-button>
+        <a-button key="back" @click="close">重置</a-button>
+        <a-button key="submit" type="primary" @click="handleOkAll" :loading="loadingElse">提交</a-button>
+      </template>
+      <k-form-build v-if="lcModa.visible" :defaultValue="defaultValue" :value="jsonData" ref="kfb" />
     </a-modal>
   </a-card>
-
 </template>
 
 <script>
-  import {
-    activitiMixin
-  } from '@/views/activiti/mixins/activitiMixin'
-  import JEllipsis from '@/components/jeecg/JEllipsis'
-  import JTreeSelect from '@/components/jeecg/JTreeSelect'
-  import {
-    initDictOptions,
-    filterDictText
-  } from '@/components/dict/JDictSelectUtil'
-  import historicDetail from '@/views/activiti/historicDetail'
-  export default {
-    name: "applyHome",
-    mixins: [activitiMixin],
-    components: {
-      JEllipsis,
-      JTreeSelect,
-      historicDetail
-    },
-    data() {
-      return {
-        typeList: ["input", "textarea", "number", "select", "checkbox", "radio", "date", "time", "rate", "slider",
-          "uploadFile", "uploadImg", "switch", "button",  "text",  "card", "grid", "table"
-        ],//"alert","html", "divider",
-        description: '所有',
-        dictOptions: [],
-        url: {
-          getProcessDataList: "/activiti_process/listData",
-          getFirstNode: '/actProcessIns/getFirstNode',
-          applyBusiness: '/actBusiness/apply',
-        },
-        // 查询条件
-        queryParam: {
-          createTimeRange: [],
-          keyWord: '',
+import { activitiMixin } from '@/views/activiti/mixins/activitiMixin'
+import JEllipsis from '@/components/jeecg/JEllipsis'
+import JTreeSelect from '@/components/jeecg/JTreeSelect'
+import { initDictOptions, filterDictText } from '@/components/dict/JDictSelectUtil'
+import historicDetail from '@/views/activiti/historicDetail'
+import { deleteAction, getAction, downFile,postAction } from '@/api/manage'
+export default {
+  name: 'applyHome',
+  mixins: [activitiMixin],
+  components: {
+    JEllipsis,
+    JTreeSelect,
+    historicDetail,
+  },
+  data() {
+    return {
+      loadingElse: false,
+      typeList: [
+        'input',
+        'textarea',
+        'number',
+        'select',
+        'checkbox',
+        'radio',
+        'date',
+        'time',
+        'rate',
+        'slider',
+        'uploadFile',
+        'uploadImg',
+        'switch',
+        'button',
+        'text',
+        'card',
+        'grid',
+        'table',
+      ], //"alert","html", "divider",
+      description: '所有',
+      dictOptions: [],
+      url: {
+        getProcessDataList: '/activiti_process/listData',
+        getFirstNode: '/actProcessIns/getFirstNode',
+        applyBusiness: '/actBusiness/apply',
+      },
+      // 查询条件
+      queryParam: {
+        createTimeRange: [],
+        keyWord: '',
+      },
+      // 表头
+      labelCol: {
+        xs: {
+          span: 4,
         },
-        // 表头
-        labelCol: {
-          xs: {
-            span: 4
-          },
-          sm: {
-            span: 4
-          },
+        sm: {
+          span: 4,
         },
-        wrapperCol: {
-          xs: {
-            span: 20
-          },
-          sm: {
-            span: 20
-          },
+      },
+      wrapperCol: {
+        xs: {
+          span: 20,
         },
-        processModalVisible: null,
-        activeKeyAll: [],
-        activeKey: [],
-        processDataMap: {},
-        searchProcessKey: null,
-        addApplyLoading: false,
-        lcModa: {
-          title: '',
-          disabled: false,
-          visible: false,
-          formComponent: null,
-          isNew: false
+        sm: {
+          span: 20,
         },
-      }
-    },
-    computed: {},
-    mounted() {
-      this.initDictConfig()
-      this.getProcessList()
-    },
-    methods: {
-      saveJson(json) {
-        console.log(json)
       },
-      initDictConfig() {
-        //初始化字典 - 流程分类
-        initDictOptions('bpm_process_type').then((res) => {
-          if (res.success) {
-            this.dictOptions = res.result;
-          }
-        });
+      processModalVisible: null,
+      activeKeyAll: [],
+      activeKey: [],
+      processDataMap: {},
+      searchProcessKey: null,
+      addApplyLoading: false,
+      lcModa: {
+        title: '',
+        disabled: false,
+        visible: false,
+        formComponent: null,
+        isNew: false,
       },
-      filterDictText(dictOptions, text) {
-        if (dictOptions instanceof Array) {
-          for (let dictItem of dictOptions) {
-            if (text === dictItem.value) {
-              return dictItem.text
-            }
+      jsonData: {},
+      defaultValue: {},
+    }
+  },
+  computed: {},
+  mounted() {
+    this.initDictConfig()
+    this.getProcessList()
+  },
+  methods: {
+    handleOkAll() {
+      this.loadingElse = true
+      var that = this
+      this.$refs.kfb
+        .getData()
+        .then((values) => {
+          console.log('验证通过', values,this.lcModa.processData)
+          var formData = {}
+          formData.tableId = this.lcModa.processData.customTableId
+          formData.procDefId = this.lcModa.processData.id
+          formData.procDeTitle = this.lcModa.processData.name
+          formData.formText = JSON.stringify(values)
+          postAction('/actBusiness/add', formData)
+            .then((res) => {
+              if (res.success) {
+                that.lcModa.visible=false
+                that.$message.success('保存成功!')
+                that.loadingElse = false
+              } else {
+                that.$message.error(res.message)
+                that.loadingElse = false
+              }
+            })
+            .finally(() => {
+              that.loadingElse = false
+            })
+        })
+    },
+    close() {
+      this.$refs.kfb.reset()
+    },
+    saveJson(json) {
+      console.log(json)
+    },
+    initDictConfig() {
+      //初始化字典 - 流程分类
+      initDictOptions('bpm_process_type').then((res) => {
+        if (res.success) {
+          this.dictOptions = res.result
+        }
+      })
+    },
+    filterDictText(dictOptions, text) {
+      if (dictOptions instanceof Array) {
+        for (let dictItem of dictOptions) {
+          if (text === dictItem.value) {
+            return dictItem.text
           }
         }
-        return text || text == 'null' ? '' : text
-      },
-      /*加载流程列表*/
-      getProcessList() {
-        this.addApplyLoading = true;
-        this.getAction(this.url.getProcessDataList, {
-          status: 1,
-          roles: true
-        }).then(res => {
-          this.activeKeyAll = [];
+      }
+      return text || text == 'null' ? '' : text
+    },
+    /*加载流程列表*/
+    getProcessList() {
+      this.addApplyLoading = true
+      this.getAction(this.url.getProcessDataList, {
+        status: 1,
+        roles: true,
+      })
+        .then((res) => {
+          this.activeKeyAll = []
           if (res.success) {
-            var result = res.result || [];
+            var result = res.result || []
             if (result.length > 0) {
-              let searchProcessKey = this.searchProcessKey;
-              if (searchProcessKey) { //过滤条件
+              let searchProcessKey = this.searchProcessKey
+              if (searchProcessKey) {
+                //过滤条件
                 result = _.filter(result, function (o) {
-                  return o.name.indexOf(searchProcessKey) > -1;
-                });
+                  return o.name.indexOf(searchProcessKey) > -1
+                })
               }
-              this.processDataMap = _.groupBy(result, 'categoryId');
+              this.processDataMap = _.groupBy(result, 'categoryId')
               for (const categoryId in this.processDataMap) {
                 this.activeKeyAll.push(categoryId)
               }
-              this.activeKey = this.activeKeyAll;
+              this.activeKey = this.activeKeyAll
             }
-            this.processModalVisible = true;
+            this.processModalVisible = true
           } else {
             this.$message.warning(res.message)
           }
-        }).finally(() => this.addApplyLoading = false);
-      },
-      onSearchProcess(value) {
-        this.searchProcessKey = value;
-        this.getProcessList()
-      },
-      chooseProcess(v) {
-        if (!v.routeName) {
-          this.$message.warning(
-            "该流程信息未配置表单,请联系开发人员!"
-          );
-          return;
-        }
-        this.lcModa.formComponent = this.getFormComponent(v.routeName).component;
-        this.lcModa.title = '发起流程:' + v.name;
-        this.lcModa.isNew = true;
-        this.lcModa.processData = v;
-        this.lcModa.visible = true;
-        console.log("发起", v)
-      },
-      /*提交成功申请后*/
-      afterSub(formData) {
-        this.lcModa.visible = false;
-        this.$message("请前往我的申请列表提交审批!")
-      },
-      /*前往我的申请页面*/
-      handleToApplyList() {
-        this.$router.push({
-          path: '/activiti/applyList'
         })
-      }
-    }
-  }
+        .finally(() => (this.addApplyLoading = false))
+    },
+    onSearchProcess(value) {
+      this.searchProcessKey = value
+      this.getProcessList()
+    },
+    chooseProcess(v) {
+      getAction('/ctop/actCustomForm/queryById', { id: v.customTableId }).then((res) => {
+        if (res.success) {
+          this.jsonData = JSON.parse(res.result.text)
+        } else {
+          this.$message.warning('该流程信息未配置表单,请联系开发人员!')
+          return
+        }
+      })
+      this.loadingElse = false
+      // this.lcModa.formComponent = this.getFormComponent(v.routeName).component
+      this.lcModa.title = '发起流程:' + v.name
+      this.lcModa.isNew = true
+      this.lcModa.processData = v
+      this.lcModa.visible = true
+      console.log('发起', v)
+    },
+    /*提交成功申请后*/
+    afterSub(formData) {
+      this.lcModa.visible = false
+      this.$message('请前往我的申请列表提交审批!')
+    },
+    /*前往我的申请页面*/
+    handleToApplyList() {
+      this.$router.push({
+        path: '/activiti/applyList',
+      })
+    },
+  },
+}
 </script>
 <style scoped>
-  @import '~@assets/less/common.less';
+@import '~@assets/less/common.less';
 </style>

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 504 - 387
src/views/activiti/applyList.vue


+ 268 - 0
src/views/activiti/customerForm.vue

@@ -0,0 +1,268 @@
+<style>
+.form-designer-container-9136076486841527 {
+  height: 55vh !important;
+}
+</style>
+<template>
+  <a-card :bordered="false">
+    <!-- 查询区域 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline">
+        <a-row :gutter="24">
+          <a-col :md="6" :sm="8">
+            <a-form-item label="表单名称">
+              <a-input placeholder="请输入表单名称" v-model="queryParam.name"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :md="6" :sm="8">
+            <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
+              <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
+              <a-button type="primary" @click="searchRe" icon="reload" style="margin-left: 8px">重置</a-button>
+            </span>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+
+    <!-- 操作按钮区域 -->
+    <div class="table-operator">
+      <a-button @click="handleAddForm" type="primary" icon="plus">新增</a-button>
+    </div>
+
+    <!-- table区域-begin -->
+    <div>
+      <a-table
+        ref="table"
+        size="middle"
+        bordered
+        rowKey="id"
+        :columns="columns"
+        :dataSource="dataSource"
+        :pagination="ipagination"
+        :loading="loading"
+        @change="handleTableChange"
+      >
+        <span slot="action" slot-scope="text, record">
+          <a @click="lookView(record)">预览</a>
+        </span>
+      </a-table>
+    </div>
+    <!-- table区域-end -->
+    <a-modal
+      :maskClosable="false"
+      title="表单新增"
+      v-model="addForm"
+      :footer="null"
+      :width="1200"
+      :okButtonProps="{ props: { disabled: false } }"
+      :cancelButtonProps="{ props: { disabled: false } }"
+    >
+      <a-form :form="form">
+        <a-form-item
+          label="表单名称"
+          :label-col="{ xs: { span: 24 }, sm: { span: 5 } }"
+          :wrapper-col="{ xs: { span: 24 }, sm: { span: 16 } }"
+        >
+          <a-input
+            v-decorator="[
+            'name',
+            {rules: [{ required: true, message: '请输入表单名称' }]}
+          ]"
+            placeholder="请输入表单名称"
+          />
+        </a-form-item>
+        <k-form-design
+          v-if="addForm"
+          style="border:2px solid #ccc"
+          :fields="typeList"
+          :toolbars="['save', 'preview', 'reset', 'close' ]"
+          :showHead="false"
+          toolbarsTop
+          showToolbarsText
+          @save="saveJson"
+        />
+      </a-form>
+    </a-modal>
+    <a-modal
+      :maskClosable="false"
+      title="表单预览"
+      v-model="viewLookVisible"
+      :footer="null"
+      :width="1200"
+      :okButtonProps="{ props: { disabled: false } }"
+      :cancelButtonProps="{ props: { disabled: false } }"
+    >
+      <k-form-build
+        v-if="viewLookVisible"
+        :defaultValue="defaultValue"
+        :value="jsonData"
+        ref="KFB"
+      />
+    </a-modal>
+
+    <!-- 表单区域 -->
+    <j-select-user ref="userForm" @ok="modalFormOk"></j-select-user>
+  </a-card>
+</template>
+
+<script>
+import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+import JTreeSelect from '@/components/jeecg/JTreeSelect'
+import AFormItem from 'ant-design-vue/es/form/FormItem'
+import JSelectDepart from '@/components/jeecgbiz/JSelectDepart'
+import JSelectUserByDep from '@/components/jeecgbiz/JSelectUserByDep'
+import JSelectUser from '@/components/jeecgbiz/JSelectUser'
+import { postAction } from '../../api/manage'
+import { mapActions, mapGetters } from 'vuex'
+
+export default {
+  name: 'AdvertiserList',
+  mixins: [JeecgListMixin],
+  components: {
+    AFormItem,
+    JTreeSelect,
+    JSelectUserByDep,
+    JSelectDepart,
+    JSelectUser,
+  },
+  data() {
+    return {
+      typeList: [
+        'input',
+        'textarea',
+        'number',
+        'select',
+        'checkbox',
+        'radio',
+        'date',
+        'time',
+        'rate',
+        'slider',
+        'uploadFile',
+        'uploadImg',
+        'switch',
+        'button',
+        'text',
+        'card',
+        'grid',
+        'table',
+      ], //"alert","html", "divider",
+      addForm: false,
+      viewLookVisible: false,
+      jsonData: {},
+      defaultValue: {},
+      departDisabled: false, //是否是我的部门调用该页面
+      users: this.userInfo().realname, //用户名称
+      departmentId: this.userInfo().departId, //部门id
+      description: '广告主信息表管理页面',
+      visible: false,
+      accountName: '',
+      password: '',
+      mode: 'login',
+      showLogin: true,
+      showAuthorization: false,
+      authorizationType: 'kuaishou',
+      loginType: 'kuaishou',
+      form: this.$form.createForm(this),
+      // 表头
+      columns: [
+        {
+          title: '表单名称',
+          align: 'center',
+          dataIndex: 'name',
+        },
+        {
+          title: '创建人',
+          align: 'center',
+          dataIndex: 'userId_dictText',
+        },
+        {
+          title: '操作',
+          dataIndex: 'action',
+          align: 'center',
+          scopedSlots: { customRender: 'action' },
+        },
+      ],
+      url: {
+        list: '/ctop/actCustomForm/list',
+        delete: '/ctop/advertiser/delete',
+        deleteBatch: '/ctop/advertiser/deleteBatch',
+        exportXlsUrl: 'ctop/advertiser/exportXls',
+        importExcelUrl: 'ctop/advertiser/importExcel',
+        bindAccountLoginUrl: '/ctop/bindAccountLogin/login',
+        bindAuthorizationUrl: '/ctop/bindAccountAuth/authorization',
+        getLoginInfo: '/ctop/bindAccount/getLoginInfo',
+        userId: '/sys/user/generateUserId', // 引入生成添加用户情况下的url
+        userWithDepart: '/sys/user/userDepartList', // 引入为指定用户查看部门信息需要的url
+      },
+    }
+  },
+  computed: {
+    importExcelUrl: function () {
+      return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`
+    },
+  },
+  created() {
+    this.queryParam.userId = this.userInfo().id
+  },
+  methods: {
+    lookView(item) {
+      this.viewLookVisible = true
+      this.jsonData = JSON.parse(item.text)
+    },
+    saveJson(json) {
+      console.log(json)
+      this.form.validateFields((err) => {
+        if (!err) {
+          var params = {}
+          params.name = this.getData('name')
+          params.text = json
+          postAction('/ctop/actCustomForm/add', params).then((res) => {
+            if (res.success) {
+              this.addForm = false
+              this.loadData(1)
+            }
+          })
+        }
+      })
+    },
+    handleAddForm() {
+      this.addForm = true
+    },
+    getData(className) {
+      return this.form.getFieldValue(className)
+    },
+    ...mapGetters(['nickname', 'avatar', 'userInfo']),
+    searchRe() {
+      this.queryParam.name = ''
+      this.queryParam.industryId = ''
+      this.loadData()
+    },
+    handleSubmit() {
+      // 触发表单验证
+    },
+
+    onChageLogin() {
+      console.log(this.mode)
+      if (this.mode == 'login') {
+        this.showLogin = true
+        this.showAuthorization = false
+      } else {
+        this.showLogin = false
+        this.showAuthorization = true
+      }
+    },
+    showModal(record) {
+      this.record = record
+      this.visible = true
+    },
+
+    handleCancel() {
+      this.close()
+    },
+  },
+}
+</script>
+<style scoped>
+@import '~@assets/less/common.less';
+</style>

+ 0 - 0
src/views/activiti/doneManage.vue


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels