Преглед на файлове

feat():banner 和 加盟

geek преди 4 години
родител
ревизия
d8446019e2

+ 40 - 0
src/api/banner.js

@@ -0,0 +1,40 @@
+import request from '@/utils/request'
+
+export function fetchList(query) {
+  return request({
+    url: '/Carousel/index',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchBanner(id) {
+  return request({
+    url: '/Carousel/read',
+    method: 'get',
+    params: { id }
+  })
+}
+
+export function createBanner(data) {
+  return request({
+    url: '/Carousel/save',
+    method: 'post',
+    data
+  })
+}
+
+export function updateBanner(data) {
+  return request({
+    url: '/Carousel/update?id=' + data.id,
+    method: 'post',
+    data
+  })
+}
+
+export function deleteBanner(id) {
+  return request({
+    url: '/Carousel/delete?id=' + id,
+    method: 'get'
+  })
+}

+ 40 - 0
src/api/join.js

@@ -0,0 +1,40 @@
+import request from '@/utils/request'
+
+export function fetchList(query) {
+  return request({
+    url: '/join/index',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchJoin(id) {
+  return request({
+    url: '/join/read',
+    method: 'get',
+    params: { id }
+  })
+}
+
+export function createBanner(data) {
+  return request({
+    url: '/Carousel/save',
+    method: 'post',
+    data
+  })
+}
+
+export function updateBanner(data) {
+  return request({
+    url: '/Join/update?id=' + data.id,
+    method: 'post',
+    data
+  })
+}
+
+export function deleteBanner(id) {
+  return request({
+    url: '/Join/delete?id=' + id,
+    method: 'get'
+  })
+}

+ 50 - 0
src/router/index.js

@@ -90,6 +90,56 @@ export const constantRoutes = [
     ]
   },
   {
+    path: '/join',
+    component: Layout,
+    children: [
+      {
+        path: 'list',
+        name: 'join',
+        component: () => import('@/views/join/list'),
+        meta: { title: '加盟申请列表', icon: 'el-icon-s-help' }
+      },
+      {
+        path: 'edit/:id(\\d+)',
+        name: 'editJoin',
+        component: () => import('@/views/join/edit'),
+        meta: { title: '加盟申请查看', icon: 'el-icon-s-help' },
+        hidden: true
+      }
+    ]
+  },
+  {
+    path: '/banner',
+    component: Layout,
+    redirect: '/banner/list',
+    name: 'article',
+    meta: {
+      title: '轮播设置',
+      icon: 'el-icon-s-help'
+    },
+    children: [
+      {
+        path: 'create',
+        component: () => import('@/views/banner/create'),
+        name: 'CreateBanner',
+        meta: { title: '创建轮播图', icon: 'el-icon-edit' }
+      },
+      {
+        path: 'edit/:id(\\d+)',
+        component: () => import('@/views/banner/edit'),
+        name: 'EditBanner',
+        meta: { title: '修改轮播图', noCache: true, activeMenu: '/banner/list' },
+        hidden: true
+      },
+      {
+        path: 'list',
+        component: () => import('@/views/banner/list'),
+        name: 'BannerList',
+        meta: { title: '轮播图列表', icon: 'el-icon-s-order' }
+      }
+    ]
+  },
+  {
     path: '/article',
     component: Layout,
     redirect: '/article/list',

+ 3 - 2
src/utils/request.js

@@ -9,7 +9,7 @@ console.log(process.env.VUE_APP_BASE_API)
 const service = axios.create({
   baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
   // withCredentials: true, // send cookies when cross-domain requests
-  timeout: 5000 // request timeout
+  timeout: 10000 // request timeout
 })
 
 // request interceptor
@@ -22,8 +22,9 @@ service.interceptors.request.use(
       // let each request carry token
       // ['X-Token'] is a custom headers key
       // please modify it according to the actual situation
-      config.headers['X-Token'] = getToken()
+      // config.headers['X-Token'] = getToken()
       config.headers['token'] = getToken()
+      config.headers['flag'] = 'admin'
     }
     return config
   },

+ 265 - 0
src/views/banner/components/ArticleDetail.vue

@@ -0,0 +1,265 @@
+<template>
+  <div class="createPost-container">
+    <el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
+      <div class="createPost-main-container">
+        <el-row>
+          <el-col :span="24">
+            <div class="postInfo-container">
+              <div style="margin-bottom: 10px;">banner图</div>
+              <el-form-item prop="url" style="margin-bottom: 30px;" label="">
+                <Upload v-model="postForm.url" />
+              </el-form-item>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row>
+          <div class="postInfo-container">
+            <div style="margin-bottom: 10px;">跳转路劲</div>
+            <el-form-item prop="redirect" style="margin-bottom: 30px;" label="">
+              <el-input v-model="postForm.redirect" placeholder="请输入路径" style="width: 300px;" />
+            </el-form-item>
+          </div>
+        </el-row>
+        <el-row>
+          <div class="postInfo-container">
+            <div style="margin-bottom: 10px;">排序</div>
+            <el-form-item prop="sort" style="margin-bottom: 30px;" label="">
+              <el-input v-model="postForm.sort" placeholder="排序(倒序)" style="width: 100px;" />
+            </el-form-item>
+          </div>
+        </el-row>
+        <el-row>
+          <div class="postInfo-container">
+            <div style="margin-bottom: 10px;">所属位置</div>
+            <el-form-item prop="position" style="margin-bottom: 30px;" label="">
+              <template>
+                <el-radio-group v-model="postForm.position">
+                  <el-radio :label="1">首页</el-radio>
+                  <el-radio :label="2">活动页</el-radio>
+                  <el-radio :label="3">备选项</el-radio>
+                </el-radio-group>
+              </template>
+            </el-form-item>
+          </div>
+        </el-row>
+        <el-row>
+          <div class="postInfo-container">
+            <div style="margin-bottom: 10px;">状态</div>
+            <el-form-item prop="status" style="margin-bottom: 30px;" label="">
+              <el-switch v-model="postForm.status" active-text="激活" inactive-text="隐藏" />
+            </el-form-item>
+          </div>
+        </el-row>
+        <el-row>
+          <el-button v-if="!isEdit" v-loading="loading" type="success" @click="submitForm">
+            提交
+          </el-button>
+          <el-button v-if="isEdit" v-loading="loading" type="success" @click="updateArticle">
+            修改
+          </el-button>
+        </el-row>
+      </div>
+    </el-form>
+  </div>
+</template>
+
+<script>
+
+import Upload from '@/components/Upload/SingleImage3'
+
+// import Sticky from '@/components/Sticky' // 粘性header组件
+// import { validURL } from '@/utils/validate'
+import { fetchBanner, createBanner, updateBanner } from '@/api/banner'
+// import Warning from './Warning'
+// import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
+
+const defaultForm = {
+  // status: 'draft',
+  redirect: '', // 文章题目
+  status: true, // 文章内容
+  position: 1, // 文章摘要
+  url: '', // 文章外链
+  cover_img: '', // 文章图片
+  display_time: undefined, // 前台展示时间
+  id: undefined,
+  platforms: ['a-platform'],
+  comment_disabled: false,
+  importance: 0
+}
+
+export default {
+  name: 'ArticleDetail',
+  components: { Upload },
+  props: {
+    isEdit: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    const validateRequire = (rule, value, callback) => {
+      if (value === '') {
+        this.$message({
+          message: rule.field + '为必传项',
+          type: 'error'
+        })
+        callback(new Error(rule.field + '为必传项'))
+      } else {
+        callback()
+      }
+    }
+    return {
+      postForm: Object.assign({}, defaultForm),
+      loading: false,
+      userListOptions: [],
+      rules: {
+        url: [{ message: 'banner图不为空', validator: validateRequire }]
+      },
+      tempRoute: {}
+    }
+  },
+  computed: {
+  },
+  created() {
+    if (this.isEdit) {
+      const id = this.$route.params && this.$route.params.id
+      this.fetchData(id)
+    }
+    // Why need to make a copy of this.$route here?
+    // Because if you enter this page and quickly switch tag, may be in the execution of the setTagsViewTitle function, this.$route is no longer pointing to the current page
+    // https://github.com/PanJiaChen/vue-element-admin/issues/1221
+    this.tempRoute = Object.assign({}, this.$route)
+  },
+  methods: {
+    fetchData(id) {
+      fetchBanner(id).then(response => {
+        this.postForm = response.data.info
+        this.postForm.status = this.postForm.status === 1
+        this.postForm.position = this.editPosition(this.postForm.position)
+        // set tags view title
+        // this.setTagsViewTitle()
+        // set page title
+        // this.setPageTitle()
+      }).catch(err => {
+        console.log(err)
+      })
+    },
+    setPageTitle() {
+      const title = 'Edit Article'
+      document.title = `${title} - ${this.postForm.id}`
+    },
+    submitForm() {
+      this.$refs.postForm.validate(valid => {
+        if (valid) {
+          this.loading = true
+          console.log(this.postForm)
+          if (this.postForm.status || this.postForm.status === '') {
+            this.postForm.status = 1
+          }
+          this.postForm.createPosition = this.formatCreatePosition(this.postForm.position)
+          createBanner(this.postForm).then(response => {
+            this.$notify({
+              title: '成功',
+              message: 'banner创建成功',
+              type: 'success',
+              duration: 2000
+            })
+            this.loading = false
+            this.listLoading = false
+            this.$router.push(`/banner/list`)
+          })
+        } else {
+          console.log('error submit!!')
+          return false
+        }
+      })
+    },
+    updateArticle() {
+      console.log(this.postForm)
+      updateBanner(this.postForm).then(response => {
+        this.$notify({
+          title: '修改',
+          message: '修改成功',
+          type: 'success',
+          duration: 2000
+        })
+        this.postForm.status = 'published'
+        this.loading = false
+        this.listLoading = false
+        this.$router.push(`/banner/list`)
+      })
+    },
+    draftForm() {
+      if (this.postForm.content.length === 0 || this.postForm.title.length === 0) {
+        this.$message({
+          message: '请填写必要的标题和内容',
+          type: 'warning'
+        })
+        return
+      }
+      this.$message({
+        message: '保存成功',
+        type: 'success',
+        showClose: true,
+        duration: 1000
+      })
+      this.postForm.status = 'draft'
+    },
+    formatCreatePosition(position) {
+      switch (position) {
+        case 1:
+          return 'HOME'
+        case 2:
+          return 'ACTIVITY'
+      }
+    },
+    editPosition(position) {
+      switch (position) {
+        case 'HOME':
+          return 1
+        case 'ACTIVITY':
+          return 2
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "~@/styles/mixin.scss";
+
+.createPost-container {
+  position: relative;
+
+  .createPost-main-container {
+    padding: 40px 45px 20px 50px;
+
+    .postInfo-container {
+      position: relative;
+      @include clearfix;
+      margin-bottom: 10px;
+
+      .postInfo-container-item {
+        float: left;
+      }
+    }
+  }
+
+  .word-counter {
+    width: 40px;
+    position: absolute;
+    right: 10px;
+    top: 0px;
+  }
+}
+
+.article-textarea ::v-deep {
+  textarea {
+    padding-right: 40px;
+    resize: none;
+    border: none;
+    border-radius: 0px;
+    border-bottom: 1px solid #bfcbd9;
+  }
+}
+</style>

+ 41 - 0
src/views/banner/components/Dropdown/Comment.vue

@@ -0,0 +1,41 @@
+<template>
+  <el-dropdown :show-timeout="100" trigger="click">
+    <el-button plain>
+      {{ !comment_disabled?'Comment: opened':'Comment: closed' }}
+      <i class="el-icon-caret-bottom el-icon--right" />
+    </el-button>
+    <el-dropdown-menu slot="dropdown" class="no-padding">
+      <el-dropdown-item>
+        <el-radio-group v-model="comment_disabled" style="padding: 10px;">
+          <el-radio :label="true">
+            Close comment
+          </el-radio>
+          <el-radio :label="false">
+            Open comment
+          </el-radio>
+        </el-radio-group>
+      </el-dropdown-item>
+    </el-dropdown-menu>
+  </el-dropdown>
+</template>
+
+<script>
+export default {
+  props: {
+    value: {
+      type: Boolean,
+      default: false
+    }
+  },
+  computed: {
+    comment_disabled: {
+      get() {
+        return this.value
+      },
+      set(val) {
+        this.$emit('input', val)
+      }
+    }
+  }
+}
+</script>

+ 46 - 0
src/views/banner/components/Dropdown/Platform.vue

@@ -0,0 +1,46 @@
+<template>
+  <el-dropdown :hide-on-click="false" :show-timeout="100" trigger="click">
+    <el-button plain>
+      Platfroms({{ platforms.length }})
+      <i class="el-icon-caret-bottom el-icon--right" />
+    </el-button>
+    <el-dropdown-menu slot="dropdown" class="no-border">
+      <el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
+        <el-checkbox v-for="item in platformsOptions" :key="item.key" :label="item.key">
+          {{ item.name }}
+        </el-checkbox>
+      </el-checkbox-group>
+    </el-dropdown-menu>
+  </el-dropdown>
+</template>
+
+<script>
+export default {
+  props: {
+    value: {
+      required: true,
+      default: () => [],
+      type: Array
+    }
+  },
+  data() {
+    return {
+      platformsOptions: [
+        { key: 'a-platform', name: 'a-platform' },
+        { key: 'b-platform', name: 'b-platform' },
+        { key: 'c-platform', name: 'c-platform' }
+      ]
+    }
+  },
+  computed: {
+    platforms: {
+      get() {
+        return this.value
+      },
+      set(val) {
+        this.$emit('input', val)
+      }
+    }
+  }
+}
+</script>

+ 38 - 0
src/views/banner/components/Dropdown/SourceUrl.vue

@@ -0,0 +1,38 @@
+<template>
+  <el-dropdown :show-timeout="100" trigger="click">
+    <el-button plain>
+      Link
+      <i class="el-icon-caret-bottom el-icon--right" />
+    </el-button>
+    <el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:400px">
+      <el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
+        <el-input v-model="source_uri" placeholder="Please enter the content">
+          <template slot="prepend">
+            URL
+          </template>
+        </el-input>
+      </el-form-item>
+    </el-dropdown-menu>
+  </el-dropdown>
+</template>
+
+<script>
+export default {
+  props: {
+    value: {
+      type: String,
+      default: ''
+    }
+  },
+  computed: {
+    source_uri: {
+      get() {
+        return this.value
+      },
+      set(val) {
+        this.$emit('input', val)
+      }
+    }
+  }
+}
+</script>

+ 3 - 0
src/views/banner/components/Dropdown/index.js

@@ -0,0 +1,3 @@
+export { default as CommentDropdown } from './Comment'
+export { default as PlatformDropdown } from './Platform'
+export { default as SourceUrlDropdown } from './SourceUrl'

+ 9 - 0
src/views/banner/components/Warning.vue

@@ -0,0 +1,9 @@
+<template>
+  <aside>
+    <!--<a
+      href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
+      target="_blank"
+    >Document</a>-->
+  </aside>
+</template>
+

+ 13 - 0
src/views/banner/create.vue

@@ -0,0 +1,13 @@
+<template>
+  <article-detail :is-edit="false" />
+</template>
+
+<script>
+import ArticleDetail from './components/ArticleDetail'
+
+export default {
+  name: 'CreateArticle',
+  components: { ArticleDetail }
+}
+</script>
+

+ 13 - 0
src/views/banner/edit.vue

@@ -0,0 +1,13 @@
+<template>
+  <article-detail :is-edit="true" />
+</template>
+
+<script>
+import ArticleDetail from './components/ArticleDetail'
+
+export default {
+  name: 'EditForm',
+  components: { ArticleDetail }
+}
+</script>
+

+ 166 - 0
src/views/banner/list.vue

@@ -0,0 +1,166 @@
+<template>
+  <div class="app-container">
+    <div class="filter-container">
+      <router-link :to="{ path: '/banner/create' }">
+        <el-button v-waves class="filter-item" type="primary" icon="el-icon-edit">
+          新建banner
+        </el-button>
+      </router-link>
+      <!--<el-checkbox v-model="showReviewer" class="filter-item" style="margin-left:15px;" @change="tableKey=tableKey+1">
+        reviewer
+      </el-checkbox>-->
+    </div>
+    <el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
+      <el-table-column align="center" label="ID" width="80">
+        <template slot-scope="scope">
+          <span>{{ scope.row.id }}</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column width="180px" align="center" label="日期">
+        <template slot-scope="scope">
+          <span>{{ scope.row.create_time }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column class-name="status-col" label="状态" width="80px">
+        <template slot-scope="{row}">
+          <el-tag :type="row.status | statusFilter">
+            {{ row.status == '1' ? "正常" : "隐藏" }}
+          </el-tag>
+        </template>
+      </el-table-column>
+
+      <el-table-column min-width="300px" label="位置" width="100px">
+        <template slot-scope="{row}">
+          <router-link :to="'/banner/edit/'+row.id" class="link-type">
+            <el-tag :type="row.posiiton | positionFilter">
+              {{ row.position === 'HOME' ? '首页' : '活动页' }}
+            </el-tag>
+          </router-link>
+        </template>
+      </el-table-column>
+      <el-table-column min-width="300px" label="排序" width="80px">
+        <template slot-scope="{row}">
+          <el-tag :type="info">
+            {{ row.sort }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column min-width="300px" label="跳转路径">
+        <template slot-scope="{row}">
+          <span>{{ row.redirect }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column align="center" label="Actions" width="190" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <router-link :to="'/banner/edit/'+scope.row.id">
+            <el-button type="primary" size="mini" icon="el-icon-edit">
+              修改
+            </el-button>
+          </router-link>
+          <el-button type="danger" size="mini" icon="el-icon-delete" style="margin-left: 10px;" @click="handleDelete(scope)">
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.pageSize" @pagination="getList" />
+  </div>
+</template>
+
+<script>
+import { fetchList, deleteBanner } from '@/api/banner'
+import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
+import waves from '@/directive/waves'
+
+export default {
+  name: 'ArticleList',
+  components: { Pagination },
+  directives: { waves },
+  filters: {
+    statusFilter(status) {
+      const statusMap = {
+        1: 'success',
+        draft: 'info',
+        2: 'danger'
+      }
+      return statusMap[status]
+    },
+    positionFilter(status) {
+      const statusMap = {
+        'HOME': 'success',
+        draft: 'info',
+        2: 'danger'
+      }
+      return statusMap[status]
+    }
+  },
+  data() {
+    return {
+      list: null,
+      total: 0,
+      listLoading: true,
+      listQuery: {
+        page: 1,
+        pageSize: 10
+      }
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    getList() {
+      this.listLoading = true
+      fetchList(this.listQuery).then(response => {
+        this.list = response.data.list
+        this.total = response.data.count
+        this.listLoading = false
+      })
+    },
+    handleFilter() {
+      this.listLoading = true
+      fetchList(this.listQuery).then(response => {
+        this.list = response.data.list
+        this.total = response.data.count
+        this.listLoading = false
+      })
+    },
+    handleDelete({ $index, row }) {
+      console.log(row.id)
+      this.$confirm('您确定要隐藏吗', '警告', {
+        confirmButtonText: '是的',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async() => {
+          await deleteBanner(row.id)
+          this.list.splice($index, 1)
+          this.$message({
+            type: 'success',
+            message: '隐藏成功'
+          })
+        })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.edit-input {
+  padding-right: 100px;
+}
+.cancel-btn {
+  position: absolute;
+  right: 15px;
+  top: 10px;
+}
+.filter-container{
+  margin-bottom: 20px;
+}
+.filter-item {
+  margin-right: 10px;
+}
+
+</style>

+ 265 - 0
src/views/join/components/ArticleDetail.vue

@@ -0,0 +1,265 @@
+<template>
+  <div class="createPost-container">
+    <el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
+      <div class="createPost-main-container">
+        <el-row>
+          <el-col :span="24">
+            <div class="postInfo-container">
+              <div style="margin-bottom: 10px;">banner图</div>
+              <el-form-item prop="url" style="margin-bottom: 30px;" label="">
+                <Upload v-model="postForm.url" />
+              </el-form-item>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row>
+          <div class="postInfo-container">
+            <div style="margin-bottom: 10px;">跳转路劲</div>
+            <el-form-item prop="redirect" style="margin-bottom: 30px;" label="">
+              <el-input v-model="postForm.redirect" placeholder="请输入路径" style="width: 300px;" />
+            </el-form-item>
+          </div>
+        </el-row>
+        <el-row>
+          <div class="postInfo-container">
+            <div style="margin-bottom: 10px;">排序</div>
+            <el-form-item prop="sort" style="margin-bottom: 30px;" label="">
+              <el-input v-model="postForm.sort" placeholder="排序(倒序)" style="width: 100px;" />
+            </el-form-item>
+          </div>
+        </el-row>
+        <el-row>
+          <div class="postInfo-container">
+            <div style="margin-bottom: 10px;">所属位置</div>
+            <el-form-item prop="position" style="margin-bottom: 30px;" label="">
+              <template>
+                <el-radio-group v-model="postForm.position">
+                  <el-radio :label="1">首页</el-radio>
+                  <el-radio :label="2">活动页</el-radio>
+                  <el-radio :label="3">备选项</el-radio>
+                </el-radio-group>
+              </template>
+            </el-form-item>
+          </div>
+        </el-row>
+        <el-row>
+          <div class="postInfo-container">
+            <div style="margin-bottom: 10px;">状态</div>
+            <el-form-item prop="status" style="margin-bottom: 30px;" label="">
+              <el-switch v-model="postForm.status" active-text="激活" inactive-text="隐藏" />
+            </el-form-item>
+          </div>
+        </el-row>
+        <el-row>
+          <el-button v-if="!isEdit" v-loading="loading" type="success" @click="submitForm">
+            提交
+          </el-button>
+          <el-button v-if="isEdit" v-loading="loading" type="success" @click="updateArticle">
+            修改
+          </el-button>
+        </el-row>
+      </div>
+    </el-form>
+  </div>
+</template>
+
+<script>
+
+import Upload from '@/components/Upload/SingleImage3'
+
+// import Sticky from '@/components/Sticky' // 粘性header组件
+// import { validURL } from '@/utils/validate'
+import { fetchBanner, createBanner, updateBanner } from '@/api/banner'
+// import Warning from './Warning'
+// import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
+
+const defaultForm = {
+  // status: 'draft',
+  redirect: '', // 文章题目
+  status: true, // 文章内容
+  position: 1, // 文章摘要
+  url: '', // 文章外链
+  cover_img: '', // 文章图片
+  display_time: undefined, // 前台展示时间
+  id: undefined,
+  platforms: ['a-platform'],
+  comment_disabled: false,
+  importance: 0
+}
+
+export default {
+  name: 'ArticleDetail',
+  components: { Upload },
+  props: {
+    isEdit: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    const validateRequire = (rule, value, callback) => {
+      if (value === '') {
+        this.$message({
+          message: rule.field + '为必传项',
+          type: 'error'
+        })
+        callback(new Error(rule.field + '为必传项'))
+      } else {
+        callback()
+      }
+    }
+    return {
+      postForm: Object.assign({}, defaultForm),
+      loading: false,
+      userListOptions: [],
+      rules: {
+        url: [{ message: 'banner图不为空', validator: validateRequire }]
+      },
+      tempRoute: {}
+    }
+  },
+  computed: {
+  },
+  created() {
+    if (this.isEdit) {
+      const id = this.$route.params && this.$route.params.id
+      this.fetchData(id)
+    }
+    // Why need to make a copy of this.$route here?
+    // Because if you enter this page and quickly switch tag, may be in the execution of the setTagsViewTitle function, this.$route is no longer pointing to the current page
+    // https://github.com/PanJiaChen/vue-element-admin/issues/1221
+    this.tempRoute = Object.assign({}, this.$route)
+  },
+  methods: {
+    fetchData(id) {
+      fetchBanner(id).then(response => {
+        this.postForm = response.data.info
+        this.postForm.status = this.postForm.status === 1
+        this.postForm.position = this.editPosition(this.postForm.position)
+        // set tags view title
+        // this.setTagsViewTitle()
+        // set page title
+        // this.setPageTitle()
+      }).catch(err => {
+        console.log(err)
+      })
+    },
+    setPageTitle() {
+      const title = 'Edit Article'
+      document.title = `${title} - ${this.postForm.id}`
+    },
+    submitForm() {
+      this.$refs.postForm.validate(valid => {
+        if (valid) {
+          this.loading = true
+          console.log(this.postForm)
+          if (this.postForm.status || this.postForm.status === '') {
+            this.postForm.status = 1
+          }
+          this.postForm.createPosition = this.formatCreatePosition(this.postForm.position)
+          createBanner(this.postForm).then(response => {
+            this.$notify({
+              title: '成功',
+              message: 'banner创建成功',
+              type: 'success',
+              duration: 2000
+            })
+            this.loading = false
+            this.listLoading = false
+            this.$router.push(`/banner/list`)
+          })
+        } else {
+          console.log('error submit!!')
+          return false
+        }
+      })
+    },
+    updateArticle() {
+      console.log(this.postForm)
+      updateBanner(this.postForm).then(response => {
+        this.$notify({
+          title: '修改',
+          message: '修改成功',
+          type: 'success',
+          duration: 2000
+        })
+        this.postForm.status = 'published'
+        this.loading = false
+        this.listLoading = false
+        this.$router.push(`/banner/list`)
+      })
+    },
+    draftForm() {
+      if (this.postForm.content.length === 0 || this.postForm.title.length === 0) {
+        this.$message({
+          message: '请填写必要的标题和内容',
+          type: 'warning'
+        })
+        return
+      }
+      this.$message({
+        message: '保存成功',
+        type: 'success',
+        showClose: true,
+        duration: 1000
+      })
+      this.postForm.status = 'draft'
+    },
+    formatCreatePosition(position) {
+      switch (position) {
+        case 1:
+          return 'HOME'
+        case 2:
+          return 'ACTIVITY'
+      }
+    },
+    editPosition(position) {
+      switch (position) {
+        case 'HOME':
+          return 1
+        case 'ACTIVITY':
+          return 2
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "~@/styles/mixin.scss";
+
+.createPost-container {
+  position: relative;
+
+  .createPost-main-container {
+    padding: 40px 45px 20px 50px;
+
+    .postInfo-container {
+      position: relative;
+      @include clearfix;
+      margin-bottom: 10px;
+
+      .postInfo-container-item {
+        float: left;
+      }
+    }
+  }
+
+  .word-counter {
+    width: 40px;
+    position: absolute;
+    right: 10px;
+    top: 0px;
+  }
+}
+
+.article-textarea ::v-deep {
+  textarea {
+    padding-right: 40px;
+    resize: none;
+    border: none;
+    border-radius: 0px;
+    border-bottom: 1px solid #bfcbd9;
+  }
+}
+</style>

+ 41 - 0
src/views/join/components/Dropdown/Comment.vue

@@ -0,0 +1,41 @@
+<template>
+  <el-dropdown :show-timeout="100" trigger="click">
+    <el-button plain>
+      {{ !comment_disabled?'Comment: opened':'Comment: closed' }}
+      <i class="el-icon-caret-bottom el-icon--right" />
+    </el-button>
+    <el-dropdown-menu slot="dropdown" class="no-padding">
+      <el-dropdown-item>
+        <el-radio-group v-model="comment_disabled" style="padding: 10px;">
+          <el-radio :label="true">
+            Close comment
+          </el-radio>
+          <el-radio :label="false">
+            Open comment
+          </el-radio>
+        </el-radio-group>
+      </el-dropdown-item>
+    </el-dropdown-menu>
+  </el-dropdown>
+</template>
+
+<script>
+export default {
+  props: {
+    value: {
+      type: Boolean,
+      default: false
+    }
+  },
+  computed: {
+    comment_disabled: {
+      get() {
+        return this.value
+      },
+      set(val) {
+        this.$emit('input', val)
+      }
+    }
+  }
+}
+</script>

+ 46 - 0
src/views/join/components/Dropdown/Platform.vue

@@ -0,0 +1,46 @@
+<template>
+  <el-dropdown :hide-on-click="false" :show-timeout="100" trigger="click">
+    <el-button plain>
+      Platfroms({{ platforms.length }})
+      <i class="el-icon-caret-bottom el-icon--right" />
+    </el-button>
+    <el-dropdown-menu slot="dropdown" class="no-border">
+      <el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
+        <el-checkbox v-for="item in platformsOptions" :key="item.key" :label="item.key">
+          {{ item.name }}
+        </el-checkbox>
+      </el-checkbox-group>
+    </el-dropdown-menu>
+  </el-dropdown>
+</template>
+
+<script>
+export default {
+  props: {
+    value: {
+      required: true,
+      default: () => [],
+      type: Array
+    }
+  },
+  data() {
+    return {
+      platformsOptions: [
+        { key: 'a-platform', name: 'a-platform' },
+        { key: 'b-platform', name: 'b-platform' },
+        { key: 'c-platform', name: 'c-platform' }
+      ]
+    }
+  },
+  computed: {
+    platforms: {
+      get() {
+        return this.value
+      },
+      set(val) {
+        this.$emit('input', val)
+      }
+    }
+  }
+}
+</script>

+ 38 - 0
src/views/join/components/Dropdown/SourceUrl.vue

@@ -0,0 +1,38 @@
+<template>
+  <el-dropdown :show-timeout="100" trigger="click">
+    <el-button plain>
+      Link
+      <i class="el-icon-caret-bottom el-icon--right" />
+    </el-button>
+    <el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:400px">
+      <el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
+        <el-input v-model="source_uri" placeholder="Please enter the content">
+          <template slot="prepend">
+            URL
+          </template>
+        </el-input>
+      </el-form-item>
+    </el-dropdown-menu>
+  </el-dropdown>
+</template>
+
+<script>
+export default {
+  props: {
+    value: {
+      type: String,
+      default: ''
+    }
+  },
+  computed: {
+    source_uri: {
+      get() {
+        return this.value
+      },
+      set(val) {
+        this.$emit('input', val)
+      }
+    }
+  }
+}
+</script>

+ 3 - 0
src/views/join/components/Dropdown/index.js

@@ -0,0 +1,3 @@
+export { default as CommentDropdown } from './Comment'
+export { default as PlatformDropdown } from './Platform'
+export { default as SourceUrlDropdown } from './SourceUrl'

+ 9 - 0
src/views/join/components/Warning.vue

@@ -0,0 +1,9 @@
+<template>
+  <aside>
+    <!--<a
+      href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
+      target="_blank"
+    >Document</a>-->
+  </aside>
+</template>
+

+ 13 - 0
src/views/join/create.vue

@@ -0,0 +1,13 @@
+<template>
+  <article-detail :is-edit="false" />
+</template>
+
+<script>
+import ArticleDetail from './components/ArticleDetail'
+
+export default {
+  name: 'CreateArticle',
+  components: { ArticleDetail }
+}
+</script>
+

+ 13 - 0
src/views/join/edit.vue

@@ -0,0 +1,13 @@
+<template>
+  <article-detail :is-edit="true" />
+</template>
+
+<script>
+import ArticleDetail from './components/ArticleDetail'
+
+export default {
+  name: 'EditForm',
+  components: { ArticleDetail }
+}
+</script>
+

+ 165 - 0
src/views/join/list.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="app-container">
+    <div class="filter-container">
+      <el-input v-model="listQuery.mobile" placeholder="手机号" style="width: 200px;" class="filter-item" @keyup.enter.native="handleFilter" />
+      <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
+        搜索
+      </el-button>
+    </div>
+    <el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
+      <el-table-column align="center" label="ID" width="80">
+        <template slot-scope="scope">
+          <span>{{ scope.row.id }}</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column width="180px" align="center" label="申请日期">
+        <template slot-scope="scope">
+          <span>{{ scope.row.create_time }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column class-name="status-col" label="手机号" width="100px">
+        <template slot-scope="scope">
+          <span>{{ scope.row.mobile }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column class-name="status-col" label="申请人姓名" width="100px">
+        <template slot-scope="scope">
+          <span>{{ scope.row.name }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column class-name="status-col" label="商务对接人" width="100px">
+        <template slot-scope="scope">
+          <span>{{ scope.row.meet_people }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column class-name="status-col" label="投资金额" width="100px">
+        <template slot-scope="scope">
+          <span>{{ scope.row.investment_money }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column class-name="status-col" label="沪上阿姨了解情况">
+        <template slot-scope="scope">
+          <span>{{ scope.row.sub_ayi }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column class-name="status-col" label="状态" width="80px">
+        <template slot-scope="{row}">
+          <el-tag :type="row.status | statusFilter">
+            {{ row.status == '1' ? "已处理" : "未处理" }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column align="center" label="Actions" width="190" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <router-link :to="'/join/edit/'+scope.row.id">
+            <el-button type="primary" size="mini" icon="el-icon-edit">
+              查看
+            </el-button>
+          </router-link>
+          <el-button type="danger" size="mini" icon="el-icon-delete" style="margin-left: 10px;" @click="handleDelete(scope)">
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.pageSize" @pagination="getList" />
+  </div>
+</template>
+
+<script>
+import { fetchList, deleteBanner } from '@/api/join'
+import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
+import waves from '@/directive/waves'
+
+export default {
+  name: 'ArticleList',
+  components: { Pagination },
+  directives: { waves },
+  filters: {
+    statusFilter(status) {
+      const statusMap = {
+        1: 'success',
+        draft: 'info',
+        0: 'danger'
+      }
+      return statusMap[status]
+    },
+    positionFilter(status) {
+      const statusMap = {
+        'HOME': 'success',
+        draft: 'info',
+        2: 'danger'
+      }
+      return statusMap[status]
+    }
+  },
+  data() {
+    return {
+      list: null,
+      total: 0,
+      listLoading: true,
+      listQuery: {
+        page: 1,
+        pageSize: 10
+      }
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    getList() {
+      this.listLoading = true
+      fetchList(this.listQuery).then(response => {
+        this.list = response.data.list
+        this.total = response.data.count
+        this.listLoading = false
+      })
+    },
+    handleFilter() {
+      this.listLoading = true
+      fetchList(this.listQuery).then(response => {
+        this.list = response.data.list
+        this.total = response.data.count
+        this.listLoading = false
+      })
+    },
+    handleDelete({ $index, row }) {
+      console.log(row.id)
+      this.$confirm('您确定要隐藏吗', '警告', {
+        confirmButtonText: '是的',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async() => {
+          await deleteBanner(row.id)
+          this.list.splice($index, 1)
+          this.$message({
+            type: 'success',
+            message: '隐藏成功'
+          })
+        })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.edit-input {
+  padding-right: 100px;
+}
+.cancel-btn {
+  position: absolute;
+  right: 15px;
+  top: 10px;
+}
+.filter-container{
+  margin-bottom: 20px;
+}
+.filter-item {
+  margin-right: 10px;
+}
+
+</style>