Vue 3.3 + Vite 4.3 + TypeScript 5+ Element-Plus:从零到一构建企业级后台管理系统(前后端开源)(四)

简介: Vue 3.3 + Vite 4.3 + TypeScript 5+ Element-Plus:从零到一构建企业级后台管理系统(前后端开源)(四)

组件封装

wangEditor 富文本

参考: wangEditor 官方文档


安装 wangEditor


npm install @wangeditor/editor @wangeditor/editor-for-vue@next

1

wangEditor 组件封装

</code></div><div><code>import { onBeforeUnmount, shallowRef, reactive, toRefs } from 'vue';</code></div><div><code>import { Editor, Toolbar } from '@wangeditor/editor-for-vue';</code></div><div><code>// API 引用</code></div><div><code>import { uploadFileApi } from '@/api/file';</code></div><div><code>const props = defineProps({</code></div><div><code>  modelValue: {</code></div><div><code>    type: [String],</code></div><div><code>    default: ''</code></div><div><code>  }</code></div><div><code>});</code></div><div><code>const emit = defineEmits(['update:modelValue']);</code></div><div><code>// 编辑器实例,必须用 shallowRef</code></div><div><code>const editorRef = shallowRef();</code></div><div><code>const state = reactive({</code></div><div><code>  toolbarConfig: {},</code></div><div><code>  editorConfig: {</code></div><div><code>    placeholder: '请输入内容...',</code></div><div><code>    MENU_CONF: {</code></div><div><code>      uploadImage: {</code></div><div><code>        // 自定义图片上传</code></div><div><code>        async customUpload(file: any, insertFn: any) {</code></div><div><code>          uploadFileApi(file).then(response => {</code></div><div><code>            const url = response.data.url;</code></div><div><code>            insertFn(url);</code></div><div><code>          });</code></div><div><code>        }</code></div><div><code>      }</code></div><div><code>    }</code></div><div><code>  },</code></div><div><code>  defaultHtml: props.modelValue,</code></div><div><code>  mode: 'default'</code></div><div><code>});</code></div><div><code>const { toolbarConfig, editorConfig, defaultHtml, mode } = toRefs(state);</code></div><div><code>const handleCreated = (editor: any) => {</code></div><div><code>  editorRef.value = editor; // 记录 editor 实例,重要!</code></div><div><code>};</code></div><div><code>function handleChange(editor: any) {</code></div><div><code>  emit('update:modelValue', editor.getHtml());</code></div><div><code>}</code></div><div><code>// 组件销毁时,也及时销毁编辑器</code></div><div><code>onBeforeUnmount(() => {</code></div><div><code>  const editor = editorRef.value;</code></div><div><code>  if (editor == null) return;</code></div><div><code>  editor.destroy();</code></div><div><code>});</code></div><div><code>


使用案例

</code></div><div><code>import Editor from '@/components/WangEditor/index.vue';</code></div><div><code>const value = ref('初始内容');</code></div><div><code>



效果预览

微信图片_20230706152812.png



Echarts 图表

参考:📊 Echarts 官方示例


安装 Echarts


npm install echarts

1

组件封装

 

</code></div><div><code>import * as echarts from 'echarts';</code></div><div><code>const props = defineProps({</code></div><div><code>  id: {</code></div><div><code>    type: String,</code></div><div><code>    default: 'barChart'</code></div><div><code>  },</code></div><div><code>  className: {</code></div><div><code>    type: String,</code></div><div><code>    default: ''</code></div><div><code>  },</code></div><div><code>  width: {</code></div><div><code>    type: String,</code></div><div><code>    default: '200px',</code></div><div><code>    required: true</code></div><div><code>  },</code></div><div><code>  height: {</code></div><div><code>    type: String,</code></div><div><code>    default: '200px',</code></div><div><code>    required: true</code></div><div><code>  }</code></div><div><code>});</code></div><div><code>const options = {</code></div><div><code>  grid: {</code></div><div><code>    left: '2%',</code></div><div><code>    right: '2%',</code></div><div><code>    bottom: '10%',</code></div><div><code>    containLabel: true</code></div><div><code>  },</code></div><div><code>  tooltip: {</code></div><div><code>    trigger: 'axis',</code></div><div><code>    axisPointer: {</code></div><div><code>      type: 'cross',</code></div><div><code>      crossStyle: {</code></div><div><code>        color: '#999'</code></div><div><code>      }</code></div><div><code>    }</code></div><div><code>  },</code></div><div><code>  legend: {</code></div><div><code>    x: 'center',</code></div><div><code>    y: 'bottom',</code></div><div><code>    data: ['收入', '毛利润', '收入增长率', '利润增长率'],</code></div><div><code>    textStyle: {</code></div><div><code>      color: '#999'</code></div><div><code>    }</code></div><div><code>  },</code></div><div><code>  xAxis: [</code></div><div><code>    {</code></div><div><code>      type: 'category',</code></div><div><code>      data: ['浙江', '北京', '上海', '广东', '深圳'],</code></div><div><code>      axisPointer: {</code></div><div><code>        type: 'shadow'</code></div><div><code>      }</code></div><div><code>    }</code></div><div><code>  ],</code></div><div><code>  yAxis: [</code></div><div><code>    {</code></div><div><code>      type: 'value',</code></div><div><code>      min: 0,</code></div><div><code>      max: 10000,</code></div><div><code>      interval: 2000,</code></div><div><code>      axisLabel: {</code></div><div><code>        formatter: '{value} '</code></div><div><code>      }</code></div><div><code>    },</code></div><div><code>    {</code></div><div><code>      type: 'value',</code></div><div><code>      min: 0,</code></div><div><code>      max: 100,</code></div><div><code>      interval: 20,</code></div><div><code>      axisLabel: {</code></div><div><code>        formatter: '{value}%'</code></div><div><code>      }</code></div><div><code>    }</code></div><div><code>  ],</code></div><div><code>  series: [</code></div><div><code>    {</code></div><div><code>      name: '收入',</code></div><div><code>      type: 'bar',</code></div><div><code>      data: [7000, 7100, 7200, 7300, 7400],</code></div><div><code>      barWidth: 20,</code></div><div><code>      itemStyle: {</code></div><div><code>        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [</code></div><div><code>          { offset: 0, color: '#83bff6' },</code></div><div><code>          { offset: 0.5, color: '#188df0' },</code></div><div><code>          { offset: 1, color: '#188df0' }</code></div><div><code>        ])</code></div><div><code>      }</code></div><div><code>    },</code></div><div><code>    {</code></div><div><code>      name: '毛利润',</code></div><div><code>      type: 'bar',</code></div><div><code>      data: [8000, 8200, 8400, 8600, 8800],</code></div><div><code>      barWidth: 20,</code></div><div><code>      itemStyle: {</code></div><div><code>        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [</code></div><div><code>          { offset: 0, color: '#25d73c' },</code></div><div><code>          { offset: 0.5, color: '#1bc23d' },</code></div><div><code>          { offset: 1, color: '#179e61' }</code></div><div><code>        ])</code></div><div><code>      }</code></div><div><code>    },</code></div><div><code>    {</code></div><div><code>      name: '收入增长率',</code></div><div><code>      type: 'line',</code></div><div><code>      yAxisIndex: 1,</code></div><div><code>      data: [60, 65, 70, 75, 80],</code></div><div><code>      itemStyle: {</code></div><div><code>        color: '#67C23A'</code></div><div><code>      }</code></div><div><code>    },</code></div><div><code>    {</code></div><div><code>      name: '利润增长率',</code></div><div><code>      type: 'line',</code></div><div><code>      yAxisIndex: 1,</code></div><div><code>      data: [70, 75, 80, 85, 90],</code></div><div><code>      itemStyle: {</code></div><div><code>        color: '#409EFF'</code></div><div><code>      }</code></div><div><code>    }</code></div><div><code>  ]</code></div><div><code>};</code></div><div><code>onMounted(() => {</code></div><div><code>  // 图表初始化</code></div><div><code>  const chart = echarts.init(</code></div><div><code>    document.getElementById(props.id) as HTMLDivElement</code></div><div><code>  );</code></div><div><code>  chart.setOption(options);</code></div><div><code>  // 大小自适应</code></div><div><code>  window.addEventListener('resize', () => {</code></div><div><code>    chart.resize();</code></div><div><code>  });</code></div><div><code>});</code></div><div><code>

组件使用

</code></div><div><code>import BarChart from './components/BarChart.vue';</code></div><div><code>


效果预览


微信图片_20230706152858.png


图标选择器

组件封装


</code></div><div><code>const props = defineProps({</code></div><div><code>  modelValue: {</code></div><div><code>    type: String,</code></div><div><code>    require: false</code></div><div><code>  }</code></div><div><code>});</code></div><div><code>const emit = defineEmits(['update:modelValue']);</code></div><div><code>const inputValue = toRef(props, 'modelValue');</code></div><div><code>const visible = ref(false); // 弹窗显示状态</code></div><div><code>const iconNames: string[] = []; // 所有的图标名称集合</code></div><div><code>const filterValue = ref(''); // 筛选的值</code></div><div><code>const filterIconNames = ref<string[]>([]); // 过滤后的图标名称集合</code></div><div><code>const iconSelectorRef = ref(null);</code></div><div><code>/**</code></div><div><code> * 加载 ICON</code></div><div><code> */</code></div><div><code>function loadIcons() {</code></div><div><code>  const icons = import.meta.glob('../../assets/icons/*.svg');</code></div><div><code>  for (const icon in icons) {</code></div><div><code>    const iconName = icon.split('assets/icons/')[1].split('.svg')[0];</code></div><div><code>    iconNames.push(iconName);</code></div><div><code>  }</code></div><div><code>  filterIconNames.value = iconNames;</code></div><div><code>}</code></div><div><code>/**</code></div><div><code> * 筛选图标</code></div><div><code> */</code></div><div><code>function handleFilter() {</code></div><div><code>  if (filterValue.value) {</code></div><div><code>    filterIconNames.value = iconNames.filter(iconName =></code></div><div><code>      iconName.includes(filterValue.value)</code></div><div><code>    );</code></div><div><code>  } else {</code></div><div><code>    filterIconNames.value = iconNames;</code></div><div><code>  }</code></div><div><code>}</code></div><div><code>/**</code></div><div><code> * 选择图标</code></div><div><code> */</code></div><div><code>function handleSelect(iconName: string) {</code></div><div><code>  emit('update:modelValue', iconName);</code></div><div><code>  visible.value = false;</code></div><div><code>}</code></div><div><code>/**</code></div><div><code> * 点击容器外的区域关闭弹窗 VueUse onClickOutside</code></div><div><code> */</code></div><div><code>onClickOutside(iconSelectorRef, () => (visible.value = false));</code></div><div><code>onMounted(() => {</code></div><div><code>  loadIcons();</code></div><div><code>});</code></div><div><code>





组件使用

</code></div><div><code>const iconName = ref('edit');</code></div><div><code>



效果预览

微信图片_20230706152918.gif



规范配置

代码统一规范

【vue3-element-admin】ESLint+Prettier+Stylelint+EditorConfig 约束和统一前端代码规范


Git 提交规范

【vue3-element-admin】Husky + Lint-staged + Commitlint + Commitizen + cz-git 配置 Git 提交规范


启动部署

项目启动

# 安装 pnpm

npm install pnpm -g

/

# 安装依赖

pnpm install


# 项目运行

pnpm run dev


项目部署

# 项目打包

pnpm run build:prod

1

2

生成的静态文件在工程根目录 dist 文件夹


FAQ

1: defineProps is not defined

问题描述


‘defineProps’ is not defined.eslint no-undef


微信图片_20230706152934.png


解决方案


根据 Eslint 官方解决方案描述,解析器使用 vue-eslint-parser v9.0.0 + 版本


微信图片_20230706152937.png


安装 vue-eslint-parser 解析器


npm install -D vue-eslint-parser

1

微信图片_20230706152942.png


.eslintrc.js 关键配置( v9.0.0 及以上版本无需配置编译宏 vue/setup-compiler-macros)如下 :


 parser: 'vue-eslint-parser',

 extends: [

   'eslint:recommended',

// ...  

 ],


重启 VSCode 已无报错提示


微信图片_20230706153023.png


2: Vite 首屏加载慢(白屏久)

问题描述


Vite 项目启动很快,但首次打开界面加载慢?


参考文章:为什么有人说 vite 快,有人却说 vite 慢


vite 启动时,并不像 webpack 那样做一个全量的打包构建,所以启动速度非常快。启动以后,浏览器发起请求时, Dev Server 要把请求需要的资源发送给浏览器,中间需要经历预构建、对请求文件做路径解析、加载源文件、对源文件做转换,然后才能把内容返回给浏览器,这个时间耗时蛮久的,导致白屏时间较长。


解决方案升级 vite 4.3 版本

https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md

微信图片_20230706153034.png


结语

本篇从项目介绍、环境准备、VSCode 的代码规范配置 、整合各种框架 、再到最后的启动部署,完整讲述如何基于 Vue3 + Vite4 + TypeScript + Element Plus 等主流技术栈从 0 到 1构建一个企业应用级管理前端框架。


项目有问题建议 issue 或者可以通过项目 关于我们 加入交流群反馈。

相关文章
|
2月前
|
JavaScript 前端开发 开发者
深入理解TypeScript:类型系统与实用技巧
【10月更文挑战第8天】深入理解TypeScript:类型系统与实用技巧
|
1月前
|
开发框架 JavaScript 前端开发
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势。通过明确的类型定义,TypeScript 能够在编码阶段发现潜在错误,提高代码质量;支持组件的清晰定义与复用,增强代码的可维护性;与 React、Vue 等框架结合,提供更佳的开发体验;适用于大型项目,优化代码结构和性能。随着 Web 技术的发展,TypeScript 的应用前景广阔,将继续引领 Web 开发的新趋势。
40 2
|
5月前
|
JavaScript 前端开发 开发工具
TypeScript的介绍,let age:number = xxx,可以直接看出数据类型,Type由微软开发,可以在任何浏览器和系统中运行,比较适合大型项目,TypeScript的安装
TypeScript的介绍,let age:number = xxx,可以直接看出数据类型,Type由微软开发,可以在任何浏览器和系统中运行,比较适合大型项目,TypeScript的安装
|
2月前
|
JavaScript 前端开发 开发者
深入理解TypeScript:类型系统与最佳实践
【10月更文挑战第8天】深入理解TypeScript:类型系统与最佳实践
|
1月前
|
JavaScript 前端开发 安全
TypeScript进阶:类型系统与高级类型的应用
【10月更文挑战第25天】TypeScript作为JavaScript的超集,其类型系统是其核心特性之一。本文通过代码示例介绍了TypeScript的基本数据类型、联合类型、交叉类型、泛型和条件类型等高级类型的应用。这些特性不仅提高了代码的可读性和可维护性,还帮助开发者构建更健壮的应用程序。
33 0
|
2月前
|
JavaScript 前端开发 开发者
深入理解TypeScript:类型系统与实用技巧
【10月更文挑战第8天】深入理解TypeScript:类型系统与实用技巧
|
4月前
|
JavaScript 前端开发 安全
【技术革新】Vue.js + TypeScript:如何让前端开发既高效又安心?
【8月更文挑战第30天】在使用Vue.js构建前端应用时,结合TypeScript能显著提升代码质量和开发效率。TypeScript作为JavaScript的超集,通过添加静态类型检查帮助早期发现错误,减少运行时问题。本文通过具体案例展示如何在Vue.js项目中集成TypeScript,并利用其类型系统提升代码质量。首先,使用Vue CLI创建支持TypeScript的新项目,然后构建一个简单的待办事项应用,通过定义接口描述数据结构并在组件中使用类型注解,确保代码符合预期并提供更好的编辑器支持。
91 0
|
4月前
|
JavaScript 前端开发 安全
立等可取的 Vue + Typescript 函数式组件实战
立等可取的 Vue + Typescript 函数式组件实战
|
4月前
|
JavaScript 前端开发 安全
TypeScript:解锁JavaScript的超级英雄模式!类型系统如何化身守护神,拯救你的代码免于崩溃与混乱,戏剧性变革开发体验!
【8月更文挑战第22天】TypeScript作为JavaScript的超集,引入了强大的类型系统,提升了编程的安全性和效率。本文通过案例展示TypeScript如何增强JavaScript:1) 显式类型声明确保函数参数与返回值的准确性;2) 接口和类加强类型检查,保证对象结构符合预期;3) 泛型编程提高代码复用性和灵活性。这些特性共同推动了前端开发的标准化和规模化。
70 0
|
5月前
|
存储 JavaScript 开发者
【Vue3+TypeScript】CRM系统项目搭建之 — 关于 VUE3 语法新变化(四)
【Vue3+TypeScript】CRM系统项目搭建之 — 关于 VUE3 语法新变化(四)
56 0

热门文章

最新文章