Commit c1b532ad9ca4f9d04954c7dd1d1b601b43ff0440

Authored by 梁保满
1 parent ae233dc9

权限配置,路由基础设置

src/api/apis/login.js 0 → 100644
  1 +
  2 +import axios from "../axios"
  3 +import loginUrls from "../urls/login"
  4 +
  5 +export default {
  6 + // 账号密码登陆
  7 + fetchLogin (data) {
  8 + return axios.post(loginUrls.login, data)
  9 + }
  10 +}
... ...
src/api/apis/role.js 0 → 100644
  1 +
  2 +import axios from "../axios"
  3 +import roleUrls from "../urls/role"
  4 +
  5 +export default {
  6 + // 获取权限列表
  7 + fetchGetRoleList () {
  8 + return axios.post(roleUrls.getRoleList)
  9 + },
  10 + // 增加角色
  11 + fetchAddRole (data) {
  12 + return axios.post(roleUrls.addRole, data)
  13 + },
  14 + // 删除角色
  15 + fetchDelRole (data) {
  16 + return axios.post(roleUrls.delRole, data)
  17 + },
  18 + // 分配角色权限
  19 + fetchRolePermissions (data) {
  20 + return axios.post(roleUrls.rolePermissions, data)
  21 + }
  22 +}
  23 +
... ...
src/api/apis/user.js 0 → 100644
  1 +
  2 +import axios from "../axios"
  3 +import userUrls from "../urls/user"
  4 +
  5 +export default {
  6 + // 注册/添加账号
  7 + fetchRegister (data) {
  8 + return axios.post(userUrls.register, data)
  9 + },
  10 + // 删除用户
  11 + fetchDelUser (data) {
  12 + return axios.post(userUrls.delUser, data)
  13 + },
  14 + // 获取用户列表
  15 + fetchUserList (data) {
  16 + return axios.post(userUrls.userList, data)
  17 + },
  18 + // 修改用户信息
  19 + fetchEditUser (data) {
  20 + return axios.post(userUrls.editUser, data)
  21 + },
  22 + // 获取当前用户信息
  23 + fetchGetUserInfo () {
  24 + return axios.get(userUrls.getUserInfo)
  25 + },
  26 + // 获取用户信息
  27 + fetchGetUserInfoId (data) {
  28 + return axios.post(userUrls.getUserInfoId, data)
  29 + },
  30 + // 修改密码
  31 + fetchEditPassword (data) {
  32 + return axios.post(userUrls.editPassword, data)
  33 + }
  34 +}
  35 +
... ...
src/api/axios.js 0 → 100644
  1 +import axios from "axios"
  2 +import Cookies from "js-cookie"
  3 +import NProgress from "nprogress"
  4 +import { Message } from "element-ui"
  5 +// axios默认配置
  6 +axios.defaults.timeout = 10000 // 超时时间
  7 +axios.defaults.baseURL = process.env.API_HOST
  8 +
  9 +// http request 拦截器
  10 +axios.interceptors.request.use(config => {
  11 + NProgress.start()
  12 + config.headers["Content-Type"] = "application/json;charset=UTF-8"
  13 + if (Cookies.get("access_token")) {
  14 + config.headers.Authorization = "Bearer" + Cookies.get("access_token")
  15 + }
  16 + return config
  17 +},
  18 +error => {
  19 + return Promise.reject(error.response)
  20 +})
  21 +
  22 +// http response 拦截器
  23 +axios.interceptors.response.use(
  24 + response => {
  25 + NProgress.done()
  26 + if (response.data.code === 11000) {
  27 + Cookies.set("access_token", response.data.message, { expires: 1 / 12 })
  28 + return Promise.resolve()
  29 + } else if (response.data.code === 10000) { // 约定报错信息
  30 + Message({
  31 + message: response.data.message,
  32 + type: "warning"
  33 + })
  34 + return Promise.reject(response)
  35 + } else {
  36 + return Promise.resolve(response)
  37 + }
  38 + },
  39 + error => {
  40 + if (error.response.status === 404) {
  41 + Message({
  42 + message: "请求地址出错",
  43 + type: "warning"
  44 + })
  45 + } else if (error.response.status === 401) {
  46 + Message({
  47 + message: error.response.data.message,
  48 + type: "warning"
  49 + })
  50 + Cookies.remove("access_token")
  51 + setTimeout(() => {
  52 + location.reload()
  53 + }, 3000)
  54 + }
  55 + return Promise.reject(error.response) // 返回接口返回的错误信息
  56 + })
  57 +export default axios
... ...
src/api/index.js 0 → 100644
  1 +const modulesFiles = require.context("./apis", true, /\.js$/)
  2 +const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  3 + const value = modulesFiles(modulePath)
  4 + modules = Object.assign(modules, value.default)
  5 + return modules
  6 +}, {})
  7 +
  8 +export default modules
... ...
src/api/urls/login.js 0 → 100644
  1 +
  2 +export default {
  3 + // 账号密码登陆
  4 + login: "/admin/user/login"
  5 +}
... ...
src/api/urls/role.js 0 → 100644
  1 +
  2 +export default {
  3 + // 获取角色列表
  4 + getRoleList: "/permissions/getRoleList",
  5 + // 增加角色
  6 + addRole: "/permissions/addRole",
  7 + // 删除角色
  8 + delRole: "/permissions/delRole",
  9 + // 分配角色权限
  10 + rolePermissions: "/permissions/rolePermissions"
  11 +}
  12 +
... ...
src/api/urls/user.js 0 → 100644
  1 +
  2 +export default {
  3 + // 注册/添加账号
  4 + register: "/admin/user/register",
  5 + // 删除用户
  6 + delUser: "/user/delUser",
  7 + // 修改用户信息
  8 + editUser: "/user/editUserInfo",
  9 + // 获取当前用户信息
  10 + getUserInfo: "/user/getUserInfo",
  11 + // 获取用户信息
  12 + getUserInfoId: "/user/getUserInfoId",
  13 + // 获取用户列表
  14 + userList: "/user/userList",
  15 + // 修改密码
  16 + editPassword: "/user/editPassword"
  17 +}
... ...
src/assets/images/chiken.png 0 → 100644

56.7 KB

src/assets/images/login-bg.png 0 → 100644

277 KB

src/assets/images/logo.png 0 → 100644

7.89 KB

src/assets/images/mandefault.png 0 → 100644

5.58 KB

src/assets/images/womandefault.png 0 → 100644

5.45 KB

src/assets/img404/bg404.jpg 0 → 100644

35.8 KB

src/assets/img404/i404.png 0 → 100644

16.8 KB

src/assets/img404/sign.png 0 → 100644

423 KB

src/assets/lang.png 0 → 100644

6.05 KB

src/components/ECharts/lineEcharts.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <div :id="id" :style="{width: width, height: height}"></div>
  4 + </div>
  5 +</template>
  6 +
  7 +<script>
  8 +import * as echarts from "echarts"
  9 +export default {
  10 + name: "lineEcharts",
  11 + props: {
  12 + id: {
  13 + type: String,
  14 + default: "myChart"
  15 + },
  16 + width: {
  17 + type: String,
  18 + default: "100%"
  19 + },
  20 + height: {
  21 + type: String,
  22 + default: "100%"
  23 + }
  24 + },
  25 + data () {
  26 + return {
  27 + chart: null
  28 + }
  29 + },
  30 + mounted () {
  31 + this.initChart()
  32 + },
  33 + methods: {
  34 + initChart () {
  35 + this.chart = echarts.init(document.getElementById(this.id))
  36 +
  37 + this.chart.setOption({
  38 + title: {
  39 + text: "折线图堆叠"
  40 + },
  41 + tooltip: {
  42 + trigger: "axis"
  43 + },
  44 + legend: {
  45 + data: ["邮件营销", "联盟广告", "视频广告", "直接访问", "搜索引擎"]
  46 + },
  47 + grid: {
  48 + left: "3%",
  49 + right: "4%",
  50 + bottom: "3%",
  51 + containLabel: true
  52 + },
  53 + toolbox: {
  54 + feature: {
  55 + saveAsImage: {}
  56 + }
  57 + },
  58 + xAxis: {
  59 + type: "category",
  60 + boundaryGap: false,
  61 + data: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
  62 + },
  63 + yAxis: {
  64 + type: "value"
  65 + },
  66 + series: [
  67 + {
  68 + name: "邮件营销",
  69 + type: "line",
  70 + stack: "总量",
  71 + data: [8200, 6320, 5010, 4340, 3400, 2300, 1100]
  72 + },
  73 + {
  74 + name: "联盟广告",
  75 + type: "line",
  76 + stack: "总量",
  77 + data: [2200, 3820, 1910, 2340, 4900, 3300, 1100]
  78 + },
  79 + {
  80 + name: "视频广告",
  81 + type: "line",
  82 + stack: "总量",
  83 + data: [2500, 4302, 5010, 2540, 6900, 5300, 6410]
  84 + },
  85 + {
  86 + name: "直接访问",
  87 + type: "line",
  88 + stack: "总量",
  89 + data: [5320, 7332, 9301, 6334, 5390, 4330, 1320]
  90 + },
  91 + {
  92 + name: "搜索引擎",
  93 + type: "line",
  94 + stack: "总量",
  95 + data: [8820, 1932, 5901, 7304, 2900, 3300, 8200]
  96 + }
  97 + ]
  98 + })
  99 + }
  100 + }
  101 +}
  102 +</script>
  103 +
  104 +<style scoped>
  105 +
  106 +</style>
... ...
src/components/lang/langSelect.vue 0 → 100644
  1 +<template>
  2 + <el-dropdown class='international' @command="handleSetLanguage">
  3 + <div>
  4 + <span class="el-dropdown-link"><i class="fa fa-language fa-lg"></i>&nbsp;{{language}}<i class="el-icon-arrow-down el-icon--right"></i>
  5 + </span>
  6 + </div>
  7 + <el-dropdown-menu slot="dropdown">
  8 + <el-dropdown-item command="cn">中文</el-dropdown-item>
  9 + <el-dropdown-item command="en">English</el-dropdown-item>
  10 + </el-dropdown-menu>
  11 + </el-dropdown>
  12 +</template>
  13 +<script>
  14 +export default {
  15 + name: "langSelect",
  16 + data () {
  17 + return {
  18 + language: ""
  19 + }
  20 + },
  21 + mounted () {
  22 + const _lang = localStorage.lang || "cn"
  23 + this.getLanguage(_lang)
  24 + },
  25 + methods: {
  26 + handleSetLanguage (lang) {
  27 + this.$i18n.locale = lang
  28 + localStorage.setItem("lang", lang)
  29 + this.getLanguage(lang)
  30 + },
  31 + getLanguage (val) {
  32 + if (val === "cn") {
  33 + this.language = "中文"
  34 + }
  35 + if (val === "en") {
  36 + this.language = "English"
  37 + }
  38 + }
  39 + }
  40 +}
  41 +</script>
  42 +<style>
  43 + .international .el-dropdown-link { cursor: pointer; color: #666666; font-size: 14px; }
  44 + .el-icon-arrow-down { font-size: 14px; }
  45 +</style>
  46 +<style scoped>
  47 + .international-icon {
  48 + font-size: 20px;
  49 + cursor: pointer;
  50 + vertical-align: -5px !important;
  51 + }
  52 +</style>
  53 +
... ...
src/components/userForm/editPassword.vue 0 → 100644
  1 +<template>
  2 + <el-dialog title="修改密码" width="700px" :visible.sync="visible" destroy-on-close @close="closeCallback">
  3 + <div class="card">
  4 + <p class="title"><i class="fa fa-th-large fa-lg"></i>修改密码</p>
  5 + <el-form :model="ruleForm2" status-icon :rules="rules2" ref="ruleForm2" label-width="100px" class="demo-ruleForm">
  6 + <el-form-item label="原密码" prop="oldPassword">
  7 + <el-input type="password" v-model="ruleForm2.oldPassword" autocomplete="off"></el-input>
  8 + </el-form-item>
  9 + <el-form-item label="密码" prop="password">
  10 + <el-input type="password" v-model="ruleForm2.password" autocomplete="off"></el-input>
  11 + </el-form-item>
  12 + <el-form-item label="确认密码" prop="checkPass">
  13 + <el-input type="password" v-model="ruleForm2.checkPass" autocomplete="off"></el-input>
  14 + </el-form-item>
  15 + <el-form-item>
  16 + <el-button type="primary" @click="submitForm('ruleForm2')">提交</el-button>
  17 + <el-button @click="resetForm('ruleForm2')">重置</el-button>
  18 + </el-form-item>
  19 + </el-form>
  20 + </div>
  21 + </el-dialog>
  22 +</template>
  23 +
  24 +<script>
  25 +import Cookies from "js-cookie"
  26 +export default {
  27 + name: "editPassword",
  28 + props: {
  29 + dialogVisible: {
  30 + type: Boolean,
  31 + default: false
  32 + }
  33 + },
  34 + data () {
  35 + var validatePass = (rule, value, callback) => {
  36 + if (value === "") {
  37 + callback(new Error("请输入密码"))
  38 + } else if (value.toString().length < 6) {
  39 + callback(new Error("密码长度不能低于6位"))
  40 + } else {
  41 + if (this.ruleForm2.checkPass !== "") {
  42 + this.$refs.ruleForm2.validateField("checkPass")
  43 + }
  44 + callback()
  45 + }
  46 + }
  47 + var validatePass2 = (rule, value, callback) => {
  48 + if (value === "") {
  49 + callback(new Error("请再次输入密码"))
  50 + } else if (value.toString().length < 6) {
  51 + callback(new Error("密码长度不能低于6位"))
  52 + } else if (value !== this.ruleForm2.password) {
  53 + callback(new Error("两次输入密码不一致!"))
  54 + } else {
  55 + callback()
  56 + }
  57 + }
  58 + return {
  59 + visible: this.dialogVisible,
  60 + ruleForm2: {
  61 + oldPassword: "",
  62 + password: "",
  63 + checkPass: ""
  64 + },
  65 + rules2: {
  66 + oldPassword: [
  67 + {required: true, validator: validatePass, trigger: "blur"}
  68 + ],
  69 + password: [
  70 + {required: true, validator: validatePass, trigger: "blur"}
  71 + ],
  72 + checkPass: [
  73 + {required: true, validator: validatePass2, trigger: "blur"}
  74 + ]
  75 + }
  76 + }
  77 + },
  78 + methods: {
  79 + closeCallback () {
  80 + this.$emit("editPwdCallback")
  81 + },
  82 + submitForm (formName) {
  83 + let that = this
  84 + this.$refs[formName].validate((valid) => {
  85 + if (valid) {
  86 + this.$request.fetchEditPassword({
  87 + oldPassword: that.ruleForm2.oldPassword,
  88 + newPassword: that.ruleForm2.password
  89 + }).then((res) => {
  90 + that.$message({
  91 + showClose: true,
  92 + message: res.data.message,
  93 + type: "success"
  94 + })
  95 + setTimeout(function () {
  96 + Cookies.remove("access_token")
  97 + location.reload()
  98 + }, 3000)
  99 + }).catch((err) => {
  100 + that.$message({
  101 + showClose: true,
  102 + message: err.data.message,
  103 + type: "error"
  104 + })
  105 + })
  106 + } else {
  107 + console.log("error submit!!")
  108 + return false
  109 + }
  110 + })
  111 + },
  112 + resetForm (formName) {
  113 + this.$refs[formName].resetFields()
  114 + }
  115 + }
  116 +}
  117 +</script>
  118 +
  119 +<style scoped>
  120 + .demo-ruleForm {
  121 + width: 460px;
  122 + padding-top: 25px;
  123 + }
  124 +
  125 + .card {
  126 + width: 560px;
  127 + padding-bottom: 15px;
  128 + margin: 0px auto;
  129 + }
  130 +</style>
... ...
src/components/userForm/userInfo.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="title" width="800px" :visible.sync="visible" destroy-on-close @close="closeCallback">
  3 + <div class="card">
  4 + <p class="title"><i class="fa fa-th-large fa-lg"></i>个人资料</p>
  5 + <el-form :model="ruleForm2" status-icon :rules="rules" ref="ruleForm2" label-width="100px" class="demo-ruleForm">
  6 + <el-form-item label="用户名" prop="username">
  7 + <el-input v-model="ruleForm2.username" autocomplete="off"></el-input>
  8 + </el-form-item>
  9 + <el-form-item v-if="!userId" label="密码" prop="password">
  10 + <el-input type="password" v-model="ruleForm2.password" autocomplete="off"></el-input>
  11 + </el-form-item>
  12 + <el-form-item v-if="!userId" label="确认密码" prop="checkPass">
  13 + <el-input type="password" v-model="ruleForm2.checkPass" autocomplete="off"></el-input>
  14 + </el-form-item>
  15 + <el-form-item label="角色">
  16 + <el-select v-if="roleName" v-model="ruleForm2.roleId" disabled placeholder="请选择等级">
  17 + <el-option
  18 + v-for="item in roleData"
  19 + :key="item.id"
  20 + :label="item.name"
  21 + :value="item.id">
  22 + </el-option>
  23 + </el-select>
  24 + <el-select v-else v-model="ruleForm2.roleId" placeholder="请选择等级">
  25 + <el-option
  26 + v-for="item in roleData"
  27 + :key="item.id"
  28 + :label="item.name"
  29 + :value="item.id"
  30 + :disabled="item.disabled">
  31 + </el-option>
  32 + </el-select>
  33 + </el-form-item>
  34 + <el-form-item label="姓名" prop="name">
  35 + <el-input v-model="ruleForm2.name" autocomplete="off"></el-input>
  36 + </el-form-item>
  37 + <el-form-item label="性别">
  38 + <el-radio-group v-model="ruleForm2.sex">
  39 + <el-radio label="1">男</el-radio>
  40 + <el-radio label="2">女</el-radio>
  41 + </el-radio-group>
  42 + </el-form-item>
  43 + <el-form-item label="年龄" prop="age">
  44 + <el-input v-model="ruleForm2.age" autocomplete="off"></el-input>
  45 + </el-form-item>
  46 + <el-form-item label="手机号" prop="mobilePhone">
  47 + <el-input v-model="ruleForm2.mobilePhone" autocomplete="off"></el-input>
  48 + </el-form-item>
  49 + <el-form-item label="是否启用">
  50 + <el-switch v-model="ruleForm2.status"></el-switch>
  51 + </el-form-item>
  52 + <el-form-item label="头像上传">
  53 + <el-upload
  54 + class="avatar-uploader"
  55 + action="/api/editor/uploadImg"
  56 + :show-file-list="false"
  57 + :on-success="handleAvatarSuccess"
  58 + :before-upload="beforeAvatarUpload">
  59 + <img v-if="ruleForm2.avatar" :src="ruleForm2.avatar" class="avatar">
  60 + <i v-else class="el-icon-plus avatar-uploader-icon"></i>
  61 + </el-upload>
  62 + </el-form-item>
  63 + <el-form-item>
  64 + <el-button type="primary" @click="submitForm('ruleForm2')">确 定</el-button>
  65 + <el-button @click="resetForm('ruleForm2')">重 置</el-button>
  66 + </el-form-item>
  67 + </el-form>
  68 + </div>
  69 + </el-dialog>
  70 +</template>
  71 +
  72 +<script>
  73 +export default {
  74 + name: "userInfo",
  75 + props: {
  76 + title: {
  77 + type: String,
  78 + default: "账号信息"
  79 + },
  80 + dialogVisible: {
  81 + type: Boolean,
  82 + default: false
  83 + },
  84 + userId: {
  85 + type: String,
  86 + default: ""
  87 + }
  88 + },
  89 + data () {
  90 + var validatePass = (rule, value, callback) => {
  91 + if (value === "") {
  92 + callback(new Error("请输入密码"))
  93 + } else {
  94 + if (this.ruleForm2.checkPass !== "") {
  95 + this.$refs.ruleForm2.validateField("checkPass")
  96 + }
  97 + callback()
  98 + }
  99 + }
  100 + var validatePass2 = (rule, value, callback) => {
  101 + if (value === "") {
  102 + callback(new Error("请再次输入密码"))
  103 + } else if (value !== this.ruleForm2.password) {
  104 + callback(new Error("两次输入密码不一致!"))
  105 + } else {
  106 + callback()
  107 + }
  108 + }
  109 + return {
  110 + roleName: false,
  111 + roleData: "",
  112 + visible: this.dialogVisible,
  113 + ruleForm2: {
  114 + mobilePhone: "",
  115 + username: "",
  116 + password: "",
  117 + checkPass: "",
  118 + roleId: "",
  119 + status: "",
  120 + sex: "1",
  121 + age: 0,
  122 + name: "",
  123 + avatar: ""
  124 + },
  125 + rules: {
  126 + username: [
  127 + { required: true, message: "请输入用户名", trigger: "blur" },
  128 + { min: 3, max: 18, message: "长度在 3 到 18 个字符", trigger: "blur" }
  129 + ],
  130 + password: [
  131 + {required: true, validator: validatePass, trigger: "blur"}
  132 + ],
  133 + checkPass: [
  134 + {required: true, validator: validatePass2, trigger: "blur"}
  135 + ],
  136 + roleId: [
  137 + { required: true, message: "请选择用户角色", trigger: "change" }
  138 + ]
  139 + }
  140 + }
  141 + },
  142 + methods: {
  143 + handleAvatarSuccess (res, file) {
  144 + this.ruleForm2.avatar = res.data[0]
  145 + },
  146 + closeCallback () {
  147 + this.$emit("successCallback")
  148 + },
  149 + beforeAvatarUpload (file) {
  150 + const isJPG = file.type === "image/jpeg"
  151 + const isPNG = file.type === "image/png"
  152 + const isLt2M = file.size / 1024 / 1024 < 2
  153 +
  154 + if (!(isJPG || isPNG)) {
  155 + this.$message.error("上传头像图片只能是 JPG/PNG 格式!")
  156 + }
  157 + if (!isLt2M) {
  158 + this.$message.error("上传头像图片大小不能超过 2MB!")
  159 + }
  160 + // eslint-disable-next-line no-mixed-operators
  161 + return isLt2M && isJPG || isPNG
  162 + },
  163 + getList () {
  164 + let that = this
  165 + this.$request.fetchGetRoleList().then(function (response) {
  166 + that.roleData = response.data.rows
  167 + let userId = that.userId
  168 + if (!userId) {
  169 + return false
  170 + }
  171 + that.$request.fetchGetUserInfoId({id: userId})
  172 + .then(function (res) {
  173 + console.log(res)
  174 + res.data.password = ""
  175 + if (res.data.status === "1") {
  176 + res.data.status = true
  177 + } else {
  178 + res.data.status = false
  179 + }
  180 +
  181 + that.ruleForm2 = res.data
  182 + that.roleName = true
  183 + for (let i = 0; i < that.roleData.length; i++) {
  184 + if (that.$store.getters.info.role === "超级管理员" && that.$store.getters.info.uid !== userId) {
  185 + that.roleName = false
  186 + }
  187 + }
  188 + return false
  189 + })
  190 + .catch(function (error) {
  191 + console.log(error)
  192 + })
  193 + })
  194 + .catch(function (error) {
  195 + console.log(error)
  196 + })
  197 + },
  198 + submitForm (formName) {
  199 + let that = this
  200 + this.$refs[formName].validate((valid) => {
  201 + if (valid) {
  202 + let newData = {}
  203 + let fetchFn = this.$request.fetchEditUser
  204 + if (!that.userId) {
  205 + for (let item in that.ruleForm2) {
  206 + if (item !== "checkPass") {
  207 + newData[item] = that.ruleForm2[item]
  208 + }
  209 + }
  210 + fetchFn = this.$request.fetchRegister
  211 + } else {
  212 + for (let item in that.ruleForm2) {
  213 + if (item !== "password" && item !== "checkPass") {
  214 + newData[item] = that.ruleForm2[item]
  215 + }
  216 + }
  217 + }
  218 + fetchFn(newData).then((res) => {
  219 + that.$message({
  220 + showClose: true,
  221 + message: res.data.message,
  222 + type: "success"
  223 + })
  224 + }).catch((err) => {
  225 + console.log(err)
  226 + })
  227 + } else {
  228 + console.log("error submit!!")
  229 + return false
  230 + }
  231 + })
  232 + },
  233 + resetForm (formName) {
  234 + this.$refs[formName].resetFields()
  235 + }
  236 + },
  237 + mounted () {
  238 + // this.getList()
  239 + }
  240 +}
  241 +</script>
  242 +<style scoped>
  243 + .demo-ruleForm {
  244 + width: 460px;
  245 + padding-top: 25px;
  246 + }
  247 + .el-select {
  248 + width: 100%;
  249 + }
  250 + .card {
  251 + width: 560px;
  252 + padding-bottom: 15px;
  253 + margin: 0px auto;
  254 + }
  255 + .avatar-uploader .el-upload {
  256 + border: 1px dashed #d9d9d9;
  257 + border-radius: 6px;
  258 + cursor: pointer;
  259 + position: relative;
  260 + overflow: hidden;
  261 + }
  262 + .avatar-uploader .el-upload:hover {
  263 + border-color: #409EFF;
  264 + }
  265 + .avatar-uploader-icon {
  266 + font-size: 28px;
  267 + color: #8c939d;
  268 + width: 178px;
  269 + height: 178px;
  270 + line-height: 178px;
  271 + text-align: center;
  272 + }
  273 + .avatar {
  274 + width: 178px;
  275 + height: 178px;
  276 + display: block;
  277 + }
  278 +</style>
... ...
src/config/index.js 0 → 100644
  1 +const modeUrl = {
  2 + // 开发环境
  3 + development: {
  4 + baseURL: "/",
  5 + authBaseURL: "",
  6 + $cdn:""
  7 + },
  8 + // 生产环境
  9 + production: {
  10 + authBaseURL: "",
  11 + $cdn:""
  12 + },
  13 +};
  14 +module.exports = modeUrl[process.env.NODE_ENV]
... ...
src/directive/permission/button.js 0 → 100644
  1 +export default {
  2 + install (Vue, options) {
  3 + Vue.directive("roleBtn", {
  4 + componentUpdated: function (el, binding) {
  5 + let roleArr = binding.value
  6 + let userRole = JSON.parse(localStorage.getItem("info")).role
  7 + if (roleArr && roleArr.indexOf(userRole) !== -1) {
  8 + return false
  9 + } else {
  10 + el.parentNode.removeChild(el)
  11 + }
  12 + },
  13 + inserted: function (el, binding) {
  14 + let roleArr = binding.value
  15 + let userRole = JSON.parse(localStorage.getItem("info")).role
  16 + if (roleArr && roleArr.indexOf(userRole) !== -1) {
  17 + return false
  18 + } else {
  19 + el.parentNode.removeChild(el)
  20 + }
  21 + }
  22 + })
  23 + }
  24 +}
0 25 \ No newline at end of file
... ...
src/i18n/i18n.js 0 → 100644
  1 +import Vue from "vue"
  2 +import locale from "element-ui/lib/locale"
  3 +import VueI18n from "vue-i18n"
  4 +import messages from "./lang"
  5 +
  6 +Vue.use(VueI18n)
  7 +const i18n = new VueI18n({
  8 + locale: localStorage.lang || "cn",
  9 + messages
  10 +})
  11 +locale.i18n((key, value) => i18n.t(key, value))
  12 +
  13 +export default i18n
... ...
src/i18n/lang/cn.js 0 → 100644
  1 +
  2 +import zhLocale from "element-ui/lib/locale/lang/zh-CN"
  3 +const cn = {
  4 + routeName: {
  5 + home: "主页",
  6 + icon: "图标",
  7 + builtInIcon: "内置图标",
  8 + permissions: "权限管理",
  9 + multiDirectory: "多级目录",
  10 + "menu2-1": "二级-1",
  11 + "menu2-3": "二级-3",
  12 + "menu3-1": "三级-1",
  13 + "menu4-1": "四级-1",
  14 + },
  15 + rightMenu: {
  16 + close: "关闭",
  17 + closeOther: "关闭其他",
  18 + closeAll: "全部关闭"
  19 + },
  20 + role: {
  21 + superAdmin: "超级管理员",
  22 + admin: "管理员",
  23 + ordinary: "普通用户"
  24 + },
  25 + userDropdownMenu: {
  26 + basicInfor: "基本资料",
  27 + changePassword: "修改密码",
  28 + logout: "退出"
  29 + },
  30 +
  31 + ...zhLocale // 合并element-ui内置翻译
  32 +}
  33 +
  34 +export default cn
... ...
src/i18n/lang/en.js 0 → 100644
  1 +import enLocale from "element-ui/lib/locale/lang/en"
  2 +const en = {
  3 + routeName: {
  4 + home: "home",
  5 + icon: "icon",
  6 + builtInIcon: "builtInIcon",
  7 + permissions: "permissions",
  8 + multiDirectory: "multiDirectory",
  9 + "menu2-1": "menu2-1",
  10 + "menu2-3": "menu2-3",
  11 + "menu3-1": "menu3-1",
  12 + "menu4-1": "menu4-1",
  13 + },
  14 + rightMenu: {
  15 + close: "close",
  16 + closeOther: "closeOther",
  17 + closeAll: "closeAll"
  18 + },
  19 + role: {
  20 + superAdmin: "superAdmin",
  21 + admin: "admin",
  22 + ordinary: "ordinary"
  23 + },
  24 + userDropdownMenu: {
  25 + basicInfor: "basicInfor",
  26 + changePassword: "changePassword",
  27 + logout: "logout"
  28 + },
  29 +
  30 + ...enLocale // 合并element-ui内置翻译
  31 +}
  32 +
  33 +export default en
... ...
src/i18n/lang/index.js 0 → 100644
  1 +import en from "./en"
  2 +import cn from "./cn"
  3 +export default {
  4 + en,
  5 + cn
  6 +}
... ...
src/router/index.js 0 → 100755
  1 +import en from "../i18n/lang/en"
  2 +import Vue from "vue"
  3 +import Router from "vue-router"
  4 +import Login from "@/views/login/index"
  5 +import Layout from "@/views/layout/layout"
  6 +import HomeMain from "@/views/index/mainIndex"
  7 +
  8 +// 不是必须加载的组件使用懒加载
  9 +const NotFound = () => import("@/views/page404")
  10 +
  11 +/**
  12 + * 重写路由的push方法
  13 + */
  14 +const routerPush = Router.prototype.push
  15 +Router.prototype.push = function push (location) {
  16 + return routerPush.call(this, location).catch(error => error)
  17 +}
  18 +Vue.use(Router)
  19 +let routeName = en.routeName
  20 +let defaultRouter = [
  21 + { path: "/",
  22 + redirect: "/index",
  23 + hidden: true,
  24 + children: []
  25 + },
  26 + {
  27 + path: "/login",
  28 + component: Login,
  29 + name: "登录",
  30 + hidden: true,
  31 + children: []
  32 + },
  33 + {
  34 + path: "/index",
  35 + iconCls: "fa fa-dashboard", // 图标样式class
  36 + name: "首页",
  37 + component: Layout,
  38 + alone: true,
  39 + children: [
  40 + {
  41 + path: "/index",
  42 + iconCls: "fa fa-dashboard", // 图标样式class
  43 + name: "主页",
  44 + component: HomeMain,
  45 + children: []
  46 + }
  47 + ]
  48 + },
  49 + {
  50 + path: "/404",
  51 + component: NotFound,
  52 + name: "404",
  53 + hidden: true,
  54 + children: []
  55 + }
  56 +]
  57 +
  58 +export default new Router({
  59 + routes: defaultRouter
  60 +})
  61 +export {defaultRouter}
... ...
src/router/permission.js 0 → 100644
  1 +import NProgress from "nprogress"
  2 +import en from "../i18n/lang/en"
  3 +import Layout from "@/views/layout/layout"
  4 +import CommerViews from "@/views/commerViews"
  5 +import router from "./index"
  6 +import store from "../store"
  7 +let routeName = en.routeName
  8 +
  9 +const Erji = () => import("@/views/duoji/erji")
  10 +const Sanji = () => import("@/views/duoji/sanji")
  11 + let addrouters = [ //测试用,后续后端获取
  12 +
  13 + {
  14 + path: "/",
  15 + iconCls: "fa fa-server",
  16 + // name: routeName.multiDirectory,
  17 + name: '多级',
  18 + component: Layout,
  19 + children: [
  20 + {
  21 + path: "/erji1",
  22 + iconCls: "fa fa-server",
  23 + // name: routeName["menu2-1"],
  24 + name: '二级',
  25 + component: Erji,
  26 + meta:{
  27 + role:['admin']
  28 + },
  29 + children: []
  30 + },
  31 + {
  32 + path: "/erji3",
  33 + iconCls: "fa fa-server",
  34 + name: "三级",
  35 + component: CommerViews, // 无限极菜单的容器
  36 + children: [
  37 + {
  38 + path: "/sanji1",
  39 + iconCls: "fa fa-server",
  40 + name: "三级子页面",
  41 + component: Sanji,
  42 + children: []
  43 + },
  44 + ]
  45 + }
  46 + ]
  47 + },
  48 +
  49 + { path: "*",
  50 + redirect: "/404",
  51 + hidden: true,
  52 + children: []
  53 + }
  54 +]
  55 +
  56 +
  57 +// 获取角色信息,根据用户权限动态加载路由
  58 +router.beforeEach((to, from, next) => {
  59 + NProgress.start()
  60 + if (store.getters.token) {
  61 + if (to.path === "/login") {
  62 + next({path: "/"})
  63 + } else {
  64 + if (!store.getters.info.role) {
  65 + !(async function getAddRouters () {
  66 + // 省略 axios 请求代码 通过 token 向后台请求用户权限等信息,这里用假数据赋值
  67 + await store.dispatch("setInfo", {
  68 + role: "superAdmin",
  69 + permissions: "超级管理员",
  70 + name:"张老师",
  71 + })
  72 + await store.dispatch("newRoutes",addrouters)
  73 + let newAddRouters = store.getters.addRouters
  74 + await router.addRoutes(newAddRouters)
  75 + next({path: to.path})
  76 + }())
  77 + } else {
  78 + let is404 = to.matched.some(record => {
  79 + if (record.meta.role) {
  80 + return store.getters.info.authorityRouter === -1
  81 + }
  82 + })
  83 + if (is404) {
  84 + next({path: "/404"})
  85 + return false
  86 + }
  87 + next()
  88 + }
  89 + }
  90 + } else {
  91 + if (to.path === "/login") {
  92 + next()
  93 + }
  94 + next({path: "/login"})
  95 + }
  96 +})
  97 +
  98 +router.afterEach(() => {
  99 + NProgress.done()
  100 +})
  101 +
  102 +
  103 +// // 真实使用
  104 +// import fetchUser from "@/api/apis/user"
  105 +//
  106 +// router.beforeEach((to, from, next) => {
  107 +// if (store.getters.token && store.getters.token !== "undefined") {
  108 +// // store.dispatch('setToken', store.getters.token)
  109 +// if (to.path === "/login") {
  110 +// next({path: "/"})
  111 +// } else {
  112 +// if (!store.getters.info) {
  113 +// (async function getAddRouters () {
  114 +// fetchUser.fetchGetUserInfo().then(async function (response) {
  115 +// await store.dispatch("setInfo", response.data)
  116 + // await store.dispatch("newRoutes", store.getters.info.authorityRouter)
  117 +// await router.addRoutes(store.getters.addRouters)
  118 +// next({path: "/index"})
  119 +// }).catch(function (error) {
  120 +// console.log(error)
  121 +// })
  122 +// }())
  123 +// } else {
  124 +// let is404 = to.matched.some(record => {
  125 +// console.log(record)
  126 +// if (record.meta.role) {
  127 +// return store.getters.info.authorityRouter === -1
  128 +// }
  129 +// })
  130 +// if (is404) {
  131 +// next({path: "/404"})
  132 +// return false
  133 +// }
  134 +// next()
  135 +// }
  136 +// }
  137 +// } else {
  138 +// if (to.path === "/login") {
  139 +// next()
  140 +// }
  141 +// next({path: "/login"})
  142 +// }
  143 +// })
... ...
src/store/index.js 0 → 100644
  1 +import Vue from "vue"
  2 +import Vuex from "vuex"
  3 +import Cookies from "js-cookie"
  4 +import routerData from "./modules/routerData"
  5 +import role from "./modules/role"
  6 +import layout from "./modules/layout/index"
  7 +
  8 +Vue.use(Vuex)
  9 +
  10 +const store = new Vuex.Store({
  11 + state: {
  12 + token: Cookies.get("token")
  13 + },
  14 + mutations: {
  15 + setToken (state, token) {
  16 + state.token = token
  17 + Cookies.set("token", token, { expires: 1 / 24 })
  18 + }
  19 + },
  20 + actions: {
  21 + setToken ({commit}, token) {
  22 + return new Promise((resolve, reject) => {
  23 + commit("setToken", token)
  24 + resolve()
  25 + })
  26 + }
  27 + },
  28 + getters: {
  29 + addRouters: state => state.routerData.addRouters,
  30 + token: state => state.token,
  31 + info: state => state.role.info,
  32 + routers: state => state.routerData.routers,
  33 + logoShow: state => state.layout.logoShow,
  34 + isCollapse: state => state.layout.isCollapse,
  35 + uniquerouter: state => state.layout.uniquerouter,
  36 + tabnavBox: state => state.layout.tabnavBox,
  37 + rightNav: state => state.layout.rightNav
  38 + },
  39 + modules: {
  40 + routerData,
  41 + role,
  42 + layout
  43 + }
  44 +})
  45 +
  46 +export default store
... ...
src/store/modules/layout/index.js 0 → 100644
  1 +
  2 +export default {
  3 + state: {
  4 + isCollapse: false,
  5 + logoShow: false,
  6 + uniquerouter: true,
  7 + rightNav: {},
  8 + tabnavBox: JSON.parse(sessionStorage.getItem("addTab")) || [{
  9 + title: "home",
  10 + path: "/index"
  11 + }]
  12 + },
  13 + mutations: {
  14 + addTab (state, arg) {
  15 + state.isActive = arg.path
  16 + if (state.tabnavBox[0] && state.tabnavBox[0].title !== "home") {
  17 + state.tabnavBox.unshift({
  18 + title: "home",
  19 + path: "/index"
  20 + })
  21 + }
  22 +
  23 + for (let i = 0; i < state.tabnavBox.length; i++) {
  24 + if (state.tabnavBox[i].path === arg.path) {
  25 + return false
  26 + }
  27 + }
  28 + state.tabnavBox.push({
  29 + title: arg.title,
  30 + path: arg.path
  31 + })
  32 +
  33 + sessionStorage.setItem("addTab", JSON.stringify(state.tabnavBox))
  34 + },
  35 + openMenu (state, arg) {
  36 + state.rightNav = arg
  37 + },
  38 + removeTab (state, arg) {
  39 + let index = state.tabnavBox.findIndex(function (value, key) {
  40 + return value.path === arg.tabItem.path
  41 + })
  42 + state.tabnavBox.splice(index, 1)
  43 + if (arg.tabItem.path === arg.fullPath) {
  44 + let tabActive = state.tabnavBox[index] || state.tabnavBox[index - 1]
  45 + arg.router.push(tabActive.path)
  46 + }
  47 + sessionStorage.setItem("addTab", JSON.stringify(state.tabnavBox))
  48 + },
  49 + removeOtherTab (state, arg) {
  50 + state.tabnavBox = [{
  51 + title: "home",
  52 + path: "/index"
  53 + }]
  54 + if (arg.all) {
  55 + arg.router.push("/index")
  56 + return false
  57 + }
  58 + state.tabnavBox.push(arg.tabItem)
  59 + arg.router.push(arg.tabItem.path)
  60 + sessionStorage.setItem("addTab", JSON.stringify(state.tabnavBox))
  61 + },
  62 + collapse (state, arg) {
  63 + state.isCollapse = !state.isCollapse
  64 + if (state.logoShow) {
  65 + setTimeout(function () {
  66 + state.logoShow = false
  67 + }, 300)
  68 + } else {
  69 + state.logoShow = true
  70 + }
  71 + }
  72 + },
  73 + actions: {
  74 + addTab ({commit}, arg) {
  75 + commit("addTab", arg)
  76 + },
  77 + openMenu ({commit}, arg) {
  78 + commit("openMenu", arg)
  79 + },
  80 + removeTab ({commit}, arg) {
  81 + commit("removeTab", arg)
  82 + },
  83 + removeOtherTab ({commit}, arg) {
  84 + commit("removeOtherTab", arg)
  85 + },
  86 + collapse ({commit}, arg) {
  87 + commit("collapse", arg)
  88 + }
  89 + }
  90 +}
... ...
src/store/modules/role.js 0 → 100644
  1 +import store from "../index"
  2 +import router from "../../router/index"
  3 +export default {
  4 + state: {
  5 + info: "" // 每次刷新都要通过token请求个人信息来筛选动态路由
  6 + },
  7 + mutations: {
  8 + setInfo (state, data) {
  9 + if (data.authorityRouter) {
  10 + data.authorityRouter = data.authorityRouter.indexOf(",") !== -1 ? data.authorityRouter.split(",") : new Array(data.authorityRouter)
  11 + } else {
  12 + data.authorityRouter = []
  13 + }
  14 +
  15 +
  16 +
  17 + state.info = {
  18 + role: data.role,
  19 + name: data.name,
  20 + authorityRouter: data.authorityRouter,
  21 + avatar: data.avatar ? data.avatar : "",
  22 + uid: data.id
  23 + }
  24 + localStorage.setItem("info", JSON.stringify(store.getters.info))
  25 + }
  26 + },
  27 + actions: {
  28 + setInfo ({commit}, data) {
  29 + commit("setInfo", data)
  30 + }
  31 + }
  32 +}
... ...
src/store/modules/routerData.js 0 → 100644
  1 +import {defaultRouter} from "@/router/index"
  2 +
  3 +const routerData = {
  4 + state: {
  5 + routers: [],
  6 + addRouters: [],
  7 + },
  8 + mutations: {
  9 + setRouters: (state, routers) => {
  10 + state.addRouters = routers // 保存动态路由用来addRouter
  11 + state.routers = defaultRouter.concat(routers) // 所有有权限的路由表,用来生成菜单列表
  12 + },
  13 + },
  14 + actions: {
  15 + newRoutes ({commit},addRouter) {
  16 + // 通过递归路由表,删除掉没有权限的路由
  17 + function eachSelect (routers) {
  18 + for (let i = 0; i < routers.length; i++) {
  19 + if (routers[i].meta && routers[i].meta.role.length && routers[i].meta.role.indexOf(userRole) === -1) {
  20 + routers.splice(i, 1)
  21 + i = i !== 0 ? i - 1 : i
  22 + }
  23 + if (routers[i].children && routers[i].children.length) {
  24 + eachSelect(routers[i].children, userRole)
  25 + }
  26 + }
  27 + }
  28 + // 仅限演示
  29 + let newArr = [...addRouter] // 拷贝这个数组是因为做权限测试的时候可以从低级切回到高级角色,仅限演示,正式开发时省略这步直接使用 addRouter
  30 + // eachSelect(newArr)
  31 + commit("setRouters", newArr)
  32 +
  33 + // 正式开发
  34 + // eachSelect(addRouter, role)
  35 + // commit('setRouters', addRouter)
  36 + }
  37 + }
  38 +}
  39 +
  40 +export default routerData
... ...
src/utils/global.js 0 → 100644
  1 +import rules from "./rules"
  2 +import request from "@/api"
  3 +import * as echarts from "echarts"
  4 +
  5 +export default {
  6 + install (Vue, options) {
  7 + Vue.prototype.$echarts = echarts
  8 + Vue.prototype.$request = request
  9 + Vue.prototype.$rules = rules
  10 + /** 时间字符串
  11 + * @method $getDateDiff
  12 + * @param timespan
  13 + */
  14 + Vue.prototype.$getDateDiff = function (timespan) {
  15 + var dateTime = new Date(timespan)
  16 + var year = dateTime.getFullYear()
  17 + var month = (dateTime.getMonth() + 1) < 10 ? "0" + (dateTime.getMonth() + 1) : (dateTime.getMonth() + 1)
  18 + var day = dateTime.getDate() < 10 ? "0" + dateTime.getDate() : dateTime.getDate()
  19 + var hour = dateTime.getHours() < 10 ? "0" + dateTime.getHours() : dateTime.getHours()
  20 + var minute = dateTime.getMinutes() < 10 ? "0" + dateTime.getMinutes() : dateTime.getMinutes()
  21 + var seconds = dateTime.getSeconds() < 10 ? "0" + dateTime.getSeconds() : dateTime.getSeconds()
  22 + var now = new Date()
  23 + var nowNew = now.getTime()
  24 + var milliseconds = 0
  25 + var timeSpanStr
  26 + milliseconds = nowNew - dateTime
  27 +
  28 + if (milliseconds <= 1000 * 60 * 1) {
  29 + timeSpanStr = "刚刚"
  30 + } else if (1000 * 60 * 1 < milliseconds && milliseconds <= 1000 * 60 * 60) {
  31 + timeSpanStr = Math.round((milliseconds / (1000 * 60))) + "分钟前"
  32 + } else if (1000 * 60 * 60 * 1 < milliseconds && milliseconds <= 1000 * 60 * 60 * 24) {
  33 + timeSpanStr = Math.round(milliseconds / (1000 * 60 * 60)) + "小时前"
  34 + } else if (1000 * 60 * 60 * 24 < milliseconds && milliseconds <= 1000 * 60 * 60 * 24 * 3) {
  35 + timeSpanStr = Math.round(milliseconds / (1000 * 60 * 60 * 24)) + "天前"
  36 + } else if (milliseconds > 1000 * 60 * 60 * 24 * 3 && year === now.getFullYear()) {
  37 + timeSpanStr = month + "-" + day + " " + hour + ":" + minute + ":" + seconds
  38 + } else {
  39 + timeSpanStr = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + seconds
  40 + }
  41 + return timeSpanStr
  42 + }
  43 +
  44 + /** 当前地址ip
  45 + * @method $path
  46 + * @param {}
  47 + */
  48 + Vue.prototype.$path = process.env.API_HOST
  49 +
  50 + /** 传入路径转换成完整连接
  51 + * @method $getPath
  52 + * @param {url: 路径}
  53 + */
  54 + Vue.prototype.$getPath = function (url) {
  55 + const base = process.env.API_HOST
  56 + if (/^http/.test(url)) return url
  57 + return base + url
  58 + }
  59 + /** 是否开发模式
  60 + * @method $env
  61 + * @param {}
  62 + */
  63 + Vue.prototype.$env = process.env.NODE_ENV
  64 +
  65 + /** 导出,下载处理文件流
  66 + * @method $exportClick
  67 + * @param {res:文件流,name : 下载文件名}
  68 + */
  69 + Vue.prototype.$exportClick = function (res, name = "下载.zip") {
  70 + const content = res
  71 + const blob = new Blob([content])
  72 + const fileName = name
  73 + if ("download" in document.createElement("a")) { // 非IE下载
  74 + const elink = document.createElement("a")
  75 + elink.download = fileName
  76 + elink.style.display = "none"
  77 + elink.href = URL.createObjectURL(blob)
  78 + document.body.appendChild(elink)
  79 + elink.click()
  80 + URL.revokeObjectURL(elink.href) // 释放URL 对象
  81 + document.body.removeChild(elink)
  82 + } else { // IE10+下载
  83 + navigator.msSaveBlob(blob, fileName)
  84 + }
  85 + }
  86 +
  87 + /** 当res.code === 200 时
  88 + * @method $restBack
  89 + * @param res
  90 + * @param fn
  91 + * @param message
  92 + * @param type
  93 + */
  94 + Vue.prototype.$restBack = function (res, fn = () => {}, message, type = "success") {
  95 + if (res.code === 200) {
  96 + this.$message({
  97 + message: message || res.message,
  98 + type: type
  99 + })
  100 + fn()
  101 + }
  102 + }
  103 + }
  104 +}
... ...
src/utils/index.js 0 → 100644
  1 +
  2 +import CryptoJS from "crypto-js"
  3 +
  4 +const encryptKey = "WfJTKO9S4eLkrPz2JKrAnzdb"
  5 +const encryptIV = "D076D35C"
  6 +
  7 +// 深度复制
  8 +export function deepClone (obj) {
  9 + let result = Array.isArray(obj) ? [] : {}
  10 + for (let key in obj) {
  11 + if (obj.hasOwnProperty(key)) {
  12 + if (typeof obj[key] === "object") {
  13 + result[key] = deepClone(obj[key])
  14 + } else {
  15 + result[key] = obj[key]
  16 + }
  17 + }
  18 + }
  19 + return result
  20 +}
  21 +
  22 +// 3DES加密
  23 +export function desEncrypt (str, key = encryptKey, iv = encryptIV) {
  24 + var cryptoKey = CryptoJS.enc.Utf8.parse(key)
  25 + var cryptoIv = CryptoJS.enc.Utf8.parse(iv.substr(0, 8))
  26 + var encodeStr = CryptoJS.TripleDES.encrypt(str, cryptoKey, {
  27 + iv: cryptoIv,
  28 + mode: CryptoJS.mode.CBC,
  29 + padding: CryptoJS.pad.Pkcs7
  30 + })
  31 + return encodeStr.toString()
  32 +}
  33 +
  34 +// 3DES解密
  35 +export function desDecrypt (str, key = encryptKey, iv = encryptIV) {
  36 + var cryptoKey = CryptoJS.enc.Utf8.parse(key)
  37 + var cryptoIv = CryptoJS.enc.Utf8.parse(iv.substr(0, 8))
  38 + var decryptStr = CryptoJS.TripleDES.decrypt(str, cryptoKey, {
  39 + iv: cryptoIv,
  40 + mode: CryptoJS.mode.CBC,
  41 + padding: CryptoJS.pad.Pkcs7
  42 + })
  43 + return decryptStr.toString(CryptoJS.enc.Utf8)
  44 +}
  45 +
  46 +// 随机生成由字母+数字的字符串
  47 +export function randomWord (randomFlag, min, max) {
  48 + // randomFlag: Boolean 是否随机个数
  49 + // min 最少个数
  50 + // max 最大个数
  51 + var str = ""
  52 + var range = min
  53 + var arr = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
  54 + // 随机产生
  55 + if (randomFlag) {
  56 + range = Math.round(Math.random() * (max - min)) + min
  57 + }
  58 + var pos = ""
  59 + for (var i = 0; i < range; i++) {
  60 + pos = Math.round(Math.random() * (arr.length - 1))
  61 + str += arr[pos]
  62 + }
  63 + return str
  64 +}
  65 +
  66 +// 判断数组中是否存在相同值
  67 +export function hasRepeatValue (arr, key = null) {
  68 + if (key) arr = arr.map(d => d[key])
  69 + if (arr.length) {
  70 + let nameNum = arr.reduce((pre, cur) => {
  71 + if (cur in pre) {
  72 + pre[cur]++
  73 + } else {
  74 + pre[cur] = 1
  75 + }
  76 + return pre
  77 + }, {})
  78 + return Object.values(nameNum).findIndex(d => d > 1) < 0
  79 + }
  80 + return true
  81 +}
  82 +
  83 +// 获取cookie值
  84 +export function getCookie (name, defaultValue) {
  85 + const result = new RegExp("(^| )" + name + "=([^;]*)(;|$)")
  86 + return result[0] === document.cookie.match(result[1]) ? unescape(result[0][2]) : defaultValue
  87 +}
  88 +
  89 +// base64ToFile
  90 +export function base64ToFile (base64Data, tempfilename, contentType) {
  91 + contentType = contentType || ""
  92 + var sliceSize = 1024
  93 + var byteCharacters = atob(base64Data)
  94 + var bytesLength = byteCharacters.length
  95 + var slicesCount = Math.ceil(bytesLength / sliceSize)
  96 + var byteArrays = new Array(slicesCount)
  97 +
  98 + for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
  99 + var begin = sliceIndex * sliceSize
  100 + var end = Math.min(begin + sliceSize, bytesLength)
  101 +
  102 + var bytes = new Array(end - begin)
  103 + for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
  104 + bytes[i] = byteCharacters[offset].charCodeAt(0)
  105 + }
  106 + byteArrays[sliceIndex] = new Uint8Array(bytes)
  107 + }
  108 + var file = new File(byteArrays, tempfilename, { type: contentType })
  109 + return file
  110 +}
  111 +
  112 +// 将base64转换为文件
  113 +export function dataURLtoFile (dataurl, filename) {
  114 + var arr = dataurl.split(",")
  115 + var mime = arr[0].match(/:(.*?);/)[1]
  116 + var bstr = atob(arr[1])
  117 + var n = bstr.length
  118 + var u8arr = new Uint8Array(n)
  119 + while (n--) {
  120 + u8arr[n] = bstr.charCodeAt(n)
  121 + }
  122 + return new File([u8arr], filename, { type: mime })
  123 +}
  124 +
  125 +// 将图片转换为Base64
  126 +export function getImgToBase64 (url, callback, outputFormat) {
  127 + var canvas = document.createElement("canvas")
  128 + var ctx = canvas.getContext("2d")
  129 + var img = new Image()
  130 + img.crossOrigin = "Anonymous"
  131 + img.onload = function () {
  132 + canvas.height = img.height
  133 + canvas.width = img.width
  134 + ctx.drawImage(img, 0, 0)
  135 + var dataURL = canvas.toDataURL(outputFormat || "image/png")
  136 + callback(dataURL)
  137 + canvas = null
  138 + }
  139 + img.src = url
  140 +}
  141 +
  142 +// 转换级联下拉数据
  143 +export function loopOptions (list, option = {}) {
  144 + option = {
  145 + value: "id",
  146 + label: "name",
  147 + children: "children",
  148 + ...option
  149 + }
  150 + if (list instanceof Array && list.length) {
  151 + return list.map((d, i) => {
  152 + d.value = d[option.value] || i + 1
  153 + d.label = d[option.label]
  154 + if (d[option.children]) {
  155 + d[option.children] = loopOptions(d[option.children], option)
  156 + }
  157 + return d
  158 + })
  159 + }
  160 + return []
  161 +}
  162 +
  163 +// 通过Id获取级联数据id数组
  164 +export function getTreeIds (tree, currentId, key = "id") {
  165 + let parent = {}
  166 + let pid = {}
  167 + const loop = (list, level) => {
  168 + if (list instanceof Array && list.length) {
  169 + for (let index = 0; index < list.length; index++) {
  170 + const d = list[index]
  171 + parent[level] = d.id
  172 + if (d[key] === currentId) {
  173 + for (let idx = 1; idx <= level; idx++) {
  174 + pid[idx] = parent[idx]
  175 + }
  176 + break
  177 + } else if (d.children) {
  178 + loop(d.children, level + 1)
  179 + }
  180 + }
  181 + }
  182 + }
  183 + loop(tree, 1)
  184 + let result = []
  185 + Object.keys(pid).sort((a, b) => a - b).forEach(k => {
  186 + result.push(pid[k])
  187 + })
  188 + return result
  189 +}
  190 +
  191 +// 秒转换时分秒
  192 +export function transverterMss (result) {
  193 + var h = Math.floor(result / 3600) < 10 ? "0" + Math.floor(result / 3600) : Math.floor(result / 3600)
  194 + var m = Math.floor((result / 60 % 60)) < 10 ? "0" + Math.floor((result / 60 % 60)) : Math.floor((result / 60 % 60))
  195 + var s = Math.floor((result % 60)) < 10 ? "0" + Math.floor((result % 60)) : Math.floor((result % 60))
  196 + return h + ":" + m + ":" + s
  197 +}
  198 +
  199 +// 获取日期时间戳
  200 +export function getTime (dayNum) {
  201 + var myDate = new Date()
  202 + var lw = new Date(myDate - 1000 * 60 * 60 * 24 * dayNum)// 最后一个数字多少天前的意思
  203 + var lastY = lw.getFullYear()
  204 + var lastM = lw.getMonth() + 1
  205 + var lastD = lw.getDate()
  206 + var startdate = lastY + "-" + (lastM < 10 ? "0" + lastM : lastM) + "-" + (lastD < 10 ? "0" + lastD : lastD)
  207 + var b = startdate.split(/\D/)
  208 + var date = new Date(b[0], b[1] - 1, b[2])
  209 + var time = date.getTime()
  210 + return time
  211 +}
  212 +
  213 +// 获取几天之前日期
  214 +export function getData (dayNum) {
  215 + var myDate = new Date()
  216 + var lw = new Date(myDate - 1000 * 60 * 60 * 24 * dayNum)// 最后一个数字多少天前的意思
  217 + var lastY = lw.getFullYear()
  218 + var lastM = lw.getMonth() + 1
  219 + var lastD = lw.getDate()
  220 + var startdate = lastY + "-" + (lastM < 10 ? "0" + lastM : lastM) + "-" + (lastD < 10 ? "0" + lastD : lastD)
  221 + return startdate
  222 +}
  223 +
  224 +// 日期转换时间戳
  225 +export function getNewTime (dayNum) {
  226 + var b = dayNum.split(/\D/)
  227 + var date = new Date(b[0], b[1] - 1, b[2])
  228 + var time = date.getTime()
  229 + return time
  230 +}
... ...
src/utils/rules.js 0 → 100644
  1 +export default {
  2 + // 必选
  3 + requiredSelect: (mes = "此项必选", trigger = ["blur", "change"]) => {
  4 + return {
  5 + required: true,
  6 + message: mes,
  7 + trigger: trigger
  8 + }
  9 + },
  10 + // 固定长度
  11 + $length: (len = 1, message = `长度必须为${len}`, trigger = ["blur", "change"]) => {
  12 + return {
  13 + len: len,
  14 + message: message,
  15 + trigger: trigger
  16 + }
  17 + },
  18 + // 长度验证
  19 + len: (min = 1, max = 255, message = `长度为${min}-${max}`, trigger = ["blur", "change"]) => {
  20 + return {
  21 + min: min,
  22 + max: max,
  23 + message: message,
  24 + trigger: trigger
  25 + }
  26 + },
  27 + // 最小长度
  28 + min: (min = 1, message = `最小长度为${min}`, trigger = ["blur", "change"]) => {
  29 + return {
  30 + min: min,
  31 + message: message,
  32 + trigger: trigger
  33 + }
  34 + },
  35 + // 最大长度
  36 + max: (max = 255, message = `最大长度为${max}`, trigger = ["blur", "change"]) => {
  37 + return {
  38 + max: max,
  39 + message: message,
  40 + trigger: trigger
  41 + }
  42 + },
  43 + // 最小数
  44 + minNum: (min = 1, type = "number", message = `不能小于${min}`, trigger = ["blur", "change"]) => {
  45 + return {
  46 + type: type,
  47 + min: min,
  48 + message: message,
  49 + trigger: trigger
  50 + }
  51 + },
  52 + // 最大数
  53 + maxNum: (max = 255, type = "number", message = `不能大于${max}`, trigger = ["blur", "change"]) => {
  54 + return {
  55 + type: type,
  56 + max: max,
  57 + message: message,
  58 + trigger: trigger
  59 + }
  60 + },
  61 + // 数值范围
  62 + rangeNum: (min = 1, max = 255, message = `只能输入${min}-${max}之间的数`, trigger = ["blur", "change"]) => {
  63 + const validator = (rule, value, callback) => {
  64 + if (value === "" || (Number(value) >= min && Number(value) <= max)) {
  65 + callback()
  66 + } else {
  67 + callback(new Error(message))
  68 + }
  69 + }
  70 + return {
  71 + validator,
  72 + trigger
  73 + }
  74 + },
  75 + // 类型
  76 + type: (type = "string", message = `输入的类型必须为${type}`, trigger = ["blur", "change"]) => {
  77 + return {
  78 + type: type,
  79 + message: message,
  80 + trigger: trigger
  81 + }
  82 + },
  83 +
  84 + password: (callback) => {
  85 + return {
  86 + validator: callback,
  87 + trigger: "change"
  88 + }
  89 + },
  90 + // 邮箱
  91 + email: (message = "请输入正确的邮箱地址", trigger = ["blur", "change"]) => {
  92 + // eslint-disable-next-line no-useless-escape
  93 + const reg = /^[a-zA-Z0-9][a-zA-Z0-9 . _-]+(@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+)$/
  94 + const validator = (rule, value, callback) => {
  95 + if (value === "" || reg.test(value)) {
  96 + callback()
  97 + } else {
  98 + callback(new Error(message))
  99 + }
  100 + }
  101 + return {
  102 + validator,
  103 + trigger
  104 + }
  105 + },
  106 + // 大写字母
  107 + upperCase: (message = "请输入大写字母", trigger = ["blur", "change"]) => {
  108 + const reg = /^[A-Z]+$/
  109 + const validator = (rule, value, callback) => {
  110 + if (value === "" || reg.test(value)) {
  111 + callback()
  112 + } else {
  113 + callback(new Error(message))
  114 + }
  115 + }
  116 + return {
  117 + validator,
  118 + trigger
  119 + }
  120 + },
  121 + // 小写字母
  122 + lowerCase: (message = "请输入小写字母", trigger = ["blur", "change"]) => {
  123 + const reg = /^[A-Z]+$/
  124 + const validator = (rule, value, callback) => {
  125 + if (value === "" || reg.test(value)) {
  126 + callback()
  127 + } else {
  128 + callback(new Error(message))
  129 + }
  130 + }
  131 + return {
  132 + validator,
  133 + trigger
  134 + }
  135 + },
  136 + // 大小写字母
  137 + english: (message = "请输入英文", trigger = ["blur", "change"]) => {
  138 + const reg = /^[A-Za-z]+$/
  139 + const validator = (rule, value, callback) => {
  140 + if (value === "" || reg.test(value)) {
  141 + callback()
  142 + } else {
  143 + callback(new Error(message))
  144 + }
  145 + }
  146 + return {
  147 + validator,
  148 + trigger
  149 + }
  150 + },
  151 + // 只能输中文
  152 + chinese: (message = "请输入中文", trigger = ["blur", "change"]) => {
  153 + const reg = /^[\u4e00-\u9fa5]+$/
  154 + const validator = (rule, value, callback) => {
  155 + if (value === "" || reg.test(value)) {
  156 + callback()
  157 + } else {
  158 + callback(new Error(message))
  159 + }
  160 + }
  161 + return {
  162 + validator,
  163 + trigger
  164 + }
  165 + },
  166 + // 不能输中文
  167 + noChinese: (message = "不能包含中文", trigger = ["blur", "change"]) => {
  168 + const validator = (rule, value, callback) => {
  169 + if (value === "" || escape(value).indexOf("%u") >= 0) {
  170 + callback()
  171 + } else {
  172 + callback(new Error(message))
  173 + }
  174 + }
  175 + return {
  176 + validator,
  177 + trigger
  178 + }
  179 + },
  180 + // 中文英文数字
  181 + cnEnNum: (message = "请输入中文、大小写英文、数字", trigger = ["blur", "change"]) => {
  182 + const reg = /^[\u4e00-\u9fa5|A-Za-z0-9]+$/
  183 + const validator = (rule, value, callback) => {
  184 + if (value === "" || reg.test(value)) {
  185 + callback()
  186 + } else {
  187 + callback(new Error(message))
  188 + }
  189 + }
  190 + return {
  191 + validator,
  192 + trigger
  193 + }
  194 + },
  195 + // 路径
  196 + path: (message = "请输入英文、数字、划线、斜线", trigger = ["blur", "change"]) => {
  197 + const reg = /^[-|_|/|A-Za-z0-9]+$/
  198 + const validator = (rule, value, callback) => {
  199 + if (value === "" || reg.test(value)) {
  200 + callback()
  201 + } else {
  202 + callback(new Error(message))
  203 + }
  204 + }
  205 + return {
  206 + validator,
  207 + trigger
  208 + }
  209 + },
  210 + // 英文数字下划线
  211 + enNumLine: (message = "请输入英文、数字、下划线", trigger = ["blur", "change"]) => {
  212 + const reg = /^[_|A-Za-z0-9]+$/
  213 + const validator = (rule, value, callback) => {
  214 + if (value === "" || reg.test(value)) {
  215 + callback()
  216 + } else {
  217 + callback(new Error(message))
  218 + }
  219 + }
  220 + return {
  221 + validator,
  222 + trigger
  223 + }
  224 + },
  225 + // 英文数字
  226 + enNum: (message = "请输入英文、数字", trigger = ["blur", "change"]) => {
  227 + const reg = /^[A-Za-z0-9]+$/
  228 + const validator = (rule, value, callback) => {
  229 + if (value === "" || reg.test(value)) {
  230 + callback()
  231 + } else {
  232 + callback(new Error(message))
  233 + }
  234 + }
  235 + return {
  236 + validator,
  237 + trigger
  238 + }
  239 + },
  240 + // 必须包含英文数字
  241 + haveCnEnNum: (message = "必须包含英文和数字", trigger = ["blur", "change"]) => {
  242 + const reg = /^(?=.*[a-zA-Z]+)(?=.*[0-9]+)[a-zA-Z0-9]+$/
  243 + const validator = (rule, value, callback) => {
  244 + if (value === "" || reg.test(value)) {
  245 + callback()
  246 + } else {
  247 + callback(new Error(message))
  248 + }
  249 + }
  250 + return {
  251 + validator,
  252 + trigger
  253 + }
  254 + },
  255 + // 手机号
  256 + mobile: (message = "请输入正确的手机号码", trigger = ["blur", "change"]) => { // 手机号码验证
  257 + const reg = /^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(16[2|6|7])|(18[0-9])|(17([0-1]|[3]|[5-8]))|(19[1|8|9]))\d{8}$/
  258 + const validator = (rule, value, callback) => {
  259 + if (value === "" || reg.test(value)) {
  260 + callback()
  261 + } else {
  262 + callback(new Error(message))
  263 + }
  264 + }
  265 + return {
  266 + validator,
  267 + trigger
  268 + }
  269 + },
  270 + // 座机
  271 + phone: (message = "请输入正确的座机号码", trigger = ["blur", "change"]) => {
  272 + const reg = /^((\d{3,4}-\d{7,8})|(\d{7}-\d{1,12})|(\d{8}-\d{1,11})|(\d{11}-\d{1,8})|(\d{7,8})|(\d{11,20})|(\d{3}-\d{8}-\d{1,7})|(\d{3}-\d{7}-\d{1,8})|(\d{4}-\d{7}-\d{1,7})|(\d{4}-\d{8}-\d{1,6}))$/
  273 + const validator = (rule, value, callback) => {
  274 + if (value === "" || reg.test(value)) {
  275 + callback()
  276 + } else {
  277 + callback(new Error(message))
  278 + }
  279 + }
  280 + return {
  281 + validator,
  282 + trigger
  283 + }
  284 + },
  285 + // 手机或座机
  286 + phoneAll: (message = "请输入正确的电话号码", trigger = ["blur", "change"]) => {
  287 + const reg = /^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(16[2|6|7])|(18[0-9])|(17([0-1]|[3]|[5-8]))|(19[1|8|9]))\d{8}$|^((\d{3,4}-\d{7,8})|(\d{7}-\d{1,12})|(\d{8}-\d{1,11})|(\d{11}-\d{1,8})|(\d{7,8})|(\d{11,20})|(\d{3}-\d{8}-\d{1,7})|(\d{3}-\d{7}-\d{1,8})|(\d{4}-\d{7}-\d{1,7})|(\d{4}-\d{8}-\d{1,6}))$/
  288 + const validator = (rule, value, callback) => {
  289 + if (value === "" || reg.test(value)) {
  290 + callback()
  291 + } else {
  292 + callback(new Error(message))
  293 + }
  294 + }
  295 + return {
  296 + validator,
  297 + trigger
  298 + }
  299 + },
  300 + // 小数
  301 + float: (message = "请输入合法的数字", trigger = ["blur", "change"]) => {
  302 + const reg = /^[0-9]+([.]{1}[0-9]+){0,1}$/
  303 + const va = (rule, value, callback) => {
  304 + if (value === null || value === "") {
  305 + callback()
  306 + }
  307 + if (reg.test(value)) {
  308 + callback()
  309 + } else {
  310 + callback(new Error(message))
  311 + }
  312 + }
  313 + return {
  314 + validator: va,
  315 + trigger: trigger
  316 + }
  317 + },
  318 + // 两位小数
  319 + decimal: (message = "请输入最多两位小数", trigger = ["blur", "change"]) => {
  320 + const reg = /^(([0-9]*)|(([0]\.\d{1,2}|[0-9]*\.\d{1,2})))$/
  321 + const va = (rule, value, callback) => {
  322 + if (value === null || value === "") {
  323 + callback()
  324 + }
  325 + if (reg.test(value)) {
  326 + callback()
  327 + } else {
  328 + callback(new Error(message))
  329 + }
  330 + }
  331 + return {
  332 + validator: va,
  333 + trigger: trigger
  334 + }
  335 + },
  336 + // 整数
  337 + number: (message = "请输入合法的数字", trigger = ["blur", "change"]) => {
  338 + const reg = /^[0-9]\d*$/
  339 + const va = (rule, value, callback) => {
  340 + if (value === "") {
  341 + callback()
  342 + } else if (reg.test(value)) {
  343 + callback()
  344 + } else {
  345 + callback(new Error(message))
  346 + }
  347 + }
  348 + return {
  349 + validator: va,
  350 + trigger: trigger
  351 + }
  352 + },
  353 + // 正数
  354 + plusNumber: (message = "请输入合法的数字", trigger = ["blur", "change"]) => {
  355 + const reg = /^(0|[1-9][0-9]*)(\.\d+)?$/
  356 + const va = (rule, value, callback) => {
  357 + if (value === "") {
  358 + callback()
  359 + } else if (reg.test(value)) {
  360 + callback()
  361 + } else {
  362 + callback(new Error(message))
  363 + }
  364 + }
  365 + return {
  366 + validator: va,
  367 + trigger: trigger
  368 + }
  369 + },
  370 + // 大于0的正数
  371 + moreThanZeroNumber: (message = "只能输入大于0的整数", trigger = ["blur", "change"]) => {
  372 + const reg = /^\+?[1-9]\d*$/
  373 + const va = (rule, value, callback) => {
  374 + if (value === "") {
  375 + callback()
  376 + } else if (reg.test(value)) {
  377 + callback()
  378 + } else {
  379 + callback(new Error(message))
  380 + }
  381 + }
  382 + return {
  383 + validator: va,
  384 + trigger: trigger
  385 + }
  386 + },
  387 + // 网址
  388 + url: (message = "请输入正确的网址", trigger = ["blur", "change"]) => {
  389 + const reg = /^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$/
  390 + const va = (rule, value, callback) => {
  391 + if (value === "") {
  392 + callback()
  393 + }
  394 + if (reg.test(value)) {
  395 + callback()
  396 + } else {
  397 + callback(new Error(message))
  398 + }
  399 + }
  400 + return {
  401 + validator: va,
  402 + trigger: trigger
  403 + }
  404 + },
  405 + // ip地址
  406 + ip: (message = "请输入正确的ip", trigger = ["blur", "change"]) => {
  407 + const reg = /^(?=(\b|\D))(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))(?=(\b|\D))*$/
  408 + const va = (rule, value, callback) => {
  409 + if (value === "") {
  410 + callback()
  411 + }
  412 + if (reg.test(value)) {
  413 + callback()
  414 + } else {
  415 + callback(new Error(message))
  416 + }
  417 + }
  418 + return {
  419 + validator: va,
  420 + trigger: trigger
  421 + }
  422 + },
  423 + // 身份证号
  424 + identity: (message = "请输入正确的身份证号", trigger = ["blur", "change"]) => {
  425 + const reg = /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X|x)$/
  426 + // ^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$
  427 + const va = (rule, value, callback) => {
  428 + if (value === "") {
  429 + callback()
  430 + }
  431 + if (reg.test(value)) {
  432 + callback()
  433 + } else {
  434 + callback(new Error(message))
  435 + }
  436 + }
  437 + return {
  438 + validator: va,
  439 + trigger: trigger
  440 + }
  441 + },
  442 + // 邮政编码
  443 + postal: (message = "请输入正确的邮政编码", trigger = ["blur", "change"]) => {
  444 + const reg = /^\d{6}$/
  445 + const va = (rule, value, callback) => {
  446 + if (value === "") {
  447 + callback()
  448 + }
  449 + if (reg.test(value)) {
  450 + callback()
  451 + } else {
  452 + callback(new Error(message))
  453 + }
  454 + }
  455 + return {
  456 + validator: va,
  457 + trigger: trigger
  458 + }
  459 + }
  460 +}
  461 +
... ...
src/views/commerViews.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <router-view></router-view>
  4 + </div>
  5 +</template>
  6 +
  7 +<script>
  8 +export default {
  9 + name: "commerViews"
  10 +}
  11 +</script>
  12 +
  13 +<style scoped>
  14 +
  15 +</style>
... ...
src/views/duoji/erji.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + 无限级菜单测试======二级页面
  4 + </div>
  5 +</template>
  6 +
  7 +<script>
  8 +export default {
  9 + name: "erji"
  10 +}
  11 +</script>
  12 +
  13 +<style scoped>
  14 +
  15 +</style>
... ...
src/views/duoji/sanji.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + 无限级菜单测试======三级页面
  4 + </div>
  5 +</template>
  6 +
  7 +<script>
  8 +export default {
  9 + name: "sanji"
  10 +}
  11 +</script>
  12 +
  13 +<style scoped>
  14 +
  15 +</style>
... ...
src/views/duoji/siji.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + 无限级菜单测试======四级页面
  4 + </div>
  5 +</template>
  6 +
  7 +<script>
  8 +export default {
  9 + name: "siji"
  10 +}
  11 +</script>
  12 +
  13 +<style scoped>
  14 +
  15 +</style>
... ...
src/views/index/mainIndex.vue 0 → 100644
  1 +<template>
  2 + <el-row>
  3 + <el-col :span="24">
  4 + <line-echarts
  5 + id="lineEcharts"
  6 + height="300px"
  7 + ref="echarts"
  8 + ></line-echarts>
  9 + </el-col>
  10 + </el-row>
  11 +</template>
  12 +
  13 +<script>
  14 +import LineEcharts from "components/ECharts/lineEcharts";
  15 +export default {
  16 + name: "mainIndex",
  17 + components: { LineEcharts },
  18 + mounted() {
  19 + this.selfAdaption();
  20 + },
  21 + methods: {
  22 + // echart自适应
  23 + selfAdaption() {
  24 + let that = this;
  25 + setTimeout(() => {
  26 + window.onresize = function () {
  27 + if (that.$refs.echarts) {
  28 + that.$refs.echarts.chart.resize();
  29 + }
  30 + };
  31 + }, 10);
  32 + },
  33 + },
  34 +};
  35 +</script>
  36 +
  37 +<style lang="scss">
  38 +$top: top;
  39 +$bottom: bottom;
  40 +$left: left;
  41 +$right: right;
  42 +$leftright: ($left, $right);
  43 +$pinkk: #eec2d3;
  44 +$bluee: #409eff;
  45 +$yelloww: #f4d884;
  46 +$grennn: #89dda0;
  47 +$purplee: #78a2ea;
  48 +$lightBluee: #b8d6ff;
  49 +
  50 +$list: bluee pinkk yelloww grennn purplee lightBluee;
  51 +$list1: $bluee $pinkk $yelloww $grennn $purplee $lightBluee;
  52 +%shadow {
  53 + background: #fff;
  54 + -webkit-box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.2);
  55 + box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.2);
  56 + border-color: rgba(0, 0, 0, 0.2);
  57 + .title {
  58 + font-size: 14px;
  59 + padding: 10px;
  60 + i {
  61 + margin-#{$right}: 5px;
  62 + }
  63 + }
  64 +}
  65 +
  66 +@mixin flex($direction: row, $content: space-between) {
  67 + display: flex;
  68 + flex-direction: $direction;
  69 + justify-content: $content;
  70 +}
  71 +.card {
  72 + color: #666;
  73 + @extend %shadow;
  74 +
  75 + ul {
  76 + @include flex;
  77 + li {
  78 + flex: 1;
  79 + a {
  80 + color: #666666;
  81 + align-items: center;
  82 + padding-#{$top}: 20px;
  83 + padding-#{$bottom}: 20px;
  84 + @include flex(column);
  85 + span {
  86 + height: 44px;
  87 + }
  88 + .num {
  89 + line-height: 44px;
  90 + font-size: 42px;
  91 + color: $bluee;
  92 + margin: 0px;
  93 + }
  94 + }
  95 + .kjfs-bluee {
  96 + color: $bluee;
  97 + }
  98 + .kjfs-pinkk {
  99 + color: $pinkk;
  100 + }
  101 + .kjfs-yelloww {
  102 + color: $yelloww;
  103 + }
  104 + .kjfs-grennn {
  105 + color: $grennn;
  106 + }
  107 + .kjfs-purplee {
  108 + color: $purplee;
  109 + }
  110 + .kjfs-lightBluee {
  111 + color: $lightBluee;
  112 + }
  113 + &:hover {
  114 + .kjfs-bluee {
  115 + color: #ffffff;
  116 + background: $bluee;
  117 + }
  118 + .kjfs-pinkk {
  119 + color: #ffffff;
  120 + background: $pinkk;
  121 + }
  122 + .kjfs-yelloww {
  123 + color: #ffffff;
  124 + background: $yelloww;
  125 + }
  126 + .kjfs-grennn {
  127 + color: #ffffff;
  128 + background: $grennn;
  129 + }
  130 + .kjfs-purplee {
  131 + color: #ffffff;
  132 + background: $purplee;
  133 + }
  134 + .kjfs-lightBluee {
  135 + color: #ffffff;
  136 + background: $lightBluee;
  137 + }
  138 + }
  139 + }
  140 + }
  141 +}
  142 +#lineEcharts {
  143 + margin-#{$top}: 30px;
  144 + padding-#{$top}: 30px;
  145 + @extend %shadow;
  146 +}
  147 +</style>
... ...
src/views/layout/Footer/bottom.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <p>Copyright © 2022 guoke</p>
  4 + </div>
  5 +</template>
  6 +
  7 +<script>
  8 +export default {
  9 + name: "bottom"
  10 +}
  11 +</script>
  12 +
  13 +<style scoped>
  14 +p{
  15 + height: 28px;
  16 + line-height: 28px;
  17 + text-align: left;
  18 + font-size: 12px;
  19 + color: #999999;
  20 +}
  21 +</style>
... ...
src/views/layout/aside/aside.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <el-aside id="asideNav">
  4 + <div class="logo-name">
  5 + <p v-if="$store.getters.logoShow">XU</p>
  6 + <p v-else>admin</p>
  7 + </div>
  8 + <el-menu
  9 + :default-active="$route.path"
  10 + class="el-menu-vertical"
  11 + @select="selectmenu"
  12 + :collapse="$store.getters.isCollapse"
  13 + background-color="#03152A"
  14 + text-color="rgba(255,255,255,.7)"
  15 + active-text-color="#ffffff"
  16 + :router="$store.getters.uniquerouter"
  17 + :unique-opened="$store.getters.uniquerouter"
  18 + :collapse-transition="true"
  19 + >
  20 + <template v-for="(item, index) in $store.getters.routers">
  21 + <template v-if="!item.hidden">
  22 + <el-submenu
  23 + v-if="!item.alone && item.children.length > 0"
  24 + :index="index + ''"
  25 + :key="index"
  26 + >
  27 + <template slot="title">
  28 + <i :class="item.iconCls ? item.iconCls : [fa, fa - server]"></i>
  29 + <span slot="title">{{item.name}}</span>
  30 + </template>
  31 +
  32 + <menu-tree :menuData="item.children"></menu-tree>
  33 + </el-submenu>
  34 + <el-menu-item :index="item.path" v-else :key="item.path">
  35 + <i :class="item.iconCls ? item.iconCls : [fa, fa - file]" />
  36 + <span slot="title">{{ item.name }}</span>
  37 + </el-menu-item>
  38 + </template>
  39 + </template>
  40 + </el-menu>
  41 + </el-aside>
  42 + </div>
  43 +</template>
  44 +
  45 +<script>
  46 +import menuTree from "./menuTree";
  47 +
  48 +export default {
  49 + name: "asideNav",
  50 + components: {
  51 + menuTree,
  52 + },
  53 + watch: {
  54 + // 监听浏览器直接输入路由,将此路由添加到tabnavBox
  55 + "$route.path": function (val) {
  56 + this.selectmenu(val);
  57 + },
  58 + },
  59 + methods: {
  60 + selectmenu(key, indexpath) {
  61 + // 如果不使用 elemenUI 菜单的 vue-router 的模式将用以下方式进行页面跳转 el-menu的router设置为false
  62 + // this.$router.push(indexpath.join("/"))
  63 + let router = this.$store.getters.routers;
  64 + let name = "";
  65 + let navTitle = function (path, routerARR) {
  66 + for (let i = 0; i < routerARR.length; i++) {
  67 + if (routerARR[i].children.length > 0 || routerARR[i].path === path) {
  68 + if (
  69 + routerARR[i].path === path &&
  70 + routerARR[i].children.length < 1
  71 + ) {
  72 + name = routerARR[i].name;
  73 + break;
  74 + }
  75 + navTitle(path, routerARR[i].children);
  76 + }
  77 + }
  78 + return name;
  79 + };
  80 + this.$store.dispatch("addTab", {
  81 + title: navTitle(key, router),
  82 + path: key,
  83 + });
  84 + },
  85 + },
  86 +};
  87 +</script>
  88 +
  89 +<style lang="scss">
  90 +$top: top;
  91 +$bottom: bottom;
  92 +$left: left;
  93 +$right: right;
  94 +%w100 {
  95 + width: 100%;
  96 +}
  97 +
  98 +%h100 {
  99 + height: 100%;
  100 +}
  101 +
  102 +%cursor {
  103 + cursor: pointer;
  104 +}
  105 +
  106 +@mixin set-value($side, $value) {
  107 + @each $prop in $leftright {
  108 + #{$side}-#{$prop}: $value;
  109 + }
  110 +}
  111 +
  112 +#asideNav {
  113 + width: auto !important;
  114 + display: flex;
  115 + flex-direction: column;
  116 + border-right: solid 1px #e6e6e6;
  117 + .logo-name {
  118 + background-color: #03152a !important;
  119 + @extend %w100;
  120 + font-weight: 300;
  121 + z-index: 999;
  122 + p {
  123 + height: 50px;
  124 + line-height: 50px;
  125 + text-align: center;
  126 + font-size: 16px;
  127 + color: #5e6d82;
  128 + }
  129 + }
  130 + .el-menu-vertical:not(.el-menu--collapse) {
  131 + width: 200px;
  132 + @extend %h100;
  133 + overflow-y: scroll;
  134 + overflow-x: hidden;
  135 + }
  136 + .el-menu {
  137 + flex: 1;
  138 + overflow: inherit;
  139 + border-right: none;
  140 + &::-webkit-scrollbar {
  141 + display: none;
  142 + }
  143 + .fa {
  144 + vertical-align: middle;
  145 + margin-right: 5px;
  146 + width: 24px;
  147 + text-align: center;
  148 + font-size: 18px;
  149 + }
  150 + .el-menu-item {
  151 + background-color: #020f1d !important;
  152 + border-bottom: 1px solid #020f1d;
  153 + &:hover {
  154 + color: #ffffff !important;
  155 + background-color: #375573 !important;
  156 + }
  157 + }
  158 + .el-menu-item.is-active {
  159 + background-color: #56a9ff !important;
  160 + }
  161 + .is-opened > .el-submenu__title > .el-icon-arrow-down {
  162 + color: #ffffff;
  163 + font-weight: 500;
  164 + font-size: 18px;
  165 + }
  166 + }
  167 +}
  168 +</style>
... ...
src/views/layout/aside/menuTree.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <template v-for="(child) in menuData">
  4 + <el-submenu v-if="child.children.length > 0" :index="child.path" :key="child.path">
  5 + <template slot="title">
  6 + <i :class="child.iconCls?child.iconCls:[fa,fa-file]"/>
  7 + <span slot="title">{{ child.name }}</span>
  8 + </template>
  9 + <menu-tree :menuData="child.children"/>
  10 + </el-submenu>
  11 +
  12 + <el-menu-item v-else-if="!child.hidden" :index="child.path" :key="child.path">
  13 + <i :class="child.iconCls?child.iconCls:[fa,fa-file]"/>
  14 + <span slot="title">{{ child.name}}</span>
  15 + </el-menu-item>
  16 + </template>
  17 + </div>
  18 +</template>
  19 +
  20 +<script>
  21 +export default {
  22 + name: "menuTree",
  23 + props: ["menuData"]
  24 +}
  25 +</script>
  26 +
  27 +<style scoped>
  28 +
  29 +</style>
... ...
src/views/layout/header/header.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <el-header id="header">
  4 + <span class="hideAside" @click="collapse"
  5 + ><i class="fa fa-indent fa-lg"></i
  6 + ></span>
  7 + <ul class="personal">
  8 + <li class="fullScreen" @click="fullScreen">
  9 + <el-tooltip
  10 + class="item"
  11 + effect="dark"
  12 + content="全屏"
  13 + placement="bottom"
  14 + ><i class="fa fa-arrows-alt fa-lg"></i
  15 + ></el-tooltip>
  16 + </li>
  17 + <!-- <li>
  18 + <langSelect></langSelect>
  19 + </li> -->
  20 + <li>{{ $t(`role.${this.$store.getters.info.role}`) }}</li>
  21 + <li>
  22 + <el-dropdown @command="handleCommand">
  23 + <span class="el-dropdown-link">
  24 + {{ $t(`${this.$store.getters.info.name}`) }}<i class="el-icon-arrow-down el-icon--right"></i>
  25 + </span>
  26 + <el-dropdown-menu slot="dropdown">
  27 + <el-dropdown-item command="info">{{
  28 + $t("userDropdownMenu.basicInfor")
  29 + }}</el-dropdown-item>
  30 + <el-dropdown-item command="editPassword">{{
  31 + $t("userDropdownMenu.changePassword")
  32 + }}</el-dropdown-item>
  33 + <el-dropdown-item command="logout" divided>{{
  34 + $t("userDropdownMenu.logout")
  35 + }}</el-dropdown-item>
  36 + </el-dropdown-menu>
  37 + </el-dropdown>
  38 + </li>
  39 + <li class="icon"><img :src="avatar" /></li>
  40 + </ul>
  41 + </el-header>
  42 + <!-- <tabNav></tabNav> -->
  43 + </div>
  44 +</template>
  45 +
  46 +<script>
  47 +import Cookies from "js-cookie";
  48 +import langSelect from "../../../components/lang/langSelect";
  49 +import tabNav from "./tabNav";
  50 +import UserInfo from "../../../components/userForm/userInfo";
  51 +import EditPassword from "../../../components/userForm/editPassword";
  52 +
  53 +export default {
  54 + name: "Header",
  55 + components: { EditPassword, tabNav, langSelect, UserInfo },
  56 + data() {
  57 + return {
  58 + isfullScreen: true,
  59 + avatar: require("@/assets/images/womandefault.png"),
  60 + dialogInfoVisible: false,
  61 + dialogPassVisible: false,
  62 + title: "",
  63 + userId: "",
  64 + };
  65 + },
  66 + methods: {
  67 + collapse() {
  68 + this.$store.dispatch("collapse");
  69 + },
  70 + successCallback() {
  71 + this.dialogInfoVisible = false;
  72 + },
  73 + editPwdCallback() {
  74 + this.dialogPassVisible = false;
  75 + },
  76 + fullScreen() {
  77 + if (this.isfullScreen) {
  78 + var docElm = document.documentElement;
  79 + if (docElm.requestFullscreen) {
  80 + docElm.requestFullscreen();
  81 + } else if (docElm.mozRequestFullScreen) {
  82 + docElm.mozRequestFullScreen();
  83 + } else if (docElm.webkitRequestFullScreen) {
  84 + docElm.webkitRequestFullScreen();
  85 + } else if (elem.msRequestFullscreen) {
  86 + elem.msRequestFullscreen();
  87 + }
  88 + this.isfullScreen = false;
  89 + } else {
  90 + if (document.exitFullscreen) {
  91 + document.exitFullscreen();
  92 + } else if (document.mozCancelFullScreen) {
  93 + document.mozCancelFullScreen();
  94 + } else if (document.webkitCancelFullScreen) {
  95 + document.webkitCancelFullScreen();
  96 + } else if (document.msExitFullscreen) {
  97 + document.msExitFullscreen();
  98 + }
  99 + this.isfullScreen = true;
  100 + }
  101 + },
  102 + handleCommand(command) {
  103 + if (command === "info") {
  104 + this.dialogInfoVisible = true;
  105 + this.title = "编辑信息";
  106 + // this.userId = this.$store.getters.info.uid
  107 + } else if (command === "editPassword") {
  108 + this.dialogPassVisible = true;
  109 + } else if (command === "logout") {
  110 + Cookies.remove("token");
  111 + location.reload();
  112 + }
  113 + },
  114 + },
  115 +};
  116 +</script>
  117 +
  118 +<style lang="scss">
  119 +$top: top;
  120 +$bottom: bottom;
  121 +$left: left;
  122 +$right: right;
  123 +$leftright: ($left, $right);
  124 +%w100 {
  125 + width: 100%;
  126 +}
  127 +
  128 +%h100 {
  129 + height: 100%;
  130 +}
  131 +
  132 +%cursor {
  133 + cursor: pointer;
  134 +}
  135 +
  136 +html,
  137 +body,
  138 +#app,
  139 +.el-container,
  140 +#asideNav,
  141 +ul.el-menu {
  142 + @extend %h100;
  143 +}
  144 +
  145 +@mixin set-value($side, $value) {
  146 + @each $prop in $leftright {
  147 + #{$side}-#{$prop}: $value;
  148 + }
  149 +}
  150 +
  151 +#header {
  152 + max-height: 50px;
  153 + line-height: 50px;
  154 + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
  155 + display: flex;
  156 + justify-content: space-between;
  157 + .hideAside {
  158 + @extend %cursor;
  159 + }
  160 + .personal {
  161 + display: flex;
  162 + flex-direction: row;
  163 + li {
  164 + @include set-value(margin, 13px);
  165 + font-size: 12px;
  166 + }
  167 + .fullScreen {
  168 + @extend %cursor;
  169 + }
  170 + .el-dropdown-link {
  171 + @extend %cursor;
  172 + }
  173 + .icon img {
  174 + margin-#{$top}: 7px;
  175 + -webkit-border-radius: 5px;
  176 + -moz-border-radius: 5px;
  177 + border-radius: 5px;
  178 + width: 40px;
  179 + height: 40px;
  180 + }
  181 + }
  182 +}
  183 +</style>
... ...
src/views/layout/header/tabNav.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <div class="tabnavBox">
  4 + <transition-group name="list" tag="ul">
  5 + <li
  6 + v-for="(item, index) in $store.getters.tabnavBox"
  7 + :key="item.path"
  8 + @contextmenu.prevent="openMenu(item, $event, index)"
  9 + class="tabnav"
  10 + :class="{ active: $route.path === item.path }"
  11 + >
  12 + <router-link :to="item.path">{{
  13 + $t(`routeName.${item.title}`)
  14 + }}</router-link>
  15 + <i
  16 + @click="removeTab(item)"
  17 + class="el-icon-error"
  18 + v-if="index !== 0"
  19 + ></i>
  20 + </li>
  21 + </transition-group>
  22 + </div>
  23 + <ul
  24 + v-show="this.rightMenuShow"
  25 + :style="{ left: this.left + 'px', top: this.top + 'px' }"
  26 + class="menuBox"
  27 + >
  28 + <li @click="removeTab($store.getters.rightNav)">
  29 + <i class="fa fa-remove"></i>{{ $t("rightMenu.close") }}
  30 + </li>
  31 + <li @click="removeOtherTab($store.getters.rightNav)">
  32 + {{ $t("rightMenu.closeOther") }}
  33 + </li>
  34 + <li @click="removeAllTab">{{ $t("rightMenu.closeAll") }}</li>
  35 + </ul>
  36 + </div>
  37 +</template>
  38 +
  39 +<script>
  40 +export default {
  41 + name: "tabNav",
  42 + data() {
  43 + return {
  44 + rightMenuShow: false,
  45 + left: 0,
  46 + top: 0,
  47 + };
  48 + },
  49 + methods: {
  50 + openMenu(item, e, index) {
  51 + if (index === 0) {
  52 + return false;
  53 + }
  54 + this.rightMenuShow = true;
  55 + this.left = e.clientX + 10;
  56 + this.top = e.clientY;
  57 + this.$store.dispatch("openMenu", item);
  58 + },
  59 + removeTab(tabItem) {
  60 + this.$store.dispatch("removeTab", {
  61 + tabItem,
  62 + fullPath: this.$route.fullPath,
  63 + router: this.$router,
  64 + });
  65 + },
  66 + removeOtherTab(tabItem) {
  67 + this.$store.dispatch("removeOtherTab", { tabItem, router: this.$router });
  68 + },
  69 + removeAllTab() {
  70 + this.$store.dispatch("removeOtherTab", {
  71 + all: true,
  72 + router: this.$router,
  73 + });
  74 + },
  75 + },
  76 + watch: {
  77 + rightMenuShow(value) {
  78 + if (value) {
  79 + document.body.addEventListener("click", () => {
  80 + this.rightMenuShow = false;
  81 + });
  82 + } else {
  83 + document.body.removeEventListener("click", () => {
  84 + this.rightMenuShow = false;
  85 + });
  86 + }
  87 + },
  88 + },
  89 +};
  90 +</script>
  91 +<style>
  92 +.tabnav {
  93 + display: inline-block;
  94 + transition: all 0.5s;
  95 +}
  96 +
  97 +.list-enter,
  98 +.list-leave-to {
  99 + opacity: 0;
  100 + transform: translateY(30px);
  101 +}
  102 +
  103 +.list-enter-active {
  104 + transition: all 0.5s;
  105 +}
  106 +
  107 +.list-leave-active {
  108 + position: absolute;
  109 + transition: all 1s;
  110 +}
  111 +</style>
  112 +<style lang="scss">
  113 +$top: top;
  114 +$bottom: bottom;
  115 +$left: left;
  116 +$right: right;
  117 +$leftright: ($left, $right);
  118 +
  119 +%w100 {
  120 + width: 100%;
  121 +}
  122 +
  123 +%h100 {
  124 + height: 100%;
  125 +}
  126 +
  127 +%cursor {
  128 + cursor: pointer;
  129 +}
  130 +
  131 +@mixin set-value($side, $value) {
  132 + @each $prop in $leftright {
  133 + #{$side}-#{$prop}: $value;
  134 + }
  135 +}
  136 +
  137 +.tabnavBox {
  138 + @extend %w100;
  139 + height: 36px;
  140 + min-height: 36px;
  141 + overflow: hidden;
  142 + border-#{$top}: 1px solid #f6f6f6;
  143 + border-#{$bottom}: 1px solid #d8dce5;
  144 +
  145 + -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12),
  146 + 0 0 3px 0 rgba(0, 0, 0, 0.04);
  147 + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
  148 + ul {
  149 + display: flex;
  150 + justify-content: flex-start;
  151 + padding-#{$left}: 20px;
  152 + flex-wrap: nowrap;
  153 + overflow-x: auto;
  154 + li {
  155 + line-height: 22px;
  156 + display: flex;
  157 + align-items: center;
  158 + @extend %cursor;
  159 + margin-#{$top}: 6px;
  160 + margin-#{$right}: 5px;
  161 + border:1px solid #e2e2e2;
  162 + box-sizing: border-box;
  163 + overflow: hidden;
  164 + &:not(:first-child) {
  165 + padding-#{$right}: 10px;
  166 + min-width: 80px;
  167 + }
  168 + a {
  169 + @include set-value(padding, 10px);
  170 + display: inline-block;
  171 + @extend %h100;
  172 + font-size: 12px;
  173 + color: #999999;
  174 + }
  175 + &:nth-child(n + 2) {
  176 + a {
  177 + padding-#{$right}: 15px;
  178 + }
  179 + }
  180 + i {
  181 + @extend %cursor;
  182 + &:hover {
  183 + color: red;
  184 + }
  185 + }
  186 + }
  187 + li.active {
  188 + line-height: 24px;
  189 + background: #409eff;
  190 + color: #ffffff;
  191 + border:none;
  192 + a {
  193 + color: #ffffff;
  194 + }
  195 + }
  196 + }
  197 +}
  198 +
  199 +.menuBox {
  200 + margin: 0;
  201 + background: #fff;
  202 + z-index: 999;
  203 + position: absolute;
  204 + padding: 5px 0;
  205 + border: 1px solid #cccccc;
  206 + font-size: 12px;
  207 + font-weight: 400;
  208 + color: #333;
  209 + box-shadow: 2px 1px 6px 0 rgba(0, 0, 0, 0.3);
  210 + li {
  211 + margin: 0;
  212 + padding: 7px 16px;
  213 + @extend %cursor;
  214 + &:hover {
  215 + background: #e1e6ea;
  216 + }
  217 + }
  218 +}
  219 +</style>
... ...
src/views/layout/layout.vue 0 → 100644
  1 +<template>
  2 + <div id="loyout">
  3 + <el-container>
  4 + <layoutAside></layoutAside>
  5 + <el-container>
  6 + <layoutHeader></layoutHeader>
  7 + <el-main id="elmain">
  8 + <transition name="main" mode="out-in">
  9 + <router-view></router-view>
  10 + </transition>
  11 + </el-main>
  12 + <el-footer height="28px">
  13 + <Bottom></Bottom>
  14 + </el-footer>
  15 + </el-container>
  16 + </el-container>
  17 + </div>
  18 +</template>
  19 +
  20 +<script>
  21 +import layoutAside from "./aside/aside";
  22 +import layoutHeader from "./header/header";
  23 +import Bottom from "./Footer/bottom";
  24 +import langSelect from "../../components/lang/langSelect";
  25 +
  26 +export default {
  27 + name: "layout",
  28 + components: {
  29 + layoutHeader,
  30 + Bottom,
  31 + langSelect,
  32 + layoutAside,
  33 + },
  34 +};
  35 +</script>
  36 +
  37 +<style>
  38 +.main-enter,
  39 +.main-leave-to {
  40 + opacity: 0;
  41 + transform: translateY(30px);
  42 +}
  43 +.main-enter-active {
  44 + transition: all 0.2s;
  45 +}
  46 +.main-leave-active {
  47 + position: absolute;
  48 + transition: all 0.3s;
  49 +}
  50 +</style>
  51 +<style lang="scss">
  52 +* {
  53 + margin: 0px;
  54 + padding: 0px;
  55 +}
  56 +
  57 +body {
  58 + background-color: #f2f2f2;
  59 + font-size: 14px;
  60 + color: #333333;
  61 +}
  62 +
  63 +li {
  64 + list-style: none;
  65 +}
  66 +
  67 +a {
  68 + text-decoration: none;
  69 +}
  70 +
  71 +$top: top;
  72 +$bottom: bottom;
  73 +$left: left;
  74 +$right: right;
  75 +$leftright: ($left, $right);
  76 +%w100 {
  77 + width: 100%;
  78 +}
  79 +
  80 +%h100 {
  81 + height: 100%;
  82 +}
  83 +
  84 +%cursor {
  85 + cursor: pointer;
  86 +}
  87 +html,
  88 +body {
  89 + overflow: hidden;
  90 +}
  91 +html,
  92 +body,
  93 +#loyout,
  94 +.el-container,
  95 +#asideNav,
  96 +ul.el-menu {
  97 + @extend %h100;
  98 +}
  99 +
  100 +@mixin set-value($side, $value) {
  101 + @each $prop in $leftright {
  102 + #{$side}-#{$prop}: $value;
  103 + }
  104 +}
  105 +
  106 +#elmain {
  107 + background-color: #f0f2f5;
  108 +}
  109 +
  110 +.avatar-uploader .el-upload {
  111 + border: 1px dashed #d9d9d9 !important;
  112 + border-radius: 6px;
  113 + cursor: pointer;
  114 + position: relative;
  115 + overflow: hidden;
  116 +}
  117 +.avatar-uploader .el-upload:hover {
  118 + border-color: #409eff;
  119 +}
  120 +.avatar-uploader-icon {
  121 + font-size: 28px;
  122 + color: #8c939d;
  123 + width: 178px;
  124 + height: 178px;
  125 + line-height: 178px;
  126 + text-align: center;
  127 +}
  128 +.avatar {
  129 + width: 178px;
  130 + height: 178px;
  131 + display: block;
  132 +}
  133 +</style>
... ...
src/views/login/index.vue 0 → 100644
  1 +<template>
  2 + <div class="login-container">
  3 + <el-form
  4 + ref="loginForm"
  5 + :model="loginForm"
  6 + :rules="loginRules"
  7 + class="login-form"
  8 + auto-complete="on"
  9 + label-position="left"
  10 + >
  11 + <!-- <img
  12 + src="../../assets/images/logo.png"
  13 + style="
  14 + width: 120px;
  15 + height: 120px;
  16 + position: absolute;
  17 + top: -250px;
  18 + left: 190px;
  19 + "
  20 + alt
  21 + /> -->
  22 + <img
  23 + src="../../assets/images/chiken.png"
  24 + style="width: 428px; height: 142px; position: absolute; top: -142px"
  25 + alt
  26 + />
  27 + <div class="title-container">
  28 + <h3 class="title">欢迎使用教育云平台!</h3>
  29 + </div>
  30 +
  31 + <el-form-item prop="username">
  32 + <i class="fa fa-user" aria-hidden="true"></i>
  33 + <el-input
  34 + ref="username"
  35 + v-model="loginForm.username"
  36 + placeholder="请输入用户名"
  37 + name="username"
  38 + type="text"
  39 + autofocus="autofocus"
  40 + tabindex="1"
  41 + maxlength="40"
  42 + auto-complete="on"
  43 + />
  44 + </el-form-item>
  45 +
  46 + <el-form-item prop="password">
  47 + <i class="fa fa-unlock-alt" aria-hidden="true"></i>
  48 + <el-input
  49 + :key="passwordType"
  50 + ref="password"
  51 + v-model="loginForm.password"
  52 + :type="passwordType"
  53 + placeholder="请输入登录密码"
  54 + name="password"
  55 + class="mima"
  56 + maxlength="16"
  57 + tabindex="2"
  58 + auto-complete="on"
  59 + @keyup.enter.native="submitForm"
  60 + />
  61 + <span class="show-pwd" @click="showPwd">
  62 + <i
  63 + class="fa"
  64 + aria-hidden="true"
  65 + :class="passwordType === 'password' ? 'fa-eye-slash' : 'fa-eye'"
  66 + ></i>
  67 + </span>
  68 + </el-form-item>
  69 + <el-button
  70 + type="primary"
  71 + style="
  72 + width: 80%;
  73 + display: block;
  74 + margin: 0 auto;
  75 + margin-bottom: 30px;
  76 + margin-top: 10px;
  77 + "
  78 + round
  79 + :disabled="!disableClick"
  80 + @click.native.prevent="submitForm"
  81 + >登录</el-button
  82 + >
  83 + </el-form>
  84 + </div>
  85 +</template>
  86 +<script>
  87 +export default {
  88 + data() {
  89 + return {
  90 + disableClick: true,
  91 + passwordType: "password",
  92 + loginForm: {
  93 + username: "admin",
  94 + password: "123456",
  95 + },
  96 + loginRules: {
  97 + username: [
  98 + { required: true, message: "请输入用户账号",trigger: "blur" }
  99 + ],
  100 + password: [
  101 + { required: true, message: "请输入账号密码",trigger: "blur" }
  102 + ]
  103 + },
  104 + };
  105 + },
  106 + methods: {
  107 + showPwd() {
  108 + if (this.passwordType === "password") {
  109 + this.passwordType = "";
  110 + } else {
  111 + this.passwordType = "password";
  112 + }
  113 + this.$nextTick(() => {
  114 + this.$refs.password.focus();
  115 + });
  116 + },
  117 + submitForm() {
  118 + let that = this;
  119 + if (this.loginForm.username === "" || this.loginForm.password === "") {
  120 + this.$message({
  121 + showClose: true,
  122 + message: "账号或密码不能为空",
  123 + type: "error",
  124 + });
  125 + return false;
  126 + } else {
  127 + // 真实请求参考
  128 + // this.$request.fetchLogin({
  129 + // username: that.loginForm.username,
  130 + // password: that.loginForm.password
  131 + // }).then(res => {
  132 + // that.$restBack(res.data, () => {
  133 + // that.$store.dispatch("setToken", res.data.data.access_token).then(res => {
  134 + // that.$router.push({path: "/"})
  135 + // })
  136 + // }, "登录成功")
  137 + // }).catch((err) => {
  138 + // console.log(err)
  139 + // })
  140 +
  141 + // 将 username 设置为 token 存储在 store,仅为测试效果,实际存储 token 以后台返回为准
  142 + that.$store
  143 + .dispatch("setToken", that.loginForm.username)
  144 + .then(() => {
  145 + that.$router.push({ path: "/" });
  146 + })
  147 + .catch((res) => {
  148 + that.$message({
  149 + showClose: true,
  150 + message: res,
  151 + type: "error",
  152 + });
  153 + });
  154 + }
  155 + },
  156 + },
  157 + mounted() {},
  158 +};
  159 +</script>
  160 +<style lang="scss">
  161 +$bg: #283443;
  162 +$dark_gray: #889aa4;
  163 +$light_gray: #000;
  164 +$cursor: #000;
  165 +
  166 +@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
  167 + .login-container .el-input input {
  168 + color: $cursor;
  169 + }
  170 +}
  171 +.login-container {
  172 + width: 100%;
  173 + height: 100vh;
  174 + background: url("../../assets/images/login-bg.png") no-repeat;
  175 + background-size: cover;
  176 + overflow: hidden;
  177 + display: flex;
  178 + justify-content: center;
  179 + align-items: center;
  180 + .el-input {
  181 + display: inline-block;
  182 + height: 47px;
  183 + width: 85%;
  184 + input::-ms-reveal {
  185 + display: none;
  186 + }
  187 + input {
  188 + background: transparent;
  189 + border: 0px;
  190 + -webkit-appearance: none;
  191 + border-radius: 0px;
  192 + font-size:16px;
  193 + color: $light_gray;
  194 + height: 52px;
  195 + caret-color: $cursor;
  196 +
  197 + &:-webkit-autofill {
  198 + box-shadow: 0 0 0px 1000px #e5e5e5 inset !important;
  199 + -webkit-text-fill-color: $cursor !important;
  200 + }
  201 + }
  202 + }
  203 +
  204 + .el-form-item {
  205 + border: 1px solid #e5e5e5;
  206 + background: rgba(0, 0, 0, 0.1);
  207 + border-radius: 5px;
  208 + color: #454545;
  209 + }
  210 + .login-form {
  211 + position: relative;
  212 + width: 500px;
  213 + height: 331px;
  214 + max-width: 100%;
  215 + padding: 30px 35px 0;
  216 + border-radius: 10px;
  217 + background: #fff;
  218 + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  219 + margin-top: 200px;
  220 + box-sizing: border-box;
  221 + }
  222 +
  223 + .tips {
  224 + font-size: 14px;
  225 + color: #fff;
  226 + margin-bottom: 10px;
  227 +
  228 + span {
  229 + &:first-of-type {
  230 + margin-right: 16px;
  231 + }
  232 + }
  233 + }
  234 +
  235 + .fa {
  236 + padding: 5px 0px 6px 15px;
  237 + color: $dark_gray;
  238 + font-size: 20px;
  239 + }
  240 +
  241 + .title-container {
  242 + position: relative;
  243 +
  244 + .title {
  245 + font-size: 24px;
  246 + color: $light_gray;
  247 + margin: 0px auto 40px auto;
  248 + text-align: center;
  249 + font-weight: bold;
  250 + }
  251 + }
  252 +
  253 + .show-pwd {
  254 + position: absolute;
  255 + right: 10px;
  256 + top: 7px;
  257 + font-size: 16px;
  258 + color: $dark_gray;
  259 + cursor: pointer;
  260 + user-select: none;
  261 + }
  262 +}
  263 +</style>
  264 +
... ...
src/views/page404.vue 0 → 100644
  1 +<template>
  2 + <div class="page404">
  3 + <div class="i404">
  4 + <img src="../assets/img404/i404.png"/>
  5 + <p>您的访问页面可能被删除或者不存在</p>
  6 + <a href="/">返回首页</a>
  7 + </div>
  8 + <div class="sign">
  9 + <img src="../assets/img404/sign.png" />
  10 + </div>
  11 + </div>
  12 +</template>
  13 +
  14 +<script>
  15 +export default {
  16 + name: "page404",
  17 + mounted () {
  18 + }
  19 +}
  20 +</script>
  21 +
  22 +<style lang="scss">
  23 + .page404{
  24 + width: 100%;
  25 + height: 100%;
  26 + background: url("../assets/img404/bg404.jpg") no-repeat;
  27 + background-size: cover;
  28 + overflow: hidden;
  29 + & >div{
  30 + width: 50%;
  31 + height: 100%;
  32 + }
  33 + .i404{
  34 + float: left;
  35 + display: flex;
  36 + flex-direction: column;
  37 + justify-content: center;
  38 + align-items: center;
  39 + img{
  40 + width: 500px;
  41 + height: 200px;
  42 + margin-top: -100px;
  43 + }
  44 + p{
  45 + font-size: 20px;
  46 + color: #3d95ff;
  47 + margin-top: 2px;
  48 + }
  49 + a{
  50 + display: block;
  51 + width: 150px;
  52 + height: 50px;
  53 + color: #ffffff;
  54 + background: #56a9ff;
  55 + margin-top: 35px;
  56 + line-height: 50px;
  57 + text-align: center;
  58 + border-radius: 50px;
  59 + font-size: 18px;
  60 + }
  61 + }
  62 + .sign{
  63 + float: right;
  64 + img{
  65 + width: 600px;
  66 + margin-top: 50px;
  67 + margin-left: 50px;
  68 + }
  69 + }
  70 + }
  71 +
  72 +</style>
... ...
src/views/systemManage/roleManage.vue 0 → 100644
  1 +<template>
  2 + <div class="cardshadow roleListTable">
  3 + <div>
  4 + <el-button type="primary" icon="el-icon-circle-plus-outline" size="mini" @click="addRole" plain v-role-btn="'btn_100002'">新增</el-button>
  5 + </div>
  6 + <el-table
  7 + :data="tableData"
  8 + style="width: 100%">
  9 + <el-table-column
  10 + type="index">
  11 + </el-table-column>
  12 + <el-table-column
  13 + property="name"
  14 + label="角色名">
  15 + </el-table-column>
  16 + <el-table-column
  17 + property="describe"
  18 + label="角色描述">
  19 + </el-table-column>
  20 + <el-table-column
  21 + prop="status"
  22 + label="状态"
  23 + filter-placement="bottom-end">
  24 + <template slot-scope="scope">
  25 + <el-tag
  26 + :type="scope.row.status === '启用' ? 'primary' : 'danger'"
  27 + disable-transitions>{{scope.row.status}}
  28 + </el-tag>
  29 + </template>
  30 + </el-table-column>
  31 + <el-table-column label="操作">
  32 + <template slot-scope="scope">
  33 + <el-button
  34 + size="mini"
  35 + :disabled="scope.row.name=='超级管理员'"
  36 + @click="handleEdit(scope.$index, scope.row)">编辑
  37 + </el-button>
  38 + <el-button
  39 + size="mini"
  40 + :disabled="scope.row.name=='超级管理员'"
  41 + @click="roleEdit(scope.$index, scope.row)">权限分配
  42 + </el-button>
  43 + <el-button
  44 + size="mini"
  45 + type="danger"
  46 + :disabled="scope.row.name=='超级管理员'"
  47 + @click="handleDelete(scope.$index, scope.row)">删除
  48 + </el-button>
  49 + </template>
  50 + </el-table-column>
  51 + </el-table>
  52 + <el-dialog title="角色信息" width="700px" class="dialog1" :visible.sync="dialogFormVisible">
  53 + <el-form :model="form">
  54 + <el-form-item label="角色名称" :label-width="formLabelWidth">
  55 + <el-input v-model="form.name"
  56 + maxlength="8"
  57 + show-word-limit
  58 + autocomplete="off"></el-input>
  59 + </el-form-item>
  60 + <el-form-item label="角色描述" :label-width="formLabelWidth">
  61 + <el-input v-model="form.describe"
  62 + maxlength="15"
  63 + show-word-limit
  64 + autocomplete="off"></el-input>
  65 + </el-form-item>
  66 + <el-form-item label="是否开启" :label-width="formLabelWidth">
  67 + <el-switch v-model="form.status"></el-switch>
  68 + </el-form-item>
  69 + </el-form>
  70 + <div slot="footer" class="dialog-footer">
  71 + <el-button @click="dialogFormVisible=false">取 消</el-button>
  72 + <el-button type="primary" @click="addRoleSubmit">确 定</el-button>
  73 + </div>
  74 + </el-dialog>
  75 + <el-dialog title="权限分配" class="dialog2" @opened="setRoleData" :visible.sync="dialogFormVisible2">
  76 + <el-input
  77 + placeholder="输入关键字进行过滤"
  78 + v-model="filterText"
  79 + style="margin-bottom: 20px"
  80 + >
  81 + </el-input>
  82 + <el-tree
  83 + :data="roleTree"
  84 + node-key="r_id"
  85 + show-checkbox
  86 + check-on-click-node
  87 + default-expand-all
  88 + check-strictly
  89 + :expand-on-click-node="false"
  90 + ref="permission"
  91 + :filter-node-method="filterNode"
  92 + :props="defaultProps">
  93 + </el-tree>
  94 + <div slot="footer" class="dialog-footer">
  95 + <el-button @click="dialogFormVisible2 = false">取 消</el-button>
  96 + <el-button type="primary" @click="rolePermissionSubmit">确 定</el-button>
  97 + </div>
  98 + </el-dialog>
  99 + </div>
  100 +</template>
  101 +
  102 +<script>
  103 +export default {
  104 + name: "roleManage",
  105 + data () {
  106 + return {
  107 + tableData: [],
  108 + dialogFormVisible: false,
  109 + dialogFormVisible2: false,
  110 + form: {
  111 + name: "",
  112 + describe: "",
  113 + status: true,
  114 + permission: []
  115 + },
  116 + formLabelWidth: "120px",
  117 + defaultProps: {
  118 + children: "children",
  119 + label: "r_name",
  120 + id: "r_id"
  121 + },
  122 + selectRoleId: "",
  123 + selectData: [],
  124 + filterText: ""
  125 + }
  126 + },
  127 + methods: {
  128 + handleEdit (index, row) {
  129 + for (let item in row) {
  130 + this.form[item] = row[item]
  131 + }
  132 + this.form.status = row.status === "启用"
  133 + this.dialogFormVisible = true
  134 + },
  135 + addRole () {
  136 + this.form = {
  137 + name: "",
  138 + describe: "",
  139 + status: true
  140 + }
  141 + this.dialogFormVisible = true
  142 + },
  143 + addRoleSubmit () {
  144 + let that = this
  145 + if (!this.form.name) {
  146 + that.$message({
  147 + showClose: true,
  148 + message: "角色名称不能为空",
  149 + type: "error"
  150 + })
  151 + return false
  152 + }
  153 + this.$request.fetchAddRole(this.form).then((res) => {
  154 + that.$message({
  155 + showClose: true,
  156 + message: res.data.message,
  157 + type: "success"
  158 + })
  159 + this.dialogFormVisible = false
  160 + this.getList()
  161 + }).catch((err) => {
  162 + console.log(err)
  163 + })
  164 + },
  165 + rolePermissionSubmit () {
  166 + let that = this
  167 + let rolePermissionData = {
  168 + selectPermission: that.$refs.permission.getCheckedKeys(),
  169 + rid: that.selectRoleId
  170 + }
  171 + this.$request.fetchRolePermissions(rolePermissionData).then(res => {
  172 + that.$restBack(res.data, () => {
  173 + that.dialogFormVisible2 = false
  174 + that.getList()
  175 + })
  176 + }).catch((err) => {
  177 + console.log(err)
  178 + })
  179 + },
  180 + roleEdit (index, row) {
  181 + console.log(index, row)
  182 + this.selectRoleId = row.id
  183 + this.selectData = row.permission ? row.permission.split(",") : []
  184 + this.dialogFormVisible2 = true
  185 + },
  186 + setRoleData () {
  187 + this.$request.fetchSearchRolePermissions({rid: this.selectRoleId}).then(res => {
  188 + this.$refs.permission.setCheckedKeys([])
  189 + let permissionData = res.data.data.permissionPage + "," + res.data.data.permissionButton
  190 + this.$refs.permission.setCheckedKeys(permissionData.split(","))
  191 + })
  192 + },
  193 + handleDelete (index, row) {
  194 + let that = this
  195 + this.$request.fetchDelRole({
  196 + id: row.id
  197 + })
  198 + .then(response => {
  199 + console.log(response)
  200 + that.$message({
  201 + showClose: true,
  202 + message: response.data.message,
  203 + type: "success"
  204 + })
  205 + that.getList()
  206 + })
  207 + .catch(err => {
  208 + console.log(err)
  209 + })
  210 + },
  211 + getList () {
  212 + let that = this
  213 + this.$request.fetchGetRoleList()
  214 + .then(function (response) {
  215 + console.log(response)
  216 + for (let i = 0; i < response.data.rows.length; i++) {
  217 + if (response.data.rows[i].status) {
  218 + response.data.rows[i].status = "启用"
  219 + } else {
  220 + response.data.rows[i].status = "禁用"
  221 + }
  222 + }
  223 + that.tableData = response.data.rows
  224 + })
  225 + .catch(function (error) {
  226 + console.log(error)
  227 + })
  228 + },
  229 + filterNode (value, data) {
  230 + if (!value) return true
  231 + return data.r_name.indexOf(value) !== -1
  232 + }
  233 + },
  234 + watch: {
  235 + filterText (val) {
  236 + this.$refs.permission.filter(val)
  237 + }
  238 + },
  239 + computed: {
  240 + roleTree: function () {
  241 + let roleData = this.$store.getters.roleData
  242 + for (let i = 0; i < roleData.length; i++) {
  243 + if (roleData[i].redirect === "/404") {
  244 + roleData.splice(i, 1)
  245 + }
  246 + }
  247 + return roleData
  248 + }
  249 + },
  250 + mounted () {
  251 + this.getList()
  252 + }
  253 +}
  254 +</script>
  255 +
  256 +<style scoped>
  257 + .nameinput {
  258 + width: 150px;
  259 + }
  260 +
  261 + .phoneinput {
  262 + width: 120px;
  263 + }
  264 +
  265 + .datepicker {
  266 + width: 260px;
  267 + }
  268 + .dialog1 .el-dialog {
  269 + width: 35%;
  270 + }
  271 +
  272 + .dialog1 .el-dialog .el-form {
  273 + width: 500px;
  274 + margin: 0 auto;
  275 + }
  276 +
  277 + .el-form-item__content {
  278 + margin-left: 120px;
  279 + width: 300px;
  280 + }
  281 +
  282 +</style>
  283 +
... ...