项目的创建
使用 create-vue 脚手架创建项目。
1.执行创建命令
pnpm create vue
# or
npm init vue@latest
# or
yarn create vue
2.选择项目依赖内容。
✔ Project name: … //项目名
✔ Add TypeScript? … No / `Yes`
✔ Add JSX Support? … `No` / Yes
✔ Add Vue Router for Single Page Application development? … No / `Yes`
✔ Add Pinia for state management? … No / `Yes`
✔ Add Vitest for Unit Testing? … `No` / Yes
✔ Add Cypress for both Unit and End-to-End testing? … `No` / Yes
✔ Add ESLint for code quality? … No / `Yes`
✔ Add Prettier for code formatting? … No / `Yes`
Scaffolding project in /Users/zhousg/Desktop/patient-h5-100...
Done. Now run:
cd //项目名
pnpm install
pnpm lint
pnpm dev
项目目录结构调整
./src
├── assets # 静态资源,图片...
├── components # 通用组件
├── composable # 组合功能通用函数 - 新增的
├── icons # svg 图标
├── router # 路由
│ └── index.ts
├── services # 接口服务 API - 新增的
├── stores # 状态仓库
├── styles # 样式 - 新增的
│ └── main.scss
├── types # TS 类型 - 新增的
├── utils # 工具函数 - 新增的
├── views # 页面 - 新增的
├── App.vue # 根组件
└── main.ts # 入口文件
默认生成的路由相关代码解析
import {
createRouter,
createWebHistory
} from 'vue-router'
// createRouter 创建路由实例
// createWebHistory() 是开启history模块
// createWebHashHistory() 是开启hash模式
// vite 的配置 import.meta.env.BASE_URL 是路由的基准地址,默认是 ’/‘
// https://vitejs.dev/guide/build.html#public-base-path
// 如果将来你部署的域名路径是:http://xxx/my-path/user
// vite.config.ts 添加配置 base: my-path,路由这就会加上 my-path 前缀了
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: []
})
export default router
用户状态仓库
- 准备用户信息的类型,
types/user.d.ts
。
/* 用户信息 */
export type User = {
/* token令牌 */
token: string
/* 用户ID */
id: string
/* 用户名称 */
account: string
/* 手机号 */
mobile: string
/* 头像 */
avatar: string
}
2.设置和删除用户信息,stores/user.ts
。
import type { User } from '@/types/user'
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useUserStore = defineStore('cp-user', () => {
// 用户信息
const user = ref < User > ()
// 设置用户,登录后使用
const setUser = (u: User) => {
user.value = u
}
// 清空用户,退出后使用
const delUser = () => {
user.value = undefined
}
return {
user,
setUser,
delUser
}
})
数据的持久化
使用 pinia-plugin-persistedstate 实现 Pinia 仓库状态持久化,且完成测试。
- 安装。
pnpm i pinia-plugin-persistedstate
# or
npm i pinia-plugin-persistedstate
# or
yarn add pinia-plugin-persistedstate
2.使用 main.ts
。
++ import persist from 'pinia-plugin-persistedstate'
const app = createApp(App)
// 注意使用的方式
++ app.use(createPinia().use(persist))
3.配置 stores/user.ts
。
import type { User } from '@/types/user'
import { defineStore } from 'pinia'
import { ref } from 'vue'
export default defineStore(
'cp-user',
() => {
// 用户信息
const user = ref<User>()
// 设置用户,登录后使用
const setUser = (u: User) => {
user.value = u
}
// 清空用户,退出后使用
const delUser = () => {
user.value = undefined
}
return { user, setUser, delUser }
},
++ {
++ persist: true
++ }
)
4.测试 App.vue
。
<script setup lang="ts">
// #1
import useUserStore from './stores/user'
// #2
const store = useUserStore()
</script>
<template>
<!-- #3 -->
<p>{{ store.user }}</p>
<!-- 如果这校验失败,可以修改 .eslintrc.cjs 中的 printWidth -->
<button @click="store.setUser({ id: '1', mobile: '1', account: '1', avatar: '1', token: '1' })">
登录
</button>
<button @click="store.delUser()">退出</button>
</template>
抽取 Pinia 代码(纯代码优化)
抽取 Pinia 实例代码
stores/index
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
import { useUserStore } from './user'
export const useStore = () => {
return {
userStore: useUserStore()
}
}
// 创建 Pinia 实例
const pinia = createPinia()
// 使用 Pinia 插件
pinia.use(persist)
// 导出 Pinia 实例,给 main 使用
export default pinia
main.ts
import App from './App.vue'
import router from './router'
import pinia from './stores'
const app = createApp(App)
app.use(pinia)
app.use(router)
app.mount('#app')
stores 统一导出
统一导出,代码简洁,入口唯一, store/index
。
// 写法 1
// import { useUserStore } from './user'
// export { useUserStore }
// 写法 2
// export { useUserStore } from './user'
// 写法 3
export * from './user'
App.vue
import { useUserStore } from './stores'
vant 组件库使用
[vant]https://vant-contrib.gitee.io/vant/#/zh-CN/quickstart#dao-ru-suo-you-zu-jian-bu-tui-jian。
1.安装vant
# Vue3 项目,安装最新版 Vant
npm i vant
# 通过 yarn 安装
yarn add vant
# 通过 pnpm 安装
pnpm add vant
2.引入样式,main.ts
。
import { createApp } from 'vue'
import App from './App.vue'
import pinia from './stores'
import router from './router'
// 样式全局使用
import 'vant/lib/index.css'
import './styles/main.scss'
const app = createApp(App)
app.use(pinia)
app.use(router)
app.mount('#app')
3.组件按需使用,App.vue
。
<script setup lang="ts">
import {
Button as VanButton
} from 'vant'
</script>
<template>
<van-button type="primary">按钮</van-button>
</template>
实现 Vant 组件的自动按需加载
1.安装
pnpm add unplugin-vue-components -D
2.配置,vite.config.ts
。
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// #1
++ import Components from 'unplugin-vue-components/vite'
// #2
++ import { VantResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
// #3 自动导入的插件
++ Components({
++ // #5 默认 true,开启自动生成组件的类型定义文件,而 vant 已经自带类型了,无需生成
++ dts: false,
++ // #4 main.ts 已经引入了所有的 vant 样式,不需要自动导入样式,只需要自动导入组件即可
++ resolvers: [VantResolver({ importStyle: false })]
++ })
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
3.组件中直接使用即可。
移动端的适配(使用 vw 完成移动端适配)
- 安装(https://vant-contrib.gitee.io/vant/#/zh-CN/advanced-usage#viewport-bu-ju)
npm install postcss-px-to-viewport -D
# or
yarn add -D postcss-px-to-viewport
# or
pnpm add -D postcss-px-to-viewport
2.配置 postcss.config.js
(没有就自己创建)
// eslint-disable-next-line no-undef
module.exports = {
plugins: {
'postcss-px-to-viewport': {
// 以设备宽度 375 为基准计算 vw 的值
// 假如 100px 的 div
// 375 宽度下,最终转换出的 vw 应该是:x / 100vw = 100px / 375px,x 等于 26.66vw
// 而转换出来的 26.66vw 自然在不同的设备宽度下所表示的 div 宽度会不一样,例如 414 设备下
// 26.66vw / 100vw = div 宽度 / 414px
viewportWidth: 375,
},
},
};
3.重启项目
请求实例封装
1.基本模板准备,utils/request.ts
import axios from 'axios'
const instance = axios.create({
// TODO 1. 基础地址,超时时间
})
instance.interceptors.request.use(
(config) => {
// TODO 2. 携带 token
return config
},
(err) => Promise.reject(err)
)
instance.interceptors.response.use(
(res) => {
// TODO 3. 处理业务失败
// TODO 4. 摘取核心响应数据
return res
},
(err) => {
// TODO 5. 处理 401 错误
return Promise.reject(err)
}
)
export default instance
2.基本封装。
import { useUserStore } from '@/stores'
import router from '@/router'
import axios from 'axios'
import { showToast } from 'vant'
// 1. 新 axios 实例,基础配置
const baseURL = 'https://XXXX/' //基地址
const instance = axios.create({
baseURL,
timeout: 10000
})
// 2. 请求拦截器,携带 token
instance.interceptors.request.use(
(config) => {
const store = useUserStore()
if (store.user?.token && config.headers) {
config.headers['Authorization'] = `Bearer ${store.user?.token}`
}
return config
},
(err) => Promise.reject(err)
)
// 3. 响应拦截器,剥离无效数据,401 拦截
instance.interceptors.response.use(
(res) => {
// 后台约定,响应成功,但是 code 不是 10000,是业务逻辑失败
if (res.data?.code !== 10000) {
showToast(res.data?.message || '网络异常')
return Promise.reject(res.data)
}
// 业务逻辑成功,返回响应数据,作为 axios 成功的结果
return res.data
},
(err) => {
if (err.response.status === 401) {
// 删除用户信息
const store = useUserStore()
store.delUser()
// 跳转登录,带上接口失效所在页面的地址,登录完成后回跳使用
router.push(`/login?returnUrl=${router.currentRoute.value.fullPath}`)
}
return Promise.reject(err)
}
)
export { baseURL, instance }
请求函数封装
导出一个通用的请求工具函数,支持设置响应数据类型。(utils/request.ts接着写)
import axios, { type Method } from 'axios'
// 4. 请求工具函数
const request = (url: string, method: Method = 'get', submitData?: object) => {
return instance.request({
url,
method,
[method.toLowerCase() === 'get' ? 'params' : 'data']: submitData
})
}
instance.request 的第二个泛型参数会被直接当做返回数据的类型。
const request = (url: string, method: Method = 'get', submitData?: object) => {
// !第二个泛型参数会被直接当做返回数据的类型
return instance.request<User, {
code: string
message: string
data: User
}>({
url,
method,
[method.toLowerCase() === 'get' ? 'params' : 'data']: submitData
})
}
request('/user').then((res) => {
res.
})
User 类型肯定不能写死,继续封装 request 泛型函数。
// #1
const request = <T>(
url: string,
method: Method = 'get',
submitData?: object
) => {
// #2
return instance.request<
T,
{
code: string
message: string
data: T
}
>({
url,
method,
[method.toLowerCase() === 'get' ? 'params' : 'data']: submitData
})
}
// #3
request<User>('/user').then((res) => {
res.data.account
})
继续抽离第二个泛型参数为一个泛型 type。
type Data<T> = {
code: number
message: string
data: T
}
const request = <T>(url: string, method: Method = 'get', submitData?: object) => {
return instance.request<T, Data<T>>({
url,
method,
[method.toLowerCase() === 'get' ? 'params' : 'data']: submitData
})
}
用户登录
- 准备页面结构,
views/Login/index.vue
。
<template>
<div class="login-page">
登录
</div>
</template>
- 配置路由规则,
router/index.ts
。
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{ path: '/login', component: () => import('@/views/Login/index.vue') }
]
})
export default router
- 准备路由出口,
App.vue
。
<template>
<router-view></router-view>
</template>
CSS 主题定制
- 如何定义和使用 CSS 变量
:root { --main: #999; }
a { color: var(--main)}
- 覆盖 vant 主题色,
styles/main.scss
。
:root {
--cp-primary: #16c2a3;
--cp-plain: #eaf8f6;
--cp-orange: #fca21c;
--cp-text1: #121826;
--cp-text2: #3c3e42;
--cp-text3: #6f6f6f;
--cp-tag: #848484;
--cp-dark: #979797;
--cp-tip: #c3c3c5;
--cp-disable: #d9dbde;
--cp-line: #ededed;
--cp-bg: #f6f7f9;
--cp-price: #eb5757;
// 覆盖 Vant 主体色
// 官方文档:ConfigProvider 全局配置
--van-primary-color: var(--cp-primary);
}
- 测试主题色,
views/Login/index.vue
<script lang="ts" setup>
import { Button as VanButton } from 'vant'
</script>
<template>
<div class="login-page">
<van-button type="primary">按钮</van-button>
</div>
</template>
CpNavBar 组件封装
掌握 van-nav-bar 组件,封装自己的nav-bar 组件。
components/CpNavBar.vue
<script setup lang="ts">
import {
useRouter
} from 'vue-router'
const router = useRouter()
// #1 点击左侧返回
const onClickLeft = () => {
// 判断历史记录中是否有回退记录
if (history.state?.back) {
router.back()
} else {
router.push('/')
}
}
// #2 接收 title 和 rightText
defineProps < {
title ? : string
rightText ? : string
} > ()
// #3 右侧的点击,让外界决定做什么事情
const emit = defineEmits < {
(e: 'click-right'): void
} > ()
const onClickRight = () => {
emit('click-right')
}
</script>
<template>
<van-nav-bar left-arrow @click-left="onClickLeft" fixed :title="title" :right-text="rightText" @click-right="onClickRight"></van-nav-bar>
</template>
表单校验
提取表单校验规则(为了其他页面复用)
utils/rules.ts
// 表单校验
const mobileRules = [
{ required: true, message: '请输入手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' }
]
const passwordRules = [
{ required: true, message: '请输入密码' },
{ pattern: /^\w{8,24}$/, message: '密码需8-24个字符' }
]
export { mobileRules, passwordRules }
单个表单项校验
Login/index.vue
import { mobileRules, passwordRules } from '@/utils/rules'
const mobile = ref('13212345678')
const password = ref('13212345678')
const agree = ref(false)
+ <van-field v-model="mobile" :rules="mobileRules" placeholder="请输入手机号" type="tel"></van-field>
<van-field
v-model="password"
+ :rules="passwordRules"
placeholder="请输入密码"
type="password"
>
整体表单校验
思路: 按钮上 native-type="submit"
+ 表单 @submit
它会自动去校验填写了rules的字段。
- 设置button组件为原生 submit 类型按钮
Login/index.vue
<van-button
block round
type="primary"
native-type="submit"> 登 录 </van-button>
- 监听表单submit 事件
<van-form autocomplete="off" @submit="login">
import { mobileRules, passwordRules } from '@/utils/rules'
import { ref } from 'vue'
import { showToast } from 'vant'
const mobile = ref('13212345678')
const password = ref('13212345678')
const show = ref(false)
const agree = ref(false)
// 表单提交
const login = () => {
if (!agree.value) return showToast('请勾选我已同意')
// 验证完毕,进行登录
}
图标组件-配置 svg 图标
安装插件
pnpm install vite-plugin-svg-icons -D
配置插件
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { VantResolver } from 'unplugin-vue-components/resolvers'
// #1
++ import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
// #2
++ import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
Components({
dts: false,
resolvers: [VantResolver({ importStyle: false })]
}),
// #3
++ createSvgIconsPlugin({
++ iconDirs: [path.resolve(process.cwd(), 'src/icons')]
++ })
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
从main.ts导入
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import pinia from './stores'
// mark
++ import 'virtual:svg-icons-register'
import 'vant/lib/index.css'
import './styles/main.scss'
const app = createApp(App)
app.use(pinia)
app.use(router)
app.mount('#app')
测试使用 svg 精灵地图(页面中)
<svg aria-hidden="true">
<!-- #icon-文件夹名称-图片名称 -->
<use href="#icon-login-eye-off" />
</svg>
图标组件-封装 svg 组件
components/CpIcon.vue
<script setup lang="ts">
// 提供 name 属性即可
defineProps < {
name: string
} > ()
</script>
<template>
<svg aria-hidden="true" class="cp-icon">
<use :href="`#icon-${name}`" />
</svg>
</template>
<style lang="scss" scoped>
.cp-icon {
// 和 font-size 一样大
width: 1em;
height: 1em;
}
</style>
类型 types/components.d.ts。
import CpNavBar from '@/components/CpNavBar.vue'
import CpIcon from '@/components/CpIcon.vue'
declare module 'vue' {
interface GlobalComponents {
CpNavBar: typeof CpNavBar
CpIcon: typeof CpIcon
}
}
有些图标可以根据 style 中 color 的值来设置颜色,图标是否有这个功能取决于 UI 做图片时否开启。
测试使用
<cp-icon name="login-eye-on"></cp-icon>
<cp-icon name="login-eye-off"></cp-icon>
切换密码可见
双向绑定数据,并实现切换密码可见功能,views/Login/index.vue
。
// #1 定义表单数据
const mobile = ref('')
const password = ref('')
// #3 定义控制密码是否显示的变量
++ const show = ref(false)
<!-- #2 v-model 进行绑定 -->
<van-field v-model="mobile" placeholder="请输入手机号" type="tel"></van-field>
<!-- #4 把 show 作用于框的类型 -->
<van-field v-model="password" placeholder="请输入密码" :type="show ? 'text' : 'password'">
<template #button>
<!-- #5 把 show 作用于图标的类型 -->
<!-- #6 榜单点击事件,控制 show 的切换-->
<cp-icon @click="show = !show" :name="`login-eye-${show ? 'on' : 'off'}`"></cp-icon>
</template>
</van-field>
手机号密码登录
定义登录的 API 函数
services/user.ts
import type { User } from '@/types/user'
import { request } from '@/utils/request'
// 密码登录
export const loginByPassword = (mobile: string, password: string) => request<User>('/login/password', 'POST', { mobile, password })
进行登录
views/Login/index.vue
import { loginByPassword } from '@/services/user'
import { useUserStore } from '@/stores'
import { useRoute, useRouter } from 'vue-router'
const store = useUserStore()
const router = useRouter()
const route = useRoute()
// 表单提交
const login = async () => {
if (!agree.value) return Toast('请勾选我已同意')
// 验证完毕,进行登录
const res = await loginByPassword(mobile.value, password.value)
store.setUser(res.data)
// 如果有回跳地址就进行回跳,没有跳转到个人中心
router.replace((route.query.returnUrl as string) || '/user')
showToast('登录成功')
}
短信登录-切换效果
添加短信登录登录的界面切换功能,添加 code 的校验。
views/Login/index.vue
// #1 定义一个切换的变量
const isPass = ref(true)
<div class="login-head">
<!-- #2 控制标题 -->
<h3>{{ isPass ? '密码登录' : '短信验证码登录' }}</h3>
<!-- #4 给 a 链接绑定点击事件,修改 isPass -->
<a href="javascript:;" @click="isPass = !isPass">
<!-- #3 控制按钮内容 -->
<span>{{ !isPass ? '密码登录' : '短信验证码登录' }}</span>
<van-icon name="arrow"></van-icon>
</a>
</div>
完成表单项切换
<van-field v-if="isPass" v-model="password" :rules="passwordRules" placeholder="请输入密码" :type="show ? 'text' : 'password'">
<template #button>
<cp-icon @click="show = !show" :name="`login-eye-${show ? 'on' : 'off'}`"></cp-icon>
</template>
</van-field>
<van-field v-else placeholder="短信验证码">
<template #button>
<span class="btn-send">发送验证码</span>
</template>
</van-field>
完成 code 校验
utils/rules.ts 补充定义校验规则
const codeRules = [
{ required: true, message: '请输入验证码' },
{ pattern: /^\d{6}$/, message: '验证码6个数字' }
]
export { mobileRules, passwordRules, codeRules }
使用规则 views/Login/index.vue
<script lang="ts" setup>
import { codeRules } from '@/utils/rules'
const code = ref('')
</script>
<van-field v-else v-model="code" :rules="codeRules" placeholder="短信验证码">
短信登录-发送短信
实现点击按钮发送验证码和倒计时的功能。
- 可以先去定义 CodeType 的类型(types/user.d.ts)
// 短信验证码类型,登录|注册|修改手机号|忘记密码|绑定手机号
export type CodeType = 'login' | 'register' | 'changeMobile' | 'forgetPassword' | 'bindMobile'
- 封装 API 接口函数(services/user.ts)
import { request } from '@/utils/request'
import type { CodeType } from '@/types/user'
// 发送验证码
export const sendMobileCode = (mobile: string, type: CodeType) => request('/code', 'GET', { mobile, type })
- 给发送验证码按钮绑定点击事件和回调,校验是否正在倒计时(views/Login/index.vue)
<span class="btn-send" @click="send">发送验证码</span>
const time = ref(0)
const send = async () => {
// 如果正在进行倒计时,此时 time.value 的值大于 0,不允许发送验证码
if (time.value > 0) return
}
- 点击发送验证码时,单独校验手机表单项
<!-- 绑定 name 属性 -->
<van-field v-model="mobile" name="mobile" :rules="mobileRules" placeholder="请输入手机号" type="tel"></van-field>
import { type FormInstance } from 'vant'
const form = ref<FormInstance>()
const time = ref(0)
const send = async () => {
if (time.value > 0) return
// 验证不通过报错,阻止程序继续执行
await form.value?.validate('mobile')
}
<!-- 把 form 变量和 van-form 的 ref 属性进行绑定 -->
<van-form autocomplete="off" @submit="login" ref="form">
- 发送短信验证码。
import { sendMobileCode } from '@/services/user'
import { showToast } from 'vant'
const send = async () => {
// ...
await sendMobileCode(mobile.value, 'login')
showToast('发送成功')
}
进行倒计时
<van-field v-else v-model="code" :rules="codeRules" placeholder="短信验证码">
<template #button>
<span v-if="time === 0" class="btn-send" @click="send">发送验证码</span>
<span v-else class="btn-send">{{ time }}秒后重新发送</span>
</template>
</van-field>
import { type FormInstance } from 'vant' //
const form = ref<FormInstance>()
const time = ref(0)
const send = async () => {
// 如果正在进行倒计时,此时 time.value 的值大于 0,不允许发送验证码
if (time.value > 0) return
// 验证不通过报错,阻止程序继续执行
await form.value?.validate('mobile') //调用表单实例的validate方法,验证手机号是否填写正确。
await sendMobileCode(mobile.value, 'login') //发送获取验证码的请求
showToast('发送成功')
const startTimer = () => {
const duration: number = 10
time.value = duration
const timerInterval = setInterval(() => {
time.value--
if (time.value === 0) {
clearInterval(timerInterval)
}
}, 1000)
}
startTimer()
}
短信登录-进行登录
1. 封装接口 API,services/user.ts
。
// 短信登录
export const loginByMobile = (mobile: string, code: string) => request<User>('/login', 'POST', { mobile, code })
2. 合并短信登录,views/Login/index.vue
。
const login = async () => {
if (!agree.value) return showToast('请勾选我已同意')
if (code.value !== '') {
const res = await loginByCode(mobile.value, code.value)
store.setUser(res.data)
} else {
// 验证完毕,进行登录
const res = await loginByPassword(mobile.value, password.value)
store.setUser(res.data)
}
// 如果有回跳地址就进行回跳,没有跳转到个人中心
router.replace((route.query.returnUrl as string) || '/user')
showToast('登录成功')
}
枚举基本语法
作用:表示一组明确可选的值,使用该类型后,约定只能使用这组常量中的其中一个,和字面量类型配合联合类型类似。
enums/index.ts
export {}
// 默认是从 0 开始自增的数值
// 也可以指定初始值,Up = 10,后面是从 10 开始自增
// 成员也可以是字符串,例如 Up = 'Up',但是后面的值都需要使用字符串
enum Direction {
Up,
Down,
Left,
Right
}
// 使用枚举类型
const changeDirection = (direction: Direction) => {
console.log(direction)
}
// 好处:不会传错且具有语义
changeDirection(Direction.Up)
枚举使用场景
枚举可用于给一组没有语义的可选值,给它们添加类型。
比如后台给的数据:
- 0 是男,1 是女;
- 1 是待付款,5 是已付款,8 是已完成
通过枚举可以让成员更加语义化,提高代码可读性。
export {}
// 性别
enum GenderType {
Boy,
Girl
}
const showGender = (gender: GenderType) => {
if (gender === GenderType.Boy) {
console.log('性别:男')
}
}
showGender(GenderType.Boy)
// 订单状态
enum OrderStatus {
UnPay = 1,
Payed = 5,
Complete = 8
}
const showOrderStatus = (status: OrderStatus) => {
if (status === OrderStatus.Complete) {
console.log('状态:已完成')
}
}
showOrderStatus(OrderStatus.Complete)
既然枚举类型也可以当做值使用,所以它不能够写在 d.ts
文件中。
Partial全部可选/Pick选一部分
import { ConsultType, IllnessTime, ConsultLevel } from '@/enums'
export type Consult = {
id: string
type: ConsultType
illnessType: ConsultLevel
depId: string
illnessDesc: string
illnessTime: IllnessTime
consultFlag: 0 | 1
pictures: Image[]
patientId: string
couponId: string
}
// 全部可选,将所有的数据类型都变成可选的(?)
++ export type PartialConsult = Partial<Consult>
// 从PartialConsult中选一部分属性,生成新类型
++ export type ConsultIllness = Pick< PartialConsult, 'illnessDesc' | 'illnessTime' >
**枚举使用场景**
枚举可用于给一组**没有语义**的可选值,给它们添加类型。
比如后台给的数据:
- 0 是男,1 是女;
- 1 是待付款,5 是已付款,8 是已完成
通过枚举可以让成员更加语义化,提高代码可读性。
```js
export {}
// 性别
enum GenderType {
Boy,
Girl
}
const showGender = (gender: GenderType) => {
if (gender === GenderType.Boy) {
console.log('性别:男')
}
}
showGender(GenderType.Boy)
// 订单状态
enum OrderStatus {
UnPay = 1,
Payed = 5,
Complete = 8
}
const showOrderStatus = (status: OrderStatus) => {
if (status === OrderStatus.Complete) {
console.log('状态:已完成')
}
}
showOrderStatus(OrderStatus.Complete)
既然枚举类型也可以当做值使用,所以它不能够写在 d.ts
文件中。
Partial全部可选/Pick选一部分
import { ConsultType, IllnessTime, ConsultLevel } from '@/enums'
export type Consult = {
id: string
type: ConsultType
illnessType: ConsultLevel
depId: string
illnessDesc: string
illnessTime: IllnessTime
consultFlag: 0 | 1
pictures: Image[]
patientId: string
couponId: string
}
// 全部可选,将所有的数据类型都变成可选的(?)
++ export type PartialConsult = Partial<Consult>
// 从PartialConsult中选一部分属性,生成新类型
++ export type ConsultIllness = Pick< PartialConsult, 'illnessDesc' | 'illnessTime' >