ソースを参照

添加系统管理部分功能

ymy 1 年間 前
コミット
e4cdc58efc
44 ファイル変更4447 行追加192 行削除
  1. 37 0
      src/api/sourceShop.ts
  2. 60 0
      src/api/system/config.js
  3. 52 0
      src/api/system/dict/data.js
  4. 60 0
      src/api/system/dict/type.js
  5. 26 0
      src/api/system/logininfor.js
  6. 26 0
      src/api/system/operlog.js
  7. 44 0
      src/api/system/post.ts
  8. 111 0
      src/api/system/role.js
  9. 5 5
      src/benyun/utils/benyuntech.ts
  10. 82 0
      src/benyun/utils/dict/Dict.js
  11. 17 0
      src/benyun/utils/dict/DictConverter.js
  12. 13 0
      src/benyun/utils/dict/DictData.js
  13. 38 0
      src/benyun/utils/dict/DictMeta.js
  14. 51 0
      src/benyun/utils/dict/DictOptions.js
  15. 33 0
      src/benyun/utils/dict/index.js
  16. 21 0
      src/components/DictData/index.js
  17. 52 0
      src/components/DictTag/index.vue
  18. 114 0
      src/components/Pagination/index.vue
  19. 30 2
      src/main.ts
  20. 46 46
      src/router/index.ts
  21. 58 0
      src/utils/scroll-to.js
  22. 0 1
      src/views/login.vue
  23. 90 85
      src/views/oms/order/components/addOrder.vue
  24. 3 2
      src/views/oms/order/components/batchbyOneModal.vue
  25. 3 0
      src/views/oms/order/components/buyerInfoModal.vue
  26. 7 5
      src/views/oms/order/components/carpoolModal.vue
  27. 4 0
      src/views/oms/order/components/deliveryGoodsInfo.vue
  28. 3 0
      src/views/oms/order/components/expressDeliveryModal.vue
  29. 5 5
      src/views/oms/order/components/loadOrderModal.vue
  30. 26 34
      src/views/oms/order/components/orderTable.vue
  31. 3 3
      src/views/oms/order/index.vue
  32. 42 0
      src/views/oms/sourceShop/components/tag.vue
  33. 420 0
      src/views/oms/sourceShop/index.vue
  34. 336 0
      src/views/system/dept/index.vue
  35. 399 0
      src/views/system/dict/data.vue
  36. 346 0
      src/views/system/dict/index.vue
  37. 216 0
      src/views/system/logininfor/index.vue
  38. 2 2
      src/views/system/menu/index.vue
  39. 304 0
      src/views/system/operlog/index.vue
  40. 309 0
      src/views/system/post/index.vue
  41. 199 0
      src/views/system/role/authUser.vue
  42. 614 0
      src/views/system/role/index.vue
  43. 138 0
      src/views/system/role/selectUser.vue
  44. 2 2
      src/views/system/user/index.vue

+ 37 - 0
src/api/sourceShop.ts

@@ -0,0 +1,37 @@
+import request from '@/benyun/utils/request'
+
+//查询数据
+export function query(params:any) {
+	return request({
+		url: '/omsOrder/omsSource/page',
+		method: 'get',
+		params:params
+	})
+}
+
+//新增数据
+export function save(data:any) {
+	return request({
+		url: '/omsOrder/omsSource/save',
+		method: 'POST',
+		data:data
+	})
+}
+
+//修改数据
+export function update(data:any) {
+	return request({
+		url: '/omsOrder/omsSource/update',
+		method: 'POST',
+		data:data
+	})
+}
+
+//删除数据
+export function del(data:any) {
+	return request({
+		url: '/omsOrder/omsSource/delete',
+		method: 'POST',
+		params:data
+	})
+}

+ 60 - 0
src/api/system/config.js

@@ -0,0 +1,60 @@
+import request from '@/benyun/utils/request'
+
+// 查询参数列表
+export function listConfig(query) {
+  return request({
+    url: '/system/config/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询参数详细
+export function getConfig(configId) {
+  return request({
+    url: '/system/config/' + configId,
+    method: 'get'
+  })
+}
+
+// 根据参数键名查询参数值
+export function getConfigKey(configKey) {
+  return request({
+    url: '/system/config/configKey/' + configKey,
+    method: 'get'
+  })
+}
+
+// 新增参数配置
+export function addConfig(data) {
+  return request({
+    url: '/system/config',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改参数配置
+export function updateConfig(data) {
+  return request({
+    url: '/system/config',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除参数配置
+export function delConfig(configId) {
+  return request({
+    url: '/system/config/' + configId,
+    method: 'delete'
+  })
+}
+
+// 刷新参数缓存
+export function refreshCache() {
+  return request({
+    url: '/system/config/refreshCache',
+    method: 'delete'
+  })
+}

+ 52 - 0
src/api/system/dict/data.js

@@ -0,0 +1,52 @@
+import request from '@/benyun/utils/request'
+
+// 查询字典数据列表
+export function listData(query) {
+  return request({
+    url: '/system/dict/data/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询字典数据详细
+export function getData(dictCode) {
+  return request({
+    url: '/system/dict/data/' + dictCode,
+    method: 'get'
+  })
+}
+
+// 根据字典类型查询字典数据信息
+export function getDicts(dictType) {
+  return request({
+    url: '/system/dict/data/type/' + dictType,
+    method: 'get'
+  })
+}
+
+// 新增字典数据
+export function addData(data) {
+  return request({
+    url: '/system/dict/data',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改字典数据
+export function updateData(data) {
+  return request({
+    url: '/system/dict/data',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除字典数据
+export function delData(dictCode) {
+  return request({
+    url: '/system/dict/data/' + dictCode,
+    method: 'delete'
+  })
+}

+ 60 - 0
src/api/system/dict/type.js

@@ -0,0 +1,60 @@
+import request from '@/benyun/utils/request'
+
+// 查询字典类型列表
+export function listType(query) {
+  return request({
+    url: '/system/dict/type/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询字典类型详细
+export function getType(dictId) {
+  return request({
+    url: '/system/dict/type/' + dictId,
+    method: 'get'
+  })
+}
+
+// 新增字典类型
+export function addType(data) {
+  return request({
+    url: '/system/dict/type',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改字典类型
+export function updateType(data) {
+  return request({
+    url: '/system/dict/type',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除字典类型
+export function delType(dictId) {
+  return request({
+    url: '/system/dict/type/' + dictId,
+    method: 'delete'
+  })
+}
+
+// 刷新字典缓存
+export function refreshCache() {
+  return request({
+    url: '/system/dict/type/refreshCache',
+    method: 'delete'
+  })
+}
+
+// 获取字典选择框列表
+export function optionselect() {
+  return request({
+    url: '/system/dict/type/optionselect',
+    method: 'get'
+  })
+}

+ 26 - 0
src/api/system/logininfor.js

@@ -0,0 +1,26 @@
+import request from '@/benyun/utils/request'
+
+// 查询登录日志列表
+export function list(query) {
+  return request({
+    url: '/system/logininfor/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 删除登录日志
+export function delLogininfor(infoId) {
+  return request({
+    url: '/system/logininfor/' + infoId,
+    method: 'delete'
+  })
+}
+
+// 清空登录日志
+export function cleanLogininfor() {
+  return request({
+    url: '/system/logininfor/clean',
+    method: 'delete'
+  })
+}

+ 26 - 0
src/api/system/operlog.js

@@ -0,0 +1,26 @@
+import request from '@/benyun/utils/request'
+
+// 查询操作日志列表
+export function list(query) {
+  return request({
+    url: '/system/operlog/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 删除操作日志
+export function delOperlog(operId) {
+  return request({
+    url: '/system/operlog/' + operId,
+    method: 'delete'
+  })
+}
+
+// 清空操作日志
+export function cleanOperlog() {
+  return request({
+    url: '/system/operlog/clean',
+    method: 'delete'
+  })
+}

+ 44 - 0
src/api/system/post.ts

@@ -0,0 +1,44 @@
+import request from '@/benyun/utils/request'
+
+// 查询岗位列表
+export function listPost(query:any) {
+  return request({
+    url: '/system/post/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询岗位详细
+export function getPost(postId:any) {
+  return request({
+    url: '/system/post/' + postId,
+    method: 'get'
+  })
+}
+
+// 新增岗位
+export function addPost(data:any) {
+  return request({
+    url: '/system/post',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改岗位
+export function updatePost(data:any) {
+  return request({
+    url: '/system/post',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除岗位
+export function delPost(postId:any) {
+  return request({
+    url: '/system/post/' + postId,
+    method: 'delete'
+  })
+}

+ 111 - 0
src/api/system/role.js

@@ -0,0 +1,111 @@
+import request from '@/benyun/utils/request'
+
+// 查询角色列表
+export function listRole(query) {
+  return request({
+    url: '/system/role/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询角色详细
+export function getRole(roleId) {
+  return request({
+    url: '/system/role/' + roleId,
+    method: 'get'
+  })
+}
+
+// 新增角色
+export function addRole(data) {
+  return request({
+    url: '/system/role',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改角色
+export function updateRole(data) {
+  return request({
+    url: '/system/role',
+    method: 'put',
+    data: data
+  })
+}
+
+// 角色数据权限
+export function dataScope(data) {
+  return request({
+    url: '/system/role/dataScope',
+    method: 'put',
+    data: data
+  })
+}
+
+// 角色状态修改
+export function changeRoleStatus(roleId, status) {
+  const data = {
+    roleId,
+    status
+  }
+  return request({
+    url: '/system/role/changeStatus',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除角色
+export function delRole(roleId) {
+  return request({
+    url: '/system/role/' + roleId,
+    method: 'delete'
+  })
+}
+
+// 查询角色已授权用户列表
+export function allocatedUserList(query) {
+  return request({
+    url: '/system/role/authUser/allocatedList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询角色未授权用户列表
+export function unallocatedUserList(query) {
+  return request({
+    url: '/system/role/authUser/unallocatedList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 取消用户授权角色
+export function authUserCancel(data) {
+  return request({
+    url: '/system/role/authUser/cancel',
+    method: 'put',
+    data: data
+  })
+}
+
+// 批量取消用户授权角色
+export function authUserCancelAll(data) {
+  return request({
+    url: '/system/role/authUser/cancelAll',
+    method: 'put',
+    params: data
+  })
+}
+
+// 授权用户选择
+export function authUserSelectAll(data) {
+  return request({
+    url: '/system/role/authUser/selectAll',
+    method: 'put',
+    params: data
+  })
+}

+ 5 - 5
src/benyun/utils/benyuntech.ts

@@ -47,11 +47,11 @@ export function parseTime(time:any, pattern:string) {
 }
 
 // 表单重置
-// export function resetForm(refName:string) {
-//   if ((this as any).$refs[refName]) {
-//     (this as any).$refs[refName].resetFields();
-//   }
-// }
+export function resetForm(refName:string,that:any) {
+  if (that.$refs[refName]) {
+    that.$refs[refName].resetFields();
+  }
+}
 
 // 添加日期范围
 export function addDateRange(params:any, dateRange:any, propName:any) {

+ 82 - 0
src/benyun/utils/dict/Dict.js

@@ -0,0 +1,82 @@
+import Vue from 'vue'
+import {mergeRecursive} from "../benyuntech";
+import DictMeta from './DictMeta'
+import DictData from './DictData'
+
+const DEFAULT_DICT_OPTIONS = {
+  types: [],
+}
+
+/**
+ * @classdesc 字典
+ * @property {Object} label 标签对象,内部属性名为字典类型名称
+ * @property {Object} dict 字段数组,内部属性名为字典类型名称
+ * @property {Array.<DictMeta>} _dictMetas 字典元数据数组
+ */
+export default class Dict {
+  constructor() {
+    this.owner = null
+    this.label = {}
+    this.type = {}
+  }
+
+  init(options) {
+    if (options instanceof Array) {
+      options = { types: options }
+    }
+    const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options)
+    if (opts.types === undefined) {
+      throw new Error('need dict types')
+    }
+    const ps = []
+    this._dictMetas = opts.types.map(t => DictMeta.parse(t))
+    this._dictMetas.forEach(dictMeta => {
+      const type = dictMeta.type
+      Vue.set(this.label, type, {})
+      Vue.set(this.type, type, [])
+      if (dictMeta.lazy) {
+        return
+      }
+      ps.push(loadDict(this, dictMeta))
+    })
+    return Promise.all(ps)
+  }
+
+  /**
+   * 重新加载字典
+   * @param {String} type 字典类型
+   */
+  reloadDict(type) {
+    const dictMeta = this._dictMetas.find(e => e.type === type)
+    if (dictMeta === undefined) {
+      return Promise.reject(`the dict meta of ${type} was not found`)
+    }
+    return loadDict(this, dictMeta)
+  }
+}
+
+/**
+ * 加载字典
+ * @param {Dict} dict 字典
+ * @param {DictMeta} dictMeta 字典元数据
+ * @returns {Promise}
+ */
+function loadDict(dict, dictMeta) {
+  return dictMeta.request(dictMeta)
+    .then(response => {
+      const type = dictMeta.type
+      let dicts = dictMeta.responseConverter(response, dictMeta)
+      if (!(dicts instanceof Array)) {
+        console.error('the return of responseConverter must be Array.<DictData>')
+        dicts = []
+      } else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) {
+        console.error('the type of elements in dicts must be DictData')
+        dicts = []
+      }
+      dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts)
+      dicts.forEach(d => {
+        Vue.set(dict.label[type], d.value, d.label)
+      })
+      return dicts
+    })
+}

+ 17 - 0
src/benyun/utils/dict/DictConverter.js

@@ -0,0 +1,17 @@
+import DictOptions from './DictOptions'
+import DictData from './DictData'
+
+export default function(dict, dictMeta) {
+  const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS)
+  const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS)
+  return new DictData(dict[label], dict[value], dict)
+}
+
+/**
+ * 确定字典字段
+ * @param {DictData} dict
+ * @param  {...String} fields
+ */
+function determineDictField(dict, ...fields) {
+  return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f))
+}

+ 13 - 0
src/benyun/utils/dict/DictData.js

@@ -0,0 +1,13 @@
+/**
+ * @classdesc 字典数据
+ * @property {String} label 标签
+ * @property {*} value 标签
+ * @property {Object} raw 原始数据
+ */
+export default class DictData {
+  constructor(label, value, raw) {
+    this.label = label
+    this.value = value
+    this.raw = raw
+  }
+}

+ 38 - 0
src/benyun/utils/dict/DictMeta.js

@@ -0,0 +1,38 @@
+import {mergeRecursive} from "../benyuntech";
+import DictOptions from './DictOptions'
+
+/**
+ * @classdesc 字典元数据
+ * @property {String} type 类型
+ * @property {Function} request 请求
+ * @property {String} label 标签字段
+ * @property {String} value 值字段
+ */
+export default class DictMeta {
+  constructor(options) {
+    this.type = options.type
+    this.request = options.request
+    this.responseConverter = options.responseConverter
+    this.labelField = options.labelField
+    this.valueField = options.valueField
+    this.lazy = options.lazy === true
+  }
+}
+
+
+/**
+ * 解析字典元数据
+ * @param {Object} options
+ * @returns {DictMeta}
+ */
+DictMeta.parse= function(options) {
+  let opts = null
+  if (typeof options === 'string') {
+    opts = DictOptions.metas[options] || {}
+    opts.type = options
+  } else if (typeof options === 'object') {
+    opts = options
+  }
+  opts = mergeRecursive(DictOptions.metas['*'], opts)
+  return new DictMeta(opts)
+}

+ 51 - 0
src/benyun/utils/dict/DictOptions.js

@@ -0,0 +1,51 @@
+import {mergeRecursive} from "../benyuntech";
+import dictConverter from './DictConverter'
+
+export const options = {
+  metas: {
+    '*': {
+      /**
+       * 字典请求,方法签名为function(dictMeta: DictMeta): Promise
+       */
+      request: (dictMeta) => {
+        console.log(`load dict ${dictMeta.type}`)
+        return Promise.resolve([])
+      },
+      /**
+       * 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData
+       */
+      responseConverter,
+      labelField: 'label',
+      valueField: 'value',
+    },
+  },
+  /**
+   * 默认标签字段
+   */
+  DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'],
+  /**
+   * 默认值字段
+   */
+  DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'],
+}
+
+/**
+ * 映射字典
+ * @param {Object} response 字典数据
+ * @param {DictMeta} dictMeta 字典元数据
+ * @returns {DictData}
+ */
+function responseConverter(response, dictMeta) {
+  const dicts = response.content instanceof Array ? response.content : response
+  if (dicts === undefined) {
+    console.warn(`no dict data of "${dictMeta.type}" found in the response`)
+    return []
+  }
+  return dicts.map(d => dictConverter(d, dictMeta))
+}
+
+export function mergeOptions(src) {
+  mergeRecursive(options, src)
+}
+
+export default options

+ 33 - 0
src/benyun/utils/dict/index.js

@@ -0,0 +1,33 @@
+import Dict from './Dict'
+import {mergeOptions} from './DictOptions'
+
+export default function(Vue, options) {
+  mergeOptions(options)
+  Vue.mixin({
+    data() {
+      if (this.$options === undefined || this.$options.dicts === undefined || this.$options.dicts === null) {
+        return {}
+      }
+      const dict = new Dict()
+      dict.owner = this
+      return {
+        dict
+      }
+    },
+    created() {
+      if (!(this.dict instanceof Dict)) {
+        return
+      }
+      options.onCreated && options.onCreated(this.dict)
+      this.dict.init(this.$options.dicts).then(() => {
+        options.onReady && options.onReady(this.dict)
+        this.$nextTick(() => {
+          this.$emit('dictReady', this.dict)
+          if (this.$options.methods && this.$options.methods.onDictReady instanceof Function) {
+            this.$options.methods.onDictReady.call(this, this.dict)
+          }
+        })
+      })
+    },
+  })
+}

+ 21 - 0
src/components/DictData/index.js

@@ -0,0 +1,21 @@
+import Vue from 'vue'
+import DataDict from '@/benyun/utils/dict'
+import {getDicts as getDicts} from '@/api/system/dict/data'
+
+function install() {
+  Vue.use(DataDict, {
+    metas: {
+      '*': {
+        labelField: 'dictLabel',
+        valueField: 'dictValue',
+        request(dictMeta) {
+          return getDicts(dictMeta.type).then(res => res.data)
+        },
+      },
+    },
+  })
+}
+
+export default {
+  install,
+}

+ 52 - 0
src/components/DictTag/index.vue

@@ -0,0 +1,52 @@
+<template>
+  <div>
+    <template v-for="(item, index) in options">
+      <template v-if="values.includes(item.value)">
+        <span
+          v-if="item.raw.listClass == 'default' || item.raw.listClass == ''"
+          :key="item.value"
+          :index="index"
+          :class="item.raw.cssClass"
+          >{{ item.label }}</span
+        >
+        <el-tag
+          v-else
+          :disable-transitions="true"
+          :key="item.value"
+          :index="index"
+          :type="item.raw.listClass == 'primary' ? '' : item.raw.listClass"
+          :class="item.raw.cssClass"
+        >
+          {{ item.label }}
+        </el-tag>
+      </template>
+    </template>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "DictTag",
+  props: {
+    options: {
+      type: Array,
+      default: null,
+    },
+    value: [Number, String, Array],
+  },
+  computed: {
+    values() {
+      if (this.value !== null && typeof this.value !== 'undefined') {
+        return Array.isArray(this.value) ? this.value : [String(this.value)];
+      } else {
+        return [];
+      }
+    },
+  },
+};
+</script>
+<style scoped>
+.el-tag + .el-tag {
+  margin-left: 10px;
+}
+</style>

+ 114 - 0
src/components/Pagination/index.vue

@@ -0,0 +1,114 @@
+<template>
+  <div :class="{'hidden':hidden}" class="pagination-container">
+    <el-pagination
+      :background="background"
+      :current-page.sync="currentPage"
+      :page-size.sync="pageSize"
+      :layout="layout"
+      :page-sizes="pageSizes"
+      :pager-count="pagerCount"
+      :total="total"
+      v-bind="$attrs"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+    />
+  </div>
+</template>
+
+<script>
+import { scrollTo } from '@/utils/scroll-to'
+
+export default {
+  name: 'Pagination',
+  props: {
+    total: {
+      required: true,
+      type: Number
+    },
+    page: {
+      type: Number,
+      default: 1
+    },
+    limit: {
+      type: Number,
+      default: 20
+    },
+    pageSizes: {
+      type: Array,
+      default() {
+        return [10, 20, 30, 50, 100, 250, 500, 1000, 5000]
+      }
+    },
+    // 移动端页码按钮的数量端默认值5
+    pagerCount: {
+      type: Number,
+      default: document.body.clientWidth < 992 ? 5 : 7
+    },
+    layout: {
+      type: String,
+      default: 'total, sizes, prev, pager, next, jumper'
+    },
+    background: {
+      type: Boolean,
+      default: true
+    },
+    autoScroll: {
+      type: Boolean,
+      default: true
+    },
+    hidden: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+    };
+  },
+  computed: {
+    currentPage: {
+      get() {
+        return this.page
+      },
+      set(val) {
+        this.$emit('update:page', val)
+      }
+    },
+    pageSize: {
+      get() {
+        return this.limit
+      },
+      set(val) {
+        this.$emit('update:limit', val)
+      }
+    }
+  },
+  methods: {
+    handleSizeChange(val) {
+      if (this.currentPage * val > this.total) {
+        this.currentPage = 1
+      }
+      this.$emit('pagination', { page: this.currentPage, limit: val })
+      if (this.autoScroll) {
+        scrollTo(0, 800)
+      }
+    },
+    handleCurrentChange(val) {
+      this.$emit('pagination', { page: val, limit: this.pageSize })
+      if (this.autoScroll) {
+        scrollTo(0, 800)
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.pagination-container {
+  background: #fff;
+  padding: 32px 16px;
+}
+.pagination-container.hidden {
+  display: none;
+}
+</style>

+ 30 - 2
src/main.ts

@@ -9,6 +9,7 @@ import Cookies from 'js-cookie'
 import lodash from 'lodash'
 import plugins from '@/benyun/plugins'
 import directive from './directive' // directive
+import {getDicts} from "@/api/system/dict/data";
 
 import '@/assets/styles/index.scss' // global css
 import '@/assets/styles/benyuntech.scss' // young css
@@ -28,9 +29,26 @@ import zhCN from 'vxe-table/lib/locale/lang/zh-CN'
 VXETable.setup({
   i18n: (key, args) => XEUtils.toFormatString(XEUtils.get(zhCN, key), args)
 })
-
+import {addDateRange, handleTree, parseTime, resetForm, selectDictLabel, selectDictLabels} from "@/benyun/utils/benyuntech";
+import {getConfigKey} from "@/api/system/config";
 import gmComponent from './benyun/plugins/componentRegister'
 
+// 字典数据组件
+import DictData from '@/components/DictData'
+DictData.install()
+
+// 分页组件
+import Pagination from "@/components/Pagination/index.vue";
+// 自定义表格工具组件
+import RightToolbar from "@/components/RightToolbar/index.vue"
+// 字典标签组件
+import DictTag from '@/components/DictTag/index.vue'
+
+// 全局组件挂载
+Vue.component('DictTag', DictTag)
+Vue.component('Pagination', Pagination)
+Vue.component('RightToolbar', RightToolbar)
+
 //组件注册
 import productDialog from './components/productDialog/productDialog.vue'
 Vue.component('productDialog', productDialog);
@@ -63,11 +81,21 @@ Vue.use(directive);
 Vue.config.productionTip = false
 
 // 全局方法挂载
+Vue.prototype.getDicts = getDicts
 Vue.prototype.$request = request;
 Vue.prototype.$lodash = lodash;
 Vue.prototype.$download = download; //下载
 Vue.prototype.$XModal = VXETable.modal
-
+// 全局方法挂载
+Vue.prototype.getDicts = getDicts
+Vue.prototype.getConfigKey = getConfigKey
+Vue.prototype.parseTime = parseTime
+Vue.prototype.resetForm = resetForm
+Vue.prototype.addDateRange = addDateRange
+Vue.prototype.selectDictLabel = selectDictLabel
+Vue.prototype.selectDictLabels = selectDictLabels
+Vue.prototype.download = download
+Vue.prototype.handleTree = handleTree
 
 new Vue({
   router,

+ 46 - 46
src/router/index.ts

@@ -60,7 +60,7 @@ export const constantRoutes: Array<any> = [
     path:'/order',
     name:'Order',
     component:() => import('@/views/demo/order/order.vue')
-  },
+  }
   // {
   //   path:'/demo',
   //   name:'Demo',
@@ -125,51 +125,51 @@ export const constantRoutes: Array<any> = [
 // 动态路由,基于用户权限动态去加载
 export const dynamicRoutes = [
   
-  // {
-  //   path: '/system/user-auth',
-  //   component: Layout,
-  //   hidden: true,
-  //   permissions: ['system:user:edit'],
-  //   children: [{
-  //     path: 'role/:userId(\\d+)',
-  //     component: () => import('@/views/system/user/authRole'),
-  //     name: 'AuthRole',
-  //     meta: {
-  //       title: '分配角色',
-  //       activeMenu: '/system/user'
-  //     }
-  //   }]
-  // },
-  // {
-  //   path: '/system/role-auth',
-  //   component: Layout,
-  //   hidden: true,
-  //   permissions: ['system:role:edit'],
-  //   children: [{
-  //     path: 'user/:roleId(\\d+)',
-  //     component: () => import('@/views/system/role/authUser'),
-  //     name: 'AuthUser',
-  //     meta: {
-  //       title: '分配用户',
-  //       activeMenu: '/system/role'
-  //     }
-  //   }]
-  // },
-  // {
-  //   path: '/system/dict-data',
-  //   component: Layout,
-  //   hidden: true,
-  //   permissions: ['system:dict:list'],
-  //   children: [{
-  //     path: 'index/:dictId(\\d+)',
-  //     component: () => import('@/views/system/dict/data'),
-  //     name: 'Data',
-  //     meta: {
-  //       title: '字典数据',
-  //       activeMenu: '/system/dict'
-  //     }
-  //   }]
-  // },
+  {
+    path: '/system/user-auth',
+    component: Layout,
+    hidden: true,
+    permissions: ['system:user:edit'],
+    children: [{
+      path: 'role/:userId(\\d+)',
+      component: () => import('@/views/system/user/authRole.vue'),
+      name: 'AuthRole',
+      meta: {
+        title: '分配角色',
+        activeMenu: '/system/user'
+      }
+    }]
+  },
+  {
+    path: '/system/role-auth',
+    component: Layout,
+    hidden: true,
+    permissions: ['system:role:edit'],
+    children: [{
+      path: 'user/:roleId(\\d+)',
+      component: () => import('@/views/system/role/authUser.vue'),
+      name: 'AuthUser',
+      meta: {
+        title: '分配用户',
+        activeMenu: '/system/role'
+      }
+    }]
+  },
+  {
+    path: '/system/dict-data',
+    component: Layout,
+    hidden: true,
+    permissions: ['system:dict:list'],
+    children: [{
+      path: 'index/:dictId(\\d+)',
+      component: () => import('@/views/system/dict/data.vue'),
+      name: 'Data',
+      meta: {
+        title: '字典数据',
+        activeMenu: '/system/dict'
+      }
+    }]
+  },
   // {
   //   path: '/monitor/job-log',
   //   component: Layout,

+ 58 - 0
src/utils/scroll-to.js

@@ -0,0 +1,58 @@
+Math.easeInOutQuad = function(t, b, c, d) {
+  t /= d / 2
+  if (t < 1) {
+    return c / 2 * t * t + b
+  }
+  t--
+  return -c / 2 * (t * (t - 2) - 1) + b
+}
+
+// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
+var requestAnimFrame = (function() {
+  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
+})()
+
+/**
+ * Because it's so fucking difficult to detect the scrolling element, just move them all
+ * @param {number} amount
+ */
+function move(amount) {
+  document.documentElement.scrollTop = amount
+  document.body.parentNode.scrollTop = amount
+  document.body.scrollTop = amount
+}
+
+function position() {
+  return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
+}
+
+/**
+ * @param {number} to
+ * @param {number} duration
+ * @param {Function} callback
+ */
+export function scrollTo(to, duration, callback) {
+  const start = position()
+  const change = to - start
+  const increment = 20
+  let currentTime = 0
+  duration = (typeof (duration) === 'undefined') ? 500 : duration
+  var animateScroll = function() {
+    // increment the time
+    currentTime += increment
+    // find the value with the quadratic in-out easing function
+    var val = Math.easeInOutQuad(currentTime, start, change, duration)
+    // move the document.body
+    move(val)
+    // do the animation unless its over
+    if (currentTime < duration) {
+      requestAnimFrame(animateScroll)
+    } else {
+      if (callback && typeof (callback) === 'function') {
+        // the animation is done so lets callback
+        callback()
+      }
+    }
+  }
+  animateScroll()
+}

+ 0 - 1
src/views/login.vue

@@ -158,7 +158,6 @@ export default {
     },
     handleLogin() {
       this.$refs.loginForm.validate(valid => {
-				console.log('valid',valid);
         if (valid) {
           this.loading = true;
           if (this.loginForm.rememberMe) {

+ 90 - 85
src/views/oms/order/components/addOrder.vue

@@ -19,7 +19,7 @@
         <el-collapse-item title="基本信息" name="1" class="add-order-item">
           <by-form :propConfig="baseConfig" ref="baseform" @formChange="formChangeBase">
             <template v-slot:sourceFrom_desc='{ value }'>
-              {{ getFromText(value.sourceFrom) }}
+              {{ getFromText(value.operationFlag) }}
             </template>
             <template v-slot:shopName="{value}">
               <el-input :placeholder="!orderValue.id?'请选择标签':''" :value="value.shopName" :disabled="orderValue.id?true:false" @clear="clearShop" size="small" class="myinpuy-with-select" clearable>
@@ -35,8 +35,8 @@
         </el-collapse-item>
         <el-collapse-item title="买家信息" name="2" class="add-order-item">
           <by-form :propConfig="buyerInfoConfig" ref="infoform">
-            <template v-slot:shopBuyerId="{value}">
-              <el-input placeholder="请选择买家'" :value="value.shopBuyerId" @clear="clearBuyer" size="small" class="myinpuy-with-select" clearable>
+            <template v-slot:buyerNickname="{value}">
+              <el-input placeholder="请选择买家'" :value="value.buyerNickname" @clear="clearBuyer" size="small" class="myinpuy-with-select" clearable>
                 <el-button slot="append" icon="el-icon-more" @click="showBuyer"></el-button>
               </el-input>
             </template>
@@ -85,10 +85,9 @@
         </el-collapse-item>
         <el-collapse-item title="商品订单" name="3" class="add-order-item">
           <div class="addProductTool">
-            <by-tool :propConfig="toolConfig" ref="tool"></by-tool>
+            <by-tool :propConfig="toolConfig" v-if="orderValue.status !== 'Delivering' && orderValue.status !== 'Sent'" ref="tool"></by-tool>
             <div class="preferential">
-              <div class="pre-title">抵扣金额<i class="el-icon-info" title="支持输入数字和百分比。若输入百分比,将自动计算折扣金额=商品成交总金额*百分比(举例:打9折,请输入10%),
-                只在订单创建时计算一次,在订单创建后修改商品价格不会自动计算,运费不参与折扣。"></i>:</div>
+              <div class="pre-title">抵扣金额<i class="el-icon-info" title="支持输入数字和百分比。若输入百分比,将自动计算折扣金额=商品成交总金额*百分比(举例:打9折,请输入10%),只在订单创建时计算一次,在订单创建后修改商品价格不会自动计算,运费不参与折扣。"></i>:</div>
               <vxe-input v-if="!orderValue.id" v-model="freeAmount" class="freeAmount" placeholder="请输入" size="mini" @change="freeAmountChange"></vxe-input>
               <span v-else>{{ freeAmount }}</span>
             </div>
@@ -209,7 +208,7 @@ export default class AddOrder extends Vue {
   topStep=['WaitPay','WaitConfirm','Delivering']
   stepCon=['WaitPay','WaitConfirm','WaitFConfirm','WaitOuterSent','Sent','Delivering']
   activeNames:Array<any> =['1','2','3','4','5','save'];
-  sourceFrom:any={
+  operationFlag:any={
     'ERP':'手工下单',
     'COPY':'复制',
     'MERGE':'合并',
@@ -230,9 +229,6 @@ export default class AddOrder extends Vue {
       size:'small',
       itemCount:4,
       rules:{
-        shopName:[{
-          required: true, message: '店铺名称不能为空!', trigger: 'change'
-        }],
         orderDate:[{
           required: true, message: '订单日期不能为空!', trigger: 'blur'
         }]
@@ -241,17 +237,18 @@ export default class AddOrder extends Vue {
     columns:[
       [{
         span:6,
-        label:'店铺名称',
-        prop:'shopName',
-        slot:true,
-        // component:'by-input',
-        // compConfig:{
-        //   attr:{
-        //     type:'integer',
-        //     placeholder:'请输入店铺名称',
-        //     clearable:true
-        //   }
-        // }
+        label:'订单来源',
+        prop:'upSourceName',
+        // slot:true,
+        component:'by-input',
+        compConfig:{
+          attr:{
+            readonly:true
+            // type:'integer',
+            // placeholder:'请输入店铺名称',
+            // clearable:true
+          }
+        }
       },{
         span:6,
         label:'线上订单',
@@ -302,18 +299,6 @@ export default class AddOrder extends Vue {
           }
         }
       },
-      // {
-      //   span:6,
-      //   label:'快递单号',
-      //   prop:'logisticsId',
-      //   component:'by-input',
-      //   compConfig:{
-      //     attr:{
-      //       readonly:true,
-      //       clearable:true
-      //     }
-      //   }
-      // },
       {
         span:6,
         label:'业务员',
@@ -328,8 +313,8 @@ export default class AddOrder extends Vue {
       }],
       [{
         span:6,
-        label:'订单来源',
-        prop:'sourceFrom',
+        label:'操作标记',
+        prop:'operationFlag',
         descSlot:true,
         component:'by-select',
         colspan:2,
@@ -339,7 +324,7 @@ export default class AddOrder extends Vue {
             disabled:true,
             data:[{
               label:'手工下单',
-              value:'ERP'
+              value:'OMS'
             },{
               label:'复制',
               value:'COPY'
@@ -357,10 +342,10 @@ export default class AddOrder extends Vue {
               value:'IMPORT'
             },{
               label:'供销推送',
-              value:'drp-s'
+              value:'DRP-S'
             },{
               label:'分销推送',
-              value:'drp-d'
+              value:'DRP-D'
             },{
               label:'快手;微商城',
               value:'KWAISHOP'
@@ -426,7 +411,7 @@ export default class AddOrder extends Vue {
       size:'small',
       itemCount:4,
       rules:{
-        shopBuyerId:[{
+        buyerNickname:[{
           required: true, message: '买家账号不能为空!', trigger: 'blur'
         }],
         receiverDistrictCode:[{
@@ -446,7 +431,7 @@ export default class AddOrder extends Vue {
       [{
         span:6,
         label:'买家',
-        prop:'shopBuyerId',
+        prop:'buyerNickname',
         slot:true,
         // component:'by-input',
         // compConfig:{
@@ -469,40 +454,6 @@ export default class AddOrder extends Vue {
           }
         }
       }],
-      [{
-        span:24,
-        label:'收货地址',
-        // slot:true,
-        prop:'receiverProvinceCode',
-        component:'by-area',
-        colspan:2,
-        descSlot:true,
-        compConfig:{
-          attr:{
-            province:'receiverProvince', //省
-            provinceCode:'receiverProvinceCode', //省编码
-            city:'receiverCity',   //市
-            cityCode:'receiverCityCode',  //市编码
-            county:'receiverDistrict',  //县/区
-            countyCode:'receiverDistrictCode',   //县/区编码
-            townCode:'receiverTownCode',
-            town:'receiverTown',
-            // show:'county'
-          }
-        }
-      }],
-      [{
-        span:24,
-        label:'详细地址',
-        prop:'receiverAddress',
-        component:'by-input',
-        compConfig:{
-          attr:{
-            placeholder:'请输入详细地址',
-            clearable:true
-          }
-        }
-      }],
       [{
         span:6,
         label:'手机',
@@ -536,6 +487,40 @@ export default class AddOrder extends Vue {
             clearable:true
           }
         }
+      }],
+      [{
+        span:24,
+        label:'收货地址',
+        // slot:true,
+        prop:'receiverProvinceCode',
+        component:'by-area',
+        colspan:2,
+        descSlot:true,
+        compConfig:{
+          attr:{
+            province:'receiverProvince', //省
+            provinceCode:'receiverProvinceCode', //省编码
+            city:'receiverCity',   //市
+            cityCode:'receiverCityCode',  //市编码
+            county:'receiverDistrict',  //县/区
+            countyCode:'receiverDistrictCode',   //县/区编码
+            townCode:'receiverTownCode',
+            town:'receiverTown',
+            // show:'county'
+          }
+        }
+      }],
+      [{
+        span:24,
+        label:'详细地址',
+        prop:'receiverAddress',
+        component:'by-input',
+        compConfig:{
+          attr:{
+            placeholder:'请输入详细地址',
+            clearable:true
+          }
+        }
       }]
     ]
   }
@@ -881,11 +866,11 @@ export default class AddOrder extends Vue {
     if(f){
       let arr:Array<any>=f.split(',');
       for(const item of arr){
-        if(this.sourceFrom[item]){
+        if(this.operationFlag[item]){
           if(t){
-            t = t + ' , ' + this.sourceFrom[item]
+            t = t + ' , ' + this.operationFlag[item]
           }else{
-            t = this.sourceFrom[item]
+            t = this.operationFlag[item]
           }
         }
       }
@@ -932,7 +917,7 @@ export default class AddOrder extends Vue {
   clearBuyer(){
     let value:any = (this.$refs.infoform as any).getValue();
     value.buyerId = '';
-    value.shopBuyerId = '';
+    value.buyerNickname = '';
     (this.$refs.infoform as any).setValue(value);
   }
   setDetail(data:any){
@@ -1020,11 +1005,9 @@ export default class AddOrder extends Vue {
       let payFormValue = (this.$refs.payform as any).getValue();
       payFormValue.sellerId = this.userInfo.userId;
       payFormValue.orderId = this.orderValue.id;
-      payFormValue.sourceFrom = this.orderValue.sourceFrom;
+      payFormValue.operationFlag = this.orderValue.operationFlag;
       payFormValue.isOrderPay = 1;
-      payFormValue.shopBuyerId = this.orderValue.shopBuyerId;
-      payFormValue.shopId = this.orderValue.shopId;
-      payFormValue.shopName = this.orderValue.shopName;
+      payFormValue.buyerNickname = this.orderValue.buyerNickname;
       payFormValue.status = this.orderValue.status;
       this.load = true;
       addPay(payFormValue).then(()=>{
@@ -1044,7 +1027,7 @@ export default class AddOrder extends Vue {
   // 买家信息
   confirmBuyerInfo(data:any) {
     let v:any = {}
-    v.shopBuyerId = data[0].name
+    v.buyerNickname = data[0].name
     v.buyerId = data[0].id
     v.receiverName = data[0].contacts
     v.receiverProvince = data[0].province
@@ -1057,12 +1040,20 @@ export default class AddOrder extends Vue {
     v.receiverTown = data[0].street
     v.receiverAddress = data[0].address
     v.receiverMobile = data[0].telephone
+    v.receiverPhone = data[0].phonenum
     if(this.$refs.infoform){
       (this.$refs.infoform as any).setValue(v)
     }
   }
   //添加商品
   toolAddProduct(){
+    if (this.orderValue.status == 'Delivering' || this.orderValue.status == 'Sent') {
+      this.$message({
+        message:'该订单已发货不能进行修改!',
+        type:'warning'
+      })
+      return
+    }
     if(this.orderValue.id){
       (this.$refs.addProductModal as any).setBillValue(this.orderValue.id);
       (this.$refs.addProductModal as any).setShow(true);
@@ -1073,6 +1064,13 @@ export default class AddOrder extends Vue {
   }
   //添加赠品
   toolAddGift(){
+    if (this.orderValue.status == 'Delivering' || this.orderValue.status == 'Sent') {
+      this.$message({
+        message:'该订单已发货不能进行修改!',
+        type:'warning'
+      })
+      return
+    }
     if(this.orderValue.id){
       (this.$refs.addProductModal as any).setBillValue([this.orderValue.id]);
       (this.$refs.addProductModal as any).setShow(true);
@@ -1100,6 +1098,13 @@ export default class AddOrder extends Vue {
   }
   //商品编辑
   editProduct(){
+    if (this.orderValue.status == 'Delivering' || this.orderValue.status == 'Sent') {
+      this.$message({
+        message:'该订单已发货不能进行修改!',
+        type:'warning'
+      })
+      return
+    }
     let tableValue = (this.$refs.table as any).getValue();
     (this.$refs.editProductModal as any).setShow(true);
     (this.$refs.editProductModal as any).setOrderValue(this.orderValue);
@@ -1128,12 +1133,12 @@ export default class AddOrder extends Vue {
       info.remark = data.remark;
       info.labels = data.labels;
       info.logisticsId = data.logisticsId;
-      info.sourceFrom = data.sourceFrom;
+      info.operationFlag = data.operationFlag;
       this.baseConfig.attr.data = info;
       (this.$refs.baseform as any).setValue(info);
       //买家
       let buyerInfo:any={};
-      buyerInfo.shopBuyerId = data.shopBuyerId;
+      buyerInfo.buyerNickname = data.buyerNickname;
       buyerInfo.receiverName = data.receiverName;
       buyerInfo.receiverProvince=data.receiverProvince;//省
       buyerInfo.receiverProvinceCode=data.receiverProvinceCode; //省编码

+ 3 - 2
src/views/oms/order/components/batchbyOneModal.vue

@@ -99,7 +99,6 @@ export default class BatchbyOneModal extends Vue {
   }
   setValue(data:Array<any>){
     this.data = [];
-    
     if(data && data.length > 0){
       for(const item of data){
         const requestId =  new Date().getTime() + this.getUuid()
@@ -151,7 +150,9 @@ export default class BatchbyOneModal extends Vue {
       this.data = []
     }
     this.$nextTick(()=>{
-      (this.$refs.info as any).setValue(this.data[this.currentOrder])
+      (this.$refs.info as any).setValue(this.data[this.currentOrder]);
+      this.drawer = false;
+      (this.$refs.info as any).showModal = true;
     })
   }
   getUuid(){

+ 3 - 0
src/views/oms/order/components/buyerInfoModal.vue

@@ -101,6 +101,9 @@ export default class BuyerInfoModal extends Vue {
       },{
         title:'手机号',
         field:'telephone'
+      },{
+        title: '固定电话',
+        field: 'phonenum'
       }]
     },
   }

+ 7 - 5
src/views/oms/order/components/carpoolModal.vue

@@ -6,12 +6,12 @@
     :direction="direction"
     size="50%">
     <div class="content">
-      <div class="order-cont" v-for="(item,index) of data" :key="index">
+      <div class="order-cont" v-if="data && data[0]">
         <div class="cont-left">
-          <div class="order-code">订单号:{{item.id}}</div>
+          <div class="order-code">订单号:{{data[0].id}}</div>
           <div class="order-other">
-            <div class="receive-name">姓名:{{item.receiverName}}</div>
-            <div class="address">目的地:<span>{{item.receiverProvince+'-'+item.receiverCity}}</span></div>
+            <div class="receive-name">姓名:{{data[0].receiverName}}</div>
+            <div class="address">目的地:<span>{{data[0].receiverProvince+'-'+data[0].receiverCity}}</span></div>
             <!-- <div class="time">预计送达:8月6日 12:00前</div> -->
           </div>
         </div>
@@ -95,7 +95,9 @@ export default class CarpoolModal extends Vue {
       }
     }
     this.$nextTick(()=>{
-      (this.$refs.info as any).setValue(this.value)
+      (this.$refs.info as any).setValue(this.value);
+      this.drawer = false;
+      (this.$refs.info as any).showModal = true;
     })
   }
   getUuid(){

+ 4 - 0
src/views/oms/order/components/deliveryGoodsInfo.vue

@@ -201,6 +201,8 @@ export default class DeliveryGoodsInfo extends Vue {
         }],
         tmsShipmentContacts:[{
           required: true, message: '发货人电话不能为空!', trigger: 'blur'
+        },{
+          validator: this.validatePhone, trigger: 'blur' 
         }],
         // tmsShipmentProvince:[{
         //   required: true, message: '请选择发货地址!', trigger: 'change'
@@ -213,6 +215,8 @@ export default class DeliveryGoodsInfo extends Vue {
         }],
         tmsUnloadingContacts:[{
           required: true, message: '收货人电话不能为空!', trigger: 'blur'
+        },{
+          validator: this.validatePhone, trigger: 'blur' 
         }],
         tmsUnloadingProvince:[{
           required: true, message: '请选择收货地址!', trigger: 'change'

+ 3 - 0
src/views/oms/order/components/expressDeliveryModal.vue

@@ -263,6 +263,9 @@ export default class ExpressDeliveryModal extends Vue {
         }
       }
     }
+    this.$nextTick(() => {
+      this.edit()
+    })
   }
   getUuid(){
     return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);

+ 5 - 5
src/views/oms/order/components/loadOrderModal.vue

@@ -107,7 +107,7 @@
 <script lang="ts">
 import { Component, Prop, Vue, Watch } from "vue-property-decorator";
 import { pullOrder } from '@/api/omsOrder'
-import { query } from '@/api/shop'
+import { query } from '@/api/sourceShop'
 @Component
 export default class LoadOrderModal extends Vue {
   load = false;
@@ -198,8 +198,8 @@ export default class LoadOrderModal extends Vue {
     if(!this.shopId){
       msg = '店铺名称'
     }
-    value.shopId = this.shopId;
-    value.showName = this.shopName;
+    value.sourceId = this.shopId;
+    value.soueceName = this.shopName;
     value.type = this.type;
     if(this.type == 1){
       if(!this.sourceIds){
@@ -296,8 +296,8 @@ export default class LoadOrderModal extends Vue {
       if (res.data && res.data.records) {
         for(const item of res.data.records) {
           this.shopOptions.push({
-            label: item.shopName,
-            value: item.shopId
+            label: item.sourceName,
+            value: item.id
           })
         }
       }

+ 26 - 34
src/views/oms/order/components/orderTable.vue

@@ -19,7 +19,7 @@
         </div>
       </template>
     </vxe-column>
-    <vxe-column field="sourceId" title="线上订单号" width="140"> </vxe-column>
+    <vxe-column field="rawSourceId" title="线上订单号" width="140"> </vxe-column>
     <vxe-column field="isSubmitted" title="提交状态" width="130">
       <template #default="{ row }">
         <el-tag v-if="row.isSubmitted == 1" size="small">已提交</el-tag>
@@ -37,13 +37,14 @@
         </el-badge>
       </template>
     </vxe-column>
-    <vxe-column field="type" title="订单类型" width="120">
+    <vxe-column field="businessType" title="业务类型" width="120"></vxe-column>
+    <vxe-column field="orderType" title="订单类型" width="120">
       <template #default="{ row }">
-        <span v-if="row.type == 'Common'">普通订单</span>
-        <span v-if="row.type == 'Reissue'">补发订单</span>
-        <span v-if="row.type == 'DistributionPlus'">分销Plus</span>
-        <span v-if="row.type == 'SupplyPlus'">供销Plus</span>
-        <span v-if="row.type == 'Exchange'">换货订单</span>
+        <span v-if="row.orderType == 'Common'">普通订单</span>
+        <span v-if="row.orderType == 'Reissue'">补发订单</span>
+        <span v-if="row.orderType == 'DistributionPlus'">分销Plus</span>
+        <span v-if="row.orderType == 'SupplyPlus'">供销Plus</span>
+        <span v-if="row.orderType == 'Exchange'">换货订单</span>
       </template>
     </vxe-column>
     <vxe-column field="status" title="订单状态" width="120">
@@ -60,11 +61,13 @@
         <span v-if="row.status == 'Cancelled'">取消</span>
       </template>
     </vxe-column>
-    <vxe-column field="sourceFrom" title="订单来源" width="120">
+    <vxe-column field="upSourceName" title="订单来源" width="120"></vxe-column>
+    <vxe-column field="operationFlag" title="操作标记" width="120">
       <template #default="{ row }">
-        {{ getFromText(row.sourceFrom) }}
+        {{ getFromText(row.operationFlag) }}
       </template>
     </vxe-column>
+    <vxe-column field="shopName" title="店铺名称" width="140"></vxe-column>
     <vxe-column field="orderDate" title="订单日期" width="140">
       <!-- <template #default="{ row }">
         {{ formatDate(row.orderDate) }}
@@ -74,7 +77,7 @@
     <vxe-column field="chosenChannel" title="实发快递渠道" width="120"></vxe-column>
     <vxe-column field="logisticsId" title="快递单号" width="120"></vxe-column>
     <vxe-column field="logisticsCompany" title="快递公司" width="120"></vxe-column>
-    <vxe-column field="logisticsCompanyId" title="物流公司编码" width="120"></vxe-column>
+    <vxe-column field="logisticsCompanyCode" title="物流公司编码" width="120"></vxe-column>
     <!-- <vxe-column field="internationalLogisticsId" title="国际物流单号" width="120"></vxe-column> -->
     <!-- <vxe-column field="skus" title="skus" width="120"></vxe-column> -->
     <vxe-column field="weight" title="重量" width="70"></vxe-column>
@@ -100,13 +103,12 @@
       </template>
     </vxe-column>
     <vxe-column field="paidAmount" title="实付金额" align="right" width="120"></vxe-column>
-    <vxe-column field="shopBuyerId" title="买家昵称" show-overflow width="140"></vxe-column>
+    <vxe-column field="buyerNickname" title="买家昵称" show-overflow width="140"></vxe-column>
     <!-- <vxe-column field="openId" title="平台买家唯一值" show-overflow width="140"></vxe-column> -->
     <vxe-column field="buyerPaidAmount" title="总买家实付" width="120"></vxe-column>
     <vxe-column field="sellerIncomeAmount" title="总卖家实收" width="120"></vxe-column>
     <vxe-column field="questionType" title="问题类型" width="120"></vxe-column>
     <vxe-column field="questionDesc" title="问题描述" width="120"></vxe-column>
-    <vxe-column field="shopName" title="店铺" width="120"></vxe-column>
     <!-- <vxe-column field="invoiceType" title="发票类型" width="120"></vxe-column> -->
     <vxe-column field="invoiceTitle" title="发票抬头+税号" width="120">
       <template #default="{ row }">
@@ -141,14 +143,14 @@ export default class OrderTable extends Vue {
   data!:Array<any>
   height=null
 
-  sourceFrom:any={
-    'ERP':'手工下单',
+  operationFlag:any={
+    'OMS':'手工下单',
     'COPY':'复制',
     'MERGE':'合并',
     'SPLIT':'拆分;拆分还原',
     'MOBILE':'导入',
-    'drp-s':'供销推送',
-    'drp-d':'分销推送',
+    'DRP-S':'供销推送',
+    'DRP-D':'分销推送',
     'KWAISHOP':'快手;微商城',
     'PINDUODUO':'拼多多',
     'TOUTIAOFXG':'头条放心购',
@@ -160,11 +162,11 @@ export default class OrderTable extends Vue {
     if(f){
       let arr:Array<any>=f.split(',');
       for(const item of arr){
-        if(this.sourceFrom[item]){
+        if(this.operationFlag[item]){
           if(t){
-            t = t + ' , ' + this.sourceFrom[item]
+            t = t + ' , ' + this.operationFlag[item]
           }else{
-            t = this.sourceFrom[item]
+            t = this.operationFlag[item]
           }
         }
       }
@@ -173,16 +175,6 @@ export default class OrderTable extends Vue {
   }
   //是否显示发货
   showSend(item:any){
-    // <span v-if="row.status == 'WaitPay'">待付款</span>
-    // <span v-if="row.status == 'Delivering'">发货中</span>
-    // <span v-if="row.status == 'Merged'">被合并</span>
-    // <span v-if="row.status == 'Question'" style="color:red">异常</span>
-    // <span v-if="row.status == 'Split'">被拆分</span>
-    // <span v-if="row.status == 'WaitOuterSent'">等供销商|外仓发货</span>
-    // <span v-if="row.status == 'WaitConfirm'">已付款待审核</span>
-    // <span v-if="row.status == 'WaitFConfirm'">已客审待财审</span>
-    // <span v-if="row.status == 'Sent'">已发货</span>
-    // <span v-if="row.status == 'Cancelled'">取消</span>
     let noStatus = ['WaitPay','Delivering','Merged','Split','Question','Sent','Cancelled'];
     if(item.isSubmitted == 1 && item.pays && item.pays.length > 0 && noStatus.indexOf(item.status) == -1){
       return true
@@ -192,8 +184,8 @@ export default class OrderTable extends Vue {
   //合并按钮是否显示
   showMerge(item:any){
     let r = false;
-    if(item.isSubmitted == 1 && item.pays && item.pays.length > 0 && item.sourceFrom.indexOf('MERGE') == -1 && item.sourceFrom.indexOf('SPLIT') == -1 
-    && item.sourceFrom.indexOf('JUSHUITAN') == -1){
+    if(item.isSubmitted == 1 && item.pays && item.pays.length > 0 && (!item.operationFlag || item.operationFlag.indexOf('MERGE') == -1 && item.operationFlag.indexOf('SPLIT') == -1 
+    && item.operationFlag.indexOf('JUSHUITAN') == -1)){
       if(item.status == 'WaitConfirm' || item.status == 'Question'){
         r = true;
       }
@@ -202,8 +194,8 @@ export default class OrderTable extends Vue {
   }
   showSplit(item:any){
     let r = false;
-    if(item.isSubmitted == 1 && item.pays && item.pays.length > 0 && item.sourceFrom.indexOf('MERGE') == -1 && item.sourceFrom.indexOf('SPLIT') == -1 
-    && item.sourceFrom.indexOf('JUSHUITAN') == -1){
+    if(item.isSubmitted == 1 && item.pays && item.pays.length > 0 && (!item.operationFlag || item.operationFlag.indexOf('MERGE') == -1 && item.operationFlag.indexOf('SPLIT') == -1 
+    && item.operationFlag.indexOf('JUSHUITAN') == -1)){
       let s=['WaitConfirm','Question','Split']
       if(s.indexOf(item.status) >= 0){
         r = true;
@@ -243,7 +235,7 @@ export default class OrderTable extends Vue {
       if(row.id != item.id && item.isSubmitted == 1 && row.shopId == item.shopId && row.shopName == item.shopName && row.buyerId == item.buyerId && 
       row.receiverMobile == item.receiverMobile && row.receiverProvince == item.receiverProvince && row.receiverCity == item.receiverCity 
       && row.receiverDistrict == item.receiverDistrict && row.receiverTown == item.receiverTown && row.receiverAddress == item.receiverAddress 
-      && item.pays && item.pays.length > 0 && item.sourceFrom.indexOf('SPLIT') == -1 && item.sourceFrom.indexOf('MERGE') == -1 && (item.status == 'WaitConfirm'
+      && item.pays && item.pays.length > 0 && item.operationFlag.indexOf('SPLIT') == -1 && item.operationFlag.indexOf('MERGE') == -1 && (item.status == 'WaitConfirm'
       || item.status == 'Question')){
         data.push(item);
       }

+ 3 - 3
src/views/oms/order/index.vue

@@ -68,7 +68,7 @@
               <el-input style="width: 140px;" v-model="value.noteContent" size="mini" placeholder="备注内容"></el-input>
             </template>
           </order-radio>
-          <order-radio title="订单来源" keyName="sourceFrom" ref="searchCom11" :options="sourceFromOptions" noLimit @radioChange="onChange($event,'sourceFrom')" />
+          <order-radio title="操作标记" keyName="operationFlag" ref="searchCom11" :options="sourceFromOptions" noLimit @radioChange="onChange($event,'operationFlag')" />
           <order-checkbox title="订单类型" keyName="typeList" ref="searchCom12" :options="typeOption" noLimit @checkboxChange="onChange($event,'typeList')" />
           <el-collapse-item title="付款方式 & 是否付款" name="codAndPay">
             <div class="box01">
@@ -360,7 +360,7 @@ export default class Order extends Vue {
   //订单来源
   sourceFromOptions:Array<any>=[{
     label:'手工下单',
-    value:'ERP'
+    value:'OMS'
   },{
     label:'复制',
     value:'COPY'
@@ -378,7 +378,7 @@ export default class Order extends Vue {
     value:'IMPORT'
   },{
     label:'供销推送',
-    value:'drp-s'
+    value:'DRP-S'
   },{
     label:'快手;微商城',
     value:'KWAISHOP'

+ 42 - 0
src/views/oms/sourceShop/components/tag.vue

@@ -0,0 +1,42 @@
+<template>
+  <el-tag type="success" v-if="value == 'Authorized'" size="mini">已授权</el-tag>
+  <el-tag type="danger" v-else-if="value == 'Unauthorized'" size="mini">未授权</el-tag>
+  <el-tag type="info" v-else-if="value == 'Expired'" size="mini">已过期</el-tag>
+  <span v-else></span>
+</template>
+
+<script lang="ts">
+import { Component, Prop, Vue, Watch } from "vue-property-decorator";
+@Component
+export default class ShopTag extends Vue {
+  value:any=null;
+
+  @Prop()
+  propConfig: any
+
+  @Prop()
+  propValue?:any
+
+  @Prop()
+  parentValue:any
+
+  @Watch('propValue')
+  propValueChange(v:any){
+    this.setValue(v);
+  }
+
+  mounted(){
+    if(this.propValue){
+      this.setValue(this.propValue)
+    }
+  }
+
+  setValue(v:any){
+    this.value = v;
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 420 - 0
src/views/oms/sourceShop/index.vue

@@ -0,0 +1,420 @@
+<template>
+  <div class="sourceShop">
+    <module-view :propConfig="config" ref="view" v-loading="load" @pagination="pagination" @modalHandle="modalHandle" @onRefresh="getList" 
+    @resert="queryList" @search="queryList" @clickHandle="clickHandle" @detail="detail" />
+  </div>
+</template>
+
+<script lang="ts">
+import { Component, Prop, Vue, Watch } from "vue-property-decorator";
+import { query,save,update,del } from '@/api/sourceShop'
+import shopTag from './components/tag.vue'
+@Component({components:{shopTag}})
+export default class SourceShop extends Vue {
+  load=false;
+  time:any;
+  timeNum = 0;
+  isSearch=false;
+  config:any={
+    attr:{
+      calculateH:true
+    },
+    search:{
+      attr:{
+        size:'small'
+      },
+      columns:[
+        [{
+          label:'来源名称',
+          prop:'sourceName',
+          component:'by-input',
+          span:8,
+          compConfig:{}
+        },
+        {
+          label:'授权状态',
+          prop:'authStatus',
+          span:8,
+          component:'by-select',
+          compConfig:{
+            attr:{
+              clearable:true,
+              data:[{
+                value:'Authorized',
+                label:'已授权'
+              },{
+                value:'Unauthorized',
+                label:'未授权'
+              },{
+                value:'Expired',
+                label:'已过期'
+              }]
+            }
+          }
+        },
+        {
+          label:'所属平台',
+          prop:'channelName',
+          span:8,
+          component:'by-select',
+            compConfig:{
+              attr:{
+                clearable:true,
+                data:[{
+                  value:'JUSHUITAN',
+                  label:'聚水潭'
+                }]
+              }
+            }
+        }],
+        [{
+          label:'授权账号',
+          prop:'authAccount',
+          component:'by-input',
+          span:8,
+          compConfig:{}
+        },
+        {
+          label:'创建时间',
+          prop:'createTime',
+          component:'by-date-picker',
+          span:8,
+          compConfig:{
+            attr:{
+              editable:false,
+              type:'datetimerange',
+              format:'yyyy-MM-dd HH:mm:ss'
+            }
+          }
+        }]
+      ]
+    },
+    tool:{
+      tools:{
+        add:true,
+        // delete:true,
+        search:true,
+        refresh:true
+      }
+    },
+    table:{
+      attr:{
+        size:'mini',
+        seq:true,
+        align:'center',
+        // checkbox:true
+      },
+      columns:[{
+        title:'来源名称',
+        field:'sourceName',
+        isDetail:true,
+      },
+      {
+        title:'所属平台',
+        field:'channelName'
+      },
+      {
+        title:'账户授权',
+        field:'authAccount',
+      },
+      {
+        title:'授权状态',
+        field:'authStatus',
+        width:150,
+        component:shopTag
+      },
+      {
+        title:'租户id',
+        field:'tenantId',
+      },
+      {
+        title:'状态',
+        field:'status',
+        component: 'textChange',
+        compConfig:{
+          attr:{
+            data:[{
+              label:'已启用',
+              value:1,
+              backgroundColor:'#f0f9eb',
+              color:'#67c23a'
+            },{
+              label:'已禁用',
+              value:0,
+              backgroundColor:'#fef0f0',
+              color:'#f56c6c'
+            }]
+          }
+        }
+      },
+      {
+        title:'操作',
+        action:true,
+        width:100,
+        plugins:[{
+          name:'删除',
+          event:{
+            click:(item:any) => {
+              this.del(item);
+            }
+          }
+        }]
+      }
+    ]
+    },
+    modal:{
+      tool:{
+        tools:{
+          return:true,
+          add:true
+        }
+      },
+      form:{
+        attr:{
+          width:'1000px',
+          size:'small',
+          labelWidth:'130px',
+          rules:{
+            sourceName:[
+              {required: true, message: '请输入来源名称', trigger: 'blur'}
+            ],
+            channelName:[
+              {required: true, message: '请选择所属平台', trigger: 'change'}
+            ],
+            authStatus:[
+              { required: true, message: '请选择授权状态', trigger: 'change' }
+            ]
+          }
+        },
+        columns:[
+          [{
+            label:'来源名称',
+            prop:'sourceName',
+            component:'by-input',
+          },{
+            label:'所属平台',
+            prop:'channelName',
+            component:'by-select',
+            compConfig:{
+              attr:{
+                clearable:true,
+                defaultIndex:0,
+                retConfig:{
+                  label:'channelName',
+                  value:'channel'
+                },
+                data:[{
+                  value:'JUSHUITAN',
+                  label:'聚水潭'
+                }]
+              }
+            }
+          }],
+          [{
+            label:'授权状态',
+            prop:'authStatus',
+            component:'by-select',
+            span:12,
+            compConfig:{
+              attr:{
+                clearable:true,
+                defaultIndex:0,
+                data:[{
+                  value:'Authorized',
+                  label:'已授权'
+                },{
+                  value:'Unauthorized',
+                  label:'未授权'
+                },{
+                  value:'Expired',
+                  label:'已过期'
+                }]
+              }
+            }
+          },{
+            label:'授权账号',
+            prop:'authAccount',
+            component:'by-input'
+          }],
+          [{
+            label:'appKey',
+            prop:'appKey',
+            component:'by-input'
+          },{
+            label:'appSecret',
+            prop:'appSecret',
+            component:'by-input'
+          }],
+          [{
+            label:'accessToken',
+            prop:'accessToken',
+            component:'by-input'
+          },{
+            label:'refreshToken',
+            prop:'refreshToken',
+            component:'by-input'
+          }],
+          [{
+            label:'授权开始时间',
+            prop:'authBegin',
+            component:'byDatePicker',
+            compConfig:{
+              attr:{
+                type:'datetime',
+                format:'yyyy-MM-dd HH:mm:ss'
+              }
+            }
+          },{
+            label:'授权过期时间',
+            prop:'authExpired',
+            component:'byDatePicker',
+            compConfig:{
+              attr:{
+                type:'datetime',
+                format:'yyyy-MM-dd HH:mm:ss'
+              }
+            }
+          }]
+        ]
+      }
+    }
+  }
+  mounted(){
+    this.$nextTick(()=>{
+      this.getList()
+    })
+  }
+  //删除
+  del(item:any){
+    this.$confirm('此操作将永久删除平台来源为 “'+item.sourceName+'” 的数据, 是否继续?', '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning'
+    }).then(() => {
+      this.load = true;
+      del({id:item.id}).then(()=>{
+        this.load = false;
+        this.$message({
+          message:'删除成功!',
+          type:'success'
+        })
+        this.pagination();
+      }).catch(()=>{
+        this.load = false;
+      })
+    }).catch(() => {});
+  }
+  //分页
+  pagination(){
+    if(this.isSearch){
+      this.queryList();
+    }else{
+      this.getList()
+    }
+  }
+  //列表请求(只有分页,不包含搜素条件)
+  getList(){
+    if(!this.$refs.view){
+      if(this.timeNum > 5){
+        return
+      }
+      setTimeout(()=>{
+        this.getList()
+      },500) 
+      this.timeNum ++;
+      return
+    }
+    this.isSearch = false;
+    let data = (this.$refs.view as any).getPage();
+    this.requestList(data);
+  }
+  //列表请求(包含分页和搜素条件)
+  queryList(){
+    this.isSearch = true;
+    let data = (this.$refs.view as any).getQuery();
+    this.requestList(data);
+  }
+  requestList(data:any){
+    this.load = true;
+    query(data).then((res:any) => {
+      this.load = false;
+      (this.$refs.view as any).setTableValue(res.data.records);
+      let page = {
+        pageNo: res.data.current, //当前页
+        pageSize: res.data.size, //每页条数
+        total: res.data.total //总条数
+      };
+      (this.$refs.view as any).setPage(page)
+
+    }).catch(()=>{
+      this.load = false;
+    })
+  }
+  //修改店铺数据
+  onSave(){
+    let data = (this.$refs.view as any).getFormValue();
+    this.load = true;
+    update(data).then((res:any) => {
+      this.$message({
+        message:res.msg,
+        type:'success'
+      });
+      this.load = false;
+      (this.$refs.view as any).closeModal();
+      this.pagination();
+    }).catch(()=>{
+      this.load = false;
+    })
+  }
+  //表单工具栏按钮事件
+  modalHandle(n:string){
+    if(n == 'onAdd'){
+      (this.$refs.view as any).getFormValidate(this.addShop)
+    }
+
+    if(n == 'onSave'){
+      (this.$refs.view as any).getFormValidate(this.onSave)
+    }
+  }
+  //表格工具栏按钮事件
+  clickHandle(n:string){ 
+    if(n == 'onAdd'){
+      this.config.modal.tool.tools.add = true;
+      delete this.config.modal.tool.tools.save;
+      (this.$refs.view as any).initFormTool();
+    }
+  }
+  //添加平台
+  addShop(){
+    let value = (this.$refs.view as any).getFormValue();
+    if(value){
+      this.load = true;
+      save(value).then((res:any)=>{
+        this.load = false;
+        this.$message({
+          message:res.msg,
+          type:'success'
+        });
+        (this.$refs.view as any).closeModal();
+        this.getList();
+      }).catch(()=>{
+        this.load = false;
+      })
+    }
+  }
+  //点击详情
+  detail(){
+    this.config.modal.tool.tools.save = true;
+    delete this.config.modal.tool.tools.add;
+    (this.$refs.view as any).initFormTool();
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.sourceShop{
+  width: 100%;
+  height: 100%;
+  overflow-y: hidden;
+}
+</style>

+ 336 - 0
src/views/system/dept/index.vue

@@ -0,0 +1,336 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch">
+      <el-form-item label="部门名称" prop="deptName">
+        <el-input
+          v-model="queryParams.deptName"
+          placeholder="请输入部门名称"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="部门状态" clearable>
+          <el-option
+            v-for="dict in dict.type.sys_normal_disable"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['system:dept:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="info"
+          plain
+          icon="el-icon-sort"
+          size="mini"
+          @click="toggleExpandAll"
+        >展开/折叠</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table
+      v-if="refreshTable"
+      v-loading="loading"
+      :data="deptList"
+      row-key="deptId"
+      :default-expand-all="isExpandAll"
+      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+    >
+      <el-table-column prop="deptName" label="部门名称" width="260"></el-table-column>
+      <el-table-column prop="orderNum" label="排序" width="200"></el-table-column>
+      <el-table-column prop="status" label="状态" width="100">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="200">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['system:dept:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-plus"
+            @click="handleAdd(scope.row)"
+            v-hasPermi="['system:dept:add']"
+          >新增</el-button>
+          <el-button
+            v-if="scope.row.parentId != 0"
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['system:dept:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 添加或修改部门对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-row>
+          <el-col :span="24" v-if="form.parentId !== 0">
+            <el-form-item label="上级部门" prop="parentId">
+              <treeselect v-model="form.parentId" :options="deptOptions" :normalizer="normalizer" placeholder="选择上级部门" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="部门名称" prop="deptName">
+              <el-input v-model="form.deptName" placeholder="请输入部门名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="显示排序" prop="orderNum">
+              <el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="负责人" prop="leader">
+              <el-input v-model="form.leader" placeholder="请输入负责人" maxlength="20" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="联系电话" prop="phone">
+              <el-input v-model="form.phone" placeholder="请输入联系电话" maxlength="11" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="邮箱" prop="email">
+              <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="部门状态">
+              <el-radio-group v-model="form.status">
+                <el-radio
+                  v-for="dict in dict.type.sys_normal_disable"
+                  :key="dict.value"
+                  :label="dict.value"
+                >{{dict.label}}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+  name: "Dept",
+  dicts: ['sys_normal_disable'],
+  components: { Treeselect },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 表格树数据
+      deptList: [],
+      // 部门树选项
+      deptOptions: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 是否展开,默认全部展开
+      isExpandAll: true,
+      // 重新渲染表格状态
+      refreshTable: true,
+      // 查询参数
+      queryParams: {
+        deptName: undefined,
+        status: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        parentId: [
+          { required: true, message: "上级部门不能为空", trigger: "blur" }
+        ],
+        deptName: [
+          { required: true, message: "部门名称不能为空", trigger: "blur" }
+        ],
+        orderNum: [
+          { required: true, message: "显示排序不能为空", trigger: "blur" }
+        ],
+        email: [
+          {
+            type: "email",
+            message: "请输入正确的邮箱地址",
+            trigger: ["blur", "change"]
+          }
+        ],
+        phone: [
+          {
+            pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
+            message: "请输入正确的手机号码",
+            trigger: "blur"
+          }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询部门列表 */
+    getList() {
+      this.loading = true;
+      listDept(this.queryParams).then(response => {
+        this.deptList = this.handleTree(response.data, "deptId");
+        this.loading = false;
+      });
+    },
+    /** 转换部门数据结构 */
+    normalizer(node) {
+      if (node.children && !node.children.length) {
+        delete node.children;
+      }
+      return {
+        id: node.deptId,
+        label: node.deptName,
+        children: node.children
+      };
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        deptId: undefined,
+        parentId: undefined,
+        deptName: undefined,
+        orderNum: undefined,
+        leader: undefined,
+        phone: undefined,
+        email: undefined,
+        status: "0"
+      };
+      this.resetForm("form",this);
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm",this);
+      this.handleQuery();
+    },
+    /** 新增按钮操作 */
+    handleAdd(row) {
+      this.reset();
+      if (row != undefined) {
+        this.form.parentId = row.deptId;
+      }
+      this.open = true;
+      this.title = "添加部门";
+      listDept().then(response => {
+        this.deptOptions = this.handleTree(response.data, "deptId");
+      });
+    },
+    /** 展开/折叠操作 */
+    toggleExpandAll() {
+      this.refreshTable = false;
+      this.isExpandAll = !this.isExpandAll;
+      this.$nextTick(() => {
+        this.refreshTable = true;
+      });
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      getDept(row.deptId).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改部门";
+      });
+      listDeptExcludeChild(row.deptId).then(response => {
+        this.deptOptions = this.handleTree(response.data, "deptId");
+      });
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.deptId != undefined) {
+            updateDept(this.form).then(response => {
+              this.$modal.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addDept(this.form).then(response => {
+              this.$modal.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      this.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的数据项?').then(function() {
+        return delDept(row.deptId);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("删除成功");
+      }).catch(() => {});
+    }
+  }
+};
+</script>

+ 399 - 0
src/views/system/dict/data.vue

@@ -0,0 +1,399 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="字典名称" prop="dictType">
+        <el-select v-model="queryParams.dictType">
+          <el-option
+            v-for="item in typeOptions"
+            :key="item.dictId"
+            :label="item.dictName"
+            :value="item.dictType"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="字典标签" prop="dictLabel">
+        <el-input
+          v-model="queryParams.dictLabel"
+          placeholder="请输入字典标签"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="数据状态" clearable>
+          <el-option
+            v-for="dict in dict.type.sys_normal_disable"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['system:dict:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['system:dict:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['system:dict:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:dict:export']"
+        >导出</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-close"
+          size="mini"
+          @click="handleClose"
+        >关闭</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="字典编码" align="center" prop="dictCode" />
+      <el-table-column label="字典标签" align="center" prop="dictLabel">
+        <template slot-scope="scope">
+          <span v-if="scope.row.listClass == '' || scope.row.listClass == 'default'">{{scope.row.dictLabel}}</span>
+          <el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass">{{scope.row.dictLabel}}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="字典键值" align="center" prop="dictValue" />
+      <el-table-column label="字典排序" align="center" prop="dictSort" />
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['system:dict:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['system:dict:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改参数配置对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="字典类型">
+          <el-input v-model="form.dictType" :disabled="true" />
+        </el-form-item>
+        <el-form-item label="数据标签" prop="dictLabel">
+          <el-input v-model="form.dictLabel" placeholder="请输入数据标签" />
+        </el-form-item>
+        <el-form-item label="数据键值" prop="dictValue">
+          <el-input v-model="form.dictValue" placeholder="请输入数据键值" />
+        </el-form-item>
+        <el-form-item label="样式属性" prop="cssClass">
+          <el-input v-model="form.cssClass" placeholder="请输入样式属性" />
+        </el-form-item>
+        <el-form-item label="显示排序" prop="dictSort">
+          <el-input-number v-model="form.dictSort" controls-position="right" :min="0" />
+        </el-form-item>
+        <el-form-item label="回显样式" prop="listClass">
+          <el-select v-model="form.listClass">
+            <el-option
+              v-for="item in listClassOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+          <el-radio-group v-model="form.status">
+            <el-radio
+              v-for="dict in dict.type.sys_normal_disable"
+              :key="dict.value"
+              :label="dict.value"
+            >{{dict.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data";
+import { optionselect as getDictOptionselect, getType } from "@/api/system/dict/type";
+
+export default {
+  name: "Data",
+  dicts: ['sys_normal_disable'],
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 字典表格数据
+      dataList: [],
+      // 默认字典类型
+      defaultDictType: "",
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 数据标签回显样式
+      listClassOptions: [
+        {
+          value: "default",
+          label: "默认"
+        },
+        {
+          value: "primary",
+          label: "主要"
+        },
+        {
+          value: "success",
+          label: "成功"
+        },
+        {
+          value: "info",
+          label: "信息"
+        },
+        {
+          value: "warning",
+          label: "警告"
+        },
+        {
+          value: "danger",
+          label: "危险"
+        }
+      ],
+      // 类型数据字典
+      typeOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        dictName: undefined,
+        dictType: undefined,
+        status: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        dictLabel: [
+          { required: true, message: "数据标签不能为空", trigger: "blur" }
+        ],
+        dictValue: [
+          { required: true, message: "数据键值不能为空", trigger: "blur" }
+        ],
+        dictSort: [
+          { required: true, message: "数据顺序不能为空", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    const dictId = this.$route.params && this.$route.params.dictId;
+    this.getType(dictId);
+    this.getTypeList();
+  },
+  methods: {
+    /** 查询字典类型详细 */
+    getType(dictId) {
+      getType(dictId).then(response => {
+        this.queryParams.dictType = response.data.dictType;
+        this.defaultDictType = response.data.dictType;
+        this.getList();
+      });
+    },
+    /** 查询字典类型列表 */
+    getTypeList() {
+      getDictOptionselect().then(response => {
+        this.typeOptions = response.data;
+      });
+    },
+    /** 查询字典数据列表 */
+    getList() {
+      this.loading = true;
+      listData(this.queryParams).then(response => {
+        this.dataList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        dictCode: undefined,
+        dictLabel: undefined,
+        dictValue: undefined,
+        cssClass: undefined,
+        listClass: 'default',
+        dictSort: 0,
+        status: "0",
+        remark: undefined
+      };
+      this.resetForm("form",this);
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 返回按钮操作 */
+    handleClose() {
+      const obj = { path: "/system/dict" };
+      this.$tab.closeOpenPage(obj);
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm",this);
+      this.queryParams.dictType = this.defaultDictType;
+      this.handleQuery();
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加字典数据";
+      this.form.dictType = this.queryParams.dictType;
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.dictCode)
+      this.single = selection.length!=1
+      this.multiple = !selection.length
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const dictCode = row.dictCode || this.ids
+      getData(dictCode).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改字典数据";
+      });
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.dictCode != undefined) {
+            updateData(this.form).then(response => {
+              this.$modal.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addData(this.form).then(response => {
+              this.$modal.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const dictCodes = row.dictCode || this.ids;
+      this.$modal.confirm('是否确认删除字典编码为"' + dictCodes + '"的数据项?').then(function() {
+        return delData(dictCodes);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("删除成功");
+      }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('system/dict/data/export', {
+        ...this.queryParams
+      }, `data_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>

+ 346 - 0
src/views/system/dict/index.vue

@@ -0,0 +1,346 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="字典名称" prop="dictName">
+        <el-input
+          v-model="queryParams.dictName"
+          placeholder="请输入字典名称"
+          clearable
+          style="width: 240px"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="字典类型" prop="dictType">
+        <el-input
+          v-model="queryParams.dictType"
+          placeholder="请输入字典类型"
+          clearable
+          style="width: 240px"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.status"
+          placeholder="字典状态"
+          clearable
+          style="width: 240px"
+        >
+          <el-option
+            v-for="dict in dict.type.sys_normal_disable"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker
+          v-model="dateRange"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['system:dict:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['system:dict:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['system:dict:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:dict:export']"
+        >导出</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-refresh"
+          size="mini"
+          @click="handleRefreshCache"
+          v-hasPermi="['system:dict:remove']"
+        >刷新缓存</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="字典编号" align="center" prop="dictId" />
+      <el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" />
+      <el-table-column label="字典类型" align="center" :show-overflow-tooltip="true">
+        <template slot-scope="scope">
+          <router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type">
+            <span>{{ scope.row.dictType }}</span>
+          </router-link>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['system:dict:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['system:dict:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改参数配置对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="字典名称" prop="dictName">
+          <el-input v-model="form.dictName" placeholder="请输入字典名称" />
+        </el-form-item>
+        <el-form-item label="字典类型" prop="dictType">
+          <el-input v-model="form.dictType" placeholder="请输入字典类型" />
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+          <el-radio-group v-model="form.status">
+            <el-radio
+              v-for="dict in dict.type.sys_normal_disable"
+              :key="dict.value"
+              :label="dict.value"
+            >{{dict.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type";
+
+export default {
+  name: "Dict",
+  dicts: ['sys_normal_disable'],
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 字典表格数据
+      typeList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 日期范围
+      dateRange: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        dictName: undefined,
+        dictType: undefined,
+        status: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        dictName: [
+          { required: true, message: "字典名称不能为空", trigger: "blur" }
+        ],
+        dictType: [
+          { required: true, message: "字典类型不能为空", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询字典类型列表 */
+    getList() {
+      this.loading = true;
+      listType(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+          this.typeList = response.rows;
+          this.total = response.total;
+          this.loading = false;
+        }
+      );
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        dictId: undefined,
+        dictName: undefined,
+        dictType: undefined,
+        status: "0",
+        remark: undefined
+      };
+      this.resetForm("form",this);
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = [];
+      this.resetForm("queryForm",this);
+      this.handleQuery();
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加字典类型";
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.dictId)
+      this.single = selection.length!=1
+      this.multiple = !selection.length
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const dictId = row.dictId || this.ids
+      getType(dictId).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改字典类型";
+      });
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.dictId != undefined) {
+            updateType(this.form).then(response => {
+              this.$modal.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addType(this.form).then(response => {
+              this.$modal.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const dictIds = row.dictId || this.ids;
+      this.$modal.confirm('是否确认删除字典编号为"' + dictIds + '"的数据项?').then(function() {
+        return delType(dictIds);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("删除成功");
+      }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('system/dict/type/export', {
+        ...this.queryParams
+      }, `type_${new Date().getTime()}.xlsx`)
+    },
+    /** 刷新缓存按钮操作 */
+    handleRefreshCache() {
+      refreshCache().then(() => {
+        this.$modal.msgSuccess("刷新成功");
+      });
+    }
+  }
+};
+</script>

+ 216 - 0
src/views/system/logininfor/index.vue

@@ -0,0 +1,216 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="登录地址" prop="ipaddr">
+        <el-input
+          v-model="queryParams.ipaddr"
+          placeholder="请输入登录地址"
+          clearable
+          style="width: 240px;"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="用户名称" prop="userName">
+        <el-input
+          v-model="queryParams.userName"
+          placeholder="请输入用户名称"
+          clearable
+          style="width: 240px;"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.status"
+          placeholder="登录状态"
+          clearable
+          style="width: 240px"
+        >
+          <el-option
+            v-for="dict in dict.type.sys_common_status"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="登录时间">
+        <el-date-picker
+          v-model="dateRange"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['system:logininfor:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          @click="handleClean"
+          v-hasPermi="['system:logininfor:remove']"
+        >清空</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:logininfor:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table ref="tables" v-loading="loading" :data="list" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="访问编号" align="center" prop="infoId" />
+      <el-table-column label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" />
+      <el-table-column label="地址" align="center" prop="ipaddr" width="130" :show-overflow-tooltip="true" />
+      <el-table-column label="登录状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_common_status" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="描述" align="center" prop="msg" />
+      <el-table-column label="访问时间" align="center" prop="accessTime" sortable="custom" :sort-orders="['descending', 'ascending']" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.accessTime) }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { list, delLogininfor, cleanLogininfor } from "@/api/system/logininfor";
+
+export default {
+  name: "Logininfor",
+  dicts: ['sys_common_status'],
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 表格数据
+      list: [],
+      // 日期范围
+      dateRange: [],
+      // 默认排序
+      defaultSort: {prop: 'loginTime', order: 'descending'},
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        ipaddr: undefined,
+        userName: undefined,
+        status: undefined
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询登录日志列表 */
+    getList() {
+      this.loading = true;
+      list(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+          this.list = response.rows;
+          this.total = response.total;
+          this.loading = false;
+        }
+      );
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = [];
+      this.resetForm("queryForm");
+      this.$refs.tables.sort(this.defaultSort.prop, this.defaultSort.order)
+      this.handleQuery();
+    },
+    /** 多选框选中数据 */
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.infoId)
+      this.multiple = !selection.length
+    },
+    /** 排序触发事件 */
+    handleSortChange(column, prop, order) {
+      this.queryParams.orderByColumn = column.prop;
+      this.queryParams.isAsc = column.order;
+      this.getList();
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const infoIds = row.infoId || this.ids;
+      this.$modal.confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?').then(function() {
+        return delLogininfor(infoIds);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("删除成功");
+      }).catch(() => {});
+    },
+    /** 清空按钮操作 */
+    handleClean() {
+      this.$modal.confirm('是否确认清空所有登录日志数据项?').then(function() {
+        return cleanLogininfor();
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("清空成功");
+      }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('system/logininfor/export', {
+        ...this.queryParams
+      }, `logininfor_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>
+

+ 2 - 2
src/views/system/menu/index.vue

@@ -378,7 +378,7 @@ export default {
         visible: "0",
         status: "0"
       };
-      this.resetForm("form");
+      this.resetForm("form",this);
     },
     /** 搜索按钮操作 */
     handleQuery() {
@@ -386,7 +386,7 @@ export default {
     },
     /** 重置按钮操作 */
     resetQuery() {
-      this.resetForm("queryForm");
+      this.resetForm("queryForm",this);
       this.handleQuery();
     },
     /** 新增按钮操作 */

+ 304 - 0
src/views/system/operlog/index.vue

@@ -0,0 +1,304 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="系统模块" prop="title">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入系统模块"
+          clearable
+          style="width: 240px;"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="操作人员" prop="operName">
+        <el-input
+          v-model="queryParams.operName"
+          placeholder="请输入操作人员"
+          clearable
+          style="width: 240px;"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="类型" prop="businessType">
+        <el-select
+          v-model="queryParams.businessType"
+          placeholder="操作类型"
+          clearable
+          style="width: 240px"
+        >
+          <el-option
+            v-for="dict in dict.type.sys_oper_type"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.status"
+          placeholder="操作状态"
+          clearable
+          style="width: 240px"
+        >
+          <el-option
+            v-for="dict in dict.type.sys_common_status"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="操作时间">
+        <el-date-picker
+          v-model="dateRange"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['system:operlog:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          @click="handleClean"
+          v-hasPermi="['system:operlog:remove']"
+        >清空</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:operlog:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table ref="tables" v-loading="loading" :data="list" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="日志编号" align="center" prop="operId" />
+      <el-table-column label="系统模块" align="center" prop="title" />
+      <el-table-column label="操作类型" align="center" prop="businessType">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_oper_type" :value="scope.row.businessType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="请求方式" align="center" prop="requestMethod" />
+      <el-table-column label="操作人员" align="center" prop="operName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" width="100"/>
+      <el-table-column label="主机" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
+      <el-table-column label="操作状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_common_status" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作日期" align="center" prop="operTime" sortable="custom" :sort-orders="['descending', 'ascending']" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.operTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleView(scope.row,scope.index)"
+            v-hasPermi="['system:operlog:query']"
+          >详细</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 操作日志详细 -->
+    <el-dialog title="操作日志详细" :visible.sync="open" width="700px" append-to-body>
+      <el-form ref="form" :model="form" label-width="100px" size="mini">
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="操作模块:">{{ form.title }} / {{ typeFormat(form) }}</el-form-item>
+            <el-form-item
+              label="登录信息:"
+            >{{ form.operName }} / {{ form.operIp }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="请求地址:">{{ form.operUrl }}</el-form-item>
+            <el-form-item label="请求方式:">{{ form.requestMethod }}</el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="操作方法:">{{ form.method }}</el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="请求参数:">{{ form.operParam }}</el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="返回参数:">{{ form.jsonResult }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="操作状态:">
+              <div v-if="form.status === 0">正常</div>
+              <div v-else-if="form.status === 1">失败</div>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="操作时间:">{{ parseTime(form.operTime) }}</el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="异常信息:" v-if="form.status === 1">{{ form.errorMsg }}</el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="open = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { list, delOperlog, cleanOperlog } from "@/api/system/operlog";
+
+export default {
+  name: "Operlog",
+  dicts: ['sys_oper_type', 'sys_common_status'],
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 表格数据
+      list: [],
+      // 是否显示弹出层
+      open: false,
+      // 日期范围
+      dateRange: [],
+      // 默认排序
+      defaultSort: {prop: 'operTime', order: 'descending'},
+      // 表单参数
+      form: {},
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        title: undefined,
+        operName: undefined,
+        businessType: undefined,
+        status: undefined
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询登录日志 */
+    getList() {
+      this.loading = true;
+      list(this.addDateRange(this.queryParams, this.dateRange)).then( response => {
+          this.list = response.rows;
+          this.total = response.total;
+          this.loading = false;
+        }
+      );
+    },
+    // 操作日志类型字典翻译
+    typeFormat(row, column) {
+      return this.selectDictLabel(this.dict.type.sys_oper_type, row.businessType);
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = [];
+      this.resetForm("queryForm");
+      this.$refs.tables.sort(this.defaultSort.prop, this.defaultSort.order)
+      this.handleQuery();
+    },
+    /** 多选框选中数据 */
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.operId)
+      this.multiple = !selection.length
+    },
+    /** 排序触发事件 */
+    handleSortChange(column, prop, order) {
+      this.queryParams.orderByColumn = column.prop;
+      this.queryParams.isAsc = column.order;
+      this.getList();
+    },
+    /** 详细按钮操作 */
+    handleView(row) {
+      this.open = true;
+      this.form = row;
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const operIds = row.operId || this.ids;
+      this.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项?').then(function() {
+        return delOperlog(operIds);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("删除成功");
+      }).catch(() => {});
+    },
+    /** 清空按钮操作 */
+    handleClean() {
+      this.$modal.confirm('是否确认清空所有操作日志数据项?').then(function() {
+        return cleanOperlog();
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("清空成功");
+      }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('system/operlog/export', {
+        ...this.queryParams
+      }, `operlog_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>
+

+ 309 - 0
src/views/system/post/index.vue

@@ -0,0 +1,309 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="岗位编码" prop="postCode">
+        <el-input
+          v-model="queryParams.postCode"
+          placeholder="请输入岗位编码"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="岗位名称" prop="postName">
+        <el-input
+          v-model="queryParams.postName"
+          placeholder="请输入岗位名称"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" v-if="dict && dict.type" placeholder="岗位状态" clearable>
+          <el-option
+            v-for="item in dict.type.sys_normal_disable"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['system:post:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['system:post:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['system:post:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:post:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="岗位编号" align="center" prop="postId" />
+      <el-table-column label="岗位编码" align="center" prop="postCode" />
+      <el-table-column label="岗位名称" align="center" prop="postName" />
+      <el-table-column label="岗位排序" align="center" prop="postSort" />
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag v-if="dict && dict.type" :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['system:post:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['system:post:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改岗位对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="岗位名称" prop="postName">
+          <el-input v-model="form.postName" placeholder="请输入岗位名称" />
+        </el-form-item>
+        <el-form-item label="岗位编码" prop="postCode">
+          <el-input v-model="form.postCode" placeholder="请输入编码名称" />
+        </el-form-item>
+        <el-form-item label="岗位顺序" prop="postSort">
+          <el-input-number v-model="form.postSort" controls-position="right" :min="0" />
+        </el-form-item>
+        <el-form-item label="岗位状态" prop="status">
+          <el-radio-group v-model="form.status" v-if="dict && dict.type">
+            <el-radio
+              v-for="item in dict.type.sys_normal_disable"
+              :key="item.value"
+              :label="item.value"
+            >{{item.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listPost, getPost, delPost, addPost, updatePost } from "@/api/system/post";
+
+export default {
+  name: "Post",
+  dicts: ['sys_normal_disable'],
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 岗位表格数据
+      postList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        postCode: undefined,
+        postName: undefined,
+        status: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        postName: [
+          { required: true, message: "岗位名称不能为空", trigger: "blur" }
+        ],
+        postCode: [
+          { required: true, message: "岗位编码不能为空", trigger: "blur" }
+        ],
+        postSort: [
+          { required: true, message: "岗位顺序不能为空", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询岗位列表 */
+    getList() {
+      this.loading = true;
+      listPost(this.queryParams).then(response => {
+        this.postList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        postId: undefined,
+        postCode: undefined,
+        postName: undefined,
+        postSort: 0,
+        status: "0",
+        remark: undefined
+      };
+      this.resetForm("form",this);
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm",this);
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.postId)
+      this.single = selection.length!=1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加岗位";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const postId = row.postId || this.ids
+      getPost(postId).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改岗位";
+      });
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.postId != undefined) {
+            updatePost(this.form).then(response => {
+              this.$modal.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addPost(this.form).then(response => {
+              this.$modal.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const postIds = row.postId || this.ids;
+      this.$modal.confirm('是否确认删除岗位编号为"' + postIds + '"的数据项?').then(function() {
+        return delPost(postIds);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("删除成功");
+      }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('system/post/export', {
+        ...this.queryParams
+      }, `post_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>

+ 199 - 0
src/views/system/role/authUser.vue

@@ -0,0 +1,199 @@
+<template>
+  <div class="app-container">
+     <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch">
+      <el-form-item label="用户名称" prop="userName">
+        <el-input
+          v-model="queryParams.userName"
+          placeholder="请输入用户名称"
+          clearable
+          style="width: 240px"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="手机号码" prop="phonenumber">
+        <el-input
+          v-model="queryParams.phonenumber"
+          placeholder="请输入手机号码"
+          clearable
+          style="width: 240px"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="openSelectUser"
+          v-hasPermi="['system:role:add']"
+        >添加用户</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-circle-close"
+          size="mini"
+          :disabled="multiple"
+          @click="cancelAuthUserAll"
+          v-hasPermi="['system:role:remove']"
+        >批量取消授权</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-close"
+          size="mini"
+          @click="handleClose"
+        >关闭</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
+      <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
+      <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
+      <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-circle-close"
+            @click="cancelAuthUser(scope.row)"
+            v-hasPermi="['system:role:remove']"
+          >取消授权</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+    <select-user ref="select" :roleId="queryParams.roleId" @ok="handleQuery" />
+  </div>
+</template>
+
+<script>
+import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role";
+import selectUser from "./selectUser";
+
+export default {
+  name: "AuthUser",
+  dicts: ['sys_normal_disable'],
+  components: { selectUser },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中用户组
+      userIds: [],
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 用户表格数据
+      userList: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        roleId: undefined,
+        userName: undefined,
+        phonenumber: undefined
+      }
+    };
+  },
+  created() {
+    const roleId = this.$route.params && this.$route.params.roleId;
+    if (roleId) {
+      this.queryParams.roleId = roleId;
+      this.getList();
+    }
+  },
+  methods: {
+    /** 查询授权用户列表 */
+    getList() {
+      this.loading = true;
+      allocatedUserList(this.queryParams).then(response => {
+          this.userList = response.rows;
+          this.total = response.total;
+          this.loading = false;
+        }
+      );
+    },
+    // 返回按钮
+    handleClose() {
+      const obj = { path: "/system/role" };
+      this.$tab.closeOpenPage(obj);
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm",this);
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.userIds = selection.map(item => item.userId)
+      this.multiple = !selection.length
+    },
+    /** 打开授权用户表弹窗 */
+    openSelectUser() {
+      this.$refs.select.show();
+    },
+    /** 取消授权按钮操作 */
+    cancelAuthUser(row) {
+      const roleId = this.queryParams.roleId;
+      this.$modal.confirm('确认要取消该用户"' + row.userName + '"角色吗?').then(function() {
+        return authUserCancel({ userId: row.userId, roleId: roleId });
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("取消授权成功");
+      }).catch(() => {});
+    },
+    /** 批量取消授权按钮操作 */
+    cancelAuthUserAll(row) {
+      const roleId = this.queryParams.roleId;
+      const userIds = this.userIds.join(",");
+      this.$modal.confirm('是否取消选中用户授权数据项?').then(function() {
+        return authUserCancelAll({ roleId: roleId, userIds: userIds });
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("取消授权成功");
+      }).catch(() => {});
+    }
+  }
+};
+</script>

+ 614 - 0
src/views/system/role/index.vue

@@ -0,0 +1,614 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch">
+      <el-form-item label="角色名称" prop="roleName">
+        <el-input
+          v-model="queryParams.roleName"
+          placeholder="请输入角色名称"
+          clearable
+          style="width: 240px"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="权限字符" prop="roleKey">
+        <el-input
+          v-model="queryParams.roleKey"
+          placeholder="请输入权限字符"
+          clearable
+          style="width: 240px"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.status"
+          placeholder="角色状态"
+          clearable
+          style="width: 240px"
+        >
+          <el-option
+            v-for="dict in dict.type.sys_normal_disable"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker
+          v-model="dateRange"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['system:role:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['system:role:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['system:role:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:role:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="角色编号" prop="roleId" width="120" />
+      <el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" />
+      <el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="150" />
+      <el-table-column label="显示顺序" prop="roleSort" width="100" />
+      <el-table-column label="状态" align="center" width="100">
+        <template slot-scope="scope">
+          <el-switch
+            v-model="scope.row.status"
+            active-value="0"
+            inactive-value="1"
+            @change="handleStatusChange(scope.row)"
+          ></el-switch>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope" v-if="scope.row.roleId !== 1">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['system:role:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['system:role:remove']"
+          >删除</el-button>
+          <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:role:edit']">
+            <span class="el-dropdown-link">
+              <i class="el-icon-d-arrow-right el-icon--right"></i>更多
+            </span>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item command="handleDataScope" icon="el-icon-circle-check"
+                v-hasPermi="['system:role:edit']">数据权限</el-dropdown-item>
+              <el-dropdown-item command="handleAuthUser" icon="el-icon-user"
+                v-hasPermi="['system:role:edit']">分配用户</el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改角色配置对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="角色名称" prop="roleName">
+          <el-input v-model="form.roleName" placeholder="请输入角色名称" />
+        </el-form-item>
+        <el-form-item prop="roleKey">
+          <span slot="label">
+            <el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasRole('admin')`)" placement="top">
+              <i class="el-icon-question"></i>
+            </el-tooltip>
+            权限字符
+          </span>
+          <el-input v-model="form.roleKey" placeholder="请输入权限字符" />
+        </el-form-item>
+        <el-form-item label="角色顺序" prop="roleSort">
+          <el-input-number v-model="form.roleSort" controls-position="right" :min="0" />
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-radio-group v-model="form.status">
+            <el-radio
+              v-for="dict in dict.type.sys_normal_disable"
+              :key="dict.value"
+              :label="dict.value"
+            >{{dict.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="菜单权限">
+          <el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">展开/折叠</el-checkbox>
+          <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox>
+          <el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox>
+          <el-tree
+            class="tree-border"
+            :data="menuOptions"
+            show-checkbox
+            ref="menu"
+            node-key="id"
+            :check-strictly="!form.menuCheckStrictly"
+            empty-text="加载中,请稍候"
+            :props="defaultProps"
+          ></el-tree>
+        </el-form-item>
+        <el-form-item label="备注">
+          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 分配角色数据权限对话框 -->
+    <el-dialog :title="title" :visible.sync="openDataScope" width="500px" append-to-body>
+      <el-form :model="form" label-width="80px">
+        <el-form-item label="角色名称">
+          <el-input v-model="form.roleName" :disabled="true" />
+        </el-form-item>
+        <el-form-item label="权限字符">
+          <el-input v-model="form.roleKey" :disabled="true" />
+        </el-form-item>
+        <el-form-item label="权限范围">
+          <el-select v-model="form.dataScope" @change="dataScopeSelectChange">
+            <el-option
+              v-for="item in dataScopeOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="数据权限" v-show="form.dataScope == 2">
+          <el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">展开/折叠</el-checkbox>
+          <el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox>
+          <el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox>
+          <el-tree
+            class="tree-border"
+            :data="deptOptions"
+            show-checkbox
+            default-expand-all
+            ref="dept"
+            node-key="id"
+            :check-strictly="!form.deptCheckStrictly"
+            empty-text="加载中,请稍候"
+            :props="defaultProps"
+          ></el-tree>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitDataScope">确 定</el-button>
+        <el-button @click="cancelDataScope">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listRole, getRole, delRole, addRole, updateRole, dataScope, changeRoleStatus } from "@/api/system/role";
+import { treeselect as menuTreeselect, roleMenuTreeselect } from "@/api/system/menu";
+import { treeselect as deptTreeselect, roleDeptTreeselect } from "@/api/system/dept";
+
+export default {
+  name: "Role",
+  dicts: ['sys_normal_disable'],
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 角色表格数据
+      roleList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 是否显示弹出层(数据权限)
+      openDataScope: false,
+      menuExpand: false,
+      menuNodeAll: false,
+      deptExpand: true,
+      deptNodeAll: false,
+      // 日期范围
+      dateRange: [],
+      // 数据范围选项
+      dataScopeOptions: [
+        {
+          value: "1",
+          label: "全部数据权限"
+        },
+        {
+          value: "2",
+          label: "自定数据权限"
+        },
+        {
+          value: "3",
+          label: "本部门数据权限"
+        },
+        {
+          value: "4",
+          label: "本部门及以下数据权限"
+        },
+        {
+          value: "5",
+          label: "仅本人数据权限"
+        }
+      ],
+      // 菜单列表
+      menuOptions: [],
+      // 部门列表
+      deptOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        roleName: undefined,
+        roleKey: undefined,
+        status: undefined
+      },
+      // 表单参数
+      form: {},
+      defaultProps: {
+        children: "children",
+        label: "label"
+      },
+      // 表单校验
+      rules: {
+        roleName: [
+          { required: true, message: "角色名称不能为空", trigger: "blur" }
+        ],
+        roleKey: [
+          { required: true, message: "权限字符不能为空", trigger: "blur" }
+        ],
+        roleSort: [
+          { required: true, message: "角色顺序不能为空", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询角色列表 */
+    getList() {
+      this.loading = true;
+      listRole(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+          this.roleList = response.rows;
+          this.total = response.total;
+          this.loading = false;
+        }
+      );
+    },
+    /** 查询菜单树结构 */
+    getMenuTreeselect() {
+      menuTreeselect().then(response => {
+        this.menuOptions = response.data;
+      });
+    },
+    /** 查询部门树结构 */
+    getDeptTreeselect() {
+      deptTreeselect().then(response => {
+        this.deptOptions = response.data;
+      });
+    },
+    // 所有菜单节点数据
+    getMenuAllCheckedKeys() {
+      // 目前被选中的菜单节点
+      let checkedKeys = this.$refs.menu.getCheckedKeys();
+      // 半选中的菜单节点
+      let halfCheckedKeys = this.$refs.menu.getHalfCheckedKeys();
+      checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
+      return checkedKeys;
+    },
+    // 所有部门节点数据
+    getDeptAllCheckedKeys() {
+      // 目前被选中的部门节点
+      let checkedKeys = this.$refs.dept.getCheckedKeys();
+      // 半选中的部门节点
+      let halfCheckedKeys = this.$refs.dept.getHalfCheckedKeys();
+      checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
+      return checkedKeys;
+    },
+    /** 根据角色ID查询菜单树结构 */
+    getRoleMenuTreeselect(roleId) {
+      return roleMenuTreeselect(roleId).then(response => {
+        this.menuOptions = response.menus;
+        return response;
+      });
+    },
+    /** 根据角色ID查询部门树结构 */
+    getRoleDeptTreeselect(roleId) {
+      return roleDeptTreeselect(roleId).then(response => {
+        this.deptOptions = response.depts;
+        return response;
+      });
+    },
+    // 角色状态修改
+    handleStatusChange(row) {
+      let text = row.status === "0" ? "启用" : "停用";
+      this.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?').then(function() {
+        return changeRoleStatus(row.roleId, row.status);
+      }).then(() => {
+        this.$modal.msgSuccess(text + "成功");
+      }).catch(function() {
+        row.status = row.status === "0" ? "1" : "0";
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 取消按钮(数据权限)
+    cancelDataScope() {
+      this.openDataScope = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      if (this.$refs.menu != undefined) {
+        this.$refs.menu.setCheckedKeys([]);
+      }
+      this.menuExpand = false,
+      this.menuNodeAll = false,
+      this.deptExpand = true,
+      this.deptNodeAll = false,
+      this.form = {
+        roleId: undefined,
+        roleName: undefined,
+        roleKey: undefined,
+        roleSort: 0,
+        status: "0",
+        menuIds: [],
+        deptIds: [],
+        menuCheckStrictly: true,
+        deptCheckStrictly: true,
+        remark: undefined
+      };
+      this.resetForm("form",this);
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = [];
+      this.resetForm("queryForm",this);
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.roleId)
+      this.single = selection.length!=1
+      this.multiple = !selection.length
+    },
+    // 更多操作触发
+    handleCommand(command, row) {
+      switch (command) {
+        case "handleDataScope":
+          this.handleDataScope(row);
+          break;
+        case "handleAuthUser":
+          this.handleAuthUser(row);
+          break;
+        default:
+          break;
+      }
+    },
+    // 树权限(展开/折叠)
+    handleCheckedTreeExpand(value, type) {
+      if (type == 'menu') {
+        let treeList = this.menuOptions;
+        for (let i = 0; i < treeList.length; i++) {
+          this.$refs.menu.store.nodesMap[treeList[i].id].expanded = value;
+        }
+      } else if (type == 'dept') {
+        let treeList = this.deptOptions;
+        for (let i = 0; i < treeList.length; i++) {
+          this.$refs.dept.store.nodesMap[treeList[i].id].expanded = value;
+        }
+      }
+    },
+    // 树权限(全选/全不选)
+    handleCheckedTreeNodeAll(value, type) {
+      if (type == 'menu') {
+        this.$refs.menu.setCheckedNodes(value ? this.menuOptions: []);
+      } else if (type == 'dept') {
+        this.$refs.dept.setCheckedNodes(value ? this.deptOptions: []);
+      }
+    },
+    // 树权限(父子联动)
+    handleCheckedTreeConnect(value, type) {
+      if (type == 'menu') {
+        this.form.menuCheckStrictly = value ? true: false;
+      } else if (type == 'dept') {
+        this.form.deptCheckStrictly = value ? true: false;
+      }
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.getMenuTreeselect();
+      this.open = true;
+      this.title = "添加角色";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const roleId = row.roleId || this.ids
+      const roleMenu = this.getRoleMenuTreeselect(roleId);
+      getRole(roleId).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.$nextTick(() => {
+          roleMenu.then(res => {
+            let checkedKeys = res.checkedKeys
+            checkedKeys.forEach((v) => {
+                this.$nextTick(()=>{
+                    this.$refs.menu.setChecked(v, true ,false);
+                })
+            })
+          });
+        });
+        this.title = "修改角色";
+      });
+    },
+    /** 选择角色权限范围触发 */
+    dataScopeSelectChange(value) {
+      if(value !== '2') {
+        this.$refs.dept.setCheckedKeys([]);
+      }
+    },
+    /** 分配数据权限操作 */
+    handleDataScope(row) {
+      this.reset();
+      const roleDeptTreeselect = this.getRoleDeptTreeselect(row.roleId);
+      getRole(row.roleId).then(response => {
+        this.form = response.data;
+        this.openDataScope = true;
+        this.$nextTick(() => {
+          roleDeptTreeselect.then(res => {
+            this.$refs.dept.setCheckedKeys(res.checkedKeys);
+          });
+        });
+        this.title = "分配数据权限";
+      });
+    },
+    /** 分配用户操作 */
+    handleAuthUser: function(row) {
+      const roleId = row.roleId;
+      this.$router.push("/system/role-auth/user/" + roleId);
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.roleId != undefined) {
+            this.form.menuIds = this.getMenuAllCheckedKeys();
+            updateRole(this.form).then(response => {
+              this.$modal.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            this.form.menuIds = this.getMenuAllCheckedKeys();
+            addRole(this.form).then(response => {
+              this.$modal.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 提交按钮(数据权限) */
+    submitDataScope: function() {
+      if (this.form.roleId != undefined) {
+        this.form.deptIds = this.getDeptAllCheckedKeys();
+        dataScope(this.form).then(response => {
+          this.$modal.msgSuccess("修改成功");
+          this.openDataScope = false;
+          this.getList();
+        });
+      }
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const roleIds = row.roleId || this.ids;
+      this.$modal.confirm('是否确认删除角色编号为"' + roleIds + '"的数据项?').then(function() {
+        return delRole(roleIds);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("删除成功");
+      }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('system/role/export', {
+        ...this.queryParams
+      }, `role_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>

+ 138 - 0
src/views/system/role/selectUser.vue

@@ -0,0 +1,138 @@
+<template>
+  <!-- 授权用户 -->
+  <el-dialog title="选择用户" :visible.sync="visible" width="800px" top="5vh" append-to-body>
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
+      <el-form-item label="用户名称" prop="userName">
+        <el-input
+          v-model="queryParams.userName"
+          placeholder="请输入用户名称"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="手机号码" prop="phonenumber">
+        <el-input
+          v-model="queryParams.phonenumber"
+          placeholder="请输入手机号码"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row>
+      <el-table @row-click="clickRow" ref="table" :data="userList" @selection-change="handleSelectionChange" height="260px">
+        <el-table-column type="selection" width="55"></el-table-column>
+        <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
+        <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
+        <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
+        <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
+        <el-table-column label="状态" align="center" prop="status">
+          <template slot-scope="scope">
+            <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+          <template slot-scope="scope">
+            <span>{{ parseTime(scope.row.createTime) }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        v-show="total>0"
+        :total="total"
+        :page.sync="queryParams.pageNum"
+        :limit.sync="queryParams.pageSize"
+        @pagination="getList"
+      />
+    </el-row>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="handleSelectUser">确 定</el-button>
+      <el-button @click="visible = false">取 消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { unallocatedUserList, authUserSelectAll } from "@/api/system/role";
+export default {
+  dicts: ['sys_normal_disable'],
+  props: {
+    // 角色编号
+    roleId: {
+      type: [Number, String]
+    }
+  },
+  data() {
+    return {
+      // 遮罩层
+      visible: false,
+      // 选中数组值
+      userIds: [],
+      // 总条数
+      total: 0,
+      // 未授权用户数据
+      userList: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        roleId: undefined,
+        userName: undefined,
+        phonenumber: undefined
+      }
+    };
+  },
+  methods: {
+    // 显示弹框
+    show() {
+      this.queryParams.roleId = this.roleId;
+      this.getList();
+      this.visible = true;
+    },
+    clickRow(row) {
+      this.$refs.table.toggleRowSelection(row);
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.userIds = selection.map(item => item.userId);
+    },
+    // 查询表数据
+    getList() {
+      unallocatedUserList(this.queryParams).then(res => {
+        this.userList = res.rows;
+        this.total = res.total;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm",this);
+      this.handleQuery();
+    },
+    /** 选择授权用户操作 */
+    handleSelectUser() {
+      const roleId = this.queryParams.roleId;
+      const userIds = this.userIds.join(",");
+      if (userIds == "") {
+        this.$modal.msgError("请选择要分配的用户");
+        return;
+      }
+      authUserSelectAll({ roleId: roleId, userIds: userIds }).then(res => {
+        this.$modal.msgSuccess(res.msg);
+        if (res.code === 200) {
+          this.visible = false;
+          this.$emit("ok");
+        }
+      });
+    }
+  }
+};
+</script>

+ 2 - 2
src/views/system/user/index.vue

@@ -526,7 +526,7 @@ export default {
         postIds: [],
         roleIds: []
       };
-      this.resetForm("form");
+      this.resetForm("form", this);
     },
     /** 搜索按钮操作 */
     handleQuery() {
@@ -536,7 +536,7 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.dateRange = [];
-      this.resetForm("queryForm");
+      this.resetForm("queryForm", this);
       this.handleQuery();
     },
     // 多选框选中数据