?? 技術棧

技術棧描述官網
Vue3漸進式JavaScript框架https://cn.vuejs.org/
Element-plus基于 Vue 3,面向設計師和開發者的組件庫https://element-plus.org/zh-CN/
Vite前端構建工具https://vitejs.cn/vite3-cn/
Pinia符合直覺的 Vue.js 狀態管理庫https://pinia.web3doc.top/
Echarts一個基于 JavaScript 的開源可視化圖表庫https://echarts.apache.org/zh/index.html
VueUse基于Vue組合式API的實用工具集https://www.vueusejs.com/
animate.css一個現成的跨瀏覽器動畫庫https://animate.style/
wangEditor開源 Web 富文本編輯器,開箱即用,配置簡單https://www.wangeditor.com/

?? 項目基本配置

項目全局配置

代碼統一規范

.eslintrc.cjs

// @see https://eslint.bootcss.com/docs/rules/
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
jest: true,
},
/* 指定如何解析語法 */
parser: 'vue-eslint-parser',
/** 優先級低于 parse 的語法解析配置 */
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parser: '@typescript-eslint/parser',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
},
},
/* 繼承已有的規則 */
extends: [
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
plugins: ['vue', '@typescript-eslint'],
/*
* "off" 或 0 ==> 關閉規則
* "warn" 或 1 ==> 打開的規則作為警告(不影響代碼執行)
* "error" 或 2 ==> 規則作為一個錯誤(代碼不能執行,界面報錯)
*/
rules: {
// eslint(https://eslint.bootcss.com/docs/rules/)
'no-var': 'error', // 要求使用 let 或 const 而不是 var
'no-multiple-empty-lines': ['warn', { max: 1 }], // 不允許多個空行
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-unexpected-multiline': 'error', // 禁止空余的多行
'no-useless-escape': 'off', // 禁止不必要的轉義字符

// typeScript (https://typescript-eslint.io/rules)
'@typescript-eslint/no-unused-vars': 'off', // 禁止定義未使用的變量
'@typescript-eslint/prefer-ts-expect-error': 'off', // 禁止使用 @ts-ignore
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 類型
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定義 TypeScript 模塊和命名空間。
'@typescript-eslint/semi': 'off',

// eslint-plugin-vue (https://eslint.vuejs.org/rules/)
'vue/multi-word-component-names': 'off', // 要求組件名稱始終為 “-” 鏈接的單詞
'vue/script-setup-uses-vars': 'error', // 防止<script setup>使用的變量<template>被標記為未使用
'vue/no-mutating-props': 'off', // 不允許組件 prop的改變
'vue/attribute-hyphenation': 'off', // 對模板中的自定義組件強制執行屬性命名樣式
},
}

.prettierrc.json

{
"singleQuote": false,
"semi": false,
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "ignore",
"endOfLine": "auto",
"trailingComma": "all",
"tabWidth": 2
}

stylelintrc.cjs

// @see https://stylelint.bootcss.com/
// 美化css書寫的樣式
module.exports = {
extends: [
'stylelint-config-standard', // 配置stylelint拓展插件
'stylelint-config-html/vue', // 配置 vue 中 template 樣式格式化
'stylelint-config-standard-scss', // 配置stylelint scss插件
'stylelint-config-recommended-vue/scss', // 配置 vue 中 scss 樣式格式化
'stylelint-config-recess-order', // 配置stylelint css屬性書寫順序插件,
'stylelint-config-prettier', // 配置stylelint和prettier兼容
],
overrides: [
{
files: ['**/*.(scss|css|vue|html)'],
customSyntax: 'postcss-scss',
},
{
files: ['**/*.(html|vue)'],
customSyntax: 'postcss-html',
},
],
ignoreFiles: [
'**/*.js',
'**/*.jsx',
'**/*.tsx',
'**/*.ts',
'**/*.json',
'**/*.md',
'**/*.yaml',
],
/**
* null => 關閉該規則
* always => 必須
*/
rules: {
'value-keyword-case': null, // 在 css 中使用 v-bind,不報錯
'no-descending-specificity': null, // 禁止在具有較高優先級的選擇器后出現被其覆蓋的較低優先級的選擇器
'function-url-quotes': 'always', // 要求或禁止 URL 的引號 "always(必須加上引號)"|"never(沒有引號)"
'no-empty-source': null, // 關閉禁止空源碼
'selector-class-pattern': null, // 關閉強制選擇器類名的格式
'property-no-unknown': null, // 禁止未知的屬性(true 為不允許)
'block-opening-brace-space-before': 'always', //大括號之前必須有一個空格或不能有空白符
'value-no-vendor-prefix': null, // 關閉 屬性值前綴 --webkit-box
'property-no-vendor-prefix': null, // 關閉 屬性前綴 -webkit-mask
'selector-pseudo-class-no-unknown': [
// 不允許未知的選擇器
true,
{
ignorePseudoClasses: ['global', 'v-deep', 'deep'], // 忽略屬性,修改element默認樣式的時候能使用到
},
],
},
}

?? 按鈕主題色

style/element/index.scss中配置

/* 只需要重寫你需要的即可 */
@forward 'element-plus/theme-chalk/src/common/var.scss' with ($colors: (
'primary': (// 主色
'base':#0F5197,
),
'success': ( // 成功色
'base': #199D33,
),
'info': ('base': #4e4f51,
),
'warning': ( // 警告色
'base': #e26f03,
),
'danger': ( // 危險色
'base': #de2e06,
),
'error': ( // 危險色
'base': #de2e06,
),
));

vite.config.ts 配置

 css: {
preprocessorOptions: {
scss: {
javascriptEnabled: true,
// 自動導入定制化樣式文件進行樣式覆蓋
additionalData: `
@use "@/styles/element/index.scss" as *;
@use "@/config/public.scss" as *;
`,
},
},
},

?? 項目集成

mock.js

mock.js 生成隨機數據,攔截 Ajax 請求

參考:mock.js 官網

安裝

pnpm install -D vite-plugin-mock mockjs

vite.config.ts 配置

import { viteMockServe } from 'vite-plugin-mock'

plugins: [
viteMockServe({
mockPath: "./src/mock",
// localEnabled: true,
})
]

UnoCSS

UnoCSS 是即時原子 CSS 引擎,通俗易懂的講,就是在template模版中書寫css

參考:UnoCSS中文文檔

安裝

pnpm install -D unocss

vite.config.ts 配置

復制代碼
// vite.config.ts
import UnoCSS from 'unocss/vite'

export default {
plugins: [
UnoCSS({ /* options */ }),
],
}

main.js中

// main.js
import "virtual:uno.css"

uno.config.js中可自行配置

// uno.config.ts
import {
defineConfig,
presetAttributify,
presetIcons,
presetTypography,
presetUno,
presetWebFonts,
transformerDirectives,
transformerVariantGroup,
} from "unocss"

export default defineConfig({
shortcuts: {
"flex-center": "flex justify-center items-center",
"flex-x-center": "flex justify-center",
"flex-y-center": "flex items-center",
"wh-full": "w-full h-full",
"flex-x-between": "flex items-center justify-between",
"flex-x-end": "flex items-center justify-end",
"absolute-lt": "absolute left-0 top-0",
"absolute-rt": "absolute right-0 top-0 ",
"fixed-lt": "fixed left-0 top-0",
"b1-red": "b-1 border-solid b-red",
},
theme: {
colors: {
primary: "var(--el-color-primary)",
primary_dark: "var(--el-color-primary-light-5)",
},
},
presets: [
presetUno(),
presetAttributify(),
presetIcons(),
presetTypography(),
presetWebFonts({
fonts: {
// ...
},
}),
],
transformers: [transformerDirectives(), transformerVariantGroup()],
})

VsCode安裝提示插件

插件效果預覽

canvas 基本使用

這里通過一個案例對canvas展示了一個基本使用,僅供參考,以業務為主。

功能代碼附上

<script setup lang="ts">
import debounce from "lodash/debounce"
import { message } from "@/Hooks/Element-plus"
import { nextTick, onMounted, ref, watch } from "vue"
import cat1 from "../../assets/image/cat1.png"
import cat2 from "../../assets/image/cat2.png"
import dog1 from "../../assets/image/dog1.png"
import dog2 from "../../assets/image/dog2.png"
import canvaas_bg from "../../assets/image/canvas.webp"
const picList = [dog1, cat2, dog2, cat1] as string[]
const colorList = [
"#fbd04f",
"#a8a1dc",
"#83b6da",
"#92c7ba",
"#f6a356 ",
"#5e5f61",
"#f98787 ",
] as string[]
// canvas實例
const canvas = ref()
// 畫布
const ctx = ref()
// 初始化背景圖片
const initCanvas_bg = async () => {
await new Promise((resolve, reject) => {
// 初始化畫布
ctx.value = canvas.value.getContext("2d")!
const width = 700
const height = 800
canvas.value.width = width
canvas.value.height = height
const img = new Image()

img.onload = () => {
ctx.value.drawImage(img, 0, 0, canvas.value.width, canvas.value.height) // 繪制第一張圖片
resolve("ok")
}
img.src = canvaas_bg
img.onerror = () => {
reject("失敗")
}
})
}
onMounted(async () => {
try {
// 初始化背景canvas
await initCanvas_bg()
} catch (err: any) {
message("error", "初始化背景失敗")
}
})

// 選擇圖片
const pic_val = ref("")
const imgIndex = ref<number>()
const selectImg = async (pic: string, i: number) => {
pic_val.value = pic
imgIndex.value = i
}
// 選擇背景色
const color_val = ref("")
const colorIndex = ref<number>()
const selectBg = async (color: string, i: number) => {
color_val.value = color
colorIndex.value = i
}
// 輸入值改變
const input_val = ref("")

watch(
[() => pic_val.value, () => color_val.value, () => input_val.value],
debounce(async (newVal) => {
try {
// 初始化背景canvas
await initCanvas_bg()
let [newPic_val, newColor_val, newInut_val] = newVal
// 先看背景顏色有沒有
if (newColor_val) {
// 設置填充色
ctx.value.fillStyle = newColor_val
// 繪制正方形
ctx.value.fillRect(99, 75, 342, 477)
// 再看文本
if (newInut_val) {
ctx.value.fillStyle = "black"
ctx.value.font = 35px 隸書 ctx.value.fillText("名:" + newInut_val, 190, 180) } // 先鋪背景在畫圖片 if (newPic_val) { const img = new Image() img.src = newPic_val img.onload = () => { ctx.value.drawImage(img, 100, 202, 350, 350) // 繪制選擇的圖片 } } } else { // 再看文本 if (newInut_val) { ctx.value.fillStyle = "black" ctx.value.font = 35px 隸書 ctx.value.fillText("名:" + newInut_val, 190, 180) } // 先鋪背景在畫圖片 if (newPic_val) { const img = new Image() img.src = newPic_val img.onload = () => { ctx.value.drawImage(img, 100, 202, 350, 350) // 繪制選擇的圖片 } } } } catch (err: any) { message("error", "初始化背景失敗") } }, 100), ) // 導出圖片 const outPic = () => { let base64 = canvas.value.toDataURL() let link = document.createElement("a") link.textContent = "download image" link.href = base64 link.download = "mypainting.jpeg" link.click() } </script>

?? 功能封裝

Svg使用

// <template>中
<SvgIcon icon="" width="15px" height="15px" color="#fff" />
//vite中自行調整svg圖標的儲存位置
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), "src/assets/svgs")],
symbolId: "icon-[dir]-[name]",
}),

svg圖標存放位置為 assets/svgs

WangEditor編輯器集成

官網

// 父組件中
<template>
<WangEditor v-model="editVal" height="calc(100vh - 180px)" />
</template>

<script setup lang="ts">
import { ref, watchEffect } from "vue"
//初始值
const editVal = ref("Hello! WangEditor")
watchEffect(() => {
console.log(editVal.value)
})
</script>
<template>
<div class="wang_edit" :class="{ dark: useSetting.dark }">
<Toolbar
id="toolbar-container"
:editor="editorRef"
:defaultConfig="toolbarConfig"
mode="default"
/>
<Editor
id="editor-container"
v-model="valueHtml"
:defaultConfig="editorConfig"
mode="default"
@onCreated="handleCreated"
/>
</div>
</template>

<script setup lang="ts">
import { useSettingStore } from "@/stores/modules/setting"
import "@wangeditor/editor/dist/css/style.css" // 引入 css
import { onBeforeUnmount, ref, shallowRef, onMounted } from "vue"
import { Editor, Toolbar } from "@wangeditor/editor-for-vue"
const useSetting = useSettingStore()
const props = withDefaults(
defineProps<{
height: string
}>(),
{
height: "560px",
},
)

const valueHtml = defineModel()
// 編輯器實例,必須用 shallowRef
const editorRef = shallowRef()
const toolbarConfig = {}
const editorConfig = {
placeholder: "請輸入內容...",
MENU_CONF: {
uploadImage: {
// 自定義圖片上傳
async customUpload(file: any, insertFn: any) {
console.log(file, insertFn)
// 調用后端接口,上傳圖片
// 拿到上傳路勁插入編輯器
// insertFn(url)
},
},
},
}

// 組件銷毀時,也及時銷毀編輯器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})

const handleCreated = (editor: any) => {
editorRef.value = editor // 記錄 editor 實例,重要!
}
</script>

<style scoped lang="scss">
.wang_edit {
border: 1px var(--el-border-color-dark) solid;
border-radius: 5px;
&.dark {
--w-e-textarea-bg-color: #333;
--w-e-toolbar-bg-color: #333;
--w-e-toolbar-color: gray;
--w-e-textarea-color: #fff;
}

#toolbar-container {
border-bottom: 1px solid var(--el-border-color-dark);
border-top-right-radius: 5px;
border-top-left-radius: 5px;
}
#editor-container {
height: v-bind("props.height") !important;
overflow: hidden;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
}
}
</style>

?? 項目運行

項目啟動

# 安裝 
pnpm npm install pnpm -g

# 安裝依賴
pnpm install

# 項目運行
pnpm run dev

項目語法檢查

#語法檢查
pnpm run lint

#語法修復
pnpm run fix

#css美化
pnpm run format

項目打包

pnpm run build

?? 寫在最后

本篇從項目規范,項目運行,項目打包,詳細講述基于Vue3.4+Element-plus技術棧0到1搭建簡潔,易懂的前端后臺管理框架。如有問題歡迎指出。

本文章轉載微信公眾號@稀土掘金技術社區

上一篇:

機器學習模型的保存與加載,完全指南

下一篇:

NLP文本分類任務實戰,附代碼模板,手把手帶你跑通
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費