目录
目标
配置中心
config/config.js
皮肤/国际化
config/theme.js
config/language.js
app.js
App.vue
权限管理
src/views/about.vue
src/views/403.vue
config/permission.js
src/router.js
src/store.js
献上一张通过ai生成的图片~
目标
- 配置中心
- 皮肤/国际化
- 权限控制
配置中心
config/config.js
// config/config.js
class Config {
constructor() {// 构造函数
this._config = {};
}
regist(type, value) { // 注册配置
this._config[type] = value;
}
get(type) { // 获取配置
return this._config[type];
}
}
// 返回一个单例
export default new Config();
皮肤/国际化
config/theme.js
// config/theme.js
// 皮肤配置
import config from "./config";
// 启动加载数据
export const init = () => {
config.regist("theme", {
blue: {
primary: "#007fff",
highlight: "#00a6ff",
},
red: {
primary: "#A83733",
highlight: "rgb(195, 75, 73)",
},
});
};
config/language.js
// config/language.js
// 国际化,(中英切换)
import config from "./config";
import { LIST_TYPE } from "../module/topic/store";
export const init = () => {
config.regist("language", {
Chinese: {
navs: [
{
name: "热门",
path: LIST_TYPE.HOT,
},
{
name: "最新",
path: LIST_TYPE.NEW,
},
{
name: "热榜",
path: LIST_TYPE.TOP,
},
{
name: "关于我",
path: "about",
},
]
},
English: {
navs: [
{
name: "pop",
path: LIST_TYPE.HOT,
},
{
name: "new",
path: LIST_TYPE.NEW,
},
{
name: "hot",
path: LIST_TYPE.TOP,
},
{
name: "aboutMe",
path: "about",
},
]
},
});
};
app.js
import Vue from "vue";
import App from "./App.vue";
import { createStore } from "./store";
import { createRouter } from "./router";
import intersect from "./directive/intersect";
// 皮肤配置,初始化方法init重命名为themeInit
import { init as themeInit } from "./config/theme";
// 国际化
import { init as languageInit } from "./config/language";
// 权限
import { init as permissionInit } from "./config/permission";
// 注册指令
Vue.directive("intersect", intersect);
// app.$mount("#app");
export function createApp() {
const store = createStore();
const router = createRouter({ store });
// 应用初始化之前,初始化皮肤相关配置信息
themeInit();
// 应用初始化之前,初始化国际化相关配置信息
languageInit();
// 初始化权限
permissionInit();
const app = new Vue({
store,
router,
render: (h) => h(App),
});
return {
app,
store,
router,
};
}
App.vue
<template>
<div id="app">
<div class="m-top" :style="{ backgroundColor: theme.primary }">
<u-link
class="m-link"
:style="{
backgroundColor:
$route.name === nav.path ? theme.highlight : theme.primary,// 读取配置
}"
v-for="nav in language.navs"
:key="nav.path"
:to="nav.path"
:prefetch="true"
>{{ nav.name }}</u-link>
</div>
<div class="m-content">
<u-keep-alive max="2">
<router-view></router-view>
</u-keep-alive>
</div>
<div class="m-side">
主题切换:
<button @click="themeType = 'red'">红</button>
<button @click="themeType = 'blue'">蓝</button>
</div>
</div>
</template>
<script>
import UTopic from "./module/topic/views/UTopic.vue";
import { LIST_TYPE } from "./module/topic/store";
import config from "./config/config";// 读取配置
export default {
components: {
UTopic,
},
data() {
return {
themeType: "blue",
languageType: "Chinese",
};
},
//provide 全局注入主题颜色;
//inject: ["theme"]获取;这个方案不对响应式数据进行传递
// 用于一次性启动的配置,在生命周期中不进行修改
// 性能考虑
provide() {
return {
theme: this.theme,
};
},
computed: {
theme() {
return config.get("theme")[this.themeType];// 读取配置
},
language() {
return config.get("language")[this.languageType];
},
},
};
</script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
margin: 0;
overflow-y: scroll;
}
a {
text-decoration: none;
color: #007fff;
}
.m-top {
height: 60px;
width: 100%;
background: #007fff;
}
.m-content {
width: 360px;
border: 1px solid #eee;
background: #fff;
margin: 20px auto;
padding: 0 20px;
}
.m-link {
display: inline-block;
color: #fff;
height: 60px;
line-height: 60px;
font-size: 19px;
padding: 0 20px;
}
.router-link-active {
background: #00a6ff;
}
.m-side {
position: fixed;
left: 50%;
margin-left: 220px;
top: 100px;
}
</style>
权限管理
src/views/about.vue
<template>
<div class="about">
about me!!!
<input type="text" v-model="x" />
</div>
</template>
<script>
export default {
data() {
return {
x: "",
};
},
};
</script>
<style scoped>
.about {
padding: 30px;
text-align: center;
}
</style>
src/views/403.vue
<template>
<div class="stop">无权限访问页面</div>
</template>
<style scoped>
.stop {
padding: 30px;
text-align: center;
}
</style>
config/permission.js
// config/permission.js
// 权限配置文件
import config from "./config";
// 暴漏常量
export const PERMISSION_MAP = {
ABOUT_PAGE: Symbol("ABOUT_PAGE"),
};
export const init = () => {
config.regist("permission", {
CEO: {
[PERMISSION_MAP.ABOUT_PAGE]: true,
},
COO: {
[PERMISSION_MAP.ABOUT_PAGE]: false,
},
});
};
// 获取身份权限
export const getPermissionByRole = (role) => config.get("permission")[role];
src/router.js
import Vue from "vue";
import VueRouter from "vue-router";
import { routes as topic } from "./module/topic/router";
import { PERMISSION_MAP, getPermissionByRole } from "./config/permission";
import store from "./store";
import { compose } from "./util/compose";
Vue.use(VueRouter);
export function createRouter({ store }) {
// 1. 获取当前角色
const getRole = () => store.state.user.role;
// 2.获取当前权限getPermissionByRole
const getPermission = (permission) =>
// 3.当前obj[permission]是否在PERMISSION_MAP上,从右往左执行
compose((obj) => obj[permission], getPermissionByRole, getRole)();
return new VueRouter({
mode: "history",
routes: [
...topic,
{
name: "about",
path: "/about",
component: () => import(/* webpackChunkName:"about" */ "./views/UAbout.vue"),
beforeEnter(to, from, next) { // 守卫钩子
// 如果有权限就去to页面,没有就去403页面
getPermission(PERMISSION_MAP.ABOUT_PAGE) ? next() : next("403");
},
},
{
name: "403",
path: "/403",
component: () => import(/* webpackChunkName:"403" */ "./views/403.vue"),
},
{
path: "/",
redirect: "/hot",
},
],
});
}
src/store.js
// 全局单例store
import Vue from "vue";
import Vuex from "vuex";
import { store as topic } from "./module/topic/store";
Vue.use(Vuex);
export function createStore() {
return new Vuex.Store({
state: {
user: {
role: "CEO",// 假装在后端请求到的身份
},
},
modules: {
topic,
},
});
}
分享的文章多是学习过程中记录的coding && 笔记~