网站配置

这部份的配置信息,不需要也不能动态的变更,直接使用即可。

项目的Logo信息,可在这里进行修改,Vite方式忽略

创建src/config/website.config.js文件,写入以下内容:

config/website.config.js
import logoImage from '@/assets/images/logo.png';
import loginImage from '@/assets/images/account-logo.png';

export const websiteConfig = Object.freeze({
title: 'CloudUpOA',
logo: logoImage,
loginImage: loginImage,
loginDesc: '云尚办公后台管理系统',
});

projectSetting

默认设置

创建src/settings/projectSetting.js文件,写入以下内容:

settings/projectSetting.js
const setting = {
//导航模式 vertical 左侧菜单模式 horizontal 顶部菜单模式
navMode: 'vertical',
//导航风格 dark 暗色侧边栏 light 白色侧边栏 header-dark 暗色顶栏
navTheme: 'dark',
// 是否处于移动端模式
isMobile: false,
//顶部
headerSetting: {
//背景色
bgColor: '#fff',
//固定顶部
fixed: true,
//显示重载按钮
isReload: true,
},
//页脚
showFooter: true,
//多标签
multiTabsSetting: {
//背景色
bgColor: '#fff',
//是否显示
show: true,
//固定多标签
fixed: true,
},
//菜单
menuSetting: {
//最小宽度
minMenuWidth: 64,
//菜单宽度
menuWidth: 200,
//固定菜单
fixed: true,
//分割菜单
mixMenu: false,
//触发移动端侧边栏的宽度
mobileWidth: 800,
// 折叠菜单
collapsed: false,
},
//面包屑
crumbsSetting: {
//是否显示
show: true,
//显示图标
showIcon: false,
},
//菜单权限模式 FIXED 前端固定路由 BACK 动态获取
permissionMode: 'FIXED',
//是否开启路由动画
isPageAnimate: true,
//路由动画类型
pageAnimateType: 'zoom-fade',
};
export default setting;

状态保存

使用Store,保存当前菜单设置的状态,方便提供动态修改页面效果的功能

创建src/store/modules/projectSetting.js,写入以下内容:

store/modules/projectSetting.js
import { defineStore } from 'pinia'
import { store } from '@/store';
import projectSetting from '@/settings/projectSetting';

const {
navMode,
navTheme,
isMobile,
headerSetting,
showFooter,
menuSetting,
multiTabsSetting,
crumbsSetting,
permissionMode,
isPageAnimate,
pageAnimateType,
} = projectSetting

export const useProjectSettingStore = defineStore({
id: 'app-project-setting',
state: () => ({
navMode: navMode, //导航模式
navTheme, //导航风格
isMobile, // 是否处于移动端模式
headerSetting, //顶部设置
showFooter, //页脚
menuSetting, //多标签
multiTabsSetting, //多标签
crumbsSetting, //面包屑
permissionMode, //权限模式
isPageAnimate, //是否开启路由动画
pageAnimateType, //路由动画类型
}),
getters: {
getNavMode() {
return this.navMode;
},
getNavTheme() {
return this.navTheme;
},
getIsMobile() {
return this.isMobile;
},
getHeaderSetting() {
return this.headerSetting;
},
getShowFooter() {
return this.showFooter;
},
getMenuSetting() {
return this.menuSetting;
},
getMultiTabsSetting() {
return this.multiTabsSetting;
},
getCrumbsSetting() {
return this.multiTabsSetting;
},
getPermissionMode() {
return this.permissionMode;
},
getIsPageAnimate() {
return this.isPageAnimate;
},
getPageAnimateType() {
return this.pageAnimateType;
},
},
actions: {
setNavTheme(value) {
this.navTheme = value;
},
setIsMobile(value) {
this.isMobile = value
},
},
});
// Need to be used outside the setup
export function useProjectSettingStoreWithOut() {
return useProjectSettingStore(store);
}

监听读取

当Store中的数据变化时,我们需要及时通知页面做出更改,因此需要监听其变化。

使用computed监听数据变化

创建src/hooks/setting/useProjectSetting.js

hooks/setting/useProjectSetting.js
import { computed } from 'vue';
import { useProjectSettingStore } from '@/store/modules/projectSetting';

export function useProjectSetting() {
const projectStore = useProjectSettingStore();

const getNavMode = computed(() => projectStore.navMode);

const getNavTheme = computed(() => projectStore.navTheme);

const getIsMobile = computed(() => projectStore.isMobile);

const getHeaderSetting = computed(() => projectStore.headerSetting);

const getMultiTabsSetting = computed(() => projectStore.multiTabsSetting);

const getMenuSetting = computed(() => projectStore.menuSetting);

const getCrumbsSetting = computed(() => projectStore.crumbsSetting);

const getPermissionMode = computed(() => projectStore.permissionMode);

const getShowFooter = computed(() => projectStore.showFooter);

const getIsPageAnimate = computed(() => projectStore.isPageAnimate);

const getPageAnimateType = computed(() => projectStore.pageAnimateType);

return {
getNavMode,
getNavTheme,
getIsMobile,
getHeaderSetting,
getMultiTabsSetting,
getMenuSetting,
getCrumbsSetting,
getPermissionMode,
getShowFooter,
getIsPageAnimate,
getPageAnimateType,
};
}

designSetting

默认设置

创建src/settings/designSetting.js文件,写入以下内容:

settings/designSetting.js
// app theme preset color
export const appThemeList = [
"#2d8cf0",
"#0960bd",
"#0084f4",
"#009688",
"#536dfe",
"#ff5c93",
"#ee4f12",
"#0096c7",
"#9c27b0",
"#ff9800",
"#FF3D68",
"#00C1D4",
"#71EFA3",
"#171010",
"#78DEC7",
"#1768AC",
"#FB9300",
"#FC5404",
];

const setting = {
//深色主题
darkTheme: false,
//系统主题色
appTheme: "#2d8cf0",
//系统内置主题色列表
appThemeList,
};

export default setting;

状态保存

创建src/store/modules/designSetting.js,写入以下内容:

store/modules/designSetting.js
import { defineStore } from "pinia";
import { store } from "@/store";
import designSetting from "@/settings/designSetting";

const { darkTheme, appTheme, appThemeList } = designSetting;

export const useDesignSettingStore = defineStore({
id: "app-design-setting",
state: () => ({
darkTheme, //深色主题
appTheme, //系统风格
appThemeList, //系统内置风格
}),
getters: {
getDarkTheme() {
return this.darkTheme;
},
getAppTheme() {
return this.appTheme;
},
getAppThemeList() {
return this.appThemeList;
},
},
actions: {},
});

// Need to be used outside the setup
export function useDesignSettingWithOut() {
return useDesignSettingStore(store);
}

监听读取

当Store中的数据变化时,我们需要及时通知页面做出更改,因此需要监听其变化。

使用computed监听数据变化

创建src/hooks/setting/useDesignSetting.js

hooks/setting/useDesignSetting.js
import { computed } from 'vue';
import { useDesignSettingStore } from '@/store/modules/designSetting';

export function useDesignSetting() {
const designStore = useDesignSettingStore();

const getDarkTheme = computed(() => designStore.darkTheme);

const getAppTheme = computed(() => designStore.appTheme);

const getAppThemeList = computed(() => designStore.appThemeList);

return {
getDarkTheme,
getAppTheme,
getAppThemeList,
};
}

环境ENV

关于文件名:必须以如下方式命名,不要乱起名,也无需专门手动控制加载哪个文件

  • .env :全局默认配置文件,不论什么环境都会加载合并
  • .env.development :开发环境下的配置文件
  • .env.production :生产环境下的配置文件

Webpack

注意:属性名必须以VUE_APP_开头,比如VUE_APP_XXX

创建src/.env

# port
PORT = 8001

# spa-title
VUE_APP_TITLE = CloudUpOA

# spa shortname
VUE_APP_SHORT_NAME = CloudUpOA

创建src/.env.development

NODE_ENV = 'development'

# 网站根目录
VUE_APP_PUBLIC_PATH = /

# 是否开启mock
VUE_APP_USE_MOCK = true

# 网站前缀
VUE_APP_BASE_URL = /

# 是否删除console
VUE_APP_DROP_CONSOLE = true

# API 接口地址
VUE_APP_API_URL =

# 图片上传地址
VUE_APP_UPLOAD_URL=

# 图片前缀地址
VUE_APP_IMG_URL=

# 接口前缀
VUE_APP_API_URL_PREFIX = /dev-api

输出控制台

创建src/utils/logs.js

utils/logs.js
const projectName = process.env.VUE_APP_TITLE;

export function warn(message) {
console.warn(`[${projectName} warn]:${message}`);
}

export function error(message) {
throw new Error(`[${projectName} error]:${message}`);
}

获取方法

创建src/utils/env.js,写入以下内容:

utils/env.js
import { warn } from '@/utils/log';

export function getAppEnvConfig() {
const ENV = process.env;
const {
VUE_APP_TITLE,
VUE_APP_API_URL,
VUE_APP_SHORT_NAME,
VUE_APP_API_URL_PREFIX,
VUE_APP_UPLOAD_URL,
VUE_APP_PROD_MOCK,
VUE_APP_IMG_URL,
} = ENV;

if (!/^[a-zA-Z_]*$/.test(VUE_APP_SHORT_NAME)) {
warn(
`VUE_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
);
}

return {
VUE_APP_TITLE,
VUE_APP_API_URL,
VUE_APP_SHORT_NAME,
VUE_APP_API_URL_PREFIX,
VUE_APP_UPLOAD_URL,
VUE_APP_PROD_MOCK,
VUE_APP_IMG_URL,
};
}

/**
* @description: Development model
*/
export const devMode = 'development';

/**
* @description: Production mode
*/
export const prodMode = 'production';

/**
* @description: Get environment variables
* @returns:
* @example:
*/
export function getEnv() {
return process.env.NODE_ENV;
}

/**
* @description: Is it a development mode
* @returns:
* @example:
*/
export function isDevMode() {
return process.env.NODE_ENV === devMode;
}

/**
* @description: Is it a production mode
* @returns:
* @example:
*/
export function isProdMode() {
return import.meta.env.NODE_ENV === devMode;
}

换名读取

以VUE_APP_XXX的方式在项目中使用,终归是不方便。这里我们提供一种读取方式,更换其名称。

创建src/hooks/setting/index.js

hooks/setting/index.js
import { warn } from '@/utils/log';
import { getAppEnvConfig } from '@/utils/env';

export const useGlobSetting = () => {
const {
VUE_APP_TITLE,
VUE_APP_API_URL,
VUE_APP_SHORT_NAME,
VUE_APP_API_URL_PREFIX,
VUE_APP_UPLOAD_URL,
VUE_APP_PROD_MOCK,
VUE_APP_IMG_URL,
} = getAppEnvConfig();

if (!/[a-zA-Z_]*/.test(VUE_APP_SHORT_NAME)) {
warn(
`VUE_APP_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
);
}

// Take global configuration
const glob = {
title: VUE_APP_TITLE,
apiUrl: VUE_APP_API_URL,
shortName: VUE_APP_SHORT_NAME,
urlPrefix: VUE_APP_API_URL_PREFIX,
uploadUrl: VUE_APP_UPLOAD_URL,
prodMock: VUE_APP_PROD_MOCK,
imgUrl: VUE_APP_IMG_URL,
};
return glob;
};

Vite

注意:属性名必须以VITE_开头,比如VUE_XXX

创建src/.env

# port
VITE_PORT = 8001

# spa-title
VITE_GLOB_APP_TITLE = 医院管理系统

# spa shortname
VITE_GLOB_APP_SHORT_NAME = 医院管理系统

创建src/.env.development

# 只在开发模式中被载入
VITE_PORT = 8001

# 网站根目录
VITE_PUBLIC_PATH = /

# 网站前缀
VITE_BASE_URL = /

# 跨域代理,可以配置多个,请注意不要换行
#VITE_PROXY = [["/appApi","http://localhost:8001"],["/upload","http://localhost:8001/upload"]]
VITE_PROXY=[["/api","http://localhost:9000"]]

# API 接口地址
VITE_GLOB_API_URL =

# 图片上传地址
VITE_GLOB_UPLOAD_URL=

# 图片前缀地址
VITE_GLOB_IMG_URL=

# 接口前缀
VITE_GLOB_API_URL_PREFIX = /api

输出控制台

创建src/utils/log.js

utils/logs.js
const projectName = import.meta.env.VITE_GLOB_APP_TITLE;

export function warn(message) {
console.warn(`[${projectName} warn]:${message}`);
}

export function error(message) {
throw new Error(`[${projectName} error]:${message}`);
}

获取方法

创建src/utils/env.js,写入以下内容:

@/build/getConfigFileName

getConfigFileName.js
/**
* Get the configuration file variable name
* @param env
*/
export const getConfigFileName = (env) => {
return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
.toUpperCase()
.replace(/\s/g, '');
};
utils/env.js
import { warn } from '@/utils/log';
import pkg from '../../package.json';
import { getConfigFileName } from '../../build/getConfigFileName';

export function getCommonStoragePrefix() {
const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig();
return `${VITE_GLOB_APP_SHORT_NAME}__${getEnv()}`.toUpperCase();
}

// Generate cache key according to version
export function getStorageShortName() {
return `${getCommonStoragePrefix()}${`__${pkg.version}`}__`.toUpperCase();
}

export function getAppEnvConfig() {
const ENV_NAME = getConfigFileName(import.meta.env);

const ENV = (import.meta.env.DEV
? // Get the global configuration (the configuration will be extracted independently when packaging)
(import.meta.env) : window[ENV_NAME]);

const {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,
VITE_GLOB_PROD_MOCK,
VITE_GLOB_IMG_URL,
} = ENV;

if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
warn(
`VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
);
}

return {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,
VITE_GLOB_PROD_MOCK,
VITE_GLOB_IMG_URL,
};
}

/**
* @description: Development model
*/
export const devMode = 'development';

/**
* @description: Production mode
*/
export const prodMode = 'production';

/**
* @description: Get environment variables
* @returns:
* @example:
*/
export function getEnv() {
return import.meta.env.MODE;
}

/**
* @description: Is it a development mode
* @returns:
* @example:
*/
export function isDevMode() {
return import.meta.env.DEV;
}

/**
* @description: Is it a production mode
* @returns:
* @example:
*/
export function isProdMode() {
return import.meta.env.PROD;
}

换名读取

以VITE_XXX的方式在项目中使用,终归是不方便。这里我们提供一种读取方式,更换其名称。

创建src/hooks/setting/index.js

hooks/setting/index.js
import { warn } from '@/utils/log';
import { getAppEnvConfig } from '@/utils/env';

export const useGlobSetting = () => {
const {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,
VITE_GLOB_PROD_MOCK,
VITE_GLOB_IMG_URL,
} = getAppEnvConfig();

if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
warn(
`VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
);
}

// Take global configuration
const glob = {
title: VITE_GLOB_APP_TITLE,
apiUrl: VITE_GLOB_API_URL,
shortName: VITE_GLOB_APP_SHORT_NAME,
urlPrefix: VITE_GLOB_API_URL_PREFIX,
uploadUrl: VITE_GLOB_UPLOAD_URL,
prodMock: VITE_GLOB_PROD_MOCK,
imgUrl: VITE_GLOB_IMG_URL,
};
return glob;
};

Env解析工具

根目录创建build/utils.js文件,用来获取所有.env.*的配置信息,内容如下

安装工具

npm install dotenv --save-dev
utils.js
import fs from 'fs';
import path from 'path';
import dotenv from 'dotenv';

export function isDevFn(mode) {
return mode === 'development';
}

export function isProdFn(mode) {
return mode === 'production';
}

/**
* Whether to generate package preview
*/
export function isReportMode() {
return process.env.REPORT === 'true';
}

// Read all environment variable configuration files to process.env
export function wrapperEnv(envConf) {
const ret = {};

for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName;

if (envName === 'VITE_PORT') {
realName = Number(realName);
}
if (envName === 'VITE_PROXY') {
try {
realName = JSON.parse(realName);
} catch (error) {}
}
ret[envName] = realName;
process.env[envName] = realName;
}
return ret;
}

/**
* Get the environment variables starting with the specified prefix
* @param match prefix
* @param confFiles ext
*/
export function getEnvConfig(match = 'VITE_GLOB_', confFiles = ['.env', '.env.production']) {
let envConfig = {};
confFiles.forEach((item) => {
try {
const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
envConfig = { ...envConfig, ...env };
} catch (error) {}
});

Object.keys(envConfig).forEach((key) => {
const reg = new RegExp(`^(${match})`);
if (!reg.test(key)) {
Reflect.deleteProperty(envConfig, key);
}
});
return envConfig;
}

/**
* Get user root directory
* @param dir file path
*/
export function getRootPath(...dir) {
return path.resolve(process.cwd(), ...dir);
}

挂载Naive UI上下文

挂载 Naive-ui 脱离上下文的 API,在 setup 外使用 useDialog、useMessage、useNotification、useLoadingBar,可以通过 createDiscreteApi 来构建对应的 API。

plugins/目录创建 naiveDiscreateApi.js

naiveDiscreateApi.js
import * as NaiveUI from 'naive-ui';
import { computed } from 'vue';
import { useDesignSettingWithOut } from '@/store/modules/designSetting';
import { lighten } from '@/utils/index';

/**
* 挂载 Naive-ui 脱离上下文的 API
* 如果你想在 setup 外使用 useDialog、useMessage、useNotification、useLoadingBar,可以通过 createDiscreteApi 来构建对应的 API。
* https://www.naiveui.com/zh-CN/dark/components/discrete
*/

export function setupNaiveDiscreteApi() {
const designStore = useDesignSettingWithOut();

const configProviderPropsRef = computed(() => ({
theme: designStore.darkTheme ? NaiveUI.darkTheme : undefined,
themeOverrides: {
common: {
primaryColor: designStore.appTheme,
primaryColorHover: lighten(designStore.appTheme, 6),
primaryColorPressed: lighten(designStore.appTheme, 6),
},
LoadingBar: {
colorLoading: designStore.appTheme,
},
},
}));
const { message, dialog, notification, loadingBar } = NaiveUI.createDiscreteApi(
['message', 'dialog', 'notification', 'loadingBar'],
{
configProviderProps: configProviderPropsRef,
}
);

window['$message'] = message;
window['$dialog'] = dialog;
window['$notification'] = notification;
window['$loading'] = loadingBar;
}

修改plugins/index.js,添加如下代码

index.js
export { setupNaive } from '@/plugins/naive';
export { setupNaiveDiscreteApi } from '@/plugins/naiveDiscreteApi';

修改main.js,添加如下代码

main.js
import { createApp } from 'vue'
import App from './App.vue'
import { setupStore } from './store'
import { setupNaive, setupNaiveDiscreteApi } from './plugins'
import router, { setupRouter } from './router';

async function bootstrap() {
const app = createApp(App);

// 挂载状态管理
setupStore(app);

// 注册全局常用的 naive-ui 组件
setupNaive(app);

// 挂载 naive-ui 脱离上下文的 Api
setupNaiveDiscreteApi();

// 挂载路由
setupRouter(app);

// 路由准备就绪后挂载 APP 实例
// https://router.vuejs.org/api/interfaces/router.html#isready
await router.isReady();

app.mount('#app', true);
}

bootstrap();