Browse Source

temp save

liujq 4 years ago
parent
commit
8247a4e0e1

+ 123 - 0
src/components/Tinymce/components/EditorImage.vue

@@ -0,0 +1,123 @@
+<template>
+  <div class="upload-container">
+    <el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
+      上传图片
+    </el-button>
+    <el-dialog :visible.sync="dialogVisible">
+        <!-- :multiple="true"
+        :file-list="fileList"
+        :show-file-list="true"
+        :on-remove="handleRemove"
+        :on-success="handleSuccess"
+        :before-upload="beforeUpload"
+        action="https://httpbin.org/post" -->
+      <el-upload
+        :action="`${domainUrl}/adm/upload/cloud`"
+        :data="{logic_type: 'textcontent', token}"
+        name="upload"
+        :file-list="fileList"
+        :show-file-list="true"
+        :on-remove="handleRemove"
+        :on-success="handleSuccess"
+        :before-upload="beforeUpload"
+        class="editor-slide-upload"
+        list-type="picture-card"
+      >
+        <el-button size="small" type="primary">
+          点击上传
+        </el-button>
+      </el-upload>
+      <el-button @click="dialogVisible = false">
+        取消
+      </el-button>
+      <el-button type="primary" @click="handleSubmit">
+        确定
+      </el-button>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// import { getToken } from 'api/qiniu'
+
+export default {
+  name: 'EditorSlideUpload',
+  props: {
+    color: {
+      type: String,
+      default: '#1890ff'
+    }
+  },
+  data() {
+    const token = window.sessionStorage.getItem('fp_token')
+    let domainUrl = process.env.VUE_APP_BASE_API
+    return {
+      domainUrl,
+      token,
+      dialogVisible: false,
+      listObj: {},
+      fileList: []
+    }
+  },
+  methods: {
+    checkAllSuccess() {
+      return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
+    },
+    handleSubmit() {
+      const arr = Object.keys(this.listObj).map(v => this.listObj[v])
+      if (!this.checkAllSuccess()) {
+        this.$message('Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!')
+        return
+      }
+      this.$emit('successCBK', arr)
+      this.listObj = {}
+      this.fileList = []
+      this.dialogVisible = false
+    },
+    handleSuccess(response, file) {
+      const uid = file.uid
+      const objKeyArr = Object.keys(this.listObj)
+      for (let i = 0, len = objKeyArr.length; i < len; i++) {
+        if (this.listObj[objKeyArr[i]].uid === uid) {
+          this.listObj[objKeyArr[i]].url = response.data.url
+          this.listObj[objKeyArr[i]].hasSuccess = true
+          return
+        }
+      }
+    },
+    handleRemove(file) {
+      const uid = file.uid
+      const objKeyArr = Object.keys(this.listObj)
+      for (let i = 0, len = objKeyArr.length; i < len; i++) {
+        if (this.listObj[objKeyArr[i]].uid === uid) {
+          delete this.listObj[objKeyArr[i]]
+          return
+        }
+      }
+    },
+    beforeUpload(file) {
+      const _self = this
+      const _URL = window.URL || window.webkitURL
+      const fileName = file.uid
+      this.listObj[fileName] = {}
+      return new Promise((resolve, reject) => {
+        const img = new Image()
+        img.src = _URL.createObjectURL(file)
+        img.onload = function() {
+          _self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
+        }
+        resolve(true)
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.editor-slide-upload {
+  margin-bottom: 20px;
+  ::v-deep .el-upload--picture-card {
+    width: 100%;
+  }
+}
+</style>

+ 59 - 0
src/components/Tinymce/dynamicLoadScript.js

@@ -0,0 +1,59 @@
+let callbacks = []
+
+function loadedTinymce() {
+  // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
+  // check is successfully downloaded script
+  return window.tinymce
+}
+
+const dynamicLoadScript = (src, callback) => {
+  const existingScript = document.getElementById(src)
+  const cb = callback || function() {}
+
+  if (!existingScript) {
+    const script = document.createElement('script')
+    script.src = src // src url for the third-party library being loaded.
+    script.id = src
+    document.body.appendChild(script)
+    callbacks.push(cb)
+    const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
+    onEnd(script)
+  }
+
+  if (existingScript && cb) {
+    if (loadedTinymce()) {
+      cb(null, existingScript)
+    } else {
+      callbacks.push(cb)
+    }
+  }
+
+  function stdOnEnd(script) {
+    script.onload = function() {
+      // this.onload = null here is necessary
+      // because even IE9 works not like others
+      this.onerror = this.onload = null
+      for (const cb of callbacks) {
+        cb(null, script)
+      }
+      callbacks = null
+    }
+    script.onerror = function() {
+      this.onerror = this.onload = null
+      cb(new Error('Failed to load ' + src), script)
+    }
+  }
+
+  function ieOnEnd(script) {
+    script.onreadystatechange = function() {
+      if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
+      this.onreadystatechange = null
+      for (const cb of callbacks) {
+        cb(null, script) // there is no way to catch loading errors in IE8
+      }
+      callbacks = null
+    }
+  }
+}
+
+export default dynamicLoadScript

+ 247 - 0
src/components/Tinymce/index.vue

@@ -0,0 +1,247 @@
+<template>
+  <div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}">
+    <textarea :id="tinymceId" class="tinymce-textarea" />
+    <div class="editor-custom-btn-container">
+      <editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" />
+    </div>
+  </div>
+</template>
+
+<script>
+/**
+ * docs:
+ * https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
+ */
+import editorImage from './components/EditorImage'
+import plugins from './plugins'
+import toolbar from './toolbar'
+import load from './dynamicLoadScript'
+
+// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
+const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js'
+
+export default {
+  name: 'Tinymce',
+  components: { editorImage },
+  props: {
+    id: {
+      type: String,
+      default: function() {
+        return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
+      }
+    },
+    value: {
+      type: String,
+      default: ''
+    },
+    toolbar: {
+      type: Array,
+      required: false,
+      default() {
+        return []
+      }
+    },
+    menubar: {
+      type: String,
+      default: 'file edit insert view format table'
+    },
+    height: {
+      type: [Number, String],
+      required: false,
+      default: 360
+    },
+    width: {
+      type: [Number, String],
+      required: false,
+      default: 'auto'
+    }
+  },
+  data() {
+    return {
+      hasChange: false,
+      hasInit: false,
+      tinymceId: this.id,
+      fullscreen: false,
+      languageTypeList: {
+        'en': 'en',
+        'zh': 'zh_CN',
+        'es': 'es_MX',
+        'ja': 'ja'
+      }
+    }
+  },
+  computed: {
+    containerWidth() {
+      const width = this.width
+      if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'`
+        return `${width}px`
+      }
+      return width
+    }
+  },
+  watch: {
+    value(val) {
+      if (!this.hasChange && this.hasInit) {
+        this.$nextTick(() =>
+          window.tinymce.get(this.tinymceId).setContent(val))
+      }
+    }
+  },
+  mounted() {
+    this.init()
+  },
+  activated() {
+    if (window.tinymce) {
+      this.initTinymce()
+    }
+  },
+  deactivated() {
+    this.destroyTinymce()
+  },
+  destroyed() {
+    this.destroyTinymce()
+  },
+  methods: {
+    init() {
+      // dynamic load tinymce from cdn
+      load(tinymceCDN, (err) => {
+        if (err) {
+          this.$message.error(err.message)
+          return
+        }
+        this.initTinymce()
+      })
+    },
+    initTinymce() {
+      const _this = this
+      window.tinymce.init({
+        selector: `#${this.tinymceId}`,
+        language: this.languageTypeList['zh'],
+        height: this.height,
+        body_class: 'panel-body ',
+        object_resizing: false,
+        toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
+        menubar: this.menubar,
+        plugins: plugins,
+        end_container_on_empty_block: true,
+        powerpaste_word_import: 'clean',
+        code_dialog_height: 450,
+        code_dialog_width: 1000,
+        advlist_bullet_styles: 'square',
+        advlist_number_styles: 'default',
+        imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
+        default_link_target: '_blank',
+        link_title: false,
+        nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
+        init_instance_callback: editor => {
+          if (_this.value) {
+            editor.setContent(_this.value)
+          }
+          _this.hasInit = true
+          editor.on('NodeChange Change KeyUp SetContent', () => {
+            this.hasChange = true
+            this.$emit('input', editor.getContent())
+          })
+        },
+        setup(editor) {
+          editor.on('FullscreenStateChanged', (e) => {
+            _this.fullscreen = e.state
+          })
+        },
+        // it will try to keep these URLs intact
+        // https://www.tiny.cloud/docs-3x/reference/configuration/Configuration3x@convert_urls/
+        // https://stackoverflow.com/questions/5196205/disable-tinymce-absolute-to-relative-url-conversions
+        convert_urls: false,
+        // 整合七牛上传
+        // images_dataimg_filter(img) {
+        //   setTimeout(() => {
+        //     const $image = $(img);
+        //     $image.removeAttr('width');
+        //     $image.removeAttr('height');
+        //     if ($image[0].height && $image[0].width) {
+        //       $image.attr('data-wscntype', 'image');
+        //       $image.attr('data-wscnh', $image[0].height);
+        //       $image.attr('data-wscnw', $image[0].width);
+        //       $image.addClass('wscnph');
+        //     }
+        //   }, 0);
+        //   return img
+        // },
+        // images_upload_handler(blobInfo, success, failure, progress) {
+        //   progress(0);
+        //   const token = _this.$store.getters.token;
+        //   getToken(token).then(response => {
+        //     const url = response.data.qiniu_url;
+        //     const formData = new FormData();
+        //     formData.append('token', response.data.qiniu_token);
+        //     formData.append('key', response.data.qiniu_key);
+        //     formData.append('file', blobInfo.blob(), url);
+        //     upload(formData).then(() => {
+        //       success(url);
+        //       progress(100);
+        //     })
+        //   }).catch(err => {
+        //     failure('出现未知问题,刷新页面,或者联系程序员')
+        //     console.log(err);
+        //   });
+        // },
+      })
+    },
+    destroyTinymce() {
+      const tinymce = window.tinymce.get(this.tinymceId)
+      if (this.fullscreen) {
+        tinymce.execCommand('mceFullScreen')
+      }
+
+      if (tinymce) {
+        tinymce.destroy()
+      }
+    },
+    setContent(value) {
+      window.tinymce.get(this.tinymceId).setContent(value)
+    },
+    getContent() {
+      window.tinymce.get(this.tinymceId).getContent()
+    },
+    imageSuccessCBK(arr) {
+      arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`))
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.tinymce-container {
+  position: relative;
+  line-height: normal;
+}
+
+.tinymce-container {
+  ::v-deep {
+    .mce-fullscreen {
+      z-index: 10000;
+    }
+  }
+}
+
+.tinymce-textarea {
+  visibility: hidden;
+  z-index: -1;
+}
+
+.editor-custom-btn-container {
+  position: absolute;
+  right: 4px;
+  top: 4px;
+  /*z-index: 2005;*/
+}
+
+.fullscreen .editor-custom-btn-container {
+  z-index: 10000;
+  position: fixed;
+}
+
+.editor-upload-btn {
+  display: inline-block;
+}
+</style>

+ 7 - 0
src/components/Tinymce/plugins.js

@@ -0,0 +1,7 @@
+// Any plugins you want to use has to be imported
+// Detail plugins list see https://www.tinymce.com/docs/plugins/
+// Custom builds see https://www.tinymce.com/download/custom-builds/
+
+const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
+
+export default plugins

+ 6 - 0
src/components/Tinymce/toolbar.js

@@ -0,0 +1,6 @@
+// Here is a list of the toolbar
+// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
+
+const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
+
+export default toolbar

+ 1 - 1
src/views/news/components/popup/IndexEdit.vue

@@ -5,7 +5,7 @@
       :show-close="false"
       :close-on-click-modal="false"
       :visible.sync="isShow"
-      :title="'编辑资讯'"
+      :title="curObj.id ? '编辑公众号文章' : '新增公众号文章'"
       :fullscreen="false"
       width="700px"
       custom-class="xl-dialog"

+ 158 - 0
src/views/news/components/popup/IndexEditSelf.vue

@@ -0,0 +1,158 @@
+<template>
+  <div>
+    <el-drawer
+      v-loading="loading"
+      :show-close="false"
+      :title="curObj.id ? '编辑资讯' : '新增资讯'"
+      :wrapper-closable="false"
+      :close-on-press-escape="false"
+      :visible.sync="isShow"
+      size="960px"
+      custom-class="xl-drawer"
+      direction="rtl"
+    >
+      <base-form ref="ruleForm" class="lib-edit" :data="formData" :is-inline="false" label-width="110px">
+        <div slot="footer" style="padding-top: 20px;">
+          <el-button class="xl-form-btn t2" @click="close">关 闭</el-button>
+          <el-button class="xl-form-btn t1" @click="close('confirm')">确定</el-button>
+        </div>
+        <div class="scoped-textarea" slot="otherItem">
+          <div class="st-text">房票科技</div>
+          <tinymce v-model="content" :height="500" />
+        </div>
+      </base-form>
+    </el-drawer>
+  </div>
+</template>
+<script>
+import Tinymce from '@/components/Tinymce'
+export default {
+  components: { Tinymce },
+  mixins,
+  props: {
+    isShow: Boolean,
+    curObj: Object
+  },
+  inject: ['parentData'],
+  data() {
+    return {
+      formData: [],
+      loading: true,
+      cObj: {},
+      content: '',
+    }
+  },
+  watch: {
+    isShow: function(val) {
+      if (val) {
+        if (val) {
+          if (this.curObj.id) {
+            this.$api.house.adminformationdetail({id: this.curObj.id}).then(res => {
+              let curData = res || {}
+              this.cObj = curData || {}
+              this.getDef()
+            })
+          } else {
+            this.cObj = this.curObj
+            this.getDef()
+          }
+        }
+      }
+    },
+  },
+  methods: {
+    getDef() {
+      const params = { ...this.cObj }
+      const disabled = false
+      const remoteOptionsIds = []
+      if (params.estate_list) {
+        params.estate_id_list = params.estate_list.map(item => {
+          remoteOptionsIds.push({ keyRO: item.estate_name, valRO: item.id })
+          return item.id
+        })
+      } else {
+        params.estate_id_list = []
+      }
+      // if (params.id) disabled = true
+      this.formData = [
+        { label: '标题', key: 'title' },
+        { label: '分类', key: 'information_category', type: 'select', class: 'c-2', options: this.$dictData.information_category },
+        { label: '作者', key: 'author', class: 'c-2' },
+        { label: '状态', key: 'hide_status', type: 'select', class: 'c-2', options: this.$dictData.hide_status },
+        { label: '主图', key: 'pri_image', type: 'upload', class: 'c-2' },
+        { label: '关联楼盘', key: 'estate_id_list', type: 'selectRemote', multiple: true, changeHandle: this.deviceChange,
+          remoteParams: { skey: 'estate_name', api: `house.admestatelist?search_EQ_status=1`, opKey: 'estate_name', opVal: 'id' },
+          remoteOptions: remoteOptionsIds
+        },
+      ]
+      this.setDefaultValue(params)
+    },
+    close(str) {
+      if (str === 'confirm') {
+        this.$refs['ruleForm'].$refs['baseForm'].validate((valid) => {
+          if (valid) {
+            const oldform = this.$refs.ruleForm.baseForm
+            const newForm = { ...oldform }
+            if (this.curObj.id) newForm.id = this.curObj.id
+            if (newForm.estate_id_list && newForm.estate_id_list.length > 0) {
+              newForm.estate_id_list = newForm.estate_id_list.join(',')
+            } else {
+              newForm.estate_id_list = ''
+            }
+            let apiStr = 'adminformationadd'
+            if (newForm.id) apiStr = 'adminformationedit'
+            if (this.content) {
+              newForm.content = this.content
+            } else {
+              this.$msg('请输入资讯文章内容')
+              return
+            }
+            this.$api.house[apiStr](newForm).then(data => {
+              this.$msgs(newForm.id ? '编辑成功' : '新增成功')
+              this.$emit('close', newForm)
+            })
+          }
+        })
+      } else {
+        this.$emit('close')
+        this.setDefaultValue()
+      }
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+@import '../../../../styles/libEdit.scss';
+@import '../../../../styles/libEdit.scss';
+.lib-edit {
+  width: 900px;
+  padding-top: 0;
+  padding-left: 0;
+  padding-bottom: 40px;
+  ::v-deep .el-form-item {
+    margin-bottom: 10px;
+  }
+  ::v-deep .el-date-editor.el-input {
+    width: 100%;
+  }
+}
+
+.scoped-textarea {
+  width: 100%;
+  position: relative;
+  .st-text {
+    position: absolute;
+    bottom: 2px;
+    right: 14px;
+    background: #fff;
+    font-size: 12px;
+    z-index: 999999;
+    height: 30px;
+    line-height: 32px;
+    padding-left: 10px;
+    padding-right: 10px;
+    color: #595959;
+    user-select: none;
+  }
+}
+</style>

+ 8 - 0
src/views/news/components/searchForm/Index.vue

@@ -2,6 +2,8 @@
   <base-form slot="content" ref="ruleForm" :data="searchData">
     <div slot="footer">
       <el-button :loading="listLoading" icon="el-icon-search" class="xl-form-btn bgc1" @click="searchHandle">查询</el-button>
+      <el-button type="small" icon="el-icon-plus" class="xl-form-btn bgc1" @click="openPopup">添加公众号文章</el-button>
+      <el-button type="small" icon="el-icon-plus" class="xl-form-btn bgc2" @click="openIESPopup">添加新资讯</el-button>
       <!-- <el-button :loading="listLoading" icon="el-icon-document" class="xl-form-btn bgc2" @click="toExportExcel">导出{{ this.$route.meta.title }}信息</el-button> -->
     </div>
   </base-form>
@@ -22,6 +24,12 @@ export default {
     }
   },
   methods: {
+    openPopup () {
+      this.parentData.openPopup()
+    },
+    openIESPopup () {
+      this.parentData.openIESPopup()
+    },
     searchHandle() {
       const oldform = this.$refs.ruleForm.baseForm
       const newForm = { ...oldform }

+ 29 - 5
src/views/news/index.vue

@@ -12,8 +12,6 @@
       :page-size="pageSize"
       :total-records="totalRecords"
       @currentChange="pageHandle"
-      :isAdd="true"
-      @add="openPopup"
       :insertSlotArr="[4]"
     >
       <div slot="OI4">
@@ -27,24 +25,35 @@
           </template>
         </el-table-column>
       </div>
+      <div slot="otherItem">
+        <el-button type="small" icon="el-icon-plus" class="xl-form-btn bgc1" @click="openPopup">添加公众号文章</el-button>
+        <el-button type="small" icon="el-icon-plus" class="xl-form-btn bgc2" @click="openIESPopup">添加新资讯</el-button>
+      </div>
     </table-list>
     <popup-edit
       :isShow="isDtlShow"
       :curObj="curObj"
       @close="closePopup"
     />
+    <index-edit-self
+      :isShow="isIESShow"
+      :curObj="curObj"
+      @close="closeIESPopup"
+    />
   </div>
 </template>
 <script>
 import { arrToObj } from '@/utils'
 import SearchForm from './components/searchForm/Index'
 import PopupEdit from './components/popup/IndexEdit'
+import IndexEditSelf from './components/popup/IndexEditSelf'
 import baseTable from '_m/baseTable.js'
 export default {
   name: 'index',
   components: {
     SearchForm,
     PopupEdit,
+    IndexEditSelf,
   },
   provide() {
     return {
@@ -57,6 +66,7 @@ export default {
       apiStr: 'house.adminformationlist',
       searchForm: null,
       isDtlShow: false,
+      isIESShow: false,
       curObj: {}
     }
   },
@@ -66,7 +76,7 @@ export default {
       const arr = [...this.tableData]
       arr.map(item => {
         item.pri_image = item.pri_image || defaultImg
-        item.hide_status = Number(item.hide_status)
+        item.hideStatus = Number(item.hide_status)
       })
       return arr
     }
@@ -80,7 +90,7 @@ export default {
         { label: '主图', prop: 'pri_image', type: 'img' },
         // { label: '链接', prop: 'link' },
         { label: '作者', prop: 'author' },
-        { label: '状态', prop: 'hide_status', type: 'tag', tags: arrToObj(this.$dictData.hide_status) }, 
+        { label: '状态', prop: 'hideStatus', type: 'tag', tags: arrToObj(this.$dictData.hide_status) }, 
         { label: '更新人', prop: 'update_by' },
         { label: '更新时间', prop: 'update_at' },
         { label: '操作', width: 120, type: 'handle2', operations:
@@ -116,7 +126,21 @@ export default {
       if (obj) {
         this.fetchData() 
       }
-    }
+    },
+    openIESPopup(row) {
+      if (row && row.id) {
+        this.curObj = row
+      } else {
+        this.curObj = {}
+      }
+      this.isIESShow = true
+    },
+    closeIESPopup(obj) {
+      this.isIESShow = false
+      if (obj) {
+        this.fetchData()
+      }
+    },
   }
 }
 </script>