long
2021-02-02 36b71142347dc524e18fa24c5ffdffff0d02287a
优化列表、回到顶部组件、优化样式
6个文件已添加
14个文件已修改
824 ■■■■■ 已修改文件
src/assets/imgs/loginBg.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/components/BackToTop/index.vue 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/HeaderSearch/index.vue 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components_simple/jun_serveLabels.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/baseConfig.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/directive/el-drag-dialog/drag.js 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/directive/el-drag-dialog/index.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/demo/form.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/demo/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/demo/list.vue 319 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/login/index.vue 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/op/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/system/admin.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/system/banner.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/system/basic.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/system/role.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/system/roleAdd.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/system/roleEdit.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/demo_router.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/imgs/loginBg.jpg
src/components/BackToTop/index.vue
New file
@@ -0,0 +1,111 @@
<template>
  <transition :name="transitionName">
    <div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
      <svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height:16px;width:16px"><path d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" /></svg>
    </div>
  </transition>
</template>
<script>
export default {
  name: 'BackToTop',
  props: {
    visibilityHeight: {
      type: Number,
      default: 400
    },
    backPosition: {
      type: Number,
      default: 0
    },
    customStyle: {
      type: Object,
      default: function() {
        return {
          right: '50px',
          bottom: '50px',
          width: '40px',
          height: '40px',
          'border-radius': '4px',
          'line-height': '45px',
          background: '#e7eaf1'
        }
      }
    },
    transitionName: {
      type: String,
      default: 'fade'
    }
  },
  data() {
    return {
      visible: false,
      interval: null,
      isMoving: false
    }
  },
  mounted() {
    window.addEventListener('scroll', this.handleScroll)
  },
  beforeDestroy() {
    window.removeEventListener('scroll', this.handleScroll)
    if (this.interval) {
      clearInterval(this.interval)
    }
  },
  methods: {
    handleScroll() {
      this.visible = window.pageYOffset > this.visibilityHeight
    },
    backToTop() {
      if (this.isMoving) return
      const start = window.pageYOffset
      let i = 0
      this.isMoving = true
      this.interval = setInterval(() => {
        const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
        if (next <= this.backPosition) {
          window.scrollTo(0, this.backPosition)
          clearInterval(this.interval)
          this.isMoving = false
        } else {
          window.scrollTo(0, next)
        }
        i++
      }, 16.7)
    },
    easeInOutQuad(t, b, c, d) {
      if ((t /= d / 2) < 1) return c / 2 * t * t + b
      return -c / 2 * (--t * (t - 2) - 1) + b
    }
  }
}
</script>
<style scoped>
.back-to-ceiling {
  position: fixed;
  display: inline-block;
  text-align: center;
  cursor: pointer;
}
.back-to-ceiling:hover {
  background: #d5dbe7;
}
.fade-enter-active,
.fade-leave-active {
  transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0
}
.back-to-ceiling .Icon {
  fill: #9aaabf;
  background: none;
}
</style>
src/components/HeaderSearch/index.vue
New file
@@ -0,0 +1,180 @@
<template>
  <div :class="{'show':show}" class="header-search">
    <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
    <el-select
      ref="headerSearchSelect"
      v-model="search"
      :remote-method="querySearch"
      filterable
      default-first-option
      remote
      placeholder="Search"
      class="header-search-select"
      @change="change"
    >
      <el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" />
    </el-select>
  </div>
</template>
<script>
// fuse is a lightweight fuzzy-search module
// make search results more in line with expectations
import Fuse from 'fuse.js'
import path from 'path'
export default {
  name: 'HeaderSearch',
  data() {
    return {
      search: '',
      options: [],
      searchPool: [],
      show: false,
      fuse: undefined
    }
  },
  computed: {
    routes() {
      return this.$store.getters.permission_routes
    }
  },
  watch: {
    routes() {
      this.searchPool = this.generateRoutes(this.routes)
    },
    searchPool(list) {
      this.initFuse(list)
    },
    show(value) {
      if (value) {
        document.body.addEventListener('click', this.close)
      } else {
        document.body.removeEventListener('click', this.close)
      }
    }
  },
  mounted() {
    this.searchPool = this.generateRoutes(this.routes)
  },
  methods: {
    click() {
      this.show = !this.show
      if (this.show) {
        this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
      }
    },
    close() {
      this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
      this.options = []
      this.show = false
    },
    change(val) {
      this.$router.push(val.path)
      this.search = ''
      this.options = []
      this.$nextTick(() => {
        this.show = false
      })
    },
    initFuse(list) {
      this.fuse = new Fuse(list, {
        shouldSort: true,
        threshold: 0.4,
        location: 0,
        distance: 100,
        maxPatternLength: 32,
        minMatchCharLength: 1,
        keys: [{
          name: 'title',
          weight: 0.7
        }, {
          name: 'path',
          weight: 0.3
        }]
      })
    },
    // Filter out the routes that can be displayed in the sidebar
    // And generate the internationalized title
    generateRoutes(routes, basePath = '/', prefixTitle = []) {
      let res = []
      for (const router of routes) {
        // skip hidden router
        if (router.hidden) { continue }
        const data = {
          path: path.resolve(basePath, router.path),
          title: [...prefixTitle]
        }
        if (router.meta && router.meta.title) {
          data.title = [...data.title, router.meta.title]
          if (router.redirect !== 'noRedirect') {
            // only push the routes with title
            // special case: need to exclude parent router without redirect
            res.push(data)
          }
        }
        // recursive child routes
        if (router.children) {
          const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
          if (tempRoutes.length >= 1) {
            res = [...res, ...tempRoutes]
          }
        }
      }
      return res
    },
    querySearch(query) {
      if (query !== '') {
        this.options = this.fuse.search(query)
      } else {
        this.options = []
      }
    }
  }
}
</script>
<style lang="scss" scoped>
.header-search {
  font-size: 0 !important;
  .search-icon {
    cursor: pointer;
    font-size: 18px;
    vertical-align: middle;
  }
  .header-search-select {
    font-size: 18px;
    transition: width 0.2s;
    width: 0;
    overflow: hidden;
    background: transparent;
    border-radius: 0;
    display: inline-block;
    vertical-align: middle;
    ::v-deep .el-input__inner {
      border-radius: 0;
      border: 0;
      padding-left: 0;
      padding-right: 0;
      box-shadow: none !important;
      border-bottom: 1px solid #d9d9d9;
      vertical-align: middle;
    }
  }
  &.show {
    .header-search-select {
      width: 210px;
      margin-left: 10px;
    }
  }
}
</style>
src/components_simple/jun_serveLabels.vue
@@ -16,6 +16,7 @@
      v-model="labelValue"
      class="input-new-tag"
      size="small"
      maxlength="50"
      placeholder="输入标签,回车分隔"
      @keyup.enter.native="handleInputConfirm"
      @blur="handleInputConfirm"
src/config/baseConfig.js
@@ -8,9 +8,9 @@
var isMock = 1 // 是否使用mock
// 打包后的环境
if (baseUrl.env.NODE_ENV !== 'development') {
  baseUrl = '/';
  isCrossDomain = 0;
if (baseUrl.env && baseUrl.env.NODE_ENV !== 'development') {
  baseUrl = '/'
  isCrossDomain = 0
  isTest = 0
  isMock = 0
}
src/directive/el-drag-dialog/drag.js
New file
@@ -0,0 +1,77 @@
export default {
  bind(el, binding, vnode) {
    const dialogHeaderEl = el.querySelector('.el-dialog__header')
    const dragDom = el.querySelector('.el-dialog')
    dialogHeaderEl.style.cssText += ';cursor:move;'
    dragDom.style.cssText += ';top:0px;'
    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const getStyle = (function() {
      if (window.document.currentStyle) {
        return (dom, attr) => dom.currentStyle[attr]
      } else {
        return (dom, attr) => getComputedStyle(dom, false)[attr]
      }
    })()
    dialogHeaderEl.onmousedown = (e) => {
      // 鼠标按下,计算当前元素距离可视区的距离
      const disX = e.clientX - dialogHeaderEl.offsetLeft
      const disY = e.clientY - dialogHeaderEl.offsetTop
      const dragDomWidth = dragDom.offsetWidth
      const dragDomHeight = dragDom.offsetHeight
      const screenWidth = document.body.clientWidth
      const screenHeight = document.body.clientHeight
      const minDragDomLeft = dragDom.offsetLeft
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
      const minDragDomTop = dragDom.offsetTop
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
      // 获取到的值带px 正则匹配替换
      let styL = getStyle(dragDom, 'left')
      let styT = getStyle(dragDom, 'top')
      if (styL.includes('%')) {
        styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
        styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
      } else {
        styL = +styL.replace(/\px/g, '')
        styT = +styT.replace(/\px/g, '')
      }
      document.onmousemove = function(e) {
        // 通过事件委托,计算移动的距离
        let left = e.clientX - disX
        let top = e.clientY - disY
        // 边界处理
        if (-(left) > minDragDomLeft) {
          left = -minDragDomLeft
        } else if (left > maxDragDomLeft) {
          left = maxDragDomLeft
        }
        if (-(top) > minDragDomTop) {
          top = -minDragDomTop
        } else if (top > maxDragDomTop) {
          top = maxDragDomTop
        }
        // 移动当前元素
        dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
        // emit onDrag event
        vnode.child.$emit('dragDialog')
      }
      document.onmouseup = function(e) {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  }
}
src/directive/el-drag-dialog/index.js
New file
@@ -0,0 +1,13 @@
import drag from './drag'
const install = function(Vue) {
  Vue.directive('el-drag-dialog', drag)
}
if (window.Vue) {
  window['el-drag-dialog'] = drag
  Vue.use(install); // eslint-disable-line
}
drag.install = install
export default drag
src/main.js
@@ -20,6 +20,8 @@
import Pagination from '@/components/Pagination'
// 自定义表格工具扩展
import RightToolbar from '@/components/RightToolbar'
// 拖拽弹窗
import drag from './directive/el-drag-dialog/drag'
// 过滤器统一处理加载
Object.keys(filter).forEach(key => {
@@ -44,6 +46,8 @@
  this.$message({ message: msg, dangerouslyUseHTMLString: true, type: 'error' })
}
Vue.directive('el-drag-dialog', drag)
Vue.config.productionTip = false
new Vue({
src/pages/demo/form.vue
@@ -1,12 +1,15 @@
<template>
  <div class="app-container">
    <el-page-header class="mb20" @back="jumpBack" />
    <el-form ref="couponForm" label-position="left" :model="mData" label-width="150px" :rules="rules">
    <el-form ref="couponForm" :model="mData" label-width="150px" :rules="rules" size="small">
      <el-form-item label="标题:" prop="title">
        <el-input v-model="mData.title" placeholder="请输入标题" class="com-edit-input" />
        <el-input v-model="mData.title" placeholder="请输入标题" class="com-edit-input" maxlength="50" />
      </el-form-item>
      <el-form-item label="面值:" prop="valueMoney">
        <el-input v-model.number="mData.valueMoney" placeholder="请输入面值" class="com-edit-input" />
        <el-input v-model.number="mData.valueMoney" placeholder="请输入面值" class="com-edit-input" maxlength="50" />
      </el-form-item>
      <el-form-item label="面值:" prop="valueMoney">
        <el-input v-model.number="mData.valueMoney" placeholder="请输入面值" class="com-edit-input" maxlength="50" />
      </el-form-item>
      <el-form-item label="省市区" prop="provinces">
        <el-cascader
@@ -30,7 +33,7 @@
      </el-form-item>
      <el-form-item prop="uploadImgs">
        <span slot="label">
          图片:<br><span>(668*164像素)</span>
          图片:<br><span>(xxx*xxx像素)</span>
        </span>
        <!-- 上传图片组件(单图) -->
        <UploadSingleImg
@@ -58,6 +61,8 @@
      <img style="max-width:100%" :src="uploadPreviewUrl" alt="">
    </el-dialog>
    <back-to-top :custom-style="myBackToTopStyle" :visibility-height="300" :back-position="50" transition-name="fade" />
  </div>
</template>
@@ -66,9 +71,10 @@
import mixin_upload from '@/mixins/upload.js' // 通用上传图片预览
import UploadSingleImg from '@/components_simple/UploadSingleImg'
import Area from '@/utils/area' // 地区选择
import BackToTop from '@/components/BackToTop'
export default {
  name: 'DemoForm',
  components: { WangEnduit, UploadSingleImg },
  components: { WangEnduit, UploadSingleImg, BackToTop },
  mixins: [
    mixin_upload
  ],
@@ -90,6 +96,17 @@
      // 初始化地区
      areaOpts: Area.getAreaOpts(Area.data), // 省市区数据
      // 回到顶部
      myBackToTopStyle: {
        right: '50px',
        bottom: '50px',
        width: '40px',
        height: '40px',
        'border-radius': '4px',
        'line-height': '45px', // 请保持与高度一致以垂直居中
        background: '#e7eaf1'// 按钮的背景颜色
      },
      // 表单校验
      rules: {
@@ -114,12 +131,6 @@
          required: true, message: '请选择图片'
        }]
      }
    }
  },
  computed: {
    // 禁用上传图片功能
    uploadDisabled() {
      return this.mData.uploadImgs.length > 0
    }
  },
  mounted() {
src/pages/demo/index.vue
@@ -9,6 +9,7 @@
          clearable
          size="small"
          style="width: 240px"
          maxlength="50"
          @keyup.enter.native="reGetList"
        />
      </el-form-item>
@@ -33,7 +34,7 @@
    </el-row>
    <!-- 操作区 ↑↑↑↑↑↑↑↑↑↑ -->
    <el-table :data="list">
    <el-table :data="list" max-height="500px" stripe>
      <el-table-column type="index" label="序号" align="center" width="60" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="是否上架" prop="isUp" align="center" min-width="100">
@@ -47,7 +48,7 @@
        </template>
      </el-table-column>
      <el-table-column label="创建时间" prop="createTime" align="center" min-width="160" />
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="100">
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120">
        <template slot-scope="scope">
          <el-button
            size="mini"
@@ -66,10 +67,10 @@
    </el-table>
    <!-- 新增&编辑 -->
    <el-dialog :title="dialogData.type=='add'?'新增医院科室':'编辑医院科室'" width="500px" :visible.sync="dialogVisible" append-to-body :before-close="hideDialog">
    <el-dialog v-el-drag-dialog :title="dialogData.type=='add'?'新增医院科室':'编辑医院科室'" width="500px" :visible.sync="dialogVisible" append-to-body :before-close="hideDialog">
      <el-form ref="refDialog" :model="dialogData" label-width="110px" :rules="rules" size="small">
        <el-form-item label="名称" prop="name">
          <el-input v-model="dialogData.name" placeholder="请输入名称" />
          <el-input v-model="dialogData.name" placeholder="请输入名称" maxlength="50" />
        </el-form-item>
        <el-form-item label="是否上架" prop="isUp">
          <el-switch
src/pages/demo/list.vue
New file
@@ -0,0 +1,319 @@
<template>
  <div class="app-container">
    <!-- 搜索区 ↓↓↓↓↓↓↓↓↓↓ -->
    <el-form v-show="showSearch" ref="searchForm" :inline="true">
      <el-form-item label="角色名称">
        <el-input
          v-model="keyWord"
          placeholder="请输入角色名称"
          clearable
          size="small"
          style="width: 240px"
          maxlength="50"
          @keyup.enter.native="reGetList"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="cyan" icon="el-icon-search" size="mini" @click="reGetList">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetHandle">重置</el-button>
      </el-form-item>
    </el-form>
    <!-- 搜索区 ↑↑↑↑↑↑↑↑↑↑ -->
    <!-- 操作区 ↓↓↓↓↓↓↓↓↓↓ -->
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          icon="el-icon-plus"
          size="mini"
          @click="showAddDialog"
        >新增</el-button>
      </el-col>
      <right-toolbar :show-search.sync="showSearch" @queryTable="getList" />
    </el-row>
    <!-- 操作区 ↑↑↑↑↑↑↑↑↑↑ -->
    <el-table :data="list" max-height="500px" stripe>
      <el-table-column type="index" label="序号" align="center" width="60" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="角色名称" prop="name" align="center" min-width="120" />
      <el-table-column label="是否上架" prop="isUp" align="center" min-width="100">
        <template slot-scope="scope">
          <el-switch
            v-model="scope.row.isUp"
            :active-value="1"
            :inactive-value="0"
            @change="handleUpChange(scope.row)"
          />
        </template>
      </el-table-column>
      <el-table-column label="创建时间" prop="createTime" align="center" min-width="160" />
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120" fixed="right">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="showEditDialog(scope.row)"
          >编辑</el-button>
          <el-button
            size="mini"
            type="text warn"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
          >删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 新增&编辑 -->
    <el-dialog v-el-drag-dialog :title="dialogData.type=='add'?'新增医院科室':'编辑医院科室'" width="500px" :visible.sync="dialogVisible" append-to-body :before-close="hideDialog">
      <el-form ref="refDialog" :model="dialogData" label-width="110px" :rules="rules" size="small">
        <el-form-item label="名称" prop="name">
          <el-input v-model="dialogData.name" placeholder="请输入名称" maxlength="50" />
        </el-form-item>
        <el-form-item label="是否上架" prop="isUp">
          <el-switch
            v-model="dialogData.isUp"
            :active-value="1"
            :inactive-value="0"
          />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="hideDialog">取消</el-button>
        <el-button type="primary" @click="submitHandle">确定</el-button>
      </div>
    </el-dialog>
    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="pageNum"
      :limit.sync="pageSize"
      @pagination="getList"
    />
  </div>
</template>
<script>
export default {
  name: 'Demo',
  data() {
    return {
      showSearch: true, // 是否显示搜索区
      keyWord: '',
      list: [],
      // 弹窗数据
      dialogVisible: false,
      dialogData: {},
      // 分页 ↓↓↓↓↓↓↓↓↓↓
      total: 0,
      pageNum: 1,
      pageSize: 20,
      // 分页 ↑↑↑↑↑↑↑↑↑↑
      // 表单校验
      rules: {
        name: [
          { required: true, message: '名称不能为空', trigger: 'change' }
        ],
        isUp: [
          { required: true, message: '是否上架不能为空', trigger: 'change' }
        ]
      }
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    // 初始化
    init() {
      this.getList()
    },
    // 获取列表
    getList() {
      var { pageNum, pageSize, keyWord } = this
      this.postFN({
        url: 'xxx',
        params: {
          pageNum: pageNum,
          pageSize: pageSize,
          keyWord: keyWord
        },
        mockData: {
          code: 100,
          msg: '',
          data: {
            list: [{
              id: 'xxx',
              index: 1,
              name: '超级管理员',
              createTime: '2020-08-09 10:10:10'
            }],
            total: 100
          }
        }
      }, (inf) => {
        for (var i = 0; i < 19; i++) {
          inf.list.push({
            id: 'xxx',
            index: 1,
            name: '超级管理员',
            createTime: '2020-08-09 10:10:10'
          })
        }
        this.list = inf.list || []
        this.total = inf.total
      })
    },
    // 重新获取列表
    reGetList() {
      this.pageNum = 1
      this.getList()
    },
    // 重置
    resetHandle() {
      this.keyWord = ''
      this.reGetList()
    },
    // 删除
    handleDelete(item) {
      // 打开二次确认弹窗
      this.$confirm('是否确认删除该角色?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // 确定回调
        this.postFN({
          url: 'xxx',
          params: {
            id: item.id
          },
          mockData: {
            code: 100,
            msg: '',
            data: {}
          }
        }, () => {
          this.getList()
          this.$messageSuc('删除成功')
        })
      }).catch(() => {})
    },
    // 修改是否上架 todo
    handleUpChange(item) {
      const text = item.isUp === 1 ? '上架' : '下架'
      this.$confirm('确认要' + text + '该角色吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.postFN({
          url: 'xxx',
          params: {
            id: item.id
          },
          mockData: {
            code: 100,
            msg: '',
            data: {}
          }
        }, () => {
          this.$messageSuc(text + '成功')
        })
      }).catch(() => {
        item.isUp = item.isUp === 1 ? 0 : 1
      })
    },
    // 打开新增弹窗
    showAddDialog() {
      var dialogData = {
        type: 'add',
        isUp: 1
      }
      this.dialogVisible = true
      this.$nextTick(() => {
        this.dialogData = dialogData
        this.$refs['refDialog'].resetFields()
      })
    },
    // 打开编辑弹框
    showEditDialog(item) {
      var dialogData = {
        type: 'edit',
        ...item
      }
      console.log('dialogData', dialogData)
      this.dialogVisible = true
      this.$nextTick(() => {
        this.dialogData = dialogData
        this.$refs['refDialog'].resetFields()
      })
    },
    // 关闭编辑弹窗
    hideDialog() {
      this.dialogData = {}
      this.dialogVisible = false
    },
    // 提交新增&编辑
    submitHandle() {
      this.$refs['refDialog'].validate(valid => {
        if (valid) {
          this.submitReq()
        }
      })
    },
    // 提交接口
    submitReq() {
      var { dialogData } = this
      var params = {
        name: dialogData.name,
        isUp: dialogData.isUp
      }
      if (dialogData.password) params.password = dialogData.password
      var isAdd = dialogData.type === 'add'
      var url = isAdd ? 'admin/hospital/department/add' : 'admin/hospital/department/edit'
      !isAdd && (params.id = dialogData.id)
      this.postFN({
        url: url,
        params: params,
        mockData: {
          code: 100,
          msg: '',
          data: {}
        }
      }, () => {
        this.$messageSuc('保存成功')
        this.hideDialog()
        this.reGetList()
      })
    }
  }
}
</script>
<style lang="scss" scoped>
</style>
src/pages/login/index.vue
@@ -3,7 +3,7 @@
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left">
      <div class="title-container">
        <h3 class="title">袁曦藏酒</h3>
        <h3 class="title">”项目名称“</h3>
      </div>
      <el-form-item prop="username">
@@ -18,6 +18,7 @@
          type="text"
          tabindex="1"
          autocomplete="on"
          maxlength="20"
          @keyup.enter.native="handleLogin"
        />
      </el-form-item>
@@ -36,6 +37,7 @@
            name="password"
            tabindex="2"
            autocomplete="on"
            maxlength="20"
            @keyup.native="checkCapslock"
            @blur="capsTooltip = false"
            @keyup.enter.native="handleLogin"
@@ -58,6 +60,7 @@
          name="safecode"
          type="text"
          tabindex="3"
          maxlength="10"
          autocomplete="off"
          @keyup.enter.native="handleLogin"
        />
@@ -114,9 +117,9 @@
        checkedId: ''
      },
      loginRules: {
        username: [{ required: true, trigger: 'change', validator: validateUsername }],
        password: [{ required: true, trigger: 'change', validator: validatePassword }],
        safecode: [{ required: true, trigger: 'change', validator: validateSafecode }]
        username: [{ required: true, trigger: 'blur', validator: validateUsername }],
        password: [{ required: true, trigger: 'blur', validator: validatePassword }],
        safecode: [{ required: true, trigger: 'blur', validator: validateSafecode }]
      },
      passwordType: 'password',
      capsTooltip: false,
@@ -144,7 +147,7 @@
  },
  methods: {
    init() {
      var loginData = localStorage.getItem('loginData')
      var loginData = localStorage.getItem('loginData_XXXX')
      // 获取保存的账号密码
      if (loginData) this.loginForm = JSON.parse(loginData)
@@ -233,7 +236,7 @@
              username: this.loginForm.username.trim(),
              password: this.loginForm.password.trim()
            })
            localStorage.setItem('loginData', loginData)
            localStorage.setItem('loginData_XXXX', loginData)
            // 保存用户数据
            setUserData(inf)
@@ -283,6 +286,11 @@
/* reset element-ui css */
.login-container {
  // 背景 ↓↓↓↓↓↓↓↓↓↓
  // background: url(../../assets/imgs/loginBg.jpg) no-repeat;
  // background-size: cover;
  // 背景 ↑↑↑↑↑↑↑↑↑↑
  background-color: $bg;
  .el-input {
    display: inline-block;
    height: 47px;
@@ -323,16 +331,23 @@
.login-container {
  min-height: 100%;
  width: 100%;
  background-color: $bg;
  // background-color: $bg;
  overflow: hidden;
  .login-form {
    position: relative;
    width: 520px;
    max-width: 100%;
    padding: 160px 35px 0;
    padding: 35px 35px 0;
    margin: 0 auto;
    margin-top: 160px;
    overflow: hidden;
    // 背景 ↓↓↓↓↓↓↓↓↓↓
    // background-color: $bg;
    // opacity: .96;
    // border-radius: 10px;
    // box-shadow: 0px 20px 60px rgba(0,0,0,0.4), 0px 0px 150px rgba(0,0,0,0.4);
    // 背景 ↑↑↑↑↑↑↑↑↑↑
  }
  .tips {
src/pages/op/index.vue
@@ -9,6 +9,7 @@
          clearable
          size="small"
          style="width: 240px"
          maxlength="10"
          @keyup.enter.native="reGetList"
        />
      </el-form-item>
src/pages/system/admin.vue
@@ -9,6 +9,7 @@
          clearable
          size="small"
          style="width: 240px"
          maxlength="50"
          @keyup.enter.native="reGetList"
        />
      </el-form-item>
@@ -85,19 +86,19 @@
    />
    <!-- 新增&编辑 -->
    <el-dialog :title="adminDialogData.type==='add'?'新增管理员':'编辑管理员'" width="500px" :visible.sync="adminDialogVisible" append-to-body>
    <el-dialog v-el-drag-dialog :title="adminDialogData.type==='add'?'新增管理员':'编辑管理员'" width="500px" :visible.sync="adminDialogVisible" append-to-body>
      <el-form ref="adminDialog" :model="adminDialogData" label-width="80px" :rules="rules" size="small">
        <el-form-item label="名称" prop="name">
          <el-input v-model="adminDialogData.name" placeholder="请输入名称" />
          <el-input v-model="adminDialogData.name" placeholder="请输入名称" maxlength="50" />
        </el-form-item>
        <el-form-item label="账号" prop="account">
          <el-input v-model="adminDialogData.account" placeholder="请输入帐号" :disabled="adminDialogData.type!='add'" />
          <el-input v-model="adminDialogData.account" placeholder="请输入帐号" maxlength="20" :disabled="adminDialogData.type!='add'" />
        </el-form-item>
        <el-form-item label="密码" :prop="adminDialogData.type==='add'?'password':'none'">
          <el-input v-model="adminDialogData.password" type="password" placeholder="请输入密码" />
          <el-input v-model="adminDialogData.password" type="password" placeholder="请输入密码" maxlength="20" />
        </el-form-item>
        <el-form-item label="确认密码" :prop="adminDialogData.type==='add'?'passwordSure':'none'">
          <el-input v-model="adminDialogData.passwordSure" type="password" placeholder="请输入确认密码" />
          <el-input v-model="adminDialogData.passwordSure" type="password" placeholder="请输入确认密码" maxlength="20" />
        </el-form-item>
        <!-- 账号类型 -->
        <el-form-item label="账号类型" prop="accountType">
@@ -140,7 +141,6 @@
</template>
<script>
export default {
  name: 'Admin',
  data() {
src/pages/system/banner.vue
@@ -58,10 +58,10 @@
    />
    <!-- 新增&编辑 -->
    <el-dialog :title="dialogData.type=='add'?'新增轮播图':'编辑轮播图'" width="500px" :visible.sync="isShowDialog" append-to-body :before-close="hideDialog">
    <el-dialog v-el-drag-dialog :title="dialogData.type=='add'?'新增轮播图':'编辑轮播图'" width="500px" :visible.sync="isShowDialog" append-to-body :before-close="hideDialog">
      <el-form :ref="formName" :model="dialogData" label-width="120px" :rules="rules" size="small">
        <el-form-item label="排序号:" prop="orderNum">
          <el-input v-model="dialogData.orderNum" placeholder="请输入排序号" />
          <el-input v-model="dialogData.orderNum" placeholder="请输入排序号" maxlength="10" />
        </el-form-item>
        <el-form-item ref="uploadFormItem" label="轮播图:" prop="uploadImgs">
          <!-- 上传图片组件(多图) -->
@@ -82,10 +82,10 @@
          </el-select>
        </el-form-item>
        <el-form-item v-if="dialogData.jumpType" label="跳转链接:" prop="jumpUrl">
          <el-input v-model="dialogData.jumpUrl" placeholder="请输入跳转链接" class="com-edit-input" />
          <el-input v-model="dialogData.jumpUrl" placeholder="请输入跳转链接" class="com-edit-input" maxlength="100" />
        </el-form-item>
        <el-form-item v-if="dialogData.jumpType===3" label="appId:" prop="jumpMpId">
          <el-input v-model="dialogData.jumpMpId" placeholder="请输入小程序appId" class="com-edit-input" />
          <el-input v-model="dialogData.jumpMpId" placeholder="请输入小程序appId" class="com-edit-input" maxlength="50" />
        </el-form-item>
        <el-form-item v-if="dialogData.jumpType===3" label="小程序版本:" prop="envVersion">
          <el-select
@@ -98,7 +98,7 @@
          </el-select>
        </el-form-item>
        <el-form-item v-if="dialogData.jumpType===3" label="额外数据:" prop="extraData">
          <el-input v-model="dialogData.extraData" placeholder="请输入小程序额外数据" class="com-edit-input" />
          <el-input v-model="dialogData.extraData" placeholder="请输入小程序额外数据" class="com-edit-input" maxlength="50" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
src/pages/system/basic.vue
@@ -5,7 +5,7 @@
    <el-form ref="basicForm" label-position="left" :model="basicData" label-width="150px" :rules="rules">
      <el-form-item label="分享标题:" prop="shareTitle">
        <el-input v-model="basicData.shareTitle" placeholder="请输入分享标题" class="com-edit-input" />
        <el-input v-model="basicData.shareTitle" placeholder="请输入分享标题" maxlength="100" class="com-edit-input" />
      </el-form-item>
      <el-form-item prop="uploadShareImgs">
src/pages/system/role.vue
@@ -9,6 +9,7 @@
          clearable
          size="small"
          style="width: 240px"
          maxlength="50"
          @keyup.enter.native="reGetList"
        />
      </el-form-item>
src/pages/system/roleAdd.vue
@@ -1,9 +1,9 @@
<template>
  <div class="app-container">
    <el-page-header class="mb20" @back="jumpBack" />
    <el-form ref="roleAddForm" label-position="left" :model="mData" label-width="150px" :rules="rules">
    <el-form ref="roleAddForm" label-position="left" :model="mData" label-width="150px" :rules="rules" size="small">
      <el-form-item label="角色名称:" prop="name">
        <el-input v-model="mData.name" placeholder="请输入名称" class="com-edit-input" />
        <el-input v-model="mData.name" placeholder="请输入名称" maxlength="50" class="com-edit-input" />
      </el-form-item>
      <el-form-item label="是否启用:">
        <el-switch v-model="mData.isUse" :active-value="1" :inactive-value="0" />
src/pages/system/roleEdit.vue
@@ -1,9 +1,9 @@
<template>
  <div class="app-container">
    <el-page-header class="mb20" @back="jumpBack" />
    <el-form ref="roleAddForm" label-position="left" :model="mData" label-width="150px" :rules="rules">
    <el-form ref="roleAddForm" label-position="left" :model="mData" label-width="150px" :rules="rules" size="small">
      <el-form-item label="角色名称:" prop="name">
        <el-input v-model="mData.name" placeholder="请输入名称" class="com-edit-input" />
        <el-input v-model="mData.name" placeholder="请输入名称" maxlength="50" class="com-edit-input" />
      </el-form-item>
      <el-form-item label="是否启用:">
        <el-switch v-model="mData.isUse" :active-value="1" :inactive-value="0" />
src/router/demo_router.js
@@ -18,6 +18,12 @@
      meta: { title: '模板列表' }
    },
    {
      path: 'list',
      component: () => import('@/pages/demo/list'), // Parent router-view
      name: 'demoList',
      meta: { title: '模板超多列表' }
    },
    {
      path: 'form',
      component: () => import('@/pages/demo/form'), // Parent router-view
      name: 'demoForm',