Browse Source

添加消息中心

wuyunfeng 1 năm trước cách đây
mục cha
commit
856acfbcb3
42 tập tin đã thay đổi với 2175 bổ sung71 xóa
  1. 78 0
      languages/editorjs/zh-CN.js
  2. 32 0
      languages/message-center/zh-CN.js
  3. 4 0
      package.json
  4. 1 0
      public/domain.js
  5. 11 0
      public/index.html
  6. 59 0
      src/api/message_type.js
  7. 34 0
      src/components/EditorjsTools/AudioTool/index.css
  8. 145 0
      src/components/EditorjsTools/AudioTool/index.js
  9. 77 0
      src/components/EditorjsTools/ImageTool/index.css
  10. 211 0
      src/components/EditorjsTools/ImageTool/index.js
  11. 33 0
      src/components/EditorjsTools/VideoTool/index.css
  12. 156 0
      src/components/EditorjsTools/VideoTool/index.js
  13. 5 1
      src/components/FileManager/VueFinder.vue
  14. 1 1
      src/components/FileManager/components/Statusbar.vue
  15. 1 1
      src/components/FileManager/components/modals/ModalAbout.vue
  16. 28 12
      src/components/FileManager/components/modals/ModalMessage.vue
  17. 8 6
      src/components/FileManager/components/modals/ModalMove.vue
  18. 1 1
      src/components/FileManager/components/modals/ModalNewFile.vue
  19. 1 1
      src/components/FileManager/components/modals/ModalNewFolder.vue
  20. 1 1
      src/components/FileManager/components/modals/ModalRename.vue
  21. 3 3
      src/components/FileManager/locales/de.js
  22. 3 3
      src/components/FileManager/locales/en.js
  23. 3 3
      src/components/FileManager/locales/fa.js
  24. 3 3
      src/components/FileManager/locales/fr.js
  25. 3 3
      src/components/FileManager/locales/he.js
  26. 3 3
      src/components/FileManager/locales/hi.js
  27. 3 3
      src/components/FileManager/locales/ru.js
  28. 3 3
      src/components/FileManager/locales/sv.js
  29. 3 3
      src/components/FileManager/locales/tr.js
  30. 6 5
      src/components/FileManager/locales/zhCN.js
  31. 3 3
      src/components/FileManager/locales/zhTW.js
  32. 1 0
      src/icons/svg/messageList.svg
  33. 1 0
      src/icons/svg/messageType.svg
  34. 1 0
      src/main.js
  35. 30 0
      src/router/index.js
  36. 10 0
      src/utils/enum/MessageOpenTypeEnum.js
  37. 3 1
      src/utils/i18n.js
  38. 123 10
      src/views/article-manager/index.vue
  39. 421 0
      src/views/message-center/message-type/index.vue
  40. 413 0
      src/views/message-center/messages/index.vue
  41. 248 0
      src/views/message-center/messages/message-edit.vue
  42. 1 1
      src/views/ncs-device/components/deviceManager.vue

+ 78 - 0
languages/editorjs/zh-CN.js

@@ -0,0 +1,78 @@
+export default {
+
+  messages: {
+    'ui': {
+      'blockTunes': {
+        'toggler': {
+          'Click to tune': '打开调节器',
+          'or drag to move': '或拖拽移动'
+        }
+      },
+      'inlineToolbar': {
+        'converter': {
+          'Convert to': '转换为'
+        }
+      },
+      'toolbar': {
+        'toolbox': {
+          'Add': '添加'
+        }
+      },
+      'popover': {
+        'Filter': '过滤',
+        'Nothing found': '无匹配'
+      }
+    },
+
+    blockTunes: {
+      spoiler: {
+        'Hide content': '隐藏内容'
+      },
+      'delete': {
+        'Delete': '删除模块',
+        'Click to delete': '点击删除'
+      },
+      'moveUp': {
+        'Move up': '上移'
+      },
+      'moveDown': {
+        'Move down': '下移'
+      },
+      'indentTune': {
+        'Add Border': '添加边框'
+      }
+    },
+    toolNames: {
+      'Text': '段落',
+      'Heading': '标题',
+      'Title': '标题',
+      'List': '列表',
+      'Warning': '提示',
+      'Checklist': '可选列表',
+      'Quote': '引用',
+      'Code': '代码',
+      'Delimiter': '分隔符',
+      'Raw HTML': 'HTML代码',
+      'Table': '表格',
+      'Link': '链接',
+      'Marker': '标记',
+      'Bold': '加粗',
+      'Italic': '斜体',
+      'Alert': '警示',
+      'InlineCode': '内连代码',
+      'ImageTool': '图片',
+      'AudioPlayer': '音频',
+      'VideoPlayer': '视频'
+
+    },
+    tools: {
+      image: {
+        'Stretch Image': '拉升图片',
+        'Add Background': '添加背景',
+        'Add Border': '添加边框',
+        'Reselection Image': '重选图片'
+      }
+    }
+  }
+
+}

+ 32 - 0
languages/message-center/zh-CN.js

@@ -0,0 +1,32 @@
+module.exports = {
+
+  mc: {
+    messageCenter: '消息中心',
+    messages: '消息列表',
+    messageType: '消息分类',
+    messageTypeModule: {
+      typeName: '分类名称',
+
+      addType: '添加消息分类',
+      editType: '修改消息分类',
+      typeNameRequired: '分类名称必填'
+    },
+    messagesModule: {
+      addMessage: '添加消息',
+      title: '消息标题',
+      description: '消息摘要',
+      openType: '展示类型',
+      titleRequired: '消息标题必填',
+      openTypeRequired: '展示类型必填',
+      messageTypeRequired: '消息分类必填',
+
+    },
+    openTypeEnum: {
+      video: '视频',
+      audio: '音频',
+      images: '图集',
+      article: '图文'
+    }
+  }
+
+}

+ 4 - 0
package.json

@@ -25,6 +25,7 @@
     "@moefe/vue-aplayer": "^2.0.0-beta.5",
     "@toast-ui/editor": "^3.1.3",
     "@uppy/locales": "^3.5.2",
+    "@videojs/themes": "^1.0.1",
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^1.0.2",
     "ace-builds": "^1.32.7",
@@ -102,6 +103,7 @@
     "chalk": "2.4.2",
     "chokidar": "2.1.5",
     "connect": "3.6.6",
+    "editorjs-inline-image": "^2.0.0",
     "eslint": "^5.16.0",
     "eslint-plugin-vue": "^7.20.0",
     "exports-loader": "^1.1.0",
@@ -113,6 +115,7 @@
     "microtip": "^0.2.2",
     "mockjs": "1.0.1-beta3",
     "plop": "2.3.0",
+    "postcss-preset-env": "^9.5.2",
     "runjs": "4.3.2",
     "sass": "1.26.2",
     "sass-loader": "8.0.2",
@@ -121,6 +124,7 @@
     "svg-sprite-loader": "4.1.3",
     "svgo": "1.2.0",
     "tailwindcss": "^3.1.8",
+    "video.js": "^8.10.0",
     "vue-template-compiler": "2.6.10"
   },
   "browserslist": [

+ 1 - 0
public/domain.js

@@ -3,6 +3,7 @@ const domain = {
   DeviceUrl: 'http://192.168.1.198:8006',
   mediaUrl: 'http://192.168.1.198:8004',
   OnlineSystemUrl: 'http://api.base.wdklian.com',
+  gateWayUrl:'http://192.168.1.198:8889',
   apiMode: 'dev',
   uiVersion: 1, // 1 医院版,2 月子中心版,3养老院版
   enableBroadcast: true, //广播使能

+ 11 - 0
public/index.html

@@ -6,6 +6,17 @@
     <meta name="renderer" content="webkit">
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <!-- City -->
+    <link href="https://unpkg.com/@videojs/themes@1/dist/city/index.css" rel="stylesheet">
+
+    <!-- Fantasy -->
+    <link href="https://unpkg.com/@videojs/themes@1/dist/fantasy/index.css" rel="stylesheet">
+
+    <!-- Forest -->
+    <link href="https://unpkg.com/@videojs/themes@1/dist/forest/index.css" rel="stylesheet">
+
+    <!-- Sea -->
+    <link href="https://unpkg.com/@videojs/themes@1/dist/sea/index.css" rel="stylesheet">
     <script src="/domain.js" ></script>
       <!--<script src="/janus.js" ></script>-->
       <!--<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/6.4.0/adapter.min.js" ></script>-->

+ 59 - 0
src/api/message_type.js

@@ -0,0 +1,59 @@
+import request from '@/utils/request'
+
+/**
+ * 消息类型接口
+ * @param params
+ * @returns {*|Promise|Promise<unknown>}
+ */
+export function getList(params) {
+  return request({
+    baseURL: domain.gateWayUrl,
+    url: '/message-center/message-type/page',
+    method: 'POST',
+    loading: true,
+    data: params,
+    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+  })
+}
+
+/** 新增消息类型 */
+export function add(params) {
+  return request({
+    baseURL: domain.gateWayUrl,
+    url: '/message-center/message-type',
+    method: 'POST',
+    loading: true,
+    data: params
+  })
+}
+
+/** 删除消息类型 */
+export function remove(params) {
+  const ids = params.toString()
+  return request({
+    baseURL: domain.gateWayUrl,
+    url: `/message-center/message-type/${ids}`,
+    method: 'DELETE',
+    loading: true,
+    data: params
+  })
+}
+
+/** 更新消息类型 */
+export function update(id, params) {
+  return request({
+    baseURL: domain.gateWayUrl,
+    url: `/message-center/message-type/${id}`,
+    method: 'put',
+    data: params
+  })
+}
+
+/** 更新消息类型 */
+export function getAll() {
+  return request({
+    baseURL: domain.gateWayUrl,
+    url: `/message-center/message-type/all`,
+    method: 'get'
+  })
+}

+ 34 - 0
src/components/EditorjsTools/AudioTool/index.css

@@ -0,0 +1,34 @@
+.wrapper {
+     display: flex;
+     flex-direction: column;
+     align-items: center;
+     min-height: 3rem;
+     justify-content: space-around;
+ }
+.wrapper .input {
+    background-color: aliceblue;
+}
+
+.wrapper input {
+    width: 100%;
+    padding: 10px;
+    border: 1px solid #e4e4e4;
+    border-radius: 3px;
+    outline: none;
+    font-size: 14px;
+}
+.cdx-plus-holder{
+    background-color: #fbfdff;
+    border: 1px dashed #c0ccda;
+    border-radius: 6px;
+    box-sizing: border-box;
+    width: 100%;
+    height: 50px;
+    cursor: pointer;
+    text-align: center;
+    line-height: 50px;
+    vertical-align: top;
+}
+.cdx-plus-holder .el-icon-plus{
+    font-style: normal;
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 145 - 0
src/components/EditorjsTools/AudioTool/index.js


+ 77 - 0
src/components/EditorjsTools/ImageTool/index.css

@@ -0,0 +1,77 @@
+.cdx-simple-image {
+}
+
+.el-loading-mask .el-loading-spinner{
+    font-size: 14px;
+}
+.cdx-plus-holder{
+    background-color: #fbfdff;
+    border: 1px dashed #c0ccda;
+    border-radius: 6px;
+    box-sizing: border-box;
+    width: 100%;
+    height: 50px;
+    cursor: pointer;
+    text-align: center;
+    line-height: 50px;
+    vertical-align: top;
+}
+.cdx-plus-holder .el-icon-plus{
+    font-style: normal;
+}
+.cdx-simple-image__picture {
+     max-width: 100%;
+     vertical-align: bottom;
+     display: block;
+    font-size: 0;
+ }
+.cdx-simple-image .cdx-loader {
+    min-height: 200px;
+}
+
+.cdx-simple-image .cdx-input {
+    margin-top: 10px;
+}
+
+.cdx-simple-image img {
+    max-width: 100%;
+    vertical-align: bottom;
+}
+
+.cdx-simple-image__caption[contentEditable=true][data-placeholder]:empty::before {
+    position: absolute;
+    content: attr(data-placeholder);
+    color: #707684;
+    font-weight: normal;
+    opacity: 0;
+}
+
+.cdx-simple-image__caption[contentEditable=true][data-placeholder]:empty::before {
+    opacity: 1;
+}
+
+.cdx-simple-image__caption[contentEditable=true][data-placeholder]:empty:focus::before {
+    opacity: 0;
+}
+
+
+.cdx-simple-image__picture--with-background {
+    background: #eff2f5;
+    padding: 10px;
+}
+
+.cdx-simple-image__picture--with-background img {
+    display: block;
+    max-width: 60%;
+    margin: 0 auto;
+}
+
+.cdx-simple-image__picture--with-border {
+    border: 1px solid #e8e8eb;
+    padding: 1px;
+}
+
+.cdx-simple-image__picture--stretched img {
+    max-width: none;
+    width: 100%;
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 211 - 0
src/components/EditorjsTools/ImageTool/index.js


+ 33 - 0
src/components/EditorjsTools/VideoTool/index.css

@@ -0,0 +1,33 @@
+.wrapper {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: space-around;
+}
+.wrapper .input {
+    background-color: aliceblue;
+}
+
+.wrapper input {
+    width: 100%;
+    padding: 10px;
+    border: 1px solid #e4e4e4;
+    border-radius: 3px;
+    outline: none;
+    font-size: 14px;
+}
+.cdx-plus-holder{
+    background-color: #fbfdff;
+    border: 1px dashed #c0ccda;
+    border-radius: 6px;
+    box-sizing: border-box;
+    width: 100%;
+    height: 50px;
+    cursor: pointer;
+    text-align: center;
+    line-height: 50px;
+    vertical-align: top;
+}
+.cdx-plus-holder .el-icon-plus{
+    font-style: normal;
+}

+ 156 - 0
src/components/EditorjsTools/VideoTool/index.js

@@ -0,0 +1,156 @@
+import { IconPlay } from '@codexteam/icons'
+import './index.css'
+import videojs from 'video.js'
+import 'video.js/dist/video-js.css'
+export default class VideoTool {
+  constructor({ data, block, config }) {
+    /**
+         * Nodes cache
+         */
+
+    this.nodes = {
+      wrapper: null,
+      video: null,
+      plusHolder: null,
+      popOver: null,
+      videoSource: null
+    }
+
+    this.data = {
+      src: data.src || '',
+      withBorder: data.withBorder !== undefined ? data.withBorder : false,
+      withBackground: data.withBackground !== undefined ? data.withBackground : false,
+      stretched: data.stretched !== undefined ? data.stretched : false
+
+    }
+    console.log('thisdata', this.data)
+    this.config = config
+    this.block = block
+
+  }
+
+  static get toolbox() {
+    return {
+      title: 'VideoPlayer',
+      icon: IconPlay
+    }
+  }
+
+  changeImageUrl(url) {
+    this.data = {
+      src: url
+    }
+  }
+  render() {
+    this.nodes.wrapper = this._make('div', '', { style: 'height:100px;' })
+    this.nodes.video = this._make('video', 'video-js', { style: 'display:none;width:100%;', id: `vedio_${this.block.id}` })
+    this.nodes.videoSource = this._make('source')
+    this.nodes.video.appendChild(this.nodes.videoSource)
+    this.nodes.wrapper.classList.add('wrapper')
+    this.nodes.video.setAttribute('controls', 'controls')
+    this.nodes.plusHolder = this._make('div', 'cdx-plus-holder', {
+      onclick: () => {
+        this.config.onChoose(this.block.id)
+      }
+    })
+    const plus = this._make('i', ['el-icon-plus'], {})
+    this.nodes.plusHolder.appendChild(plus)
+    // this.nodes.input.type = 'text'
+    // this.nodes.input.name = 'audioUrl'
+    // this.nodes.input.placeholder = 'Enter your url of audio file'
+    // this.nodes.input.value = this.data.src ? this.data.src : ''
+    // this.nodes.input.classList.add('input')
+    // this.nodes.input.addEventListener('change', () => {
+    //   const src = this.nodes.input?.value
+    //   if (src) {
+    //     this.nodes.audio.setAttribute('src', src)
+    //   } else {
+    //     this.nodes.audio.removeAttribute('src')
+    //   }
+    // })
+    if (this.data.src) {
+      this.nodes.videoSource.setAttribute('src', this.data.src)
+    }
+    // const { src } = this.data
+    // if (src) {
+    //
+    // }
+
+    // this.nodes.wrapper.appendChild(this.nodes.input)
+    this.nodes.wrapper.appendChild(this.nodes.video)
+    this.nodes.wrapper.appendChild(this.nodes.plusHolder)
+
+    if (this.data.src) {
+      this.nodes.videoSource.src = this.data.src
+      this.nodes.plusHolder.style.display = 'none'
+      this.nodes.video.style.display = 'block'
+      this.nodes.wrapper.style.height='500px'
+      setTimeout(() => {
+        videojs(`vedio_${this.block.id}`)
+      },0)
+      console.log(this.nodes.video)
+    } else {
+      this.nodes.video.style.display = 'none'
+      this.nodes.wrapper.style.height='100px'
+      this.nodes.plusHolder.style.display = 'block'
+    }
+
+    return this.nodes.wrapper
+  }
+
+  save(blockContent) {
+    const src = blockContent.querySelector('video').src
+    return {
+      src
+    }
+  }
+
+  validate(savedData) {
+    if (savedData.src === '' && !savedData.src.trim()) {
+      return false
+    }
+
+    return true
+  }
+
+  _make(tagName, classNames = null, attributes = {}) {
+    const el = document.createElement(tagName)
+
+    if (Array.isArray(classNames)) {
+      el.classList.add(...classNames)
+    } else if (classNames) {
+      el.classList.add(classNames)
+    }
+
+    for (const attrName in attributes) {
+      el[attrName] = attributes[attrName]
+    }
+
+    return el
+  }
+
+  get data() {
+    return this._data
+  }
+  static get isReadOnlySupported() {
+    return true
+  }
+  set data(data) {
+    this._data = Object.assign({}, this.data, data)
+    if (this.nodes.video && this.nodes.videoSource) {
+      if (this._data.src) {
+        this.nodes.videoSource.src = this._data.src
+        this.nodes.plusHolder.style.display = 'none'
+        this.nodes.video.style.display = 'block'
+        this.nodes.wrapper.style.height = '500px'
+        videojs(`vedio_${this.block.id}`)
+        console.log(this.nodes.video)
+      } else {
+        this.nodes.video.style.display = 'none'
+        this.nodes.plusHolder.style.display = 'block'
+        this.nodes.wrapper.style.height = '100px'
+      }
+    }
+  }
+
+}

+ 5 - 1
src/components/FileManager/VueFinder.vue

@@ -3,7 +3,7 @@
         <div :class="app.theme === 'dark' ? 'dark': ''">
             <div
                     :class="app.fullScreen ? 'fixed w-screen inset-0 z-20' : 'relative rounded-md'"
-                    :style="{'height':(this.mainAreaHeight)+'px'}"
+                    :style="{'height':(height||this.mainAreaHeight)+'px'}"
                     class="border flex flex-col bg-white dark:bg-gray-800 text-gray-700 dark:text-neutral-400 border-neutral-300 dark:border-gray-900 min-w-min select-none"
                     @mousedown="mouseDown"
                     @touchstart="mouseUp">
@@ -73,6 +73,10 @@
                 type: String,
                 default: '600px'
             },
+            height:{
+                type:Number,
+                default:null
+            },
             maxFileSize: {
                 type: String,
                 default: '10mb'

+ 1 - 1
src/components/FileManager/components/Statusbar.vue

@@ -17,7 +17,7 @@
 
             <div class="ml-3">
                 <span v-if="searchQuery.length">{{ this.ServiceContainer.data.paths.length }} items found. </span>
-                <span class="ml-1">{{ selectedItemCount > 0 ? this.$t(selectedItemCount+' item(s) selected.') : '' }}</span>
+                <span class="ml-1">{{ selectedItemCount > 0 ? this.$t('{0} item(s) selected.',[selectedItemCount]) : '' }}</span>
             </div>
         </div>
         <div class="flex leading-5 items-center justify-end">

+ 1 - 1
src/components/FileManager/components/modals/ModalAbout.vue

@@ -12,7 +12,7 @@
       </div>
       <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
         <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-400" id="modal-title">
-          {{ t('About %s', 'Vuefinder ' + app.version ) }}</h3>
+          {{ t('About {0}', [app.version] ) }}</h3>
         <div class="mt-2">
 
           <p class="text-sm text-gray-500">{{ t('Vuefinder is a file manager component for vue 3.') }}</p>

+ 28 - 12
src/components/FileManager/components/modals/ModalMessage.vue

@@ -1,5 +1,5 @@
 <template>
- <v-f-modal-layout>
+ <modal-layout>
     <div class="sm:flex sm:items-start ">
       <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-50 dark:bg-gray-500 sm:mx-0 sm:h-10 sm:w-10">
         <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 stroke-blue-600 dark:stroke-blue-100" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
@@ -7,30 +7,46 @@
         </svg>
       </div>
       <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
-        <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-400" id="modal-title">{{  app.modal.data?.title ?? 'Title' }}</h3>
+        <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-400" id="modal-title">{{  ServiceContainer.modal.data.title}}</h3>
         <div class="mt-2">
-          <p class="text-sm text-gray-500">{{  app.modal.data?.message ?? 'Message' }}</p>
+          <p class="text-sm text-gray-500">{{  ServiceContainer.modal.data.message}}</p>
         </div>
       </div>
     </div>
 
     <template v-slot:buttons>
-      <button type="button" @click="app.emitter.emit('vf-modal-close')" class="vf-btn vf-btn-secondary">
+      <button type="button" @click="close" class="vf-btn vf-btn-secondary">
         {{ t('Close') }}</button>
     </template>
-  </v-f-modal-layout>
+  </modal-layout>
 </template>
 
 <script>
+    import ModalLayout from "./ModalLayout";
 export default {
-  name: 'VFModalMessage'
+  name: 'ModalMessage',
+    components: {ModalLayout},
+    inject: ['ServiceContainer'],
+    data(){
+      return{
+
+      }
+    },
+    methods:{
+      t(str,...arg){
+          return this.$t(str,arg)
+      },
+        close(){
+        this.$bus.$emit('vf-modal-close')
+        }
+    }
 };
 </script>
 
-<script setup>
-import VFModalLayout from './ModalLayout.vue';
-import {inject} from 'vue';
-const app = inject('ServiceContainer');
-const {t} = app.i18n;
+<!--<script setup>-->
+<!--import VFModalLayout from './ModalLayout.vue';-->
+<!--import {inject} from 'vue';-->
+<!--const app = inject('ServiceContainer');-->
+<!--const {t} = app.i18n;-->
 
-</script>
+<!--</script>-->

+ 8 - 6
src/components/FileManager/components/modals/ModalMove.vue

@@ -24,7 +24,7 @@
             <div class="ml-1.5">{{ node.name }}</div>
           </div>
         </div>
-          <h4 class="font-bold text-xs text-gray-700 dark:text-gray-500 mt-3 tracking-wider">{{ 'Target Directory'}}</h4>
+          <h4 class="font-bold text-xs text-gray-700 dark:text-gray-500 mt-3 tracking-wider">{{ t('Target Directory')}}</h4>
           <p class="flex text-sm text-gray-800 dark:text-gray-400 border dark:border-gray-700 p-1 rounded">
              <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-neutral-500 fill-sky-500 stroke-sky-500 dark:fill-slate-500 dark:stroke-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1">
                 <path stroke-linecap="round" stroke-linejoin="round" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
@@ -43,7 +43,8 @@
         {{ t('Cancel') }}</button>
 
       <div class="m-1 mr-auto font-bold text-gray-500 text-sm dark:text-gray-200 self-center">
-        {{ `${items.length} item(s) selected.`  }}
+<!--        {{ t(''${items.length} item(s) selected.''  }}-->
+        {{ t('{0} item(s) selected.',[items.length])}}
       </div>
     </template>
   </ModalLayout>
@@ -54,15 +55,16 @@
 <script>
 
   import ModalLayout from "./ModalLayout";
+  import Message from './ModalMessage'
   export default {
     name: "ModalMove",
-    components: {ModalLayout},
+    components: {ModalLayout,Message},
     inject: ['ServiceContainer'],
     data(){
       return{
         items:this.ServiceContainer.modal.data.items.from,
         toItem:this.ServiceContainer.modal.data.items.to,
-        message:''
+        message:'aaa'
       }
     },
     mounted() {
@@ -106,8 +108,8 @@
         console.log(this)
         this.$bus.$emit('vf-modal-close')
       },
-      t(item){
-        return this.$t(item)
+      t(item,...arg){
+        return this.$t(item,arg)
       }
 
     }

+ 1 - 1
src/components/FileManager/components/modals/ModalNewFile.vue

@@ -62,7 +62,7 @@ const createFile = () => {
         name: name.value
       },
       onSuccess: () => {
-        app.emitter.emit('vf-toast-push', {label: t('%s is created.', name.value)});
+        app.emitter.emit('vf-toast-push', {label: t('{0} is created.', [name.value])});
       },
       onError: (e) => {
         message.value = t(e.message);

+ 1 - 1
src/components/FileManager/components/modals/ModalNewFolder.vue

@@ -67,7 +67,7 @@ export default {
           //   name: name.value
           // },
           onSuccess: () => {
-            this.$bus.$emit('vf-toast-push', {label: this.t('%s is created.', name.value)});
+            this.$bus.$emit('vf-toast-push', {label: this.t('{0} is created.', [name.value])});
             this.$bus.$emit('vf-fetch', {url:`${this.ServiceContainer.data.dirname==='/'?'':this.ServiceContainer.data.dirname}`,params:{json:''}});
           },
           onError: (e) => {

+ 1 - 1
src/components/FileManager/components/modals/ModalRename.vue

@@ -76,7 +76,7 @@ export default {
           headers:{'Destination':encodeURI(`${this.ServiceContainer.modal.data.items.href.replace(this.ServiceContainer.modal.data.items.name,this.name)}`)},
           responseType:'text',
           onSuccess: () => {
-            this.$bus.$emit('vf-toast-push', {label: this.$t('%s is renamed.', name.value)});
+            this.$bus.$emit('vf-toast-push', {label: this.$t('{0} is renamed.', [name.value])});
             this.$bus.$emit('vf-fetch', {url:`${this.ServiceContainer.data.dirname==='/'?'':this.ServiceContainer.data.dirname}`,params:{json:''}});
           },
           onError: (e) => {

+ 3 - 3
src/components/FileManager/locales/de.js

@@ -49,10 +49,10 @@ export default {
     "Are you sure you want to delete these files?": "Sind Sie sicher, dass Sie diese Dateien löschen möchten?",
     "This action cannot be undone.": "Diese Aktion kann nicht rückgängig gemacht werden.",
     "Search results for": "Suchergebnisse für",
-    "%s item(s) selected.": "%s Element(e) ausgewählt.",
-    "%s is renamed.": "%s wurde umbenannt.",
+    "{0} item(s) selected.": "{0} Element(e) ausgewählt.",
+    "{0} is renamed.": "{0} wurde umbenannt.",
     "This is a readonly storage.": "Dies ist ein schreibgeschützter Speicher.",
-    "%s is created.": "%s wurde erstellt.",
+    "{0} is created.": "{0} wurde erstellt.",
     "Files moved.": "Dateien verschoben.",
     "Files deleted.": "Dateien gelöscht.",
     "The file unarchived.": "Die Datei wurde dearchiviert.",

+ 3 - 3
src/components/FileManager/locales/en.js

@@ -49,10 +49,10 @@ export default {
   "Are you sure you want to delete these files?": "Are you sure you want to delete these files?",
   "This action cannot be undone.": "This action cannot be undone.",
   "Search results for" : "Search results for",
-  "%s item(s) selected.": "%s item(s) selected.",
-  "%s is renamed." : "%s is renamed.",
+  "{0} item(s) selected.": "{0} item(s) selected.",
+  "{0} is renamed." : "{0} is renamed.",
   "This is a readonly storage." : "This is a readonly storage.",
-  "%s is created." : "%s is created.",
+  "{0} is created." : "{0} is created.",
   "Files moved." : "Files moved.",
   "Files deleted." : "Files deleted.",
   "The file unarchived." : "The file unarchived.",

+ 3 - 3
src/components/FileManager/locales/fa.js

@@ -49,10 +49,10 @@ export default {
   "Are you sure you want to delete these files?": "آیا از حذف فایل ها مطمئن هستید ؟",
   "This action cannot be undone.": "این تغییرات قابل بازگشت نیست!",
   "Search results for" : "نتیجه جستجو برای",
-  "%s item(s) selected.": "%s آیتم(های) انتخاب شده",
-  "%s is renamed." : "تغییر نام برای %s صورت گرفت.",
+  "{0} item(s) selected.": " آیتم(های) انتخاب شده{0}",
+  "{0} is renamed." : "تغییر نام برای %s صورت گرفت.",
   "This is a readonly storage." : "این فضا فقط قابل خواندن است!",
-  "%s is created." : "%s ساخته شد!",
+  "{0} is created." : "{0} ساخته شد!",
   "Files moved." : "فایل(ها) انقال یافتند.",
   "Files deleted." : "فایل(ها) حذف شدند.",
   "The file unarchived." : "فایل فشرده شده باز شد.",

+ 3 - 3
src/components/FileManager/locales/fr.js

@@ -50,10 +50,10 @@ export default {
   "Are you sure you want to delete these files?": "Êtes-vous sûr de vouloir supprimer ces fichiers?",
   "This action cannot be undone.": "Cette action ne peut pas être annulée.",
   "Search results for" : "Résultats de recherche pour",
-  "%s item(s) selected.": "%s élément(s) sélectionné(s).",
-  "%s is renamed." : "%s est renommé.",
+  "{0} item(s) selected.": "{0} élément(s) sélectionné(s).",
+  "{0} is renamed." : "{0} est renommé.",
   "This is a readonly storage." : "C'est un stockage en lecture seule.",
-  "%s is created." : "%s est créé.",
+  "{0} is created." : "{0} est créé.",
   "Files moved." : "Fichiers déplacés.",
   "Files deleted." : "Fichiers supprimés.",
   "The file unarchived." : "Le fichier désarchivé.",

+ 3 - 3
src/components/FileManager/locales/he.js

@@ -49,10 +49,10 @@ export default {
   "Are you sure you want to delete these files?": "האם בטוח למחוק קבצים אלה?",
   "This action cannot be undone.": "לא ניתן לבטל פעולה זו.",
   "Search results for": "תוצאות חיפוש עבור",
-  "%s item(s) selected.": "%s פריט(ים) נבחרו.",
-  "%s is renamed.": "השם של %s השתנה.",
+  "{0} item(s) selected.": "{0} פריט(ים) נבחרו.",
+  "{0} is renamed.": "השם של %s השתנה.",
   "This is a readonly storage.": "זהו אחסון לקריאה בלבד.",
-  "%s is created.": "%s נוצר",
+  "{0} is created.": "{0} נוצר",
   "Files moved.": "הקבצים הועברו.",
   "Files deleted.": "הקבצים נמחקו",
   "The file unarchived.": "הקבצים חולצו.",

+ 3 - 3
src/components/FileManager/locales/hi.js

@@ -49,10 +49,10 @@ export default {
   "Are you sure you want to delete these files?": "क्या आप वाकई इन फ़ाइलों को हटाना चाहते हैं?",
   "This action cannot be undone.": "इस क्रिया को पूर्वस्थित नहीं किया जा सकता है।",
   "Search results for": "के लिए खोज परिणाम",
-  "%s item(s) selected.": "%s आइटम(आइटम) चयनित।",
-  "%s is renamed.": "%s का नाम बदला गया है।",
+  "{0} item(s) selected.": "{0} आइटम(आइटम) चयनित।",
+  "{0} is renamed.": "{0} का नाम बदला गया है।",
   "This is a readonly storage.": "यह एक केवल पठनीय संग्रह है।",
-  "%s is created.": "%s बनाया गया है।",
+  "{0} is created.": "{0} बनाया गया है।",
   "Files moved.": "फ़ाइलें मूव की गईं।",
   "Files deleted.": "फ़ाइलें हटा दी गईं।",
   "The file unarchived.": "फ़ाइल अनआर्काइव की गई है।",

+ 3 - 3
src/components/FileManager/locales/ru.js

@@ -49,10 +49,10 @@ export default {
   "Are you sure you want to delete these files?": "Вы уверены, что хотите удалить эти файлы?",
   "This action cannot be undone.": "Это действия нельзя отменить.",
   "Search results for": "Результаты поиска по",
-  "%s item(s) selected.": "%s выбраны.",
-  "%s is renamed.": "%s переименован.",
+  "{0} item(s) selected.": "{0} выбраны.",
+  "{0} is renamed.": "{0} переименован.",
   "This is a readonly storage.": "Данное хранилище только для чтения.",
-  "%s is created.": "%s создан.",
+  "{0} is created.": "{0} создан.",
   "Files moved.": "Файлы перемещены.",
   "Files deleted.": "Файлы удалены.",
   "The file unarchived.": "Файл разархивирован",

+ 3 - 3
src/components/FileManager/locales/sv.js

@@ -49,10 +49,10 @@ export default {
   "Are you sure you want to delete these files?": "Är du säker på att du vill radera dessa filer?",
   "This action cannot be undone.": "Denna åtgärd kan inte ångras.",
   "Search results for" : "Sökresultat för",
-  "%s item(s) selected.": "%s objekt valda.",
-  "%s is renamed." : "%s har fått ett nytt namn.",
+  "{0} item(s) selected.": "{0} objekt valda.",
+  "{0} is renamed." : "{0} har fått ett nytt namn.",
   "This is a readonly storage." : "Detta är en skrivskyddad lagring.",
-  "%s is created." : "%s har skapats.",
+  "{0} is created." : "{0} har skapats.",
   "Files moved." : "Filerna har flyttats.",
   "Files deleted." : "Filerna har raderats.",
   "The file unarchived." : "Filen har avarkiverats.",

+ 3 - 3
src/components/FileManager/locales/tr.js

@@ -49,10 +49,10 @@ export default {
   "Are you sure you want to delete these files?": "Bu dosyaları silmek istediğinize emin misiniz?",
   "This action cannot be undone.": "Bu işlem geri alınamaz.",
   "Search results for": "Arama sonuçları:",
-  "%s item(s) selected.": "%s dosya seçildi.",
-  "%s is renamed." : "%s yeniden adlandırılmıştır.",
+  "{0} item(s) selected.": "{0} dosya seçildi.",
+  "{0} is renamed." : "{0} yeniden adlandırılmıştır.",
   "This is a readonly storage." : "Bu salt okunur bir depolama alanıdır.",
-  "%s is created." : "%s başarıyla oluşturulmuştur.",
+  "{0} is created." : "{0} başarıyla oluşturulmuştur.",
   "Files moved." : "Dosyalar taşındı.",
   "Files deleted." : "Dosyalar silindi.",
   "The file is unarchived." : "Dosya arşivden çıkarıldı.",

+ 6 - 5
src/components/FileManager/locales/zhCN.js

@@ -49,10 +49,10 @@ export default {
   "Are you sure you want to delete these files?": "您确定要删除这些文件吗?",
   "This action cannot be undone.": "此操作不能撤销。",
   "Search results for" : "搜索结果为",
-  "%s item(s) selected.": "%s 个文件 已选择。",
-  "%s is renamed." : "%s 已重命名。",
+  "{0} item(s) selected.": "{0} 个文件 已选择。",
+  "{0} is renamed." : "{0} 已重命名。",
   "This is a readonly storage." : "这是只读存储。",
-  "%s is created." : "%s 已创建。",
+  "{0} is created." : "{0} 已创建。",
   "Files moved." : "文件已移动。",
   "Files deleted." : "文件已删除。",
   "The file unarchived." : "文件已解压。",
@@ -74,7 +74,7 @@ export default {
   "Network Error, Unable establish connection to the server or interrupted.": "网络错误,无法连接到服务器或连接被意外中断。",
   "Pending upload": "待上传",
   "Please select file to upload first." : "请先选择要上传的文件。",
-  "About %s": "关于 %s",
+  "About {0}": "关于 {0}",
   "Settings": "设置",
   "Use Metric Units": "使用公制单位",
   "Saved.": "已保存。",
@@ -85,5 +85,6 @@ export default {
   "Light": "浅色",
   "System": "系统",
   "Target Directory": "目标目录",
-  "uppy": uppyLocalezhCN
+  "uppy": uppyLocalezhCN,
+  'Select':'选择'
 }

+ 3 - 3
src/components/FileManager/locales/zhTW.js

@@ -49,10 +49,10 @@ export default {
   "Are you sure you want to delete these files?": "您確定要刪除這些檔案嗎?",
   "This action cannot be undone.": "此操作不能撤銷。",
   "Search results for" : "搜尋結果為",
-  "%s item(s) selected.": "%s 個檔案 已選取。",
-  "%s is renamed." : "%s 已重新命名。",
+  "{0} item(s) selected.": "{0} 個檔案 已選取。",
+  "{0} is renamed." : "{0} 已重新命名。",
   "This is a readonly storage." : "這是只讀存儲器。",
-  "%s is created." : "%s 已創建。",
+  "{0} is created." : "{0} 已創建。",
   "Files moved." : "檔案已移動。",
   "Files deleted." : "檔案已刪除。",
   "The file unarchived." : "檔案已解壓。",

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 0
src/icons/svg/messageList.svg


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 0
src/icons/svg/messageType.svg


+ 1 - 0
src/main.js

@@ -4,6 +4,7 @@ import Cookies from 'js-cookie'
 
 import 'normalize.css/normalize.css' // a modern alternative to CSS resets
 
+
 import Element from 'element-ui'
 import './styles/element-variables.scss'
 import enLang from 'element-ui/lib/locale/lang/en'// 如果使用中文语言包请默认支持,无需额外引入,请删除该依赖

+ 30 - 0
src/router/index.js

@@ -345,6 +345,36 @@ export const partRoutes = [
       }
     ]
   },
+  // 消息中心
+  {
+    path: '/message-center',
+    component: Layout,
+    // redirect: '/interaction/history',
+    name: 'message-center',
+    meta: { title: i18n.t('mc.messageCenter'), icon: 'el-icon-message', noCache: true },
+    children: [
+      {
+        path: 'message-type',
+        component: () => import('@/views/message-center/message-type/index'),
+        name: 'message-type',
+        meta: { title: i18n.t('mc.messageType'), icon: 'messageType', noCache: true }
+      },
+      {
+        path: 'messages',
+        component: () => import('@/views/message-center/messages/index'),
+        name: 'messages',
+        meta: { title: i18n.t('mc.messages'), icon: 'messageList', noCache: true }
+      },
+      {
+        path: 'messages-edit/:id?',
+        component: () => import('@/views/message-center/messages/message-edit'),
+        name: 'message-edit',
+        meta: { title: i18n.t('mc.messagesModule.addMessage'), icon: 'messageList', noCache: true },
+        hidden: true
+      }
+    ]
+  },
+
   // 广播
   {
     path: '/broadcast',

+ 10 - 0
src/utils/enum/MessageOpenTypeEnum.js

@@ -0,0 +1,10 @@
+import createEnum from '@/utils/enum/createEnum'
+import i18n from '@/utils/i18n'
+export const MESSAGE_OPEN_TYPE = createEnum(
+  {
+    VIDEO: ['VIDEO', i18n.t('mc.openTypeEnum.video')],
+    AUDIO: ['AUDIO', i18n.t('mc.openTypeEnum.audio')],
+    IMAGES: ['IMAGES', i18n.t('mc.openTypeEnum.images')],
+    ARTICLE: ['ARTICLE', i18n.t('mc.openTypeEnum.article')]
+  }
+)

+ 3 - 1
src/utils/i18n.js

@@ -7,6 +7,8 @@ import finderZhLang from '@/components/FileManager/locales/zhCN'
 import finderEnLang from '@/components/FileManager/locales/en'
 import finderEsLang from '@/components/FileManager/locales/sv'
 import finderRuLang from '@/components/FileManager/locales/ru'
+import editjsZhLang from '../../languages/editorjs/zh-CN'
+import messageCenterZhLang from '../../languages/message-center/zh-CN'
 import VueI18n from 'vue-i18n'
 import locale from 'element-ui/lib/locale'
 import Storage from '@/utils/storage'
@@ -18,7 +20,7 @@ const i18n = new VueI18n({
   locale: Storage.getItem('DefaultLanguage') == null ? 'zh' : Storage.getItem('DefaultLanguage'),
   // 添加多语言(每一个语言标示对应一个语言文件)
   messages: {
-    'zh': Object.assign(require('../../languages/zh-CN'), zhLang, finderZhLang),
+    'zh': Object.assign(require('../../languages/zh-CN'), zhLang, finderZhLang, editjsZhLang,messageCenterZhLang),
     'en': Object.assign(require('../../languages/en'), enLang, finderEnLang),
     'es': Object.assign(require('../../languages/es'), esLang, finderEsLang),
     'ru': Object.assign(require('../../languages/ru-RU'), ruLang, finderRuLang)

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 123 - 10
src/views/article-manager/index.vue


+ 421 - 0
src/views/message-center/message-type/index.vue

@@ -0,0 +1,421 @@
+<template>
+  <div>
+    <ag-grid-layout
+        toolbar
+        :table-height="tableHeight"
+        theme="ag-theme-alpine"
+        :column-defs="columnDefs"
+        :row-data="rowData"
+        :locale-text="localeText"
+        :grid-options="gridOptions"
+        :default-col-def="defaultColDef"
+        :animate-rows="true"
+        :row-selection="rowSelection"
+        :enable-cell-change-flash="true"
+        @filterChanged="filterModifed"
+        @sortChanged="gridSortChange"
+    >
+      <!--        @rowDoubleClicked="getList"-->
+      <div slot="toolbar" class="inner-toolbar">
+        <div class="toolbar-search">
+          <en-table-search :placeholder="this.$t('action.keywords')" @search="handlerSearch"/>
+        </div>
+        <div class="toolbar-btns">
+          <el-button type="primary" size="mini" @click="createMessageType">{{
+              this.$t('mc.messageTypeModule.addType')
+            }}
+          </el-button>
+        </div>
+      </div>
+      <el-pagination
+          v-if="pageData"
+          slot="pagination"
+          :current-page="pageData.page_no"
+          :page-sizes="[10, 20, 50, 100]"
+          :page-size="pageData.page_size"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="pageData.data_total"
+          @size-change="handlePageSizeChange"
+          @current-change="handlePageCurrentChange"
+      />
+    </ag-grid-layout>
+    <el-dialog :title.sync="formtitle" :visible.sync="formshow" width="35%">
+      <div>
+        <el-form ref="editform" :rules="rules" label-width="120px" :model="formmodel">
+
+          <el-row>
+            <el-col :span="24">
+              <el-form-item :label="this.$t('mc.messageTypeModule.typeName')" prop="type_name">
+                <el-input
+                    v-model="formmodel.type_name"
+                    clearable
+                    :maxlength="100"
+                    :placeholder="this.$t('mc.messageTypeModule.typeName')"
+                />
+              </el-form-item>
+<!--              <el-form-item :label="this.$t('mc.messageTypeModule.openType')" prop="device_type">-->
+<!--                <el-select v-model="formmodel.open_type"-->
+<!--                           :placeholder="this.$t('mc.messageTypeModule.openType')"-->
+<!--                           filterable clearable>-->
+<!--                  <el-option v-for="(item,index) in messageOpenTypeEnum" :key="index" :label="item.key"-->
+<!--                             :value="item.value"/>-->
+<!--                </el-select>-->
+<!--              </el-form-item>-->
+            </el-col>
+          </el-row>
+        </el-form>
+
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="formshow = false">{{ this.$t('action.cancel') }}</el-button>
+        <el-button type="primary" @click="handlerFormSubmit('editform')">{{ this.$t('action.yes') }}</el-button>
+      </div>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import {AG_GRID_LOCALE_CN} from '@/utils/AgGridVueLocaleCn'
+import ButtonCellRender from '@/components/AgGridCellRender/ButtonCellRender'
+import * as API_MessageType from '@/api/message_type'
+import {unix2Date} from '@/utils/Foundation'
+import ListFilter from '@/components/AgGridCustomFilter/ListFilter'
+import RadioFilter from '@/components/AgGridCustomFilter/RadioFilter'
+import ButtonCellRenderList from "@/components/AgGridCellRender/ButtonCellRenderList";
+
+
+
+export default {
+  name: "index",
+  components: {ButtonCellRenderList, ButtonCellRender, ListFilter, RadioFilter},
+  data() {
+    return {
+      tableData: [],
+      /** 列表参数 */
+      params: {
+        page_size: 20,
+        page_no: 1,
+        fixedCondition: null
+      },
+      /** 新建组织弹出参数 **/
+      formtitle: this.$t('mc.messageTypeModule.addType'),
+      formshow: false,
+      /** 频道订阅设置 */
+      formSubscribe: false,
+      formmodel: {},
+      frameGroups: [],
+      rules: {
+        type_name: [
+          {required: true, message: this.$t('mc.messageTypeModule.typeNameRequired'), trigger: 'blur'}
+        ]
+      },
+      timeRange: [new Date(2020, 11, 11, 7, 0), new Date(2020, 11, 11, 8, 0)],
+      checkWeek: [],
+      /** ag-grid参数 **/
+      pageData: [],
+      loading: false,
+      errorId: null,
+      shopVisible: false,
+      columnDefs: null,
+      rowData: null,
+      defaultColDef: null,
+      gridOptions: null,
+      gridApi: null,
+      columnApi: null,
+      localeText: AG_GRID_LOCALE_CN,
+      filterState: null,
+      rowSelection: null,
+      frameworkComponents: null,
+
+      /** 频道订阅者情况数组 */
+      channelSubscribers: [],
+      subscribeTitle: '',
+      choiceAll: this.$t('action.choiceAll')
+    }
+  },
+  computed: {
+    tableHeight() {
+      return this.mainAreaHeight - 130
+    }
+
+  },
+  beforeMount() {
+    this.gridOptions = {}
+    this.columnDefs = [
+      {
+        headerName: '#',
+        headerCheckboxSelection: true,
+        headerCheckboxSelectionFilteredOnly: true,
+        checkboxSelection: true,
+        sortable: false, filter: false,
+        width: 100,
+        resizable: false,
+        valueGetter: this.hashValueGetter
+      },
+      {headerName: 'ID', field: 'id', sortable: true, filter: 'agNumberColumnFilter', width: 130},
+      {
+        headerName: this.$t('mc.messageTypeModule.typeName'),
+        field: 'type_name',
+        filter: 'agTextColumnFilter',
+        sortable: true,
+        flex: 1
+      },
+      {
+        headerName: this.$t('action.createTime'),
+        field: 'create_time',
+        sortable: true,
+        filter: 'agNumberColumnFilter',
+        width: 200,
+        valueFormatter: this.unixDateFormatter
+      },
+      // lockPosition 锁定位置,会在第一列
+      // lockPinned = true 不能拖动然后固定
+      // resizeable 单元个大小是否可以调整
+
+      {
+        headerName: this.$t('action.handle'), field: 'id',
+        cellRendererFramework: 'ButtonCellRenderList',
+        cellRendererParams: param => {
+          return {
+            list: [
+              {
+                onClick: this.handleEdit,
+                label: this.$t('action.edit'),
+                buttonType: 'primary',
+                buttonSize: 'mini'
+              },
+
+              {
+                onClick: this.deleteSingle,
+                label: this.$t('action.delete'),
+                buttonType: 'danger',
+                buttonSize: 'mini',
+              }
+            ]
+          }
+        },
+        filter: false,
+        pinned: 'right',
+        lockPinned: true,
+        minWidth: this.$i18n.locale === 'zh' ? 310 : 350,
+        resizable: false,
+        sortable: false
+      }
+    ]
+    this.defaultColDef = {
+      sortable: true,
+      resizable: true,
+      comparator: this.dateCustomComparator,
+      filterParams: {
+        debounceMs: 200,
+        newRowsAction: 'keep',
+        textCustomComparator: this.textCustomComparator,
+        comparator: this.dateCustomComparator
+      }
+    }
+    this.rowSelection = 'multiple'
+  },
+  mounted() {
+    window.onresize = this.windowResize
+    this.gridApi = this.gridOptions.api
+    this.gridColumnApi = this.gridOptions.columnApi
+    // 设置默认排序字段,应用列状态之后会触发 gridSortChange 函数,会调用getlist,后面不需要再调用this.getlist
+    this.gridColumnApi.applyColumnState({
+      state: [
+        {
+          colId: 'id',
+          sort: 'asc'
+        }
+      ]
+    })
+  },
+  methods: {
+    windowResize() {
+      this.$set(this, 'mainAreaHeight', Number(document.documentElement.clientHeight) - 84)
+    },
+    handlerDelete(ids) {
+      this.$confirm(this.$t('action.sureDelete'), this.$t('action.waring'), {
+        confirmButtonText: this.$t('action.yes'),
+        cancelButtonText: this.$t('action.cancel'),
+        type: 'warning'
+      }).then(() => {
+        API_MessageType.remove(ids).then(
+            response => {
+              this.getList()
+              this.$message({
+                type: 'success',
+                message: this.$t('action.deleted')
+              })
+            }
+        ).catch(response => {
+          this.$message({
+            type: 'info',
+            message: response.message
+          })
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: this.$t('action.cancelDelete')
+        })
+      })
+    },
+    deleteSingle(row) {
+      this.handlerDelete(row.id)
+    },
+
+    /**
+     * 创建 消息类型
+     */
+    createMessageType() {
+      this.formshow = true
+      this.formmodel = {
+        channel_name: '',
+        part_id: this.$store.getters.partId
+      }
+    },
+    /** 分页大小发生改变 */
+    handlePageSizeChange(size) {
+      this.params.page_size = size
+      this.getList()
+    },
+
+    /** 分页页数发生改变 */
+    handlePageCurrentChange(page) {
+      this.params.page_no = page
+      this.getList()
+    },
+    /** 加载列表数据 */
+    getList() {
+      this.loading = true
+      const param = this.MixinClone(this.params)
+      this.gridApi.showLoadingOverlay()
+      API_MessageType.getList(param).then(response => {
+        this.loading = false
+        // this.tableData = [...response.data]
+        this.pageData = {
+          page_no: response.page_no,
+          page_size: response.page_size,
+          data_total: response.data_total
+        }
+        this.$nextTick(() => {
+          const node = this.gridApi.getDisplayedRowAtIndex(0)
+          if (node !== null && node !== undefined) {
+            node.setSelected(true)
+          }
+        })
+        this.rowData = [...response.data]
+        this.refreshPlayStatus()
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    /** 处理搜索 */
+    handlerSearch(keywords) {
+      this.params.query = keywords
+      this.getList()
+    },
+    /** 处理字段排序 */
+    tableSort(column) {
+      if (column.order !== null) {
+        this.params.sort = column.prop
+        this.params.dir = column.order === 'ascending' ? 'asc' : 'desc'
+      } else {
+        this.params.sort = null
+        this.params.dir = null
+      }
+      this.getList()
+    },
+    /**
+     * 格式化unix时间戳
+     **/
+    unixDateFormatter(param) {
+      if (!param.value) return ''
+      return unix2Date(param.value * 1000)
+    },
+
+    gridSortChange(param) {
+      const columnState = param.columnApi.getColumnState()
+      // 排序状态
+      const sortState = columnState.filter(function (s) {
+        return s.sort != null
+      }).map(function (s) {
+        return {
+          colId: s.colId,
+          sort: s.sort,
+          sortIndex: s.sortIndex
+        }
+      }).sort(function (a, b) {
+        return a.sortIndex - b.sortIndex
+      })
+      if (sortState.length > 0) {
+        if (sortState.length === 1) {
+          this.params.sort = sortState[0].colId
+          this.params.dir = sortState[0].sort
+        } else {
+          let sortstring = ''
+          sortState.forEach(function (item) {
+            sortstring += item.colId + ' ' + item.sort + ','
+          })
+          this.params.sort = sortstring.substring(0, sortstring.length - 1)
+          this.params.dir = ' '
+        }
+      } else {
+        delete this.params.sort
+        delete this.params.dir
+      }
+      this.getList()
+    },
+
+    filterModifed(param) { // todo 通过转换后的数值过滤,需要转回原始数值
+      var model = param.api.getFilterModel()
+      this.params.filter = JSON.stringify(model)
+      this.getList()
+    },
+
+    handleEdit(row) {
+      this.formtitle = this.$t('mc.messageTypeModule.editType')
+      this.formmodel = {
+        ...row
+      }
+      this.formshow = true
+    },
+    /**
+     * 提交新增表单
+     * @param formname
+     */
+    handlerFormSubmit(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          if (!this.formmodel.id) {
+            /** 新增 */
+            this.formmodel.part_id = this.$store.getters.partId
+
+            API_MessageType.add(this.formmodel).then(() => {
+              this.formshow = false
+              this.$message.success(this.$t('action.addSuccess'))
+              this.getList()
+            })
+          } else {
+            API_MessageType.update(this.formmodel.id, this.formmodel).then(() => {
+              this.formshow = false
+              this.$message.success(this.$t('action.editSuccess'))
+              this.getList()
+            })
+          }
+        } else {
+          this.$message.error(this.$t('action.fromError'))
+        }
+      })
+    },
+
+
+  }
+}
+
+</script>
+
+<style scoped>
+
+</style>

+ 413 - 0
src/views/message-center/messages/index.vue

@@ -0,0 +1,413 @@
+
+<template>
+  <div>
+    <ag-grid-layout
+        toolbar
+        :table-height="tableHeight"
+        theme="ag-theme-alpine"
+        :column-defs="columnDefs"
+        :row-data="rowData"
+        :locale-text="localeText"
+        :grid-options="gridOptions"
+        :default-col-def="defaultColDef"
+        :animate-rows="true"
+        :row-selection="rowSelection"
+        :enable-cell-change-flash="true"
+        @filterChanged="filterModifed"
+        @sortChanged="gridSortChange"
+    >
+      <!--        @rowDoubleClicked="getList"-->
+      <div slot="toolbar" class="inner-toolbar">
+        <div class="toolbar-search">
+          <en-table-search :placeholder="this.$t('action.keywords')" @search="handlerSearch" />
+        </div>
+        <div class="toolbar-btns">
+          <el-button type="primary" size="mini" @click="createChannel">{{ this.$t('mc.messagesModule.addMessage') }}</el-button>
+        </div>
+      </div>
+      <el-pagination
+          v-if="pageData"
+          slot="pagination"
+          :current-page="pageData.page_no"
+          :page-sizes="[10, 20, 50, 100]"
+          :page-size="pageData.page_size"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="pageData.data_total"
+          @size-change="handlePageSizeChange"
+          @current-change="handlePageCurrentChange"
+      />
+    </ag-grid-layout>
+
+
+  </div>
+</template>
+
+<script>
+import ButtonCellRenderList from "@/components/AgGridCellRender/ButtonCellRenderList.vue";
+import ButtonCellRender from "@/components/AgGridCellRender/ButtonCellRender.vue";
+import ListFilter from "@/components/AgGridCustomFilter/ListFilter.vue";
+import RadioFilter from "@/components/AgGridCustomFilter/RadioFilter.vue";
+import {AG_GRID_LOCALE_CN} from "@/utils/AgGridVueLocaleCn";
+import * as API_BoardTitle from "@/api/ncs_board_title";
+import {unix2Date} from "@/utils/Foundation";
+import {MESSAGE_OPEN_TYPE} from "@/utils/enum/MessageOpenTypeEnum";
+
+export default {
+  name: "index",
+  components: { ButtonCellRenderList, ButtonCellRender, ListFilter, RadioFilter },
+  data() {
+    return {
+      tableData: [],
+      /** 列表参数 */
+      params: {
+        page_size: 20,
+        page_no: 1,
+        fixedCondition: ' part_id = ' + this.$store.getters.partId
+      },
+      /** 新建组织弹出参数 **/
+      formtitle: this.$t('boardTitle.createTitle'),
+      formshow: false,
+      /** 频道订阅设置 */
+      formSubscribe: false,
+      formmodel: {},
+      frameGroups: [],
+      rules: {
+        board_title: [
+          { required: true, message: this.$t('boardTitle.titleRequire'), trigger: 'blur' }
+        ]
+      },
+      /** ag-grid参数 **/
+      pageData: [],
+      loading: false,
+      errorId: null,
+      shopVisible: false,
+      columnDefs: null,
+      rowData: null,
+      defaultColDef: null,
+      gridOptions: null,
+      gridApi: null,
+      columnApi: null,
+      localeText: AG_GRID_LOCALE_CN,
+      filterState: null,
+      rowSelection: null,
+      frameworkComponents: null,
+      /** 频道订阅者情况数组 */
+      messageOpenTypeEnum: MESSAGE_OPEN_TYPE.getKeyValueList(),
+      subscribeTitle: '',
+      choiceAll: this.$t('action.choiceAll')
+    }
+  },
+  computed: {
+    tableHeight() {
+      return this.mainAreaHeight - 130
+    }
+
+  },
+  beforeMount() {
+    this.gridOptions = {}
+    this.columnDefs = [
+      {
+        headerName: '#',
+        headerCheckboxSelection: true,
+        headerCheckboxSelectionFilteredOnly: true,
+        checkboxSelection: true,
+        sortable: false, filter: false,
+        width: 100,
+        resizable: false,
+        valueGetter: this.hashValueGetter
+      },
+      { headerName: 'ID', field: 'id', sortable: true, filter: 'agNumberColumnFilter', width: 130 },
+      {
+        headerName: this.$t('mc.messagesModule.title'), field: 'title', sortable: true, filter: 'agTextColumnFilter', flex: 1
+      },
+      {
+        headerName: this.$t('mc.messageType'), field: 'message_type_id', sortable: true, filter: 'agTextColumnFilter', flex: 1
+      },
+      {
+        headerName: this.$t('mc.messagesModule.description'), field: 'description', sortable: true, filter: 'agTextColumnFilter', flex: 1
+      },
+      {
+        headerName: this.$t('mc.messagesModule.openType'), field: 'open_type', sortable: true,  flex: 1,
+        filterFramework: 'ListFilter', width: 160,
+        filterParams: {
+          listData: this.messageOpenTypeEnum
+        },
+        valueGetter: this.openTypeGetter
+      },
+      {
+        headerName: this.$t('boardTitle.partId'), field: 'part_id', sortable: true, filter: 'agNumberColumnFilter', flex: 1
+      },
+
+      // lockPosition 锁定位置,会在第一列
+      // lockPinned = true 不能拖动然后固定
+      // resizeable 单元个大小是否可以调整
+      { headerName: this.$t('action.handle'), field: 'id',
+        cellRendererFramework: 'ButtonCellRenderList',
+        cellRendererParams: param => {
+          return {
+            list: [
+              {
+                onClick: this.handEdit,
+                label: this.$t('action.edit'),
+                buttonType: 'primary',
+                buttonSize: 'mini'
+              },
+              {
+                onClick: this.deleteSingle,
+                label: this.$t('action.delete'),
+                buttonType: 'danger',
+                buttonSize: 'mini',
+              },
+              {
+                onClick: this.toDesigner,
+                label: this.$t('boardTitle.design'),
+                buttonType: 'warning',
+                buttonSize: 'mini',
+              }
+
+            ]}
+        },
+        filter: false,
+        pinned: 'right',
+        lockPinned: true,
+        minWidth: this.$i18n.locale === 'zh' ? 310 : 350,
+        resizable: false,
+        sortable: false
+      }
+
+    ]
+    this.defaultColDef = {
+      sortable: true,
+      resizable: true,
+      comparator: this.dateCustomComparator,
+      filterParams: {
+        debounceMs: 200,
+        newRowsAction: 'keep',
+        textCustomComparator: this.textCustomComparator,
+        comparator: this.dateCustomComparator
+      }
+    }
+    this.rowSelection = 'multiple'
+  },
+  mounted() {
+    window.onresize = this.windowResize
+    this.gridApi = this.gridOptions.api
+    this.gridColumnApi = this.gridOptions.columnApi
+    // 设置默认排序字段,应用列状态之后会触发 gridSortChange 函数,会调用getlist,后面不需要再调用this.getlist
+    this.gridColumnApi.applyColumnState({
+      state: [
+        {
+          colId: 'id',
+          sort: 'asc'
+        }
+      ]
+    })
+
+  },
+  methods: {
+    windowResize() {
+      this.$set(this, 'mainAreaHeight', Number(document.documentElement.clientHeight) - 84)
+    },
+    handlerDelete(ids) {
+      this.$confirm(this.$t('action.sureDelete'), this.$t('action.waring'), {
+        confirmButtonText: this.$t('action.yes'),
+        cancelButtonText: this.$t('action.cancel'),
+        type: 'warning'
+      }).then(() => {
+        API_BoardTitle.remove(ids).then(
+            response => {
+              this.getList()
+              this.$message({
+                type: 'success',
+                message: this.$t('action.deleted')
+              })
+            }
+        ).catch(response => {
+          this.$message({
+            type: 'info',
+            message: response.message
+          })
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: this.$t('action.cancelDelete')
+        })
+      })
+    },
+    deleteSingle(row) {
+      this.handlerDelete(row.id)
+    },
+    /** 获取设备类型文字显示,从deviceTypeTransfer 中找出value值对应的key显示出来 */
+    openTypeGetter(params) {
+      const gridVal = params.data.open_type
+      // return this.deviceTypeTransfer.filter(p => p.value === gridVal).map(p => p.key)
+      return MESSAGE_OPEN_TYPE.getDescFromValue(gridVal)
+    },
+    /**
+     * 创建频道
+     */
+    createChannel() {
+      this.$router.push({ name: 'message-edit', params: { id: 1 }})
+    },
+    /** 分页大小发生改变 */
+    handlePageSizeChange(size) {
+      this.params.page_size = size
+      this.getList()
+    },
+
+    /** 分页页数发生改变 */
+    handlePageCurrentChange(page) {
+      this.params.page_no = page
+      this.getList()
+    },
+    /** 加载列表数据 */
+    getList() {
+      this.loading = true
+      const param = this.MixinClone(this.params)
+      this.gridApi.showLoadingOverlay()
+      API_BoardTitle.getList(param).then(response => {
+        this.loading = false
+        // this.tableData = [...response.data]
+        this.pageData = {
+          page_no: response.page_no,
+          page_size: response.page_size,
+          data_total: response.data_total
+        }
+        this.$nextTick(() => {
+          const node = this.gridApi.getDisplayedRowAtIndex(0)
+          if (node !== null && node !== undefined) {
+            node.setSelected(true)
+          }
+        })
+        this.rowData = [...response.data]
+        this.refreshPlayStatus()
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    /** 处理搜索 */
+    handlerSearch(keywords) {
+      this.params.query = keywords
+      this.getList()
+    },
+    /** 处理字段排序 */
+    tableSort(column) {
+      if (column.order !== null) {
+        this.params.sort = column.prop
+        this.params.dir = column.order === 'ascending' ? 'asc' : 'desc'
+      } else {
+        this.params.sort = null
+        this.params.dir = null
+      }
+      this.getList()
+    },
+    /**
+     * 格式化unix时间戳
+     **/
+    unixDateFormatter(param) {
+      if (!param.value) return ''
+      return unix2Date(param.value * 1000)
+    },
+
+    gridSortChange(param) {
+      const columnState = param.columnApi.getColumnState()
+      // 排序状态
+      const sortState = columnState.filter(function(s) {
+        return s.sort != null
+      }).map(function(s) {
+        return {
+          colId: s.colId,
+          sort: s.sort,
+          sortIndex: s.sortIndex
+        }
+      }).sort(function(a, b) {
+        return a.sortIndex - b.sortIndex
+      })
+      if (sortState.length > 0) {
+        if (sortState.length === 1) {
+          this.params.sort = sortState[0].colId
+          this.params.dir = sortState[0].sort
+        } else {
+          let sortstring = ''
+          sortState.forEach(function(item) {
+            sortstring += item.colId + ' ' + item.sort + ','
+          })
+          this.params.sort = sortstring.substring(0, sortstring.length - 1)
+          this.params.dir = ' '
+        }
+      } else {
+        delete this.params.sort
+        delete this.params.dir
+      }
+      this.getList()
+    },
+
+    filterModifed(param) { // todo 通过转换后的数值过滤,需要转回原始数值
+      var model = param.api.getFilterModel()
+      this.params.filter = JSON.stringify(model)
+      this.getList()
+    },
+
+    handEdit(row) {
+      this.formtitle=this.$t('boardTitle.editTitle')
+      this.formmodel = {
+        ...row
+      }
+      this.formshow = true
+    },
+    /**
+     * 提交新增表单
+     * @param formname
+     */
+    handlerFormSubmit(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          if (!this.formmodel.id) {
+            /** 新增 */
+            this.formmodel.part_id = this.$store.getters.partId
+
+            API_BoardTitle.add(this.formmodel).then(() => {
+              this.formshow = false
+              this.$message.success(this.$t('action.addSuccess'))
+              this.getList()
+            })
+          } else {
+            API_BoardTitle.update(this.formmodel.id, this.formmodel).then(() => {
+              this.formshow = false
+              this.$message.success(this.$t('action.editSuccess'))
+              this.getList()
+            })
+          }
+        } else {
+          this.$message.error(this.$t('action.fromError'))
+        }
+      })
+    },
+
+    handlerSubscribeSubmit() {
+      let subscribeIds = []
+      this.channelSubscribers.forEach(item => {
+        subscribeIds = [...item.clerks.filter(p => p.checked).map(p => p.member_id), ...subscribeIds]
+      })
+      API_BoardTitle.setChannelSubscribers({ channel_id: this.formmodel.id, member_ids: subscribeIds.length === 0 ? null : [...subscribeIds] }).then(() => {
+        this.formSubscribe = false
+        this.$message.success(this.$t('action.editSuccess'))
+      })
+    },
+
+    toDesigner(row) {
+      this.$router.push({ name: 'infoboard-designer-manager', params: { id:row.id }})
+    }
+
+
+  }
+
+}
+</script>
+
+
+
+<style scoped>
+
+</style>

+ 248 - 0
src/views/message-center/messages/message-edit.vue

@@ -0,0 +1,248 @@
+<template>
+  <el-container>
+    <el-header>
+      <el-button type="primary" @click="saveEditJs">save</el-button>
+    </el-header>
+    <el-main>
+      <el-form ref="editform" :rules="rules" label-width="120px" :model="formmodel">
+
+        <el-row>
+          <el-col :span="24">
+            <el-form-item :label="this.$t('mc.messagesModule.title')" prop="title">
+              <el-input
+                  v-model="formmodel.title"
+                  clearable
+                  :maxlength="100"
+                  :placeholder="this.$t('mc.messagesModule.title')"
+              />
+            </el-form-item>
+            <el-form-item :label="this.$t('mc.messagesModule.description')" prop="description">
+              <el-input type="textarea" v-model="formmodel.description"></el-input>
+            </el-form-item>
+            <el-form-item :label="this.$t('mc.messageType')" prop="message_type_id">
+              <el-select v-model="formmodel.message_type_id"
+                         :placeholder="this.$t('mc.messageType')"
+                         filterable clearable>
+                <el-option v-for="(item,index) in messageTypes" :key="item.id" :label="item.type_name"
+                           :value="item.id"/>
+              </el-select>
+            </el-form-item>
+            <el-form-item :label="this.$t('mc.messagesModule.openType')" prop="open_type">
+              <el-select v-model="formmodel.open_type"
+                         :placeholder="this.$t('mc.messagesModule.openType')"
+                         @change="openTypeChange"
+                         filterable clearable>
+                <el-option v-for="(item,index) in messageOpenTypeEnum" :key="index" :label="item.key"
+                           :value="item.value"/>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <el-divider></el-divider>
+      <div id="editjs">
+
+      </div>
+
+      <el-dialog :visible.sync="customerFormVisible" class="customer-dialog" width="60%">
+        <div slot="title" style="display: none;">
+        </div>
+        <VueFinder id="vuefinder" :height="500" :select-button="handleSelectButton" theme="dark" :persist="true"
+                   max-file-size="50 GB" path="" request="http://192.168.1.54:5001"></VueFinder>
+      </el-dialog>
+    </el-main>
+  </el-container>
+</template>
+<script>
+import EditorJS from "@editorjs/editorjs";
+import Header from "editorjs-header-with-alignment";
+import IndentTune from "editorjs-indent-tune";
+import Paragraph from "editorjs-paragraph-with-alignment";
+import List from "@editorjs/list";
+import InlineImage from "editorjs-inline-image";
+import Warning from "@editorjs/warning";
+import Alert from "editorjs-alert";
+import TextVariantTune from "@editorjs/text-variant-tune";
+import Title from "title-editorjs";
+import AudioTool from "@/components/EditorjsTools/AudioTool";
+import VideoTool from "@/components/EditorjsTools/VideoTool";
+import ImageTool from "@/components/EditorjsTools/ImageTool";
+import {MESSAGE_OPEN_TYPE} from "@/utils/enum/MessageOpenTypeEnum";
+import * as API_MessageType from '@/api/message_type'
+
+export default {
+  name: "message-edit",
+  data() {
+    return {
+      editjs: null,
+      customerFormVisible: false,
+      imageBlockId: '',
+      handleSelectButton: {
+        // show select button
+        active: true,
+        // allow multiple selection
+        multiple: false,
+        filters: ['png'],
+        // handle click event
+        click: (items, event) => {
+          if (!items.length) {
+            return;
+          }
+          this.fileSelect(items, event)
+          console.log(items, event);
+        }
+      },
+      formmodel: {},
+      messageOpenTypeEnum: MESSAGE_OPEN_TYPE.getKeyValueList(),
+      messageTypes: [],
+      rules: {
+        title: [
+          {required: true, message: this.$t('mc.messagesModule.titleRequired'), trigger: 'blur'},
+        ],
+        open_type: [
+          {required: true, message: this.$t('mc.messagesModule.openTypeRequired'), trigger: 'blur'},
+        ],
+        message_type_id: [
+          {required: true, message: this.$t('mc.messagesModule.messageTypeRequired'), trigger: 'blur'},
+        ],
+      },
+    }
+  },
+  async mounted() {
+    console.log('i18n', this.$i18n.messages[this.$i18n.locale].messages)
+    this.messageTypes = await API_MessageType.getAll()
+    this.editjs = new EditorJS({
+      holder: 'editjs',
+      placeholder: '点击输入内容...',
+      autofocus: true,
+      style: {
+        'max-width': '800px'
+      },
+      // readOnly:true,
+      // data: {
+      //   "time": 1711702613917,
+      //   "blocks": [{
+      //     "id": "VK-vbpfePW",
+      //     "type": "imagetool",
+      //     "data": {
+      //       "url": "http://192.168.1.54:5001/%E5%94%AE%E5%90%8E%E6%8A%80%E8%83%BD%E5%9F%B9%E8%AE%AD/assets/image-27.png",
+      //       "caption": "",
+      //       "withBorder": false,
+      //       "withBackground": false,
+      //       "stretched": false
+      //     }
+      //   }, {
+      //     "id": "UgDLeBjjED",
+      //     "type": "video",
+      //     "data": {"src": "http://192.168.1.54:5001/Loki.S01E06.%E4%B8%AD%E8%8B%B1%E5%AD%97%E5%B9%95.WEBrip.AAC.1080p.x264-%E8%BF%9C%E9%89%B4%E5%AD%97%E5%B9%95%E7%BB%84.mp4"}
+      //   }, {"id": "CKmi5ipJvw", "type": "audio", "data": {"src": "http://192.168.1.54:5001/69053700013212521.mp3"}}],
+      //   "version": "2.29.1"
+      // },
+      tools: {
+        header: {
+          class: Header, inlineToolbar: true,
+          tunes: ['textVariant']
+        },
+        indentTune: IndentTune,
+        paragraph: { // apply only for the 'paragraph' tool
+          class: Paragraph,
+          tunes: ['indentTune', 'textVariant']
+        },
+        // list: {class: List, inlineToolbar: true},
+        textVariant: TextVariantTune,
+        audio: {
+          class: AudioTool,
+          inlineToolbar: true,
+          config: {
+            onChoose: (id,index) => {
+              console.log(index)
+              console.log(this.formmodel.open_type)
+              if(this.formmodel.open_type==='VIDEO'){
+                this.editjs.blocks.delete(index)
+                return
+              }
+              this.customerFormVisible = true
+              this.imageBlockId = id
+              this.handleSelectButton.filters = ['mp3']
+            }
+          }
+        },
+        video: {
+          class: VideoTool, inlineToolbar: true,
+          config: {
+            onChoose: (id) => {
+              this.customerFormVisible = true
+              this.imageBlockId = id
+              this.handleSelectButton.filters = ['mp4']
+            }
+          }
+        },
+        image: {
+          class: ImageTool,
+          config: {
+            disabled:true,
+            onChoose: (id) => {
+              this.customerFormVisible = true
+              this.imageBlockId = id
+              this.handleSelectButton.filters = ['png', 'jpg', 'jepg', 'gif']
+            }
+          }
+        },
+        blockTool: true
+      },
+
+      i18n: {...this.$i18n.messages[this.$i18n.locale]}
+
+    })
+
+  },
+  methods: {
+    fileSelect(items, event) {
+      this.customerFormVisible = false
+      const imageBlock = this.editjs.blocks.getById(this.imageBlockId);
+      imageBlock.call('changeImageUrl', 'http://192.168.1.54:5001' + items[0].href)
+    },
+    saveEditJs() {
+      this.editjs.save().then(output => {
+        console.log('output', JSON.stringify(output))
+      }).catch(e => {
+        console.log('error', e)
+      })
+    },
+    openTypeChange(value) {
+      console.log(this.editjs.blocks)
+      // let conf = {...this.config}
+      // console.log('conf',conf,this.config)
+      // if(value==='VIDEO'){
+      //  // delete  conf.tools.header
+      //   // delete conf.tools.paragraph
+      //   // delete conf.tools.list
+      //   // delete conf.tools.audio
+      //   // delete conf.tools.image
+      //   // conf.defaultBlock='video'
+      //   conf.tools.header=false
+      //   conf.tools.list=false
+      //   conf.tools.audio=false
+      //   conf.tools.image=false
+      // }
+      //
+      // this.editjs.destroy()
+      // this.editjs = new EditorJS(conf)
+    },
+  },
+
+  destroyed() {
+    // this.videoplay=null
+  }
+}
+</script>
+
+
+<style scoped>
+.el-main .el-form {
+  max-width: 770px;
+  position: relative;
+  left: 50%;
+  margin-left: -445px;
+}
+</style>

+ 1 - 1
src/views/ncs-device/components/deviceManager.vue

@@ -224,7 +224,7 @@
                     <el-col :span="8">
                         <el-form-item :label="this.$t('deviceManage.controlLineNumber')" prop="control_line_number">
                             <el-input v-model="deviceModel.control_line_number" clearable
-                                      :placeholder="this.$t('deviceManage.controlLineNumber')" type="number"/>
+                                      :placeholder="this.$t('deviceManage.controlLineNumber')" type="text"/>
                         </el-form-item>
                     </el-col>
                 </el-row>