vue-ts-cms
一、配置环境
-
为.vue文件声明一个组件类型
//env.d.ts declare module "*.vue" { import { DefineComponent } from "vue"; const component: DefineComponent export default component }作用:防止导入的组件都是any类型,有更好的代码提示
-
当某个东西版本过低时
-
卸载之后重新安装
//卸载 npm uninstall vue-tsc //安装 npm install vue-tsc -D -
可能还安装不上最新的版本
原因:npm在本地发现已有的版本
解决:
//强制把本地缓存清空 npm cache clean --force //安装 npm install vue-tsc -D
-
二、代码规范
2.1.集成editorconfig配置
Editorconfig有助于为不同IDE编辑器上处理同一项目的多个开发人员维护一致的编码风格。
//.editorconfig
# http://editorconfig.org
root = true
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为utf-8
indent_style = space # 缩进风格(tab | space)
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行尾的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行
[*.md] # 表示仅md文件适用以下规则
max_line_length = off
trim_trailing_whitespace = false
VSCode需要安装一个插件:Editorconfig for VS Code
2.2.使用prettier工具
Prettier是一款强大的代码格式化工具,支持js,ts,css,scss,jsx,angular,vue,graphql, json,md等语言,基本上前端能用到的文件格式它都能搞定,是当前最流行的代码格式化工具。
1.安装prettier
npm install prettier -D
2.配置.prettier文件
-
useTabs: 使用tab缩进还是空格缩进,选择false;
-
tabWidth:tab是空格的情况下是几个空格,选择2个;
-
printWidth:当前字符的长度,推荐80,也可以100或者120;
-
singleQuote:使用单引号还是双引号,选择true,使用单引号;
-
traillingComma:在多行输入的尾逗号是否添加,设置为none,比如对象类型的最后一个属性是否添加一个逗号;
-
semi:语句末尾是否要加分号,默认值true,选择false表示不加;
-
//.prettier { "useTabs": false, "tabWidth": 2, "printWidth": 100, "singleQuote": true, "trailingComma": "none", "bracketSpacing": true, "semi": false }
3.创建.prettierignore忽略文件
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*
4.安装VS Code插件Prettier - Code formatter,并且在设置中搜索Editor: Default Formatter选择Prettier,format on save=>勾选
2.3.使用ESlint
1.在前面创建项目的时候,我们选择了ESlint,所以Vue会默认帮助我们配置需要的ESlint环境。
2.安装ESlint的插件
3.解决ESlint和prettier冲突的问题
安装插件:(vue在创建项目时,如果选择prettier,那么这两个插件会默认安装)
npm install eslint-plugin-prettier -D elsint -config-prettier -D
4.在.eslintrc.cjs中进行配置
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier/skip-formatting',
'plugin:prettier/recommended'
]
三、目录结构及样式重置
3.1.目录结构
3.2.样式重置
1.引入normalize.css文件
npm install normalize.css
2.在main.js中进行导入
import 'normalize.css'
3.自己手写一些重置样式
四、路由配置
4.1.安装路由
npm install vue-route
4.2.使用router
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: []
})
export default router
4.3.在main.ts进行挂载
import router from './router'
createApp(App).use(router).mount('#app')
4.4.自行配置路径关系
使用懒加载
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
redirect: '/main'
},
{
path: '/login',
component: () => import('../views/login/Login.vue')
},
{
path: '/main',
component: () => import('../views/main/Main.vue')
},
{
//未找到的路径
path: '/:pathMatch(.*)',
component: () => import('../views/not-found/NotFound.vue')
}
]
})
4.5.在App.vue中进行占位及跳转
<router-link to="/main">主要</router-link>
<router-link to="/login">登录</router-link>
<router-view></router-view>
五、状态管理(pinia)
5.1.安装pinia
npm install pinia
5.2.创建pinia
import { createPinia } from 'pinia'
const pinia = createPinia()
export default pinia
5.3.挂载pinia
import pinia from './store'
createApp(App).use(router).use(pinia).mount('#app')
5.4.创建store
import { defineStore } from 'pinia'
const useCounterStore = defineStore('counter', {
state: () => ({}),
getters: {},
actions: {}
})
export default useCounterStore
5.5.使用store
import useCounterStore from '@/store/counter'
const counterStore = useCounterStore()
六、axios的使用
6.1.安装axios
npm install axios
6.2.用ts二次封装axios
前面已封装
七、Element-Plus集成
7.1.安装Element-Plus
npm install element-plus --save
7.2.引入方式
-
完整引入
// main.ts import { createApp } from 'vue' import ElementPlus from 'element-plus' //引入CSS import 'element-plus/dist/index.css' import App from './App.vue' const app = createApp(App) app.use(ElementPlus) app.mount('#app')缺点:打包后的文件将会变大。
优点:使用组件时直接导入就行,方便快捷。
-
自动导入
首先你需要安装
unplugin-vue-components和unplugin-auto-import这两款插件npm install -D unplugin-vue-components unplugin-auto-import将下列代码插入到你的
Vite或Webpack的配置文件中-
Vite
// vite.config.ts import { defineConfig } from 'vite' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' export default defineConfig({ // ... plugins: [ // ... AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }), ], }) -
Webpack
// webpack.config.js const AutoImport = require('unplugin-auto-import/webpack') const Components = require('unplugin-vue-components/webpack') const { ElementPlusResolver } = require('unplugin-vue-components/resolvers') module.exports = { // ... plugins: [ AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }), ], } -
使其生成的两个文件生效
auto-imports.d.ts "components.d.ts
//tsconfig.json "include": [ "env.d.ts", "src/**/*", "src/**/*.vue", "auto-imports.d.ts", "components.d.ts" ] -
八、登录界面的开发
8.1.让登录框占满整个屏幕
- 不推荐
//最终组件都是要在app内,所以要设置app,html,body的占比
//index.html
<style>
#app, html, body {
height: 100%;
}
</style>
- 推荐
//App.vue
.App {
width: 100vw;
height: 100vh;
}
8.2.框
先通过element-plus搭建出基本框架
<div class="tabs">
<el-tabs type="border-card" stretch>
<el-tab-pane label="账号登录">
<div></div>
<div></div>
</el-tab-pane>
<el-tab-pane label="手机登录">
<div></div>
<div></div>
</el-tab-pane>
</el-tabs>
</div>
填充icons
同样需要导入icons
npm install @element-plus/icons-vue
-
同上有两种导入方式,如果你想像用例一样直接,你需要全局注册组件,才能够直接在项目里使用。
-
全局注册
// main.ts // 如果您正在使用CDN引入,请删除下面一行。 import * as ElementPlusIconsVue from '@element-plus/icons-vue' const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } -
自动导入
使用 unplugin-icons 和 unplugin-auto-import 从 iconify 中自动导入任何图标集
-
-
全局注册方式
//main.ts import registerIcons from './global/register-icons' app.use(registerIcons) //register-icons import type { App } from 'vue' import * as ElementPlusIconsVue from '@element-plus/icons-vue' function registerIcons(app: App<Element>) { for (const [key, component] of Object.entries(ElementPlusIconsVue)) { //注册全局组件 app.component(key, component) } } export default registerIcons -
icons使用
<div class="tabs"> <el-tabs type="border-card" stretch> <el-tab-pane label="账号登录"> <template #label> <div class="label"> <span class="label-icon"> <el-icon><UserFilled /></el-icon> </span> <span class="text">账号登陆</span> </div> </template> </el-tab-pane> <el-tab-pane label="手机登录"> <template #label> <div class="label"> <el-icon><Iphone /></el-icon> <span class="text">手机登录</span> </div> </template> </el-tab-pane> </el-tabs> </div>
8.3.底部
<!-- 底部 -->
<div class="cotrols">
<el-checkbox v-model="checked1" label="记住密码" size="large" />
<el-link type="primary">忘记密码</el-link>
</div>
<el-button class="login-btn" type="primary" size="large">立即登录</el-button>
8.4.监听登录方式
组件之间提供了v-model和name属性
<el-tabs type="border-card" stretch v-model="activename">
<el-tab-pane label="账号登录" name="account">
<el-tab-pane label="手机登录" name="phone">
//监听登录按钮的点击
function loginbtnclcik() {
if (activename.value === 'account') {
console.log('用户进行账号登录')
} else {
console.log('用户进行密码登录')
}
}
8.5.表单的功能实现
账号登录表单
使用插件提供的表单验证
-
//内容实现 <template> <div class="panel-account"> <el-form :model="account" :rules="accountRules" label-width="60px" size="large" > <el-form-item label="账号" prop="name"> <el-input v-model="account.name" /> </el-form-item> <el-form-item label="密码" prop="password"> <el-input v-model="account.password" show-password /> </el-form-item> </el-form> </div> </template> //rules实现 const accountRules: FormRules = { name: [ { required: true, message: '必须输入账号信息~', trigger: 'blur' }, { pattern: /^[a-z0-9]{6,20}$/, message: '必须是6~20位数字或字母组成', trigger: 'change' } ], password: [ { required: true, message: '必须输入密码信息~', trigger: 'blur' }, { pattern: /^[a-z0-9]{3}/, message: '必须是3位以上数字或密码组成', trigger: 'change' } ] } //要想使其能够输入需要变量来记录绑定,所以要在item上进行绑定 const account = reactive({ name: '', password: '' }) //message表示未满足时的信息 //trigger表示触发时机,change表示改变时触发,blur表示失去焦点时触发 //要想让其生效 //要将accountRules绑定到form大表单上,再在item上绑定prop="属性"
8.6.将账号密码传递给父组件
-
在子组件中定义一个方法并且将其暴露出去
-
//子组件 function loginAction() { console.log(account.name, account.password) } defineExpose({ loginAction }) //父组件 //绑定一个Ref <panel-account ref="accountRef" /> //因为PanelAccount相当于一个类,所创建的组件都是实例对象,但PanelAccount是一个值不能作为类型,所以要进行类型的转换 //typeof PanelAccount拿到构造器 const accountRef = ref<InstanceType<typeof PanelAccount>>()
-
-
点击登录登录判断是否符合规则
<el-form ref="formRef"> import type { FormRules, ElForm } from 'element-plus' const formRef = ref<InstanceType<typeof ElForm>>() //组件内部提供的方法 function loginAction() { formRef.value?.validate((valid) => { if (valid) { console.log('验证成功') } else { console.log('验证失败') } }) }-
验证失败时弹出弹窗
formRef.value?.validate((valid) => { if (valid) { console.log('验证成功') } else { ElMessage({ showClose: true, message: 'Oops, 请您输入正确格式', type: 'error' }) } }) //未显示原因是未引入ElMessage的样式 //解决一 //main.ts //全局引入样式 import 'element-plus/dist/index.css //解决二 //针对ElMessage和ElLoading进行引入 import 'element-plus/theme-chalk/el-message.css' //解决三 //使用插件 //1.安装插件 npm i -D vite-plugin-style-import npm install consola -D(依赖包) //vite.config.ts import { createStyleImportPlugin, ElementPlusResolve } from 'vite-plugin-style-import' //设置plugin createStyleImportPlugin({ resolves: [ElementPlusResolve()], libs: [ { libraryName: 'element-plus', esModule: true, resolveStyle: (name: string) => { return `element-plus/theme-chalk/${name}.css` } } ] })
-
8.7.用JS实现登录验证
function loginAction() {
formRef.value?.validate((valid) => {
if (valid) {
//1.获取用户输入的账号和密码
const name = account.name
const password = account.password
//2.向服务器发送网络请求(携带账号密码)
// accountLogin({ name, password }).then((res) => {
// console.log(res)
// })
if (name === 'icebin' && password == '123456') {
;(loginStore.id = '1'), (loginStore.name = 'icebin')
localCache.setCache('name', name)
localCache.setCache('password', password)
router.push('/main')
}
} else {
ElMessage({
showClose: true,
message: 'Oops, 请您输入正确格式',
type: 'error'
})
}
})
}
//自行封装的一个本地存储方式的类
enum CacheType {
local,
session
}
class Cache {
storage: Storage
constructor(type: CacheType) {
this.storage = type === CacheType.local ? localStorage : sessionStorage
}
setCache(key: string, value: any) {
if (value) {
this.storage.setItem(key, JSON.stringify(value))
}
}
getCache(key: string) {
const value = this.storage.getItem(key)
if (value) {
return JSON.parse(value)
}
}
removeCache(key: string) {
this.storage.removeItem(key)
}
clear() {
this.storage.clear()
}
}
const localCache = new Cache(CacheType.local)
const sessionCache = new Cache(CacheType.session)
export { localCache, sessionCache }