Explorar o código

【功能增改】新增侧边栏菜单模式下顶部切换菜单,原模式修改为顶部菜单模式头像下方显示切换应用

俞宝山 %!s(int64=4) %!d(string=hai) anos
pai
achega
da4353a44c

+ 119 - 84
_web/src/components/GlobalHeader/GlobalHeader.vue

@ -6,9 +6,18 @@
        :class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
        :style="{ padding: '0' }">
        <div v-if="mode === 'sidemenu'" class="header">
          <a-icon v-if="device==='mobile'" class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle"/>
          <a-icon v-else class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggle"/>
          <user-menu></user-menu>
          <a-menu
            mode="horizontal"
            :default-selected-keys=this.defApp
            style="border-bottom:0px;lineHeight:62px;"
          >
            <a-icon v-if="device==='mobile'" class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle"/>
            <a-icon v-else class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggle"/>
            <a-menu-item v-for='(item) in userInfo.apps' :key="item.code" style="top:0px;" @click="switchApp(item.code)">
              {{item.name}}
            </a-menu-item>
            <user-menu></user-menu>
          </a-menu>
        </div>
        <div v-else :class="['top-nav-header-index', theme]">
          <div class="header-index-wide">
@ -26,100 +35,126 @@
</template>
<script>
import UserMenu from '../tools/UserMenu'
import SMenu from '../Menu/'
import Logo from '../tools/Logo'
import { mixin } from '@/utils/mixin'
  import UserMenu from '../tools/UserMenu'
  import SMenu from '../Menu/'
  import Logo from '../tools/Logo'
  import { mixin } from '@/utils/mixin'
  import { mapActions,mapGetters } from 'vuex'
  import { ALL_APPS_MENU } from '@/store/mutation-types'
  import Vue from 'vue'
  import { message } from 'ant-design-vue/es'
export default {
  name: 'GlobalHeader',
  components: {
    UserMenu,
    SMenu,
    Logo
  },
  mixins: [mixin],
  props: {
    mode: {
      type: String,
      // sidemenu, topmenu
      default: 'sidemenu'
  export default {
    name: 'GlobalHeader',
    components: {
      UserMenu,
      SMenu,
      Logo,
    },
    menus: {
      type: Array,
      required: true
    computed: {
      ...mapGetters(['userInfo']),
    },
    theme: {
      type: String,
      required: false,
      default: 'dark'
    created(){
      this.defApp.push(Vue.ls.get(ALL_APPS_MENU)[0].code)
    },
    collapsed: {
      type: Boolean,
      required: false,
      default: false
    mixins: [mixin],
    props: {
      mode: {
        type: String,
        // sidemenu, topmenu
        default: 'sidemenu'
      },
      menus: {
        type: Array,
        required: true
      },
      theme: {
        type: String,
        required: false,
        default: 'dark'
      },
      collapsed: {
        type: Boolean,
        required: false,
        default: false
      },
      device: {
        type: String,
        required: false,
        default: 'desktop'
      }
    },
    device: {
      type: String,
      required: false,
      default: 'desktop'
    }
  },
  data () {
    return {
      visible: true,
      oldScrollTop: 0
    }
  },
  mounted () {
    document.addEventListener('scroll', this.handleScroll, { passive: true })
  },
  methods: {
    handleScroll () {
      if (!this.autoHideHeader) {
        return
    data () {
      return {
        visible: true,
        oldScrollTop: 0,
        defApp:[]
      }
    },
    mounted () {
      document.addEventListener('scroll', this.handleScroll, { passive: true })
    },
    methods: {
      ...mapActions(['MenuChange']),
      const scrollTop = document.body.scrollTop + document.documentElement.scrollTop
      if (!this.ticking) {
        this.ticking = true
        requestAnimationFrame(() => {
          if (this.oldScrollTop > scrollTop) {
            this.visible = true
          } else if (scrollTop > 300 && this.visible) {
            this.visible = false
          } else if (scrollTop < 300 && !this.visible) {
            this.visible = true
          }
          this.oldScrollTop = scrollTop
          this.ticking = false
        })
      /**
       * 应用切换
       */
      switchApp(appCode){
        this.defApp=[]
        const applicationData = this.userInfo.apps.filter(item => item.code == appCode)
        const hideMessage = message.loading('正在切换应用!', 0)
        this.MenuChange(applicationData[0]).then((res)=>{
          hideMessage()
        }).catch((err)=>{
          message.error("应用切换异常");
        });
      },
      handleScroll () {
        if (!this.autoHideHeader) {
          return
        }
        const scrollTop = document.body.scrollTop + document.documentElement.scrollTop
        if (!this.ticking) {
          this.ticking = true
          requestAnimationFrame(() => {
            if (this.oldScrollTop > scrollTop) {
              this.visible = true
            } else if (scrollTop > 300 && this.visible) {
              this.visible = false
            } else if (scrollTop < 300 && !this.visible) {
              this.visible = true
            }
            this.oldScrollTop = scrollTop
            this.ticking = false
          })
        }
      },
      toggle () {
        this.$emit('toggle')
      }
    },
    toggle () {
      this.$emit('toggle')
    beforeDestroy () {
      document.body.removeEventListener('scroll', this.handleScroll, true)
    }
  },
  beforeDestroy () {
    document.body.removeEventListener('scroll', this.handleScroll, true)
  }
}
</script>
<style lang="less">
@import '../index.less';
  @import '../index.less';
.header-animat{
  position: relative;
  z-index: @ant-global-header-zindex;
}
.showHeader-enter-active {
  transition: all 0.25s ease;
}
.showHeader-leave-active {
  transition: all 0.5s ease;
}
.showHeader-enter, .showHeader-leave-to {
  opacity: 0;
}
  .header-animat{
    position: relative;
    z-index: @ant-global-header-zindex;
  }
  .showHeader-enter-active {
    transition: all 0.25s ease;
  }
  .showHeader-leave-active {
    transition: all 0.5s ease;
  }
  .showHeader-enter, .showHeader-leave-to {
    opacity: 0;
  }
</style>

+ 163 - 178
_web/src/components/tools/UserMenu.vue

@ -6,17 +6,14 @@
          <a-icon type="question-circle-o"></a-icon>
        </span>
      </a>
      <notice-icon class="action"/>
      <a-dropdown>
        <span class="action ant-dropdown-link user-dropdown-menu">
          <a-avatar class="avatar" size="small" :src="avatar"/>
          <span>{{ nickname }}</span>
        </span>
        <a-menu slot="overlay" class="user-dropdown-menu-wrapper">
          <a-menu-item key="4" v-if="hasPerm('sysMenu:change')">
          <a-menu-item key="4" v-if="mode === 'sidemenu'">
            <a @click="appToggled()" >
              <a-icon type="swap"/>
              <span>切换应用</span>
@ -51,43 +48,31 @@
        </a-menu>
      </a-dropdown>
    </div>
    <a-modal
      title="切换应用"
      :visible="visible"
      :footer="null"
      :confirm-loading="confirmLoading"
      @ok="handleOkApp"
      @cancel="handleCancel"
    >
      <a-form class="permission-form" :form="form1" >
        <a-form-item>
          <a-form-item
            :labelCol="labelCol"
            :wrapperCol="wrapperCol"
            label="目前应用"
            has-feedback
          >
            <a-button type="primary" size="small">{{this.appName}}</a-button>
          </a-form-item>
          <a-form-item
            :labelCol="labelCol"
            :wrapperCol="wrapperCol"
            label="切换应用"
            has-feedback
      <a-form  :form="form1" >
        <a-form-item
          :labelCol="labelCol"
          :wrapperCol="wrapperCol"
          label="选择应用"
        >
          <a-menu
            mode="inline"
            :default-selected-keys=this.defApp
            style="border-bottom:0px;lineHeight:62px;"
          >
            <a-select style="width: 100%" placeholder="请选择要切换的应用" v-decorator="['application', {rules: [{ required: true, message: '请选择要切换的应用!' }]}]" >
              <a-select-option v-for='(item,index) in userInfo.apps' :key="index" :value="item.code" >{{item.name}}</a-select-option>
            </a-select>
          </a-form-item>
            <a-menu-item v-for='(item) in userInfo.apps' :key="item.code" style="top:0px;" @click="switchApp(item.code)">
              {{item.name}}
            </a-menu-item>
          </a-menu>
        </a-form-item>
      </a-form>
    </a-modal>
    <a-modal
      title="修改密码"
      :visible="visible_updPwd"
@ -95,188 +80,188 @@
      @ok="handleOkUpdPwd"
      @cancel="handleCancel"
    >
        <a-form :form="form2">
          <a-form-item
            label="原密码"
            :labelCol="labelCol"
            :wrapperCol="wrapperCol"
            has-feedback
          >
            <a-input placeholder="请输入原密码"  type="password" v-decorator="['password', {rules: [{required: true,  message: '请输入原密码!'}]}]" />
          </a-form-item>
        </a-form>
        <a-form :form="form2">
            <a-form-item
              label="新密码"
              :labelCol="labelCol"
              :wrapperCol="wrapperCol"
              has-feedback
            >
              <a-input placeholder="请输入新密码" type="password" v-decorator="['newPassword', {rules: [{required: true,  message: '请输入新密码!'},{
      <a-form :form="form2">
        <a-form-item
          label="原密码"
          :labelCol="labelCol"
          :wrapperCol="wrapperCol"
          has-feedback
        >
          <a-input placeholder="请输入原密码"  type="password" v-decorator="['password', {rules: [{required: true,  message: '请输入原密码!'}]}]" />
        </a-form-item>
        <a-form-item
          label="新密码"
          :labelCol="labelCol"
          :wrapperCol="wrapperCol"
          has-feedback
        >
          <a-input placeholder="请输入新密码" type="password" v-decorator="['newPassword', {rules: [{required: true,  message: '请输入新密码!'},{
                validator: validateToNextPassword,
              },]}]" />
            </a-form-item>
        </a-form>
        <a-form :form="form2">
          <a-form-item
            label="重复新密码"
            :labelCol="labelCol"
            :wrapperCol="wrapperCol"
            has-feedback
          >
            <a-input placeholder="请再次输入新密码" type="password" v-decorator="['confirm', {rules: [{required: true,  message: '请再次输入新密码!'},
        </a-form-item>
        <a-form-item
          label="重复新密码"
          :labelCol="labelCol"
          :wrapperCol="wrapperCol"
          has-feedback
        >
          <a-input placeholder="请再次输入新密码" type="password" v-decorator="['confirm', {rules: [{required: true,  message: '请再次输入新密码!'},
              {
                validator: compareToFirstPassword,
              }]}]" />
          </a-form-item>
        </a-form>
        </a-form-item>
      </a-form>
    </a-modal>
  </div>
</template>
<script>
import NoticeIcon from '@/components/NoticeIcon'
import { mapActions, mapGetters } from 'vuex'
import { ALL_APPS_MENU } from '@/store/mutation-types'
import Vue from 'vue'
  import NoticeIcon from '@/components/NoticeIcon'
  import { mapActions, mapGetters } from 'vuex'
  import { ALL_APPS_MENU } from '@/store/mutation-types'
  import Vue from 'vue'
  import { message } from 'ant-design-vue/es'
export default {
  name: 'UserMenu',
  components: {
    NoticeIcon,
  },
  data() {
    return {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 5 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
  export default {
    name: 'UserMenu',
    components: {
      NoticeIcon,
    },
    props: {
      mode: {
        type: String,
        // sidemenu, topmenu
        default: 'sidemenu'
      },
      visible: false,
      visible_updPwd:false,
      confirmLoading: false,
      form1: this.$form.createForm(this),
      form2: this.$form.createForm(this),
      appName:''
    };
  },
    },
    data() {
      return {
        labelCol: {
          xs: { span: 24 },
          sm: { span: 5 }
        },
        wrapperCol: {
          xs: { span: 24 },
          sm: { span: 16 }
        },
        visible: false,
        visible_updPwd:false,
        confirmLoading: false,
        form1: this.$form.createForm(this),
        form2: this.$form.createForm(this),
        defApp:[]
      };
    },
  computed: {
    ...mapGetters(['nickname', 'avatar','userInfo'])
    computed: {
      ...mapGetters(['nickname', 'avatar','userInfo'])
  },
  methods: {
    ...mapActions(['Logout','MenuChange','UpdatePwd']),
    },
    methods: {
      ...mapActions(['Logout','MenuChange','UpdatePwd']),
    handleLogout () {
      this.$confirm({
        title: '提示',
        content: '真的要注销登录吗 ?',
        onOk: () => {
          return this.Logout({}).then(() => {
            setTimeout(() => {
              window.location.reload()
            }, 16)
          }).catch(err => {
            this.$message.error({
              title: '错误',
              description: err.message
      handleLogout () {
        this.$confirm({
          title: '提示',
          content: '真的要注销登录吗 ?',
          onOk: () => {
            return this.Logout({}).then(() => {
              setTimeout(() => {
                window.location.reload()
              }, 16)
            }).catch(err => {
              this.$message.error({
                title: '错误',
                description: err.message
              })
            })
          })
        },
        onCancel () {
        }
      })
    },
          },
          onCancel () {
          }
        })
      },
    /**
     * 打开切换应用框
     */
    appToggled () {
      this.visible = true;
      this.appName=Vue.ls.get(ALL_APPS_MENU)[0].name
    },
      /**
       * 打开切换应用框
       */
      appToggled () {
        this.visible = true;
        this.defApp.push(Vue.ls.get(ALL_APPS_MENU)[0].code)
      },
    /**
     * 打开修改密码框
     */
    updatePwd () {
      this.visible_updPwd = true;
    },
      /**
       * 打开修改密码框
       */
      updatePwd () {
        this.visible_updPwd = true;
      },
    compareToFirstPassword(rule, value, callback) {
      const form2 = this.form2;
      if (value && value !== form2.getFieldValue('newPassword')) {
        callback('请确认两次输入密码的一致性!');
      } else {
      compareToFirstPassword(rule, value, callback) {
        const form2 = this.form2;
        if (value && value !== form2.getFieldValue('newPassword')) {
          callback('请确认两次输入密码的一致性!');
        } else {
          callback();
        }
      },
      validateToNextPassword(rule, value, callback) {
        const form2 = this.form2;
        if (value && this.confirmDirty) {
          form2.validateFields(['confirm'], { force: true });
        }
        callback();
      }
    },
    validateToNextPassword(rule, value, callback) {
      const form2 = this.form2;
      if (value && this.confirmDirty) {
        form2.validateFields(['confirm'], { force: true });
      }
      callback();
    },
      },
    handleOkApp(){
      const { form1: { validateFields } } = this
      validateFields((errors, values) => {
        if(!errors){
          const applicationData = this.userInfo.apps.filter(item => item.code == values.application)
          this.MenuChange(applicationData[0])
          this.handleCancel()
        }
      })
    },
      switchApp(appCode){
        this.visible = false;
        this.defApp=[]
        const applicationData = this.userInfo.apps.filter(item => item.code == appCode)
        const hideMessage = message.loading('正在切换应用!', 0)
        this.MenuChange(applicationData[0]).then((res)=>{
          hideMessage()
        }).catch((err)=>{
          message.error("应用切换异常");
        });
      },
    handleOkUpdPwd(){
      const { form2: { validateFields } } = this
      validateFields((errors, values) => {
        if(!errors){
          values.id = this.userInfo.id
          this.UpdatePwd(values).then((res) =>{
            if(res.success){
              this.$message.success('修改成功')
              this.handleCancel()
            }else{
              this.$message.error('修改失败:'+res.message)
            }
          })
      handleOkUpdPwd(){
        const { form2: { validateFields } } = this
        validateFields((errors, values) => {
          if(!errors){
            values.id = this.userInfo.id
            this.UpdatePwd(values).then((res) =>{
              if(res.success){
                this.$message.success('修改成功')
                this.handleCancel()
              }else{
                this.$message.error('修改失败:'+res.message)
              }
            })
        }
      })
    },
          }
        })
      },
    handleCancel(){
      this.form1.resetFields();
      this.form2.resetFields();
      this.visible = false;
      this.visible_updPwd = false;
      handleCancel(){
        this.form1.resetFields();
        this.form2.resetFields();
        this.visible = false;
        this.visible_updPwd = false;
      }
    }
  }
}
</script>
<style lang="less" scoped>
    .appRedio {
  .appRedio {
    border:1px solid #91d5ff;
    padding:10px 20px;
    background: #e6f7ff;
    border-radius:2px;
    margin-bottom:10px;
      color: #91d5ff;
    color: #91d5ff;
    /*display: inline;
    margin-bottom:10px;*/
    }
  }
</style>

+ 132 - 132
_web/src/store/modules/user.js

@ -9,151 +9,151 @@ import store from '../index'
import router from '../../router'
const user = {
  state: {
    token: '',
    name: '',
    welcome: '',
    avatar: '',
    buttons: [],//按钮权限
    admintype:'',//是否是超管
    roles: [],
    info: {},
  },
    state: {
      token: '',
      name: '',
      welcome: '',
      avatar: '',
      buttons: [],//按钮权限
      admintype:'',//是否是超管
      roles: [],
      info: {},
    },
  mutations: {
    SET_TOKEN: (state, token) => {
    mutations: {
      SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_NAME: (state, { name, welcome }) => {
      state.name = name
      state.welcome = welcome
    },
    SET_AVATAR: (state, avatar) => {
      state.avatar = avatar
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles
    },
    SET_INFO: (state, info) => {
      state.info = info
    },
    SET_BUTTONS: (state, buttons) => {
      state.buttons = buttons
    },
    SET_ADMINTYPE: (state, admintype) => {
      state.admintype = admintype
    }
    state.name = name
    state.welcome = welcome
  },
  SET_AVATAR: (state, avatar) => {
  state.avatar = avatar
},
SET_ROLES: (state, roles) => {
  state.roles = roles
},
SET_INFO: (state, info) => {
  state.info = info
},
SET_BUTTONS: (state, buttons) => {
  state.buttons = buttons
},
SET_ADMINTYPE: (state, admintype) => {
  state.admintype = admintype
}
},
  actions: {
    // 登录
    Login ({ commit }, userInfo) {
      return new Promise((resolve, reject) => {
        login(userInfo).then(response => {
          if(!response.success){
            reject(response.message)
            return
          }
          const result = response.data
          Vue.ls.set(ACCESS_TOKEN, result, 7 * 24 * 60 * 60 * 1000)
          commit('SET_TOKEN', result)
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
actions: {
  // 登录
  Login ({ commit }, userInfo) {
    return new Promise((resolve, reject) => {
    // 获取用户信息
    GetInfo ({ commit }) {
      return new Promise((resolve, reject) => {
        getLoginUser().then(response => {
          if(response.success){
              const data = response.data
              commit('SET_ADMINTYPE',data.adminType)
              commit('SET_ROLES',1)
              commit('SET_BUTTONS', data.permissions)
              commit('SET_INFO', data)
              commit('SET_NAME', { name: data.name, welcome: welcome() })
              commit('SET_AVATAR', data.avatar)
              resolve(data)
          }else{
            reject(new Error(data.message))
          }
        }).catch(error => {
          reject(error)
        })
      })
    },
      login(userInfo).then(response => {
        if(!response.success){
      reject(response.message)
      return
    }
    const result = response.data
    Vue.ls.set(ACCESS_TOKEN, result, 7 * 24 * 60 * 60 * 1000)
    commit('SET_TOKEN', result)
    resolve()
  }).catch(error => {
      reject(error)
    })
  })
  },
    // 登出
    Logout ({ commit, state }) {
      return new Promise((resolve) => {
        logout(state.token).then(() => {
          resolve()
        }).catch(() => {
          resolve()
        }).finally(() => {
          commit('SET_TOKEN', '')
          commit('SET_ROLES', [])
          commit('SET_BUTTONS', [])
          commit('SET_ADMINTYPE','')
          Vue.ls.remove(ACCESS_TOKEN)
          Vue.ls.remove(ALL_APPS_MENU)
        })
      })
    },
  // 获取用户信息
  GetInfo ({ commit }) {
    return new Promise((resolve, reject) => {
      getLoginUser().then(response => {
        if(response.success){
      const data = response.data
      commit('SET_ADMINTYPE',data.adminType)
      commit('SET_ROLES',1)
      commit('SET_BUTTONS', data.permissions)
      commit('SET_INFO', data)
      commit('SET_NAME', { name: data.name, welcome: welcome() })
      commit('SET_AVATAR', data.avatar)
      resolve(data)
    }else{
      reject(new Error(data.message))
    }
  }).catch(error => {
      reject(error)
    })
  })
  },
    // 切换应用菜单
    MenuChange({ commit },application) {
      return new Promise((resolve) => {
        sysMenuChange({application:application.code}).then((res) => {
          const apps={"code": "","name": "","active":"","menu":""}
          apps.active =true
          apps.menu=res.data
          const all_app_menu=Vue.ls.get(ALL_APPS_MENU)
          const new_false_all_app_menu=[]
          //先去除所有默认的,以为此时切换的即将成为前端缓存默认的应用
          all_app_menu.forEach(item=>{
            if(item.active){
              item.active =false
            }
            new_false_all_app_menu.push(item)
          })
          //此时缓存中全部都是不默认的应用
          Vue.ls.set(ALL_APPS_MENU, new_false_all_app_menu)
          apps.name =application.name
          apps.code =application.code
          const applocationR = []
          applocationR.push(apps)
          Vue.ls.set(ALL_APPS_MENU, applocationR)
          resolve(res)
          const antDesignmenus=res.data
          store.dispatch('GenerateRoutes', { antDesignmenus }).then(() => {
            router.addRoutes(store.getters.addRouters)
          })
          window.location.reload()
        }).catch(() => {
          resolve()
        })
      })
    },
  // 登出
  Logout ({ commit, state }) {
    return new Promise((resolve) => {
      logout(state.token).then(() => {
      resolve()
    }).catch(() => {
      resolve()
    }).finally(() => {
      commit('SET_TOKEN', '')
    commit('SET_ROLES', [])
    commit('SET_BUTTONS', [])
    commit('SET_ADMINTYPE','')
    Vue.ls.remove(ACCESS_TOKEN)
    Vue.ls.remove(ALL_APPS_MENU)
  })
  })
  },
    // 修改密码
    UpdatePwd({ commit },passwords) {
      return new Promise((resolve,reject) => {
        sysUserUpdatePwd(passwords).then((res) => {
          resolve(res)
          return
        }).catch(() => {
          resolve()
        })
      })
  // 切换应用菜单
  MenuChange({ commit },application) {
    return new Promise((resolve) => {
      sysMenuChange({application:application.code}).then((res) => {
        const apps={"code": "","name": "","active":"","menu":""}
        apps.active =true
        apps.menu=res.data
        const all_app_menu=Vue.ls.get(ALL_APPS_MENU)
        const new_false_all_app_menu=[]
        //先去除所有默认的,以为此时切换的即将成为前端缓存默认的应用
        all_app_menu.forEach(item=>{
          if(item.active){
      item.active =false
    }
    new_false_all_app_menu.push(item)
  })
    //此时缓存中全部都是不默认的应用
    Vue.ls.set(ALL_APPS_MENU, new_false_all_app_menu)
    apps.name =application.name
    apps.code =application.code
    const applocationR = []
    applocationR.push(apps)
    Vue.ls.set(ALL_APPS_MENU, applocationR)
    resolve(res)
    const antDesignmenus=res.data
    store.dispatch('GenerateRoutes', { antDesignmenus }).then(() => {
      router.addRoutes(store.getters.addRouters)
    })
    //切换应用刷新整体界面,暂且取消
    //window.location.reload()
  }).catch(() => {
      resolve()
    })
  })
  },
  // 修改密码
  UpdatePwd({ commit },passwords) {
    return new Promise((resolve,reject) => {
      sysUserUpdatePwd(passwords).then((res) => {
        resolve(res)
        return
      }).catch(() => {
      resolve()
    })
  })
  }
}
}
export default user