ソースを参照

初始化基础模块

ZZH-wl 3 年 前
コミット
ad97aa6543

+ 25 - 0
src/api/departmentManagement.js

@@ -0,0 +1,25 @@
+import request from '@/utils/request'
+
+export function getList(params) {
+  return request({
+    url: '/departmentManagement/getList',
+    method: 'get',
+    params,
+  })
+}
+
+export function doEdit(data) {
+  return request({
+    url: '/departmentManagement/doEdit',
+    method: 'post',
+    data,
+  })
+}
+
+export function doDelete(data) {
+  return request({
+    url: '/departmentManagement/doDelete',
+    method: 'post',
+    data,
+  })
+}

+ 25 - 0
src/api/menuManagement.js

@@ -0,0 +1,25 @@
+import request from '@/utils/request'
+
+export function getTree(params) {
+  return request({
+    url: '/menuManagement/getList',
+    method: 'get',
+    params,
+  })
+}
+
+export function doEdit(data) {
+  return request({
+    url: '/menuManagement/doEdit',
+    method: 'post',
+    data,
+  })
+}
+
+export function doDelete(data) {
+  return request({
+    url: '/menuManagement/doDelete',
+    method: 'post',
+    data,
+  })
+}

+ 9 - 0
src/api/remixIcon.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+export function getIconList(params) {
+  return request({
+    url: '/remixIcon/getList',
+    method: 'get',
+    params,
+  })
+}

+ 25 - 0
src/api/roleManagement.js

@@ -0,0 +1,25 @@
+import request from '@/utils/request'
+
+export function getList(params) {
+  return request({
+    url: '/roleManagement/getList',
+    method: 'get',
+    params,
+  })
+}
+
+export function doEdit(data) {
+  return request({
+    url: '/roleManagement/doEdit',
+    method: 'post',
+    data,
+  })
+}
+
+export function doDelete(data) {
+  return request({
+    url: '/roleManagement/doDelete',
+    method: 'post',
+    data,
+  })
+}

+ 9 - 0
src/api/systemLog.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+export function getList(params) {
+  return request({
+    url: '/systemLog/getList',
+    method: 'get',
+    params,
+  })
+}

+ 25 - 0
src/api/userManagement.js

@@ -0,0 +1,25 @@
+import request from '@/utils/request'
+
+export function getList(params) {
+  return request({
+    url: '/userManagement/getList',
+    method: 'get',
+    params,
+  })
+}
+
+export function doEdit(data) {
+  return request({
+    url: '/userManagement/doEdit',
+    method: 'post',
+    data,
+  })
+}
+
+export function doDelete(data) {
+  return request({
+    url: '/userManagement/doDelete',
+    method: 'post',
+    data,
+  })
+}

BIN
src/assets/cropper_images/user.gif


BIN
src/assets/empty_images/data_empty.png


+ 116 - 0
src/extra/VabIconSelector/index.vue

@@ -0,0 +1,116 @@
+<template>
+  <el-row :gutter="20">
+    <el-col :span="24">
+      <vab-query-form>
+        <vab-query-form-top-panel>
+          <el-form :inline="true" label-width="0" @submit.native.prevent>
+            <el-form-item label="">
+              <el-input v-model="queryForm.title" />
+            </el-form-item>
+            <el-form-item label-width="0">
+              <el-button native-type="submit" type="primary" @click="queryData">
+                查询
+              </el-button>
+            </el-form-item>
+          </el-form>
+        </vab-query-form-top-panel>
+      </vab-query-form>
+    </el-col>
+
+    <el-col v-for="(item, index) in queryIcon" :key="index" :span="6">
+      <el-card shadow="hover" @click.native="handleIcon(item)">
+        <vab-icon :icon="item" />
+      </el-card>
+    </el-col>
+    <el-col :span="24">
+      <el-pagination
+        :background="background"
+        :current-page="queryForm.pageNo"
+        :layout="layout"
+        :page-size="queryForm.pageSize"
+        :total="total"
+        @current-change="handleCurrentChange"
+        @size-change="handleSizeChange"
+      />
+    </el-col>
+  </el-row>
+</template>
+
+<script>
+  import { getIconList } from '@/api/remixIcon'
+
+  export default {
+    name: 'VabIconSelector',
+    data() {
+      return {
+        icon: '24-hours-fill',
+        layout: 'total, prev, next',
+        total: 0,
+        background: true,
+        height: 0,
+        selectRows: '',
+        queryIcon: [],
+        queryForm: {
+          pageNo: 1,
+          pageSize: 16,
+          title: '',
+        },
+      }
+    },
+    created() {
+      this.fetchData()
+    },
+    methods: {
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNo = val
+        this.fetchData()
+      },
+      queryData() {
+        this.queryForm.pageNo = 1
+        this.fetchData()
+      },
+      async fetchData() {
+        const {
+          data: { list, total },
+        } = await getIconList(this.queryForm)
+        this.queryIcon = list
+        this.total = total
+      },
+      handleIcon(item) {
+        this.icon = item
+        this.$emit('handle-icon', item)
+      },
+    },
+  }
+</script>
+
+<style lang="scss">
+  .icon-selector-popper {
+    .el-card__body {
+      position: relative;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      height: 20px;
+      cursor: pointer;
+
+      i {
+        font-size: 28px;
+        color: $base-color-grey;
+        text-align: center;
+        vertical-align: middle;
+        pointer-events: none;
+        cursor: pointer;
+      }
+    }
+
+    .el-pagination {
+      margin: 0;
+    }
+  }
+</style>

+ 111 - 0
src/views/setting/departmentManagement/components/DepartmentManagementEdit.vue

@@ -0,0 +1,111 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="dialogFormVisible"
+    width="500px"
+    @close="close"
+  >
+    <el-form ref="form" label-width="80px" :model="form" :rules="rules">
+      <el-form-item label="父节点" prop="parentName">
+        <el-select
+          v-model="form.parentId"
+          :disabled="!form.parentId"
+          placeholder="请选择父节点"
+        >
+          <el-option
+            :label="form.parentName"
+            style="height: auto; padding: 0"
+            :value="form.parentId"
+          >
+            <el-tree
+              ref="tree"
+              :data="treeData"
+              default-expand-all
+              :props="defaultProps"
+              @node-click="handleNodeClick"
+            />
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="名称" prop="name">
+        <el-input v-model="form.name" />
+      </el-form-item>
+      <el-form-item label="排序" prop="order">
+        <el-input v-model="form.order" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import { doEdit, getList } from '@/api/departmentManagement'
+
+  export default {
+    name: 'DepartmentManagementEdit',
+    data() {
+      return {
+        treeData: [],
+        defaultProps: {
+          children: 'children',
+          label: 'name',
+        },
+        form: {
+          parentName: '',
+          parentId: '',
+        },
+        rules: {
+          parentName: [
+            { required: true, trigger: 'blur', message: '请选择父节点' },
+          ],
+          name: [{ required: true, trigger: 'blur', message: '请输入名称' }],
+          order: [{ required: true, trigger: 'blur', message: '请输入排序' }],
+        },
+        title: '',
+        dialogFormVisible: false,
+      }
+    },
+    created() {
+      this.fetchData()
+    },
+    methods: {
+      async fetchData() {
+        const {
+          data: { list },
+        } = await getList()
+        this.treeData = list
+      },
+      handleNodeClick(node) {
+        this.form.parentName = node.name
+        this.form.parentId = node.id
+      },
+      showEdit(row) {
+        if (!row) {
+          this.title = '添加'
+        } else {
+          this.title = '编辑'
+          this.form = Object.assign({}, row)
+        }
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const { msg } = await doEdit(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 166 - 0
src/views/setting/departmentManagement/index.vue

@@ -0,0 +1,166 @@
+<template>
+  <div class="department-management-container">
+    <vab-query-form>
+      <vab-query-form-left-panel :span="12">
+        <el-button
+          icon="el-icon-plus"
+          type="primary"
+          @click="handleEdit($event)"
+        >
+          添加
+        </el-button>
+        <el-button
+          icon="el-icon-delete"
+          type="danger"
+          @click="handleDelete($event)"
+        >
+          批量删除
+        </el-button>
+      </vab-query-form-left-panel>
+      <vab-query-form-right-panel :span="12">
+        <el-form :inline="true" :model="queryForm" @submit.native.prevent>
+          <el-form-item>
+            <el-input
+              v-model.trim="queryForm.name"
+              clearable
+              placeholder="请输入名称"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="queryData">
+              查询
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </vab-query-form-right-panel>
+    </vab-query-form>
+
+    <el-table
+      v-loading="listLoading"
+      border
+      :data="list"
+      default-expand-all
+      row-key="id"
+      :tree-props="{ children: 'children' }"
+      @selection-change="setSelectRows"
+    >
+      <el-table-column show-overflow-tooltip type="selection" />
+      <el-table-column label="名称" prop="name" show-overflow-tooltip />
+      <el-table-column label="父节点Id" prop="parentId" show-overflow-tooltip />
+      <el-table-column label="排序" prop="order" show-overflow-tooltip />
+      <el-table-column
+        label="创建时间"
+        prop="createTime"
+        show-overflow-tooltip
+      />
+      <el-table-column label="操作" width="85">
+        <template #default="{ row }">
+          <el-button type="text" @click="handleEdit(row)">编辑</el-button>
+          <el-button
+            :disabled="!row.parentId"
+            type="text"
+            @click="handleDelete({ row })"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+      <template #empty>
+        <el-image
+          class="vab-data-empty"
+          :src="require('@/assets/empty_images/data_empty.png')"
+        />
+      </template>
+    </el-table>
+    <el-pagination
+      background
+      :current-page="queryForm.pageNo"
+      :layout="layout"
+      :page-size="queryForm.pageSize"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange"
+    />
+    <edit ref="edit" @fetch-data="fetchData" />
+  </div>
+</template>
+
+<script>
+  import { doDelete, getList } from '@/api/departmentManagement'
+  import Edit from './components/DepartmentManagementEdit'
+
+  export default {
+    name: 'DepartmentManagement',
+    components: { Edit },
+    data() {
+      return {
+        list: [],
+        listLoading: true,
+        layout: 'total, sizes, prev, pager, next, jumper',
+        total: 0,
+        selectRows: '',
+        queryForm: {
+          pageNo: 1,
+          pageSize: 10,
+          title: '',
+        },
+      }
+    },
+    created() {
+      this.fetchData()
+    },
+    methods: {
+      setSelectRows(val) {
+        this.selectRows = val
+      },
+      handleEdit(row) {
+        if (row.id) {
+          this.$refs['edit'].showEdit(row)
+        } else {
+          this.$refs['edit'].showEdit()
+        }
+      },
+      handleDelete(row) {
+        if (row.id) {
+          this.$baseConfirm('你确定要删除当前项吗', null, async () => {
+            const { msg } = await doDelete({ ids: row.id })
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            await this.fetchData()
+          })
+        } else {
+          if (this.selectRows.length > 0) {
+            const ids = this.selectRows.map((item) => item.id).join()
+            this.$baseConfirm('你确定要删除选中项吗', null, async () => {
+              const { msg } = await doDelete({ ids })
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+              await this.fetchData()
+            })
+          } else {
+            this.$baseMessage('未选中任何行', 'error', 'vab-hey-message-error')
+          }
+        }
+      },
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNo = val
+        this.fetchData()
+      },
+      queryData() {
+        this.queryForm.pageNo = 1
+        this.fetchData()
+      },
+      async fetchData() {
+        this.listLoading = true
+        const {
+          data: { list, total },
+        } = await getList(this.queryForm)
+        this.list = list
+        this.total = total
+        this.listLoading = false
+      },
+    },
+  }
+</script>

+ 147 - 0
src/views/setting/menuManagement/components/MenuManagementEdit.vue

@@ -0,0 +1,147 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="dialogFormVisible"
+    width="60%"
+    @close="close"
+  >
+    <el-form
+      ref="form"
+      :inline="true"
+      label-width="140px"
+      :model="form"
+      :rules="rules"
+    >
+      <el-form-item label="父级Id" prop="parentId">
+        <el-input v-model="form.parentId" />
+      </el-form-item>
+      <el-form-item label="name" prop="name">
+        <el-input v-model="form.name" />
+      </el-form-item>
+      <el-form-item label="路径" prop="path">
+        <el-input v-model="form.path" />
+      </el-form-item>
+      <el-form-item label="vue文件路径" prop="component">
+        <el-input v-model="form.component" />
+      </el-form-item>
+      <el-form-item label="重定向">
+        <el-input v-model="form.redirect" />
+      </el-form-item>
+      <el-form-item label="标题" prop="meta.title">
+        <el-input v-model="form.meta.title" />
+      </el-form-item>
+      <el-form-item label="图标">
+        <el-popover
+          popper-class="icon-selector-popper"
+          trigger="hover"
+          width="292"
+        >
+          <template #reference>
+            <el-input v-model="form.meta.icon" />
+          </template>
+          <vab-icon-selector @handle-icon="handleIcon" />
+        </el-popover>
+      </el-form-item>
+      <el-form-item label="badge">
+        <el-input v-model="form.meta.badge" />
+      </el-form-item>
+      <el-form-item label="dot">
+        <el-switch v-model="form.meta.dot" />
+      </el-form-item>
+      <el-form-item label="隐藏">
+        <el-switch v-model="form.meta.hidden" />
+      </el-form-item>
+      <el-form-item label="始终显示当前节点">
+        <el-switch v-model="form.meta.levelHidden" />
+      </el-form-item>
+      <el-form-item label="自定义svg图标">
+        <el-switch v-model="form.meta.isCustomSvg" />
+      </el-form-item>
+      <el-form-item label="固定">
+        <el-switch v-model="form.meta.noClosable" />
+      </el-form-item>
+      <el-form-item label="无缓存">
+        <el-switch v-model="form.meta.noKeepAlive" />
+      </el-form-item>
+      <el-form-item label="不显示当前标签页">
+        <el-switch v-model="form.meta.tabHidden" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import VabIconSelector from '@/extra/VabIconSelector'
+  import { doEdit } from '@/api/menuManagement'
+
+  export default {
+    name: 'MenuManagementEdit',
+    components: { VabIconSelector },
+    data() {
+      return {
+        form: {
+          meta: {
+            title: '',
+            icon: '',
+            badge: '',
+            dot: '',
+            hidden: '',
+            levelHidden: '',
+            isCustomSvg: '',
+            noClosable: '',
+            noKeepAlive: '',
+            tabHidden: '',
+          },
+        },
+        rules: {
+          parentId: [
+            { required: true, trigger: 'blur', message: '请输入父级id' },
+          ],
+          name: [{ required: true, trigger: 'blur', message: '请输入name' }],
+          path: [{ required: true, trigger: 'blur', message: '请输入path' }],
+          component: [
+            { required: true, trigger: 'blur', message: '请输入component' },
+          ],
+          'meta.title': [
+            { required: true, trigger: 'blur', message: '请输入标题' },
+          ],
+        },
+        title: '',
+        dialogFormVisible: false,
+      }
+    },
+    methods: {
+      handleIcon(item) {
+        this.form.meta.icon = item
+      },
+      showEdit(row) {
+        if (!row) {
+          this.title = '添加'
+        } else {
+          this.title = '编辑'
+          this.form = JSON.parse(JSON.stringify(Object.assign({}, row)))
+        }
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const { msg } = await doEdit(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 195 - 0
src/views/setting/menuManagement/index.vue

@@ -0,0 +1,195 @@
+<template>
+  <div class="menu-management-container">
+    <el-row :gutter="20">
+      <el-col :lg="4" :md="8" :sm="24" :xl="4" :xs="24">
+        <el-card shadow="hover">
+          <el-tree
+            :data="data"
+            :default-expanded-keys="['root']"
+            node-key="id"
+            :props="defaultProps"
+            @node-click="handleNodeClick"
+          />
+        </el-card>
+      </el-col>
+      <el-col :lg="20" :md="16" :sm="24" :xl="20" :xs="24">
+        <el-card shadow="hover">
+          <vab-query-form>
+            <vab-query-form-top-panel :span="12">
+              <el-button
+                icon="el-icon-plus"
+                type="primary"
+                @click="handleEdit('')"
+              >
+                添加
+              </el-button>
+            </vab-query-form-top-panel>
+          </vab-query-form>
+          <el-table
+            v-loading="listLoading"
+            border
+            :data="list"
+            default-expand-all
+            row-key="path"
+            :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+          >
+            <el-table-column
+              label="标题"
+              prop="meta.title"
+              show-overflow-tooltip
+            />
+            <el-table-column label="name" prop="name" show-overflow-tooltip />
+            <el-table-column label="路径" prop="path" show-overflow-tooltip />
+            <el-table-column label="是否隐藏" show-overflow-tooltip>
+              <template #default="{ row }">
+                {{ row.meta.hidden ? '是' : '否' }}
+              </template>
+            </el-table-column>
+            <el-table-column
+              label="是否隐藏当前节点"
+              show-overflow-tooltip
+              width="100"
+            >
+              <template #default="{ row }">
+                {{ row.meta.levelHidden ? '是' : '否' }}
+              </template>
+            </el-table-column>
+            <el-table-column
+              label="vue文件路径"
+              prop="component"
+              show-overflow-tooltip
+            />
+            <el-table-column label="重定向" show-overflow-tooltip>
+              <template #default="{ row }">
+                {{ row.redirect ? row.redirect : '无' }}
+              </template>
+            </el-table-column>
+            <el-table-column label="图标" show-overflow-tooltip>
+              <template #default="{ row }">
+                <vab-icon
+                  v-if="row.meta && row.meta.icon"
+                  :icon="row.meta.icon"
+                />
+              </template>
+            </el-table-column>
+            <el-table-column label="是否固定" show-overflow-tooltip>
+              <template #default="{ row }">
+                {{ row.meta && row.meta.noClosable ? '是' : '否' }}
+              </template>
+            </el-table-column>
+            <el-table-column
+              label="是否无缓存"
+              show-overflow-tooltip
+              width="120"
+            >
+              <template #default="{ row }">
+                {{ row.meta && row.meta.noKeepAlive ? '是' : '否' }}
+              </template>
+            </el-table-column>
+            <el-table-column label="badge" show-overflow-tooltip>
+              <template #default="{ row }">
+                <el-tag
+                  v-if="row.meta && row.meta.badge"
+                  effect="dark"
+                  type="danger"
+                >
+                  {{ row.meta.badge }}
+                </el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="dot" show-overflow-tooltip>
+              <template #default="{ row }">
+                {{ row.meta && row.meta.dot ? '是' : '否' }}
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" show-overflow-tooltip width="185">
+              <template #default="{ row }">
+                <el-button type="primary" @click="handleEdit(row)">
+                  <vab-icon icon="edit-2-line" />
+                  编辑
+                </el-button>
+                <el-button type="danger" @click="handleDelete(row)">
+                  <vab-icon icon="delete-bin-6-line" />
+                  删除
+                </el-button>
+              </template>
+            </el-table-column>
+            <template #empty>
+              <el-image
+                class="vab-data-empty"
+                :src="require('@/assets/empty_images/data_empty.png')"
+              />
+            </template>
+          </el-table>
+        </el-card>
+      </el-col>
+    </el-row>
+    <edit ref="edit" @fetch-data="fetchData" />
+  </div>
+</template>
+
+<script>
+  import { getList } from '@/api/router'
+  import { doDelete, getTree } from '@/api/menuManagement'
+  import Edit from './components/MenuManagementEdit'
+
+  export default {
+    name: 'MenuManagement',
+    components: { Edit },
+    data() {
+      return {
+        data: [],
+        defaultProps: {
+          children: 'children',
+          label: 'label',
+        },
+        list: [],
+        listLoading: true,
+      }
+    },
+    async created() {
+      const {
+        data: { list },
+      } = await getTree()
+      this.data = list
+      await this.fetchData()
+    },
+    methods: {
+      handleEdit(row) {
+        if (row && row.path) {
+          this.$refs['edit'].showEdit(row)
+        } else {
+          this.$refs['edit'].showEdit()
+        }
+      },
+      handleDelete(row) {
+        if (row.path) {
+          this.$baseConfirm('你确定要删除当前项吗', null, async () => {
+            const { msg } = await doDelete({ paths: row.path })
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            await this.fetchData()
+          })
+        }
+      },
+      async fetchData(role) {
+        this.listLoading = true
+        const {
+          data: { list },
+        } = await getList({ role })
+        this.list = list
+        this.listLoading = false
+      },
+      handleNodeClick({ role }) {
+        this.fetchData(role)
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  $base: '.menu-management';
+  #{$base}-container {
+    padding: 0 !important;
+    background: $base-color-background !important;
+  }
+</style>

+ 202 - 0
src/views/setting/personalCenter/index.vue

@@ -0,0 +1,202 @@
+<template>
+  <div class="personal-center-container">
+    <el-row :gutter="20">
+      <el-col :lg="8" :md="12" :sm="24" :xl="8" :xs="24">
+        <el-card shadow="hover">
+          <div class="personal-center-user-info">
+            <el-avatar :size="100" :src="avatar" @click.native="openDialog" />
+            <div class="personal-center-user-info-full-name">
+              {{ form.fullName }}
+            </div>
+            <div class="personal-center-user-info-description">
+              {{ form.description }}
+            </div>
+            <div class="personal-center-user-info-follow">
+              <a href="https://github.com/chuzhixin" target="_blank">
+                <el-button round type="primary">
+                  <vab-icon icon="group-line" />
+                  Follow me
+                </el-button>
+              </a>
+            </div>
+
+            <ul class="personal-center-user-info-list">
+              <li>
+                <vab-icon icon="user-3-line" />
+                前端小白白
+              </li>
+              <li>
+                <vab-icon icon="magic-line" />
+                1992/8/11
+              </li>
+              <li>
+                <vab-icon icon="women-line" />
+                女
+              </li>
+              <li>
+                <vab-icon icon="community-line" />
+                集团 - 事业群 - 技术部
+              </li>
+              <li>
+                <vab-icon icon="map-pin-2-line" />
+                中国 • 广东省 • 深圳市
+              </li>
+              <li>
+                <vab-icon icon="code-s-slash-line" />
+                JavaScript、HTML、CSS、Vue、Node
+              </li>
+              <li>
+                <el-divider />
+                <h5>个性标签</h5>
+                <el-tag size="small">腹黑</el-tag>
+                <el-tag size="small">怕麻烦</el-tag>
+                <el-tag size="small">小仙女</el-tag>
+                <el-tag size="small">仙气飘飘</el-tag>
+              </li>
+            </ul>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :lg="16" :md="12" :sm="24" :xl="16" :xs="24">
+        <el-card shadow="hover">
+          <el-tabs v-model="activeName">
+            <el-tab-pane label="基本信息" name="first">
+              <el-col :lg="12" :md="16" :sm="24" :xl="12" :xs="24">
+                <el-form ref="form" label-width="80px" :model="form">
+                  <el-form-item label="姓名">
+                    <el-input v-model="form.fullName" />
+                  </el-form-item>
+                  <el-form-item label="昵称">
+                    <el-input v-model="form.nickname" />
+                  </el-form-item>
+                  <el-form-item label="性别">
+                    <el-select v-model="form.sex" style="width: 100%">
+                      <el-option label="保密" :value="0" />
+                      <el-option label="男" :value="1" />
+                      <el-option label="女" :value="2" />
+                    </el-select>
+                  </el-form-item>
+                  <el-form-item label="个人简介">
+                    <el-input v-model="form.description" type="textarea" />
+                  </el-form-item>
+                  <el-form-item>
+                    <el-button type="primary" @click="onSubmit">保存</el-button>
+                  </el-form-item>
+                </el-form>
+              </el-col>
+            </el-tab-pane>
+          </el-tabs>
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+  import { mapGetters } from 'vuex'
+
+  export default {
+    name: 'PersonalCenter',
+    data() {
+      return {
+        activeName: 'first',
+        form: {
+          fullName: unescape('\u695a\u829d\u99a8'),
+          nickname: 'good luck',
+          sex: 2,
+          description: unescape(
+            '\u5bcc\u5728\u672f\u6570\uff0c\u4e0d\u5728\u52b3\u8eab\uff1b\u5229\u5728\u52bf\u5c45\uff0c\u4e0d\u5728\u529b\u8015\u3002'
+          ),
+        },
+      }
+    },
+    computed: {
+      ...mapGetters({
+        avatar: 'user/avatar',
+      }),
+    },
+    methods: {
+      onSubmit() {
+        this.$baseMessage('模拟保存成功', 'success', 'vab-hey-message-success')
+      },
+      openDialog() {
+        this.$refs['vabCropper'].dialogVisible = true
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  $base: '.personal-center';
+  #{$base}-container {
+    padding: 0 !important;
+    background: $base-color-background !important;
+
+    #{$base}-user-info {
+      padding: $base-padding;
+      text-align: center;
+
+      :deep() {
+        .el-avatar {
+          img {
+            cursor: pointer;
+          }
+        }
+      }
+
+      &-full-name {
+        margin-top: 15px;
+        font-size: 24px;
+        font-weight: 500;
+        color: #262626;
+      }
+
+      &-description {
+        margin-top: 8px;
+      }
+
+      &-follow {
+        margin-top: 15px;
+      }
+
+      &-list {
+        margin-top: 18px;
+        line-height: 30px;
+        text-align: left;
+        list-style: none;
+
+        h5 {
+          margin: -20px 0 5px 0;
+        }
+
+        :deep() {
+          .el-tag {
+            margin-right: 10px !important;
+          }
+
+          .el-tag + .el-tag {
+            margin-left: 0;
+          }
+        }
+      }
+    }
+
+    #{$base}-item {
+      display: flex;
+
+      i {
+        font-size: 40px;
+      }
+
+      &-content {
+        box-sizing: border-box;
+        flex: 1;
+        margin-left: $base-margin;
+
+        &-second {
+          margin-top: 8px;
+        }
+      }
+    }
+  }
+</style>

+ 140 - 0
src/views/setting/roleManagement/components/RoleManagementEdit.vue

@@ -0,0 +1,140 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="dialogFormVisible"
+    width="500px"
+    @close="close"
+  >
+    <el-form ref="form" label-width="80px" :model="form" :rules="rules">
+      <el-form-item label="角色码" prop="role">
+        <el-input v-model="form.role" />
+      </el-form-item>
+      <el-form-item label="菜单">
+        <div class="vab-tree-border">
+          <el-tree
+            ref="tree"
+            :data="list"
+            :default-checked-keys="[
+              '/',
+              '/vab',
+              '/other',
+              '/mall',
+              '/setting',
+              '/error',
+            ]"
+            :default-expanded-keys="[]"
+            node-key="path"
+            show-checkbox
+          >
+            <template #default="{ data }">
+              <span>{{ data.meta.title }}</span>
+            </template>
+          </el-tree>
+        </div>
+      </el-form-item>
+      <el-form-item label="按钮权限">
+        <el-checkbox-group v-model="form.btnRolesCheckedList">
+          <el-checkbox
+            v-for="item in btnRoles"
+            :key="item.value"
+            :label="item.value"
+          >
+            {{ item.lable }}
+          </el-checkbox>
+        </el-checkbox-group>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import { doEdit } from '@/api/roleManagement'
+  import { getList } from '@/api/router'
+
+  export default {
+    name: 'RoleManagementEdit',
+    data() {
+      return {
+        form: {
+          btnRolesCheckedList: [],
+        },
+        rules: {
+          role: [{ required: true, trigger: 'blur', message: '请输入角色码' }],
+        },
+        title: '',
+        dialogFormVisible: false,
+        list: [],
+        /* btnRoles demo */
+        btnRoles: [
+          {
+            lable: '读',
+            value: 'read:system',
+          },
+          {
+            lable: '写',
+            value: 'write:system',
+          },
+          {
+            lable: '删',
+            value: 'delete:system',
+          },
+        ],
+      }
+    },
+    created() {
+      this.fetchData()
+    },
+    methods: {
+      showEdit(row) {
+        if (!row) {
+          this.title = '添加'
+        } else {
+          this.title = '编辑'
+          this.form = Object.assign({}, row)
+        }
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      async fetchData() {
+        const {
+          data: { list },
+        } = await getList()
+        this.list = list
+      },
+      save() {
+        // 具体业务请自行调整
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const tree = this.$refs.tree.getCheckedKeys()
+            const treeObject = { 'treeArray:': tree }
+            const { msg } = await doEdit({
+              ...this.form,
+              ...treeObject,
+            })
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  .vab-tree-border {
+    height: 200px;
+    padding: $base-padding;
+    overflow-y: auto;
+    border: 1px solid #dcdfe6;
+    border-radius: $base-border-radius;
+  }
+</style>

+ 184 - 0
src/views/setting/roleManagement/index.vue

@@ -0,0 +1,184 @@
+<template>
+  <div class="role-management-container">
+    <vab-query-form>
+      <vab-query-form-left-panel :span="12">
+        <el-button
+          icon="el-icon-plus"
+          type="primary"
+          @click="handleEdit($event)"
+        >
+          添加
+        </el-button>
+        <el-button
+          icon="el-icon-delete"
+          type="danger"
+          @click="handleDelete($event)"
+        >
+          批量删除
+        </el-button>
+      </vab-query-form-left-panel>
+      <vab-query-form-right-panel :span="12">
+        <el-form :inline="true" :model="queryForm" @submit.native.prevent>
+          <el-form-item>
+            <el-input
+              v-model.trim="queryForm.role"
+              clearable
+              placeholder="请输入角色"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="queryData">
+              查询
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </vab-query-form-right-panel>
+    </vab-query-form>
+
+    <el-table
+      v-loading="listLoading"
+      border
+      :data="list"
+      @selection-change="setSelectRows"
+    >
+      <el-table-column align="center" show-overflow-tooltip type="selection" />
+      <el-table-column align="center" label="序号" width="55">
+        <template #default="{ $index }">
+          {{ $index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        align="center"
+        label="id"
+        prop="id"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        align="center"
+        label="角色码"
+        prop="role"
+        show-overflow-tooltip
+      />
+      <el-table-column align="center" label="按钮权限" show-overflow-tooltip>
+        <template #default="{ row }">
+          <el-tag v-for="(item, index) in row.btnRolesCheckedList" :key="index">
+            {{
+              {
+                'read:system': '读',
+                'write:system': '写',
+                'delete:system': '删',
+              }[item]
+            }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column
+        align="center"
+        label="操作"
+        show-overflow-tooltip
+        width="85"
+      >
+        <template #default="{ row }">
+          <el-button type="text" @click="handleEdit(row)">编辑</el-button>
+          <el-button type="text" @click="handleDelete(row)">删除</el-button>
+        </template>
+      </el-table-column>
+      <template #empty>
+        <el-image
+          class="vab-data-empty"
+          :src="require('@/assets/empty_images/data_empty.png')"
+        />
+      </template>
+    </el-table>
+    <el-pagination
+      background
+      :current-page="queryForm.pageNo"
+      :layout="layout"
+      :page-size="queryForm.pageSize"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange"
+    />
+    <edit ref="edit" @fetch-data="fetchData" />
+  </div>
+</template>
+
+<script>
+  import { doDelete, getList } from '@/api/roleManagement'
+  import Edit from './components/RoleManagementEdit'
+
+  export default {
+    name: 'RoleManagement',
+    components: { Edit },
+    data() {
+      return {
+        list: [],
+        listLoading: true,
+        layout: 'total, sizes, prev, pager, next, jumper',
+        total: 0,
+        selectRows: '',
+        queryForm: {
+          pageNo: 1,
+          pageSize: 10,
+          role: '',
+        },
+      }
+    },
+    created() {
+      this.fetchData()
+    },
+    methods: {
+      setSelectRows(val) {
+        this.selectRows = val
+      },
+      handleEdit(row) {
+        if (row.id) {
+          this.$refs['edit'].showEdit(row)
+        } else {
+          this.$refs['edit'].showEdit()
+        }
+      },
+      handleDelete(row) {
+        if (row.id) {
+          this.$baseConfirm('你确定要删除当前项吗', null, async () => {
+            const { msg } = await doDelete({ ids: row.id })
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            await this.fetchData()
+          })
+        } else {
+          if (this.selectRows.length > 0) {
+            const ids = this.selectRows.map((item) => item.id).join()
+            this.$baseConfirm('你确定要删除选中项吗', null, async () => {
+              const { msg } = await doDelete({ ids })
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+              await this.fetchData()
+            })
+          } else {
+            this.$baseMessage('未选中任何行', 'error', 'vab-hey-message-error')
+          }
+        }
+      },
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNo = val
+        this.fetchData()
+      },
+      queryData() {
+        this.queryForm.pageNo = 1
+        this.fetchData()
+      },
+      async fetchData() {
+        this.listLoading = true
+        const {
+          data: { list, total },
+        } = await getList(this.queryForm)
+        this.list = list
+        this.total = total
+        this.listLoading = false
+      },
+    },
+  }
+</script>

+ 140 - 0
src/views/setting/systemLog/index.vue

@@ -0,0 +1,140 @@
+<template>
+  <div class="system-log-container">
+    <vab-query-form>
+      <vab-query-form-top-panel>
+        <el-form
+          :inline="true"
+          label-width="60px"
+          :model="queryForm"
+          @submit.native.prevent
+        >
+          <el-form-item label="账号">
+            <el-input
+              v-model.trim="queryForm.account"
+              clearable
+              placeholder="请输入账号"
+            />
+          </el-form-item>
+          <el-form-item label="周期">
+            <el-date-picker
+              v-model="queryForm.searchDate"
+              end-placeholder="结束日期"
+              format="yyyy-MM-dd"
+              start-placeholder="开始日期"
+              type="daterange"
+              value-format="yyyy-MM-dd"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="queryData">
+              查询
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </vab-query-form-top-panel>
+    </vab-query-form>
+
+    <el-table v-loading="listLoading" :data="list">
+      <el-table-column
+        align="center"
+        label="日志类型"
+        prop="type"
+        show-overflow-tooltip
+        width="230px"
+      />
+      <el-table-column
+        align="center"
+        label="账号"
+        prop="account"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        align="center"
+        label="执行结果"
+        prop="executeResult"
+        show-overflow-tooltip
+      >
+        <template #default="{ row }">
+          <span v-if="row.executeResult === '登录成功'">
+            <span class="vab-dot vab-dot-success"><span></span></span>
+            {{ row.executeResult }}
+          </span>
+          <span v-else>
+            <span class="vab-dot vab-dot-error"><span></span></span>
+            {{ row.executeResult }}
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column align="center" label="登录IP" prop="ip" />
+      <el-table-column
+        align="center"
+        label="访问时间"
+        prop="datetime"
+        show-overflow-tooltip
+      />
+      <template #empty>
+        <el-image
+          class="vab-data-empty"
+          :src="require('@/assets/empty_images/data_empty.png')"
+        />
+      </template>
+    </el-table>
+    <el-pagination
+      background
+      :current-page="queryForm.pageNo"
+      :layout="layout"
+      :page-size="queryForm.pageSize"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange"
+    />
+  </div>
+</template>
+
+<script>
+  import { getList } from '@/api/systemLog'
+
+  export default {
+    name: 'SystemLog',
+    data() {
+      return {
+        list: [],
+        listLoading: true,
+        layout: 'total, sizes, prev, pager, next, jumper',
+        total: 0,
+        queryForm: {
+          account: '',
+          searchDate: '',
+          pageNo: 1,
+          pageSize: 20,
+        },
+      }
+    },
+    created() {
+      this.fetchData()
+    },
+    methods: {
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNo = val
+        this.fetchData()
+      },
+      queryData() {
+        this.queryForm.pageNo = 1
+        this.fetchData()
+      },
+      async fetchData() {
+        this.listLoading = true
+        const {
+          data: { list, total },
+        } = await getList(this.queryForm)
+        this.list = list
+        this.total = total
+        this.listLoading = false
+      },
+    },
+  }
+</script>

+ 83 - 0
src/views/setting/userManagement/components/UserManagementEdit.vue

@@ -0,0 +1,83 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="dialogFormVisible"
+    width="500px"
+    @close="close"
+  >
+    <el-form ref="form" label-width="80px" :model="form" :rules="rules">
+      <el-form-item label="用户名" prop="username">
+        <el-input v-model.trim="form.username" />
+      </el-form-item>
+      <el-form-item label="密码" prop="password">
+        <el-input v-model.trim="form.password" type="password" />
+      </el-form-item>
+      <el-form-item label="邮箱" prop="email">
+        <el-input v-model.trim="form.email" />
+      </el-form-item>
+      <el-form-item label="角色" prop="roles">
+        <el-checkbox-group v-model="form.roles">
+          <el-checkbox label="admin" />
+          <el-checkbox label="editor" />
+        </el-checkbox-group>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import { doEdit } from '@/api/userManagement'
+
+  export default {
+    name: 'UserManagementEdit',
+    data() {
+      return {
+        form: {
+          roles: [],
+        },
+        rules: {
+          username: [
+            { required: true, trigger: 'blur', message: '请输入用户名' },
+          ],
+          password: [
+            { required: true, trigger: 'blur', message: '请输入密码' },
+          ],
+          email: [{ required: true, trigger: 'blur', message: '请输入邮箱' }],
+          roles: [{ required: true, trigger: 'blur', message: '请选择角色' }],
+        },
+        title: '',
+        dialogFormVisible: false,
+      }
+    },
+    methods: {
+      showEdit(row) {
+        if (!row) {
+          this.title = '添加'
+        } else {
+          this.title = '编辑'
+          this.form = Object.assign({}, row)
+        }
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const { msg } = await doEdit(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 192 - 0
src/views/setting/userManagement/index.vue

@@ -0,0 +1,192 @@
+<template>
+  <div class="user-management-container">
+    <vab-query-form>
+      <vab-query-form-left-panel :span="12">
+        <el-button
+          icon="el-icon-plus"
+          type="primary"
+          @click="handleEdit($event)"
+        >
+          添加
+        </el-button>
+        <el-button
+          icon="el-icon-delete"
+          type="danger"
+          @click="handleDelete($event)"
+        >
+          批量删除
+        </el-button>
+      </vab-query-form-left-panel>
+      <vab-query-form-right-panel :span="12">
+        <el-form :inline="true" :model="queryForm" @submit.native.prevent>
+          <el-form-item>
+            <el-input
+              v-model.trim="queryForm.username"
+              clearable
+              placeholder="请输入用户名"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="queryData">
+              查询
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </vab-query-form-right-panel>
+    </vab-query-form>
+
+    <el-table
+      v-loading="listLoading"
+      border
+      :data="list"
+      @selection-change="setSelectRows"
+    >
+      <el-table-column align="center" show-overflow-tooltip type="selection" />
+      <el-table-column align="center" label="序号" width="55">
+        <template #default="{ $index }">
+          {{ $index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        align="center"
+        label="id"
+        prop="id"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        align="center"
+        label="用户名"
+        prop="username"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        align="center"
+        label="邮箱"
+        prop="email"
+        show-overflow-tooltip
+      />
+
+      <el-table-column align="center" label="角色" show-overflow-tooltip>
+        <template #default="{ row }">
+          <el-tag v-for="(item, index) in row.roles" :key="index">
+            {{ item }}
+          </el-tag>
+        </template>
+      </el-table-column>
+
+      <el-table-column
+        align="center"
+        label="修改时间"
+        prop="datatime"
+        show-overflow-tooltip
+      />
+      <el-table-column
+        align="center"
+        label="操作"
+        show-overflow-tooltip
+        width="85"
+      >
+        <template #default="{ row }">
+          <el-button type="text" @click="handleEdit(row)">编辑</el-button>
+          <el-button type="text" @click="handleDelete(row)">删除</el-button>
+        </template>
+      </el-table-column>
+      <template #empty>
+        <el-image
+          class="vab-data-empty"
+          :src="require('@/assets/empty_images/data_empty.png')"
+        />
+      </template>
+    </el-table>
+    <el-pagination
+      background
+      :current-page="queryForm.pageNo"
+      :layout="layout"
+      :page-size="queryForm.pageSize"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange"
+    />
+    <edit ref="edit" @fetch-data="fetchData" />
+  </div>
+</template>
+
+<script>
+  import { doDelete, getList } from '@/api/userManagement'
+  import Edit from './components/UserManagementEdit'
+
+  export default {
+    name: 'UserManagement',
+    components: { Edit },
+    data() {
+      return {
+        list: [],
+        listLoading: true,
+        layout: 'total, sizes, prev, pager, next, jumper',
+        total: 0,
+        selectRows: '',
+        queryForm: {
+          pageNo: 1,
+          pageSize: 10,
+          username: '',
+        },
+      }
+    },
+    created() {
+      this.fetchData()
+    },
+    methods: {
+      setSelectRows(val) {
+        this.selectRows = val
+      },
+      handleEdit(row) {
+        if (row.id) {
+          this.$refs['edit'].showEdit(row)
+        } else {
+          this.$refs['edit'].showEdit()
+        }
+      },
+      handleDelete(row) {
+        if (row.id) {
+          this.$baseConfirm('你确定要删除当前项吗', null, async () => {
+            const { msg } = await doDelete({ ids: row.id })
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            await this.fetchData()
+          })
+        } else {
+          if (this.selectRows.length > 0) {
+            const ids = this.selectRows.map((item) => item.id).join()
+            this.$baseConfirm('你确定要删除选中项吗', null, async () => {
+              const { msg } = await doDelete({ ids })
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+              await this.fetchData()
+            })
+          } else {
+            this.$baseMessage('未选中任何行', 'error', 'vab-hey-message-error')
+          }
+        }
+      },
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNo = val
+        this.fetchData()
+      },
+      queryData() {
+        this.queryForm.pageNo = 1
+        this.fetchData()
+      },
+      async fetchData() {
+        this.listLoading = true
+        const {
+          data: { list, total },
+        } = await getList(this.queryForm)
+        this.list = list
+        this.total = total
+        this.listLoading = false
+      },
+    },
+  }
+</script>