# 创建vite项目
npm init @vitejs/app
# 输入项目名称
? Project name: » vite-devil-control
# 然后选择vue (通过上下箭头切换,回车确定)
# 然后选择vue或vue+ts
安装(两个任选其一)
# router4.x版本
npm install vue-router@4
# router3.x版本
npm install vue-router
创建文件:src\router\index.ts·
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
// 仅做示例,根据业务修改路径等
const Login = () => import('../views/Login/Login.vue')
const routes: RouteRecordRaw[] = [
{
path: '/login',
component: Login,
name: 'Login',
meta: {
title: '登录页'
}
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 前置路由
router.beforeEach((to, from, next) => {
})
export default router
main.ts做出如下修改,引入router
import {createApp} from 'vue'
import router from './router'
import App from './App.vue'
const app = createApp(App)
app.use(router)
app.mount('#app')
安装vuex:
npm install vuex@next
创建store对象src\store\index.ts·
:
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
name: 'coderwhy'
}
}
})
export default store
安装store:
import {createApp} from 'vue'
import router from './router'
import store from './store'
import App from './App.vue'
const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')
# 下载scss,sass-loader,sass
npm install --save-dev sass-loader
npm install --save-dev node-sass
npm install --save-dev sass
安装:npm install element-plus
全局引入很简单,官方写的很详细,在 main.ts
中
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import router from './router'
import store from './store'
createApp(App).use(router).use(store).use(ElementPlus).mount('#app')
首先是全局非常常用的组件,例如按钮等……
安装babel的插件:npm install babel-plugin-import -D
vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
// @ts-ignore
import styleImport from 'vite-plugin-style-import';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
styleImport({
libs: [
{
libraryName: 'element-plus',
esModule: true,
ensureStyleFile: true,
resolveStyle: (name) => {
name: name.slice(3);
return `element-plus/packages/theme-chalk/src/${name}.scss`;
},
resolveComponent: (name) => {
return `element-plus/lib/${name}`;
},
},
],
}),
]
})
上述是为了引入对应的样式,如果出现问题,可采用引入全部css的方式:
import 'element-plus/lib/theme-chalk/index.css'
,引入全部css就不需要上述在 vite.config.ts 中的操作
新建 src/global/register-element.ts 文件
import 'element-plus/lib/theme-chalk/base.css'
import {App} from "vue";
import {
ElButton,
ElTable,
ElAlert,
ElAside,
ElAutocomplete,
ElAvatar,
ElBacktop,
ElBadge,
} from 'element-plus'
const components = [
ElButton,
ElTable,
ElAlert,
ElAside,
ElAutocomplete,
ElAvatar,
ElBacktop,
ElBadge
]
export function registerElement(app:App):void {
for (const cpn of components) {
app.component(cpn.name, cpn)
}
}
src/global/index.ts
import {App} from "vue";
import {registerElement} from "./register-element";
export function registerApp(app: App): void {
registerElement(app)
}
在 main.ts中引入
import {createApp} from 'vue'
import router from './router'
import {registerElement} from "./plugins/ElementPlus";
import App from './App.vue'
const app = createApp(App)
registerElement(app)
app.use(router)
app.mount('#app')
其次还有一些不常用的,可能只在某一两个页面使用的组件,可以使用这种方式,注册为组件后即可在当前页面正常使用。
import { ElButton } from 'element-plus'
export default defineComponent({
components: {
ElButton
}
})
安装:npm i -D naive-ui
直接引入的方式很简单,这里就不做说明;
另外 NaiveUI 组件多,非常不建议全局引入
目录说明:
src/global/register-naive.ts
import {
create,
NButton,
NCard,
NConfigProvider,
NInput,
} from 'naive-ui'
const naive = create({
components: [
NButton,
NCard,
NConfigProvider,
NInput,
]
})
export function registerNaive(app: any) {
app.use(naive)
}
src/global/index.ts
import {App} from "vue";
import {registerElement} from "./register-element";
import {registerNaive} from "./register-naive";
export function registerApp(app: App): void {
registerElement(app)
registerNaive(app)
}
main.ts不做更改,引入
import {createApp} from 'vue'
import router from './router'
import {registerApp} from "./global";
import App from './App.vue'
const app = createApp(App)
registerApp(app)
app.use(router)
app.mount('#app')
安装:npm install axios
vite.config.ts 配置接口代理,拦截所有api开头的请求,请求时删除api
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
// @ts-ignore
import styleImport from 'vite-plugin-style-import';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [……],
server: {
host: '0.0.0.0',
proxy: {
'^/api/.*': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
}
}
})
如果不清楚接口的代理,可以忽略此选项,跳过即可
先来看一下目录结构,service在src目录下面
config.ts
let BASE_URL = ''
const TIME_OUT = 8000
// 生产
if (process.env.NODE_ENV === 'development') {
// 如果没有接口代理,这里可以使用真实的接口地址,例如:https://192.168.22.1:8899
BASE_URL = '/api'
// 上线环境
} else if (process.env.NODE_ENV === 'production') {
BASE_URL = '/prod'
} else {
// 测试环境
BASE_URL = '/'
}
export { BASE_URL, TIME_OUT }
type.ts
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
export interface CVRequestInterceptors {
requestInterceptors: (config: AxiosRequestConfig) => AxiosRequestConfig
requestInterceptorsCatch: (error: any) => any
responseInterceptors: (config: AxiosResponse) => AxiosResponse
responseInterceptorsCatch: (error: any) => any
}
export interface CVRequestConfig extends AxiosRequestConfig {
// 自定义拦截器
interceptors?: CVRequestInterceptors,
// 是否显示Loading
showLoading?: boolean
}
src/service/request/index.ts
import axios from 'axios'
import type { AxiosInstance } from 'axios'
import type { CVRequestConfig, CVRequestInterceptors } from './type'
import { ElLoading } from 'element-plus'
import { ILoadingInstance } from 'element-plus/lib/el-loading/src/loading.type'
class CVRequest {
instance: AxiosInstance
interceptors?: CVRequestInterceptors
showLoading: boolean
loading?: ILoadingInstance
// 类的构造器
constructor(config: CVRequestConfig) {
this.instance = axios.create(config)
this.showLoading = config.showLoading ?? true
this.interceptors = config.interceptors
// request拦截器,这里解释一下->这里是接收外界传递过来的拦截器,为了增强我们Axios灵活度设定的
this.instance.interceptors.request.use(
this.interceptors?.requestInterceptors,
this.interceptors?.requestInterceptorsCatch
)
// response拦截器,这里解释一下->这里是接收外界传递过来的拦截器,为了增强我们Axios灵活度设定的
this.instance.interceptors.response.use(
this.interceptors?.responseInterceptors,
this.interceptors?.responseInterceptorsCatch
)
// 添加所有实例都有的拦截器
this.instance.interceptors.request.use(
(config) => {
// TODO 这里使用了ElmentPlus的组件,可以自行更换
this.loading = ElLoading.service({
lock: true,
text: '数据请求中...'
})
return config
}, (err) => {
return err
}
)
this.instance.interceptors.response.use(
(config) => {
// 移除loading
this.loading?.close()
// 说明,后端提供的接口,如果经过规范化处理,一般包含 状态码,提示信息,核心内容
// 这里的意思,根据后端设定,如果状态码不等于20000则说明错误,我们在页面显示错误信息,具体的显示未做实现,自行解决
const data = res.data()
if (data.code !== 20000) {
// TODO 页面显示错误信息
console.log(data.msg)
} else {
return data
}
return config
}, (err) => {
// 移除loading
this.loading?.close()
let msg = showStatus(status)
response.data.msg = msg
return err
}
)
}
request<T>(config: CVRequestConfig): Promise<T> {
return new Promise((resolve, reject) => {
if (config.interceptors?.requestInterceptors(config)) {
config = config.interceptors?.requestInterceptors(config)
}
if (config.showLoading === false) {
this.showLoading = config.showLoading
}
this.instance.request(config).then((res) => {
if (config.interceptors?.requestInterceptors) {
res = config.interceptors.responseInterceptors(res)
}
// 将 showLoading 值重置,不影响下一次请求
this.showLoading = true
}).catch(err => {
this.showLoading = true
return err
})
})
}
}
const showStatus = (status: number) => {
let message = ''
switch (status) {
case 400:
message = '请求错误(400)'
break
case 401:
message = '未授权,请重新登录(401)'
break
case 403:
message = '拒绝访问(403)'
break
case 404:
message = '请求出错(404)'
break
case 408:
message = '请求超时(408)'
break
case 500:
message = '服务器错误(500)'
break
case 501:
message = '服务未实现(501)'
break
case 502:
message = '网络错误(502)'
break
case 503:
message = '服务不可用(503)'
break
case 504:
message = '网络超时(504)'
break
case 505:
message = 'HTTP版本不受支持(505)'
break
default:
message = `连接出错(${status})!`
}
return `${message},请检查网络或联系管理员!`
}
export default CVRequest
上面的3个文件不是很清楚?没有问题
- config是基础的请求前缀,如果做了接口代理,则使用 api,或自行设计。
- 如果没有使用接口代理,则将其换成真实接口地址 https://192.168.22.1:8899
- type.ts 无需改动
- index.ts 的数据请求中,如果不需要或使用了别的UI库,可自行更换
- index.ts 需要自行根据后端状态码的设定进行更改,如果不需要可直接删除
- TODO 页面显示错误信息 这里需要自行完善,根据UI库直接展示,或不做处理都行
src/service/index.ts 这里是对我们上面的封装进行使用,暴露给最外层使用
// service统一出口
import CVRequest from './request'
import { BASE_URL, TIME_OUT } from './request/config'
// 常规接口,如果项目涉及到多个接口地址,可以创建多个,使用命名区分即可
const cvRequest = new CVRequest({
baseURL: BASE_URL,
timeout: TIME_OUT,
// 创建实例的时候传递拦截器,可根据业务需求自行更改
interceptors: {
requestInterceptors: (config) => {
const token = localStorage.getItem("token")
if (token){
config.headers.token = token
}
return config
},
requestInterceptorsCatch: (err) => {
return err
},
responseInterceptors: (res) => {
return res.data
},
responseInterceptorsCatch: (err) => {
return err
}
}
})
export default cvRequest
上面我们封装了Axios,接下来我们使用Axios,当然具体的使用是非常灵活的,这里仅提供一种解决思路
创建:src/service/devil/type.ts,
注意:devil代表的是一个类型的接口,如果是登录就换成登录,以此类推
type.ts存放接口接收类型的参数
// 查询队列
export interface QueryQueuePage {
page: number,
size: number,
name: string
}
// 方式消息
export interface SendMessageReq {
content: string,
queueName: string
}
src/service/devil/devil.ts 负责具体的接口
import cvRequest from "../index";
import {QueryQueuePage, SendMessageReq} from "./type";
/**
* 查询所有队列名称
*/
export function findAllQueueRequest() {
return cvRequest.get({
url: '/broker/count/queue/name'
})
}
/**
* 查询队列内容
* @param query 队列页数、每页条数、队列名称
*/
export function findQueueByNameRequest(query: QueryQueuePage) {
return cvRequest.get({
url: `/broker/queue/content/${query.page}/${query.size}?name=${query.name}`
})
}
/**
* 发送消息
* @param message 队列名称 消息内容
*/
export function sendMsgRequest(message: SendMessageReq) {
return cvRequest.post({
url: `/broker/add/message`,
data: message
})
}
上述应该可以满足绝大多数需求了。
例如默认的间距等等
安装normalize.css
:npm install --save normalize.css
在 main.ts
中引入:import ‘normalize.css/normalize.css’
本质上设置了两个色系的配色
新建:src/style/seeting/variable.scss,下面的配色仅做参考,是我个人喜欢的颜色,具体的根据项目进行配色
:root {
--bg-contrast: #ffffff;
--color-default: rgb(51, 54, 57); //文本默认颜色
--color-light: #797675; //浅色调文本
--color-title: rgb(34, 33, 33); //文本默认颜色
--bg-primary: #ffffff; // 左侧菜单等颜色
--bg-minor: #f1f5ff; // 主要部分颜色
--font-color-light: #777c88; // 左侧/网站 浅色调字体
--bg-hover: #f1f5ff; // 默认hover背景色
--bg-active: #e0e8f9; // 默认active背景色
--color-primary: #4ba9ff; // 文字主题色
--color-primary-active: #279bfe; // 文字主题色
--a-color: #2c98f9; // a标签颜色
--a-color-hover: #0a7be2; // a标签hover
--icon-color: #cccccc; // 图标color
--border-bg-active: rgba(0, 0, 0, .1); //边框阴影
--hr-bg: #eeeeee; // hr颜色
--webkit-scrollbar-thumb: rgba(189, 189, 189, 0.5); // 滑块颜色
--nav-bar-shadow: rgba(57, 66, 60, 0.2); // 左侧菜单阴影
--tab-bar-shadow: rgba(78, 87, 79, 0.1); // 顶部菜单阴影
--color-disappear: rgba(0, 0, 0, .16); //非常浅的颜色
--m-btn-border: rgba(193, 191, 191, 0.69); // 自定义按钮边框颜色
--m-btn-color: rgb(79, 77, 77); // 自定义按钮字体颜色
--m-btn-dot: rgb(113, 109, 109); // 自定义按钮点颜色
--bg-layer: rgba(255, 255, 255, .9); // 公司项目遮罩层背景颜色
--md-top-bg: #F3F5F6; //md顶部背景色
--md-line-bg: #E8F2FF; //md当前选中行背景色
--toolbar-item-active-hover: #e8e8e8; //顶部导航栏选中样式
--md-pre-wrapper-bg: #f8fafc;// 代码块背景颜色
--md-hr-bg: #ddd;// md中间的分割线
--md-box-bg: rgba(28,31,33,0.15); // md最外层盒子阴影
--hr-color-char: #E5E5E5; //聊天页面分割线
--char-cus-card: #f5f5f5; //聊天页面单个卡片背景色
--char-cus-card-bg-hover: #eaffee; //聊天页面单个卡片选中背景色
}
:root[theme='dark'] {
--bg-contrast: #000000;
--color-default: rgba(255, 255, 255, 0.75);
--color-light: #797675;
--color-title: rgba(255, 255, 255, 0.85);
--bg-primary: #18181C;
--bg-minor: #22242c;
--font-color-light: #d8d8d8;
--bg-hover: #222228;
--bg-active: rgba(255, 255, 255, 0.07);
--color-primary: #7FE7C4;
--color-primary-active: #65e8b0;
--a-color: #04d220;
--a-color-hover: #029a02;
--icon-color: #757575;
--border-bg-active: rgba(255, 255, 255, .2);
--hr-bg: #363636;
--nav-bar-shadow: rgba(219, 221, 219, 0.2);
--tab-bar-shadow: rgba(212, 215, 212, 0.1);
--color-disappear: rgba(255, 255, 255, .15);
--m-btn-border: rgba(216, 216, 216, 0.7);
--m-btn-color: rgb(211, 208, 208);
--m-btn-dot: rgb(222, 218, 218);
--bg-layer: rgba(210, 210, 210, 0.7);
--md-top-bg: rgb(15, 15, 15);
--md-line-bg: #0f0e0e;
--toolbar-item-active-hover: rgba(0,0,0,.8);
--md-pre-wrapper-bg: #060606;
--md-hr-bg: #232323;
--md-box-bg: rgba(227,224,222,0.1);
--hr-color-char: #515151;
--char-cus-card: #161619;
--char-cus-card-bg-hover: #002805;
}
App.Vue中引入
例如:
html {
background-color: var(--bg-primary);
}
将切换浅色/深色的按钮绑定changeTheme
方法即可
通过Vue i18n实现,官方文档:Vue I18n
在vue3中引入,注意官网的教程引入暂不支持vue3,请使用我这里的方式:npm install vue-i18n@next
创建存放英文的ts
文件,src/i18n/en.ts,具体的内容根据自身业务自行补充
const en = {
tab: {
control: 'console'
}
}
export default en
创建存放中文的ts
文件,src/i18n/zh.ts
const zh = {
tab: {
control: '中控室'
}
}
export default zh
创建index统一管理语言文件,src/i18n/index.ts
import en from './en'
import zh from './zh'
export default {
en,
zh
}
创建配置文件 src/i18n/i18n.ts
import {createI18n} from "vue-i18n";
import messages from './index'
const i18n = createI18n({
locale: localStorage.lang || 'zh', //默认中文
messages
})
export default i18n
main.ts中引入
import {createApp} from 'vue'
import router from './router'
import 'normalize.css/normalize.css'
import {registerApp} from "./global";
import i18n from "./i18n/i18n";
import App from './App.vue'
const app = createApp(App)
registerApp(app)
app.use(i18n)
app.use(router)
app.mount('#app')
核心代码
import i18n from "./i18n/i18n";
app.use(i18n)
找到你需要切换语言的页面
按钮绑定 setLangCondition 方法即可,如果设置多个语言也是类似的
如果有疑问,或者不清楚的欢迎评论区留言,如果感觉对你有帮助,请点个赞再走吧