Просмотр исходного кода

管理后台-商品管理:商品列表

panyong 3 лет назад
Родитель
Сommit
56bf338918

+ 111 - 0
htmldev/manage/src/components/Tinymce/components/EditorImage.vue

@@ -0,0 +1,111 @@
+<template>
+  <div class="upload-container">
+    <el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
+      upload
+    </el-button>
+    <el-dialog :visible.sync="dialogVisible">
+      <el-upload
+        :multiple="true"
+        :file-list="fileList"
+        :show-file-list="true"
+        :on-remove="handleRemove"
+        :on-success="handleSuccess"
+        :before-upload="beforeUpload"
+        class="editor-slide-upload"
+        action="https://httpbin.org/post"
+        list-type="picture-card"
+      >
+        <el-button size="small" type="primary">
+          Click upload
+        </el-button>
+      </el-upload>
+      <el-button @click="dialogVisible = false">
+        Cancel
+      </el-button>
+      <el-button type="primary" @click="handleSubmit">
+        Confirm
+      </el-button>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// import { getToken } from 'api/qiniu'
+
+export default {
+  name: 'EditorSlideUpload',
+  props: {
+    color: {
+      type: String,
+      default: '#1890ff'
+    }
+  },
+  data() {
+    return {
+      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.files.file
+          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
htmldev/manage/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
htmldev/manage/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['en'],
+        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
htmldev/manage/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
htmldev/manage/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

+ 0 - 1
htmldev/manage/src/views/business/pms/category/details.vue

@@ -37,7 +37,6 @@
       </el-form>
       <div slot="footer" class="dialog-footer text-center">
         <el-button @click="dialog = false">取 消</el-button>
-        <el-button @click.prevent="addDomain">新增桌号</el-button>
         <el-button type="primary" @click="handleSubmit">确 定</el-button>
       </div>
     </el-dialog>

+ 221 - 0
htmldev/manage/src/views/business/pms/goods/details.vue

@@ -0,0 +1,221 @@
+<template>
+  <div>
+    <el-dialog :title="exData.id ? '编辑': '新增'"
+               :visible.sync="dialog"
+               width="50%"
+               :close-on-click-modal="false"
+               top="50px">
+      <el-form ref="form"
+               :model="form"
+               :rules="formRules"
+               label-width="160px">
+        <el-form-item label="商品分类:">
+          <el-select v-model="form.productAttributeCategoryId" placeholder="请选择">
+            <el-option
+              v-for="item in [{id: 1, name: '柜台'},{id: 2, name: '厨房'}]"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item prop="bar_name"
+                      :rules="formRules.required"
+                      label="商品名称:">
+          <el-col :span="16">
+            <el-input v-model="form.bar_name"
+                      placeholder="请输入门店商品名称"
+                      clearable></el-input>
+          </el-col>
+        </el-form-item>
+        <el-form-item prop="bar_img_url"
+                      :rules="formRules.uploadImgs"
+                      label="图片:">
+          <el-upload :on-remove="handleRemove"
+                     :on-success="handleAvatarSuccess"
+                     :before-upload="beforeAvatarUpload"
+                     :accept="'image/*'"
+                     :file-list="fileList"
+                     list-type="picture-card"
+                     action="/api/admin/v1/upload/file"
+                     multiple>
+            <i class="el-icon-plus"></i>
+            <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过5M</div>
+          </el-upload>
+        </el-form-item>
+        <el-form-item label="出单位置:">
+          <el-select v-model="form.productAttributeCategoryId" placeholder="请选择">
+            <el-option
+              v-for="item in [{id: 1, name: '柜台'},{id: 2, name: '厨房'}]"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="商品描述:">
+          <tinymce :height="300" v-model="form.content"/>
+        </el-form-item>
+        <el-form-item prop="bar_address"
+                      :rules="formRules.required"
+                      label="设置SKU:">
+          <el-col :span="16">
+            <el-input v-model="form.bar_address"
+                      placeholder="请输入门店地址"
+                      clearable></el-input>
+          </el-col>
+        </el-form-item>
+        <el-form-item prop="bar_status"
+                      :rules="formRules.required"
+                      label="门店状态:">
+          <el-radio v-model="form.bar_status"
+                    label="1">有效
+          </el-radio>
+          <el-radio v-model="form.bar_status"
+                    label="0">无效
+          </el-radio>
+        </el-form-item>
+        <el-form-item prop="bar_song_start_time"
+                      :rules="formRules.required"
+                      label="点歌时间:">
+          <el-time-select placeholder="起始时间"
+                          v-model="form.bar_song_start_time"
+                          :picker-options="{start: '00:00', step: '00:05', end: '23:59'}">
+          </el-time-select>
+          <el-time-select placeholder="结束时间"
+                          v-model="form.bar_song_end_time"
+                          :picker-options="{start: '00:00', step: '00:05', end: '23:59', minTime: form.bar_song_start_time}">
+          </el-time-select>
+        </el-form-item>
+        <el-form-item prop="bar_place_reserve_start_time"
+                      :rules="formRules.required"
+                      label="座位预定时间:">
+          <el-time-select placeholder="起始时间"
+                          v-model="form.bar_place_reserve_start_time"
+                          :picker-options="{start: '00:00', step: '00:05', end: '23:59'}">
+          </el-time-select>
+          <el-time-select placeholder="结束时间"
+                          v-model="form.bar_place_reserve_end_time"
+                          :picker-options="{start: '00:00', step: '00:05', end: '23:59', minTime: form.bar_place_reserve_start_time}">
+          </el-time-select>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer text-center">
+        <el-button @click="dialog = false">取 消</el-button>
+        <el-button type="primary" @click="handleSubmit">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import tinymce from '@/components/Tinymce/index.vue'
+
+export default {
+  components: {
+    tinymce
+  },
+  props: {
+    value: {
+      type: Boolean,
+      default: true
+    },
+    exData: {
+      type: Object,
+      default: function () {
+        return {}
+      }
+    }
+  },
+  data () {
+    return {
+      dialog: !!this.value,
+      form: {
+        bar_name: '', // 门店名称
+        bar_address: '', // 门店地址
+        bar_status: '1', // 门店状态(0无效1有效)
+        bar_img_url: [], // 门店图片
+        bar_song_start_time: '', // 点歌开始时间
+        bar_song_end_time: '', // 点歌结束时间
+        bar_place_reserve_start_time: '', // 座位预定开始时间
+        bar_place_reserve_end_time: '', // 座位预定结束时间
+        content: ''
+      },
+      fileList: []
+    }
+  },
+  methods: {
+    beforeAvatarUpload (file) {
+      const isLt2M = file.size / 1024 / 1024 < 5
+      if (!isLt2M) {
+        this.$message.error('上传图片大小不能超过 5MB!')
+      }
+      return isLt2M
+    },
+    handleRemove (file) {
+      let path = file.url
+      if (file.response && file.response.data) {
+        path = file.response.data.path
+      }
+      this.form.bar_img_url = this.form.bar_img_url.filter(item => item !== path.replace(/^\/\//, ''))
+    },
+    handleAvatarSuccess (res) {
+      if (res.code === 200) {
+        const { path } = res.data
+        this.form.bar_img_url.push(path)
+      } else {
+        this.$message.error('图片上传失败')
+      }
+    },
+    handleSubmit () {
+      const url = this.exData.id ? '/v1/bar/modify' : '/v1/bar/add'
+      this.$refs.form.validate(async valid => {
+        if (valid) {
+          const data = await this.$fetch(url, {
+            ...this.form
+          })
+          if (data.code === 200) {
+            this.$message.success('提交成功')
+            this.$emit('success')
+            this.dialog = false
+          }
+        }
+      })
+    }
+  },
+  mounted () {
+    if (this.exData.id) {
+      this.$set(this.form, 'id', this.exData.id)
+      this.fileList = this.exData.bar_img_url.map(item => {
+        return {
+          name: '',
+          url: item
+        }
+      })
+      for (const key in this.exData) {
+        if (this.form.hasOwnProperty(key)) {
+          let value = this.exData[key]
+          if ((Array.isArray(value) && value.length > 0) || value) {
+            if (key === 'bar_status') {
+              value = value.toString()
+            }
+            this.$set(this.form, key, value)
+          }
+        }
+      }
+    }
+  },
+  watch: {
+    dialog (val) {
+      if (!val) this.$emit('input', val)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.top-tip {
+  margin-top: -20px;
+  margin-bottom: 20px;
+}
+</style>

+ 150 - 2
htmldev/manage/src/views/business/pms/goods/index.vue

@@ -1,13 +1,161 @@
 <template>
-  <div>订单</div>
+  <div class="padding-20">
+    <div class="search-box">
+      <el-form ref="form"
+               :inline="true"
+               :model="searchForm"
+               clearable
+               label-width="100px"
+               class="mt-10">
+        <el-form-item label="创建时间:">
+          <el-date-picker
+            :editable="false"
+            v-model="time"
+            @change="timearr => {timearr ? (searchForm.start_time = timearr[0] + ' 00:00:00', searchForm.end_time = timearr[1] + ' 23:59:59') : searchForm.start_time = searchForm.end_time = undefined}"
+            type="daterange"
+            value-format="yyyy-MM-dd"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+          ></el-date-picker>
+        </el-form-item>
+        <el-form-item label="部门名称:">
+          <el-input v-model="searchForm.department_name" placeholder="请输入部门名称" clearable></el-input>
+        </el-form-item>
+        <el-form-item class="ml-10">
+          <el-button icon="el-icon-search" type="primary" @click="searchSubmit">查询</el-button>
+        </el-form-item>
+        <el-form-item class="ml-10">
+          <el-button icon="el-icon-plus"
+                     type="primary"
+                     @click="add">新增
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <el-table :data="tableData"
+              v-loading="tableLoading"
+              fit
+              class="marginT-10 order-table"
+              :max-height="vheight">
+      <el-table-column type="expand">
+        <template slot-scope="props">
+          <el-form label-position="left"
+                   inline
+                   class="demo-table-expand"
+                   label-width="160px">
+            <el-form-item label="ID">
+              <span>{{ props.row.id }}</span>
+            </el-form-item>
+            <el-form-item label="商品名称">
+              <span>{{ props.row.bar_name }}</span>
+            </el-form-item>
+            <el-form-item label="价格(元)">
+              <span>{{ props.row.bar_address }}</span>
+            </el-form-item>
+            <el-form-item label="商品分类">
+              <span>{{ ['无效', '有效'][props.row.bar_status] }}</span>
+            </el-form-item>
+            <el-form-item label="上架">
+              <span>{{ props.row.bar_song_start_time }}</span>
+            </el-form-item>
+            <el-form-item label="出单位置">
+              <span>{{ props.row.bar_song_end_time }}</span>
+            </el-form-item>
+            <el-form-item label="酒吧座位预定开始时间">
+              <span>{{ props.row.bar_place_reserve_start_time }}</span>
+            </el-form-item>
+            <el-form-item label="酒吧座位预定结束时间">
+              <span>{{ props.row.bar_place_reserve_end_time }}</span>
+            </el-form-item>
+            <el-form-item label="门店图片">
+              <el-image style="width: 100px; height: 100px"
+                        :src="props.row.bar_img_url[0]"
+                        :preview-src-list="props.row.bar_img_url">
+              </el-image>
+            </el-form-item>
+          </el-form>
+        </template>
+      </el-table-column>
+      <el-table-column label="ID" prop="id" width="160"></el-table-column>
+      <el-table-column label="商品名称" prop="bar_name"></el-table-column>
+      <el-table-column label="价格(元)" prop="bar_address"></el-table-column>
+      <el-table-column label="商品分类" prop="bar_address"></el-table-column>
+      <el-table-column label="上架" width="100">
+        <template slot-scope="scope">
+          <p>{{ ['无效', '有效'][scope.row.bar_status] }}</p>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="100">
+        <template slot-scope="scope">
+          <el-button type="text" @click="edit(scope.row)">编辑</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      class="marginT-20"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+      :hide-on-single-page="true"
+      :current-page="page"
+      :page-size="page_size"
+      :page-sizes="[10, 20, 100, 200, 300, 400]"
+      background
+      layout="total, sizes, prev, pager, next, jumper"
+      :total="totalCount"/>
+    <detail v-if="detailsDialog.show"
+            v-model="detailsDialog.show"
+            :exData="detailsDialog.exData"
+            @success="init"></detail>
+  </div>
 </template>
 
 <script>
+import page from '@/mixin/page'
+import detail from './details'
+
 export default {
-  name: 'index'
+  mixins: [page],
+  components: {
+    detail,
+  },
+  data () {
+    return {
+      detailsDialog: {
+        show: false,
+        exData: {}
+      },
+      time: [],
+      searchForm: {},
+      tableData: [],
+      tableUrl: '/v1/bar/list'
+    }
+  },
+  methods: {
+    add () {
+      this.detailsDialog.exData = {}
+      this.detailsDialog.show = true
+    },
+    edit (row) {
+      this.detailsDialog.exData = row
+      this.detailsDialog.show = true
+    }
+  },
+  mounted () {
+    this.init()
+  },
 }
 </script>
 
 <style lang="scss" scoped>
+.demo-table-expand {
+  .el-form-item {
+    width: 50%;
+    margin-right: 0;
+    margin-bottom: 0;
 
+    ::v-deep &__label {
+      color: #99a9bf;
+    }
+  }
+}
 </style>