Ver código fonte

文章编辑

xinfeng 6 anos atrás
pai
commit
65f0a76360
2 arquivos alterados com 335 adições e 162 exclusões
  1. 140 162
      components/topics/editor.vue
  2. 195 0
      components/user/jishuin/topicCell.vue

+ 140 - 162
components/topics/editor.vue

@@ -1,64 +1,46 @@
 <template>
   <div class="editor">
-    <el-input v-model="title" class="title" placeholder="请输入文章标题" :maxlength="50"></el-input>
-    <el-input
-      type="textarea"
-      :autosize="{ minRows: 1, maxRows: 8}"
-      v-model="subTitle"
-      class="sub-title"
-      placeholder="请输入导语(选填)"
-      :maxlength="300"
-    ></el-input>
-    <editor :content="content" @change="handleChange"></editor>
-    <!-- <div class="quill-editor" v-model="content" v-quill:myQuillEditor="editorOption"></div>
-    <input type="file" id="imgInput" @change="handleContentFileChange" style="display: none;" />-->
+    <el-input show-word-limit v-model="title" class="title" placeholder="请输入文章标题" :maxlength="60"></el-input>
+    <editor placeholder="支持富文本、支持超链接、插入图片、插入视频,最少100字符,最多10000字符" :content="content" @change="handleChange"></editor>
     <h5 class="label">封面图</h5>
-    <el-upload
-      class="avatar-uploader"
-      action="#"
-      :show-file-list="false"
-      :multiple="false"
-      accept="image/png, image/jpeg"
-      :before-upload="handleFileChange"
-    >
-      <i
-        v-if="cover_url"
-        class="el-icon-delete avatar-uploader-icon"
-        @click.stop="handleDeleteFile"
-      ></i>
-      <img v-if="cover_url" :src="cover_url" class="avatar" />
-      <i v-else class="el-icon-plus avatar-uploader-icon"></i>
-      <div slot="tip" class="el-upload__tip">建议尺寸:480*330,大小在500k以内</div>
-    </el-upload>
-    <el-dialog :visible.sync="dialogVisible">
-      <img width="100%" :src="cover_url" alt />
-    </el-dialog>
-    <h5 class="label">选择分类</h5>
-    <el-select v-model="categoryId" placeholder="请选择文章分类" class="class">
-      <el-option
-        v-for="item in categoryList"
-        :key="item.value"
-        :label="item.label"
-        :value="item.value"
-      ></el-option>
-    </el-select>
-    <h5 class="label">标签</h5>
-    <el-select
-      class="tags"
-      v-model="tags"
-      multiple
-      filterable
-      allow-create
-      default-first-option
-      placeholder="每个标签以enter键隔开、最多5个,如:技术,经验"
-    >
+
+    <div class="uploadInfo">
+      <div class="left">
+        <el-upload
+          class="avatar-uploader"
+          action="#"
+          :show-file-list="false"
+          :multiple="false"
+          accept="image/png, image/jpeg"
+          :before-upload="handleFileChange"
+        >
+          <i v-if="cover_url" class="el-icon-delete avatar-uploader-icon"
+            @click.stop="handleDeleteFile"></i>
+          <img v-if="cover_url" :src="cover_url" class="avatar"/>
+          <div v-else class="noneImage">
+            <i class="el-icon-plus avatar-uploader-icon"></i>
+          </div>
+        </el-upload>
+      </div>
+      <div class="right">
+        <p>建议尺寸: </p>
+        <p>180*140 大小在500k以内</p>
+      </div>
+    </div>
+
+    <h5 class="label">添加到合集(选填)</h5>
+    <el-select v-model="collectionId" placeholder="请选择合集" class="class">
       <el-option
-        v-for="item in tagOptions"
-        :key="item.value"
-        :label="item.label"
-        :value="item.value"
+        v-for="item in collectionList"
+        :key="item.id"
+        :label="item.title"
+        :value="item.id"
       ></el-option>
     </el-select>
+
+    <el-dialog :visible.sync="dialogVisible">
+      <img width="100%" :src="cover_url" alt />
+    </el-dialog>
     <footer>
       <el-button type="primary" @click="publish">发布</el-button>
       <el-button @click="cancel">取消</el-button>
@@ -84,10 +66,10 @@ export default {
       subTitle: "",
       content: "",
       cover_url: "",
-      tags: [],
-      categoryId: "",
       dialogVisible: false,
       disabled: false,
+      collectionId: "",
+      collectionList: [],
       categoryList: [
         {
           value: "default",
@@ -95,63 +77,17 @@ export default {
         }
       ],
       fileList: [],
-      tagOptions: [
-        {
-          value: "技术",
-          label: "技术"
-        },
-        {
-          value: "人工智能",
-          label: "人工智能"
-        },
-        {
-          value: "区块链",
-          label: "区块链"
-        }
-      ],
       uploading: false
-      // editorOption: {
-      //   theme: "snow",
-      //   placeholder: "请输入正文...",
-      //   modules: {
-      //     toolbar: [
-      //       ["bold", "italic", "underline", "strike"],
-      //       ["blockquote", "code-block"],
-      //       [{ header: 1 }, { header: 2 }],
-      //       [{ list: "ordered" }, { list: "bullet" }],
-      //       [{ script: "sub" }, { script: "super" }],
-      //       [{ indent: "-1" }, { indent: "+1" }],
-      //       [{ direction: "rtl" }],
-      //       [{ size: ["small", false, "large", "huge"] }],
-      //       [{ header: [1, 2, 3, 4, 5, 6, false] }],
-      //       [{ font: [] }],
-      //       [{ color: [] }, { background: [] }],
-      //       [{ align: [] }],
-      //       ["clean"],
-      //       ["link", "image"]
-      //     ],
-      //     syntax: {
-      //       highlight: text => hljs.highlightAuto(text).value
-      //     }
-      //   }
-      // }
     };
   },
   computed: {},
   mounted() {
     this.needLogin();
     this.needVerify();
+    this.getHejiList()
     if (this.$route.params.id) {
       this.loadData();
     }
-    this.$axios.$post(`/api/community/topic/get_category`).then(res => {
-      if (res.status === 1) {
-        this.categoryList = res.data.map(it => ({
-          value: it.id,
-          label: it.name
-        }));
-      }
-    });
   },
   methods: {
     loadData() {
@@ -169,8 +105,6 @@ export default {
             this.title = topic.title;
             this.subTitle = topic.intro;
             this.content = topic.body;
-            this.tags = topic.label ? topic.label.split(",") : "";
-            this.categoryId = topic.category_id + "";
             if (this.cover_url) {
               this.fileList.push({
                 name: "cover_image",
@@ -183,34 +117,49 @@ export default {
           // TODO go details
         });
     },
+    getHejiList() {
+      this.$axios.post("/api/jishuquan/get_collections", {
+        uid: this.$store.state.userinfo.uid,
+        page: 1,
+        size: 200
+      }).then(res=>{
+        if (res.data.status === 1) {
+          this.collectionList = res.data.data
+        }
+      })
+    },
     publish() {
       this.needVerify();
-      if (!this.content) {
-        this.$message.error("请输入文章正文");
+      if (!this.title) {
+        this.$message.error("请输入文章标题");
         return;
       }
-      if (this.content.length > 100000) {
-        this.$message.error("文章正文不可超过10万字符,请删减");
+      if (this.title.length < 2) {
+        this.$message.error("文章标题不可少于2字符");
         return;
       }
-      if (!this.title) {
-        this.$message.error("请输入文章标题");
+      if (this.title.length > 60) {
+        this.$message.error("文章标题不可超过60字符,请删减");
+        return;
+      }
+
+      if (!this.content) {
+        this.$message.error("请输入文章正文");
         return;
       }
-      if (!this.categoryId) {
-        this.$message.error("请选择文章分类");
+      if (this.content.length < 100) {
+        this.$message.error("文章正文不可少于100字符");
         return;
       }
-      if (this.tags.length > 5) {
-        this.$message.error("标签最多可填写5个标签,请删减");
+      if (this.content.length > 100000) {
+        this.$message.error("文章正文不可超过1万字符,请删减");
         return;
       }
       const data = {
         title: this.title,
         intro: this.subTitle,
         body: this.content,
-        categoryId: parseInt(this.categoryId),
-        label: this.tags && this.tags.length > 0 ? this.tags.join(",") : "",
+        collection_id: this.collectionId,
         cover_url: this.cover_url
       };
       if (this.topicId) {
@@ -323,50 +272,6 @@ export default {
       padding-left: 0;
     }
   }
-  .el-icon-delete {
-    display: none;
-  }
-  .avatar-uploader .el-upload {
-    width: 160px;
-    height: 110px;
-    border: 1px dashed #d9d9d9;
-    border-radius: 6px;
-    cursor: pointer;
-    position: relative;
-    overflow: hidden;
-    img {
-      width: 100%;
-      height: auto;
-      object-fit: contain;
-      object-position: top left;
-    }
-  }
-  .avatar-uploader .el-upload:hover {
-    border-color: #409eff;
-    .el-icon-delete {
-      display: block;
-    }
-  }
-  .avatar-uploader-icon {
-    position: absolute;
-    top: 0;
-    left: 0;
-    font-size: 28px;
-    color: #fff;
-    width: 160px;
-    height: 110px;
-    line-height: 110px;
-    text-align: center;
-    background: rgba(1, 1, 1, 0.3);
-    :hover {
-      color: #409eff;
-    }
-  }
-  .avatar {
-    width: 178px;
-    height: 178px;
-    display: block;
-  }
   .label {
     margin: 20px auto 10px;
     font-size: 13px;
@@ -409,4 +314,77 @@ export default {
     font-style: italic !important;
   }
 }
+
+.uploadInfo {
+  display: flex;
+  align-items: center;
+  .left {
+    width: 180px;
+    flex-shrink: 0;
+    overflow: hidden;
+    position: relative;
+    background: #fff;
+    .el-icon-delete {
+      display: none;
+    }
+    .avatar-uploader .el-upload {
+      width: 180px;
+      height: 140px;
+      border: 1px dashed #dce1e8;
+      border-radius: 6px;
+      cursor: pointer;
+      position: relative;
+      overflow: hidden;
+      img {
+        width: 100%;
+        height: auto;
+        object-fit: contain;
+        object-position: top left;
+      }
+    }
+    .avatar-uploader .el-upload:hover {
+      border-color: #409eff;
+      .el-icon-delete {
+        display: block;
+      }
+    }
+    .avatar-uploader-icon {
+      position: absolute;
+      top: 0;
+      left: 0;
+      font-size: 44px;
+      color: #dce1e8;;
+      width: 180px;
+      height: 140px;
+      line-height: 140px;
+      text-align: center;
+    }
+    .avatar {
+      width: 180px;
+      height: 140px;
+      display: block;
+    }
+    .title {
+      position: absolute;
+      left: 50%;
+      bottom: 40px;
+      transform: translateX(-50%);
+      font-size: 13px;
+      font-weight: 500;
+      color: #dce1e8;
+      line-height: 18px;
+      text-decoration: underline;
+    }
+  }
+  .right {
+    margin-left: 18px;
+    height:34px;
+    p {
+      font-size:12px;
+      font-weight:400;
+      color:rgba(145,154,167,1);
+      line-height:17px;
+    }
+  }
+}
 </style>

+ 195 - 0
components/user/jishuin/topicCell.vue

@@ -0,0 +1,195 @@
+<template>
+  <div class="bindMobile">
+    <el-dialog
+      title="绑定手机号"
+      :visible.sync="isShowToast"
+      width="520px"
+      :center="true">
+      <div class="bindMobileContent">
+        <div class="phoneNum">
+          <p class="name">手机号</p>
+          <el-input v-model="phone" placeholder="请输入手机号" class="input-with-select">
+            <el-select v-model="preMobile" slot="prepend" placeholder="请选择" style="width: 140px;">
+              <el-option
+                v-for="(item,index) in selectList"
+                :key="index+'sda'"
+                :label="item.name"
+                :value="item.pre">
+              </el-option>
+            </el-select>
+            <div slot="append" @click="getCode" :class="{sendBtn: true, ok: !interval}">
+                {{interval ? `${timeLoop}重新发送` : "发送验证码"}}
+            </div>
+          </el-input>
+        </div>
+        <div class="codeNum">
+          <div class="name">验证码</div>
+          <el-input placeholder="请输入验证码" v-model="scode"/>
+        </div>
+      </div>
+      <span slot="footer" class="dialog-footer">
+    <el-button @click="isShowToast = false">取 消</el-button>
+    <el-button type="primary" @click="bindMobile">确 定</el-button>
+  </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  const Max_Time = 60
+  export default {
+    props: [ "source" ],
+    components: {},
+    data() {
+      return {
+        phone: "",
+        scode: "",
+        isShowToast: false,
+        timeLoop: Max_Time,
+        preMobile: "+86",
+        selectList: [],
+        interval: null,
+      };
+    },
+    computed: {},
+    created() {
+      this.getMobilePre()
+    },
+    mounted() {
+    },
+    methods: {
+      open() {
+        this.isShowToast = true
+      },
+      close() {
+        this.isShowToast = false
+      },
+      handleSubmit() {
+
+      },
+
+      /** 发送验证码 **/
+      getCode() {
+        const {phone, preMobile} = this
+        if ( (phone.toString().length != 11 && preMobile == 0 )
+          || (preMobile > 0 && !/^\d{8,15}$/.exec(phone)) ) {
+          this.$message.warning('请输入正确的手机号码');
+          return false;
+        }
+
+        if (this.interval) {
+          this.$message.info('请1分钟后再次发送')
+          return false
+        }
+
+        let newPhone =  preMobile + '-' + phone;
+        this.$axios.post('/api/user/check_mobile_exist', {mobile: newPhone}).then(res=>{
+          if (Number(res.data.status) === 1 && !res.data.data.exist) {
+            this.sendCode()
+          }
+        })
+      },
+
+      /** 发送验证码 **/
+      sendCode() {
+        this.timeLoop = Max_Time
+        const {phone, preMobile} = this
+        let newPhone =  phone;
+        if (preMobile !== '+86') {
+          newPhone =  preMobile + '-' + phone;
+        }
+        this.$axios.post('/api/user/send_mobile_auth_code', {
+          mobile: newPhone
+        }).then(res=>{
+          if (Number(res.data.status) === 1) {
+            this.interval = setInterval(()=>{
+              this.timeLoop--
+              if (this.timeLoop <= 0) {
+                clearInterval(this.interval)
+                this.interval = null
+              }
+            }, 1000)
+          }
+        })
+      },
+
+      /** 获取数据 -- 手机号码前缀 **/
+      getMobilePre() {
+        this.$axios.get('/api/user/get_mobile_pre_arr').then(res=>{
+          if (Number(res.data.status) === 1) {
+            let list = res.data.data && res.data.data.mobilePreArr || [{id: 0, name: "中国 +86", pre: "+86"}]
+            this.selectList = list
+          }
+        })
+      },
+      /** 开始绑定手机号 **/
+      bindMobile() {
+        const {phone, preMobile, scode} = this
+        let newPhone =  preMobile + '-' + phone;
+        this.$axios.post('/api/user/update_info', {
+          login_mobile: newPhone,
+          auth_code: scode
+        }).then(res=>{
+          if (Number(res.data.status) === 1) {
+            this.updateUserInfo()
+            this.close()
+          }
+        })
+      }
+    }
+  };
+</script>
+
+<style lang="scss">
+  .bindMobile {
+    .bindMobileContent {
+      .phoneNum, .codeNum {
+        margin-top: 20px;
+        display: flex;
+        align-items: center;
+        .name {
+          flex-shrink: 0;
+          font-size: 14px;
+          font-weight: 500;
+          color: #19222e;
+          line-height: 20px;
+        }
+        .el-input {
+          flex-grow: 1;
+          margin-left: 10px;
+
+          .el-input-group__append {
+            background-color: transparent;
+            border: none;
+            padding: 0;
+
+            .sendBtn {
+              background-color: #F5F7FA;
+              color: #909399;
+              vertical-align: middle;
+              display: table-cell;
+              position: relative;
+              border: 1px solid #DCDFE6;
+              border-radius: 4px;
+              padding: 10px 20px;
+              width: 1px;
+              white-space: nowrap;
+              cursor: pointer;
+              border-top-left-radius: 0;
+              border-bottom-left-radius: 0;
+
+              &.ok {
+                background-color: #308EFF;
+                color: #fff;
+                vertical-align: middle;
+                display: table-cell;
+                position: relative;
+                border: 1px solid #308EFF;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+</style>