目录
3.4在vite.config.js将获得到的接口ip获取到
4.2.2.components/admin/aside.vue
4.2.3.components/admin/topview.vue
4.2.4.components/admin/tables.vue
1·创建项目
E:\python\Django>npm init vue@latest
Need to install the following packages:
create-vue@3.10.3
Ok to proceed? (y) y
Vue.js - The Progressive JavaScript Framework
√ 请输入项目名称: ... vue3houtaitest
√ 是否使用 TypeScript 语法? ... 否 / 是
√ 是否启用 JSX 支持? ... 否 / 是
√ 是否引入 Vue Router 进行单页面应用开发? ... 否 / 是
√ 是否引入 Pinia 用于状态管理? ... 否 / 是
√ 是否引入 Vitest 用于单元测试? ... 否 / 是
√ 是否要引入一款端到端(End to End)测试工具? » 不需要
√ 是否引入 ESLint 用于代码质量检测? ... 否 / 是
√ 是否引入 Prettier 用于代码格式化? ... 否 / 是
√ 是否引入 Vue DevTools 7 扩展用于调试? (试验阶段) ... 否 / 是
正在初始化项目 E:\python\Django\vue3houtaitest...
项目初始化完成,可执行以下命令:
cd vue3houtaitest
npm install
npm run dev
npm notice
npm notice New minor version of npm available! 10.2.4 -> 10.7.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.7.0
npm notice Run npm install -g npm@10.7.0 to update!
npm notice
安装依赖
npm ininstall
运行项目
npm run dev
在src先创建几个常用的文件夹
- styles --->全局样式文件夹
- utils ---->工具函数文件夹
- directives --->全局指令文件夹
- composables --->组合函数文件夹
- apis ------>API接口文件夹
别名路径联想设置
jsconfig.json
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
}
vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
自动按需导入element-plus
一个 Vue 3 UI 框架 | Element Plus (element-plus.org)
1.安装element-plus
npm install element-plus --save
2.安装unplugin-vue-components
和 unplugin-auto-import
这两款插件
npm install -D unplugin-vue-components unplugin-auto-import
3.vite.config.js配置
import { fileURLToPath, URL } from 'node:url'
//ElementPlu按需导入
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
//配置element-plus插件
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
element-plus主题定制
1.安装scss
npm i sass -D
准备定制样式文件
styles/element/index.ascc
@forward 'element-plus/theme-chalk/src/common/var.scss' with(
$colors:(
'primary':(
//主色
'base':#27ba9b
),
'success':(
//成功
'base':#1dc779
),
'warning':(
//警告
'base':#ffb302
),
'danger':(
//危险
'base':#e26237
),
'error':(
//错误
'base':#cf4444
),
)
)
对ElemrntPlus样式进行覆盖
vite.config.js
import { fileURLToPath, URL } from 'node:url'
//ElementPlu按需导入
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
//配置element-plus插件
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
//1.配置elementPlus采用样式配置系统
resolvers: [
ElementPlusResolver({importStyle:"sass"}),
],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
css:{
preprocessorOptions:{
scss:{
//2.自动导入定制化样式文件进行样式覆盖
additionalData:`
@use "@/styles/element/index.scss" as *;
`,
}
}
}
})
axios配置
1.安装axios
npm i axios
2.配置接基础实例
- 1.接口基地址
- 2.接口请求超时时间
- 3.请求拦截器
- 4.响应拦截
在utils文件下创建
http.js
import axios from "axios";
const httpInstance = axios.create({
baseURL:'http://pcpai-xiaotuxian-front-devtest.ithema.net',
timeout:400
})
//拦截器
//axios请求拦截器
httpInstance.interceptors.request.use(config=>{
return config
},e=>Preomise.reject(e))
//axions响应式拦截器
httpInstance.interceptors.response.use(res=>res.data,e=>{
return Promise.reject(e)
})
export default httpInstance
在apis文件下创建测试接口
testAPI.js
import httpInstance from "@/utils/http";
export function getCategory(){
return httpInstance({
url:'home/category/head'
})
}
项目整体路由设计
在views文件夹下创建login,和layout文件
login文件下创建index.vue文件作为项目的登录页面
layout文件下创建index.vue和admin.vue文件,index.vue作为网站的首页界面admin作为页面后台页面的登录接口,可以在以及路由的vue文件中的中添加二级路由出口
然后在toter文件下配置index.js
//createRouter 创建路由
//createWebHistory 创建history路由
import { createRouter, createWebHistory } from 'vue-router'
import Login from '@/views/login/index.vue'
import Index from '@/views/layout/index.vue'
import Admin from "@/views/layout/admin.vue"
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path:'/',
component:Login
},
{
path:'/index',
component:Index
},
{
path:'/admin',
component:Admin
}
]
})
export default router
在APP.vue中添加以及路由出口
<script setup>
</script>
<template>
<!-- //一级路由出口 -->
<RouterView></RouterView>
</template>
<style scoped>
</style>
2·项目准备
iconfont 图标库
下载后保存在assets文件下并在main.js中引入
import './assets/css/iconfont.css'
fontawesome图标库
npm install font-awesome --save
在main.js中引入
import "font-awesome/css/font-awesome.min.css"
3·前端路由设计
3.1.修改vite.config.js,添加server
import { fileURLToPath, URL } from 'node:url'
//ElementPlu按需导入
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
envPrefix:["api_"],
plugins: [
vue(),
//配置element-plus插件
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
//1.配置elementPlus采用样式配置系统
resolvers: [
ElementPlusResolver({importStyle:"sass"}),
],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
css:{
preprocessorOptions:{
scss:{
//2.自动导入定制化样式文件进行样式覆盖
additionalData:`
@use "@/styles/element/index.scss" as *;
`,
}
}
},
server:{
proxy:{
"/uploads":{
target:"http://127.0.0.1:8000"
}
}
}
})
3.2在根目录下创建.env.dev文件
api_backend = "http://127.0.0.1:8000"
3.3.修改packge.json文件的dev模式
"scripts": {
"dev": "vite --mode dev",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
},
3.4在vite.config.js将获得到的接口ip获取到
import { fileURLToPath, URL } from 'node:url'
//ElementPlu按需导入
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
export default ({mode})=>{
const BaseUrl = loadEnv(mode,process.cwd())
const env = loadEnv(mode, process.cwd())
console.log(env)
// console.log(process)
return defineConfig({
envPrefix:["VITE_"],
plugins: [
vue(),
//配置element-plus插件
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
//1.配置elementPlus采用样式配置系统
resolvers: [
ElementPlusResolver({importStyle:"sass"}),
],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
css:{
preprocessorOptions:{
scss:{
//2.自动导入定制化样式文件进行样式覆盖
additionalData:`
@use "@/styles/element/index.scss" as *;
`,
}
}
},
server:{
proxy:{
"/uploads":{
target:BaseUrl
}
}
}
})
}
4·admin首页设计
4.1整体效果
首先将admin主要显示界面大概设计出来
我的理想样子如下:
views/layout/admin.vue
<template>
<div class="pvb_admin">
<div class="aside">
<div class="aside_log">
<img src="../../../src/assets/images/logo.png">
</div>
<div class="aside_title">
Double_后台
</div>
<div class="aside_body">
<el-row class="tac">
<el-col :span="24">
<h5 class="mb-2">菜单</h5>
<el-menu active-text-color="#ffd04b" background-color="#545c64" class="el-menu-vertical-demo"
default-active="2" text-color="#fff" @open="handleOpen" @close="handleClose">
<el-form v-for="menu in data.menu_list" :key="menu">
<el-sub-menu :index="menu" v-if="menu.level === 1">
<template #title>
<el-icon>
<location />
</el-icon>
<span @click="goto(menu.path)"><i :class="menu.icon"></i>{{ menu.title }}</span>
</template>
<el-menu-item :index="menuitems" v-for="menuitems in menu.children" :key="menuitems.path">
<span @click="goto(menuitems.path)" :index="menuitems.path" v-if="menuitems.children.length === 0"><i
:class="menuitems.icon"></i>{{ menuitems.title }}</span>
</el-menu-item>
</el-sub-menu>
</el-form>
</el-menu>
</el-col>
</el-row>
</div>
</div>
<div class="main">
<header>
<div class="left">
<el-breadcrumb :separator-icon="ArrowRight">
<el-breadcrumb-item :to="{ path: '/' }">index</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/admin' }">admin</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/index' }">index</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="right">
<i class="iconfont icon-tupian"></i>
<i class="iconfont icon-suofang1"></i>
<i class="iconfont icon-taiyang-copy"></i>
<i class="iconfont icon-suofang"></i>
<div class="avatar">
<img src="../../../src/assets/images/logo.png">
</div>
<div class="down">
<el-dropdown>
<span class="el-dropdown-link">
<i class="iconfont icon-down1"></i>
</span>
<template v-slot:dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</header>
<div class="tabs">tabs</div>
<main>main
</main>
</div>
</div>
</template>
<script setup>
import { reactive, ref } from "vue";
console.log("sss", import.meta.env)
const data = reactive({
menu_list: [
{
id: 2,
level: 1,
icon: "iconfont icon-yonghu1",
title: "用户管理",
name: "/index",
children: []
},
{
id: 3,
level: 1,
icon: "iconfont icon-tupian",
title: "图片管理",
path: "/admin",
children: []
},
{
id: 4,
level: 1,
icon: "iconfont icon-shuju",
title: "数据管理",
path: "/manger",
children: []
},
{
id: 5,
level: 1,
icon: "iconfont icon-xitong",
title: "系统设置",
path: "/system",
children: [
{
id: 51,
level: 2,
icon: "iconfont icon-shuju",
title: "数据管理1",
path: "/system/test1",
children: []
},
]
},
],
})
import { useRouter } from "vue-router";
const router = useRouter()
function goto(roouter_path) {
console.log(roouter_path, 111)
router.push(
roouter_path
)
}
</script>
<style lang="scss">
.pvb_admin {
width: 100%;
display: flex;
.aside {
width: 240px;
height: 100vh;
background-color: #02141b;
.aside_log {
width: 100%;
background-color: rgb(141, 136, 133);
flex-direction: column;
display: flex;
justify-content: center;
align-items: center;
img {
margin-top: 10px;
margin-bottom: 10px;
width: 70px;
height: 70px;
}
}
.aside_title {
display: flex;
justify-content: center;
align-items: center;
}
.aside_body {
margin-top: 40px;
.mb-2 {
display: flex;
font-size: large;
color: #f0eeee;
justify-content: center;
align-items: center;
}
}
}
.main {
width: calc(100% - 240px);
header {
height: 60px;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
.right {
display: flex;
justify-content: space-between;
align-items: center;
i {
margin-right: 15px;
font-size: 15px;
}
i:hover {
color: #27ba9b;
}
img {
width: 40px;
height: 40px;
border-radius: 50%;
}
.down {
margin-top: 5px;
height: auto;
}
}
}
.tabs {
background-color: rgb(46, 49, 49);
height: 30px;
border: 1px solid #f0eeee;
}
main {
background-color: yellowgreen;
height: calc(100vh - 90px);
}
}
}
</style>
4.2拆分组件
通过开始设计的页面进项组件拆分
4.2.1.admin.vue
<template>
<div class="pvb_admin">
<Aside></Aside>
<div class="main">
<Topview></Topview>
<Tables></Tables>
<main>
<!-- //admin路由出口 -->
<RouterView></RouterView>
</main>
</div>
</div>
</template>
<script setup>
import Aside from "@/components/admin/aside.vue"
import Topview from "@/components/admin/topview.vue"
import Tables from "@/components/admin/tables.vue"
</script>
<style lang="scss">
.pvb_admin {
width: 100%;
display: flex;
.main {
width: calc(100% - 240px);
main {
background-color: yellowgreen;
height: calc(100vh - 90px);
}
}
}
</style>
其中有aside,topview,tables组件,分别对应左侧菜单栏,顶部信息栏,中间tabls路由导航栏
分别在components/admin下创建组件文件
4.2.2.components/admin/aside.vue
<template>
<div class="aside">
<div class="aside_log">
<img src="../../../src/assets/images/logo.png">
</div>
<div class="aside_title">
Double_后台
</div>
<div class="aside_body">
<el-row class="tac">
<el-col :span="24">
<h5 class="mb-2">菜单</h5>
<el-menu active-text-color="#ffd04b" background-color="#545c64" class="el-menu-vertical-demo"
default-active="2" text-color="#fff" @open="handleOpen" @close="handleClose">
<el-form v-for="menu in data.menu_list" :key="menu">
<el-sub-menu :index="menu" v-if="menu.level === 1">
<template #title>
<el-icon>
<location />
</el-icon>
<span @click="goto(menu.path)"><i :class="menu.icon"></i>{{ menu.title }}</span>
</template>
<el-menu-item :index="menuitems" v-for="menuitems in menu.children" :key="menuitems.path">
<span @click="goto(menuitems.path)" :index="menuitems.path" v-if="menuitems.children.length === 0"><i
:class="menuitems.icon"></i>{{ menuitems.title }}</span>
</el-menu-item>
</el-sub-menu>
</el-form>
</el-menu>
</el-col>
</el-row>
</div>
</div>
</template>
<script setup>
import { reactive } from "vue";
const data = reactive({
menu_list: [
{
id: 2,
level: 1,
icon: "iconfont icon-yonghu1",
title: "用户管理",
path: "/admin/user_list",
children: []
},
{
id: 3,
level: 1,
icon: "iconfont icon-tupian",
title: "图片管理",
path: "/admin/image_list",
children: []
},
{
id: 4,
level: 1,
icon: "iconfont icon-shuju",
title: "数据管理",
path: "/admin/data_list",
children: []
},
{
id: 5,
level: 1,
icon: "iconfont icon-xitong",
title: "系统设置",
path: "/admin/system",
children: [
{
id: 51,
level: 2,
icon: "iconfont icon-shuju",
title: "数据管理1",
path: "/admin/system_info",
children: []
},
]
},
],
})
import { useRouter } from "vue-router";
const router = useRouter()
function goto(roouter_path) {
router.push(
roouter_path
)
}
</script>
<style lang="scss">
.aside {
width: 240px;
height: 100vh;
background-color: #02141b;
.aside_log {
width: 100%;
background-color: rgb(141, 136, 133);
flex-direction: column;
display: flex;
justify-content: center;
align-items: center;
img {
margin-top: 10px;
margin-bottom: 10px;
width: 70px;
height: 70px;
}
}
.aside_title {
display: flex;
justify-content: center;
align-items: center;
}
.aside_body {
margin-top: 40px;
.mb-2 {
display: flex;
font-size: large;
color: #f0eeee;
justify-content: center;
align-items: center;
}
}
}
</style>
4.2.3.components/admin/topview.vue
<template>
<header>
<div class="left">
<el-breadcrumb :separator-icon="ArrowRight">
<el-breadcrumb-item :to="{ path: '/' }">index</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/admin' }">admin</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/index' }">index</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="right">
<i class="iconfont icon-tupian"></i>
<i class="iconfont icon-suofang1"></i>
<i class="iconfont icon-taiyang-copy"></i>
<i class="iconfont icon-suofang"></i>
<div class="avatar">
<img src="../../../src/assets/images/logo.png">
</div>
<div class="down">
<el-dropdown>
<span class="el-dropdown-link">
<i class="iconfont icon-down1"></i>
</span>
<template v-slot:dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</header>
</template>
<script setup>
</script>
<style lang="scss">
header {
height: 60px;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
.right {
display: flex;
justify-content: space-between;
align-items: center;
i {
margin-right: 15px;
font-size: 15px;
}
i:hover {
color: #27ba9b;
}
img {
width: 40px;
height: 40px;
border-radius: 50%;
}
.down {
margin-top: 5px;
height: auto;
}
}
}
</style>
4.2.4.components/admin/tables.vue
<template>
<div class="tabs">tabs</div>
</template>
<script setup>
</script>
<style lang="scss">
.tabs {
background-color: rgb(46, 49, 49);
height: 30px;
border: 1px solid #f0eeee;
}
</style>
4.2.5.说明以及修改内容
修改了aside.vue中的men_list侧边栏的跳转路由路径,并在views/admin下创建对应的文件
例如data_list.vue(暂时没有设计内容)
<template>
<dev>数据列表</dev>
</template>
<script setup></script>
<script lang="scss"></script>
还要再index.js中添加对应的路由才能够实现跳转
index.js
//createRouter 创建路由
//createWebHistory 创建history路由
import { createRouter, createWebHistory } from 'vue-router'
import Login from '@/views/login/index.vue'
import Index from '@/views/layout/index.vue'
import Admin from "@/views/layout/admin.vue"
import UserList from "@/views/admin/user_list.vue"
import ImageList from "@/views/admin/image_list.vue"
import DataList from "@/views/admin/data_list.vue"
import System from "@/views/admin/system.vue"
import SystemInfo from "@/views/admin/system_info.vue"
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path:'/',
component:Login
},
{
path:'/index',
component:Index
},
{
path:'/admin',
component:Admin,
children:[
{
path:'user_list',
component:UserList
},
{
path:'image_list',
component:ImageList
},
{
path:'data_list',
component:DataList
},
{
path:'system',
component:System,
},
{
path:'system_info',
component:SystemInfo
},
]
}
]
})
export default router
最后的效果是会再admin的路由入口展示不同的信息,但是拆分之后各个模块更见容易管理
5.user_list.vue 界面设计
5.1.实现增删改查功能
<template>
<div class="pvb_container">
<div class="pvb_search">
<el-input v-model="input2" style="width: 240px" placeholder="Type something" :prefix-icon="Search" />
</div>
<div class="pvb_actions">
<el-button type="primary">批量删除</el-button>
<el-button type="success" @click="data.dialogOverflowVisible = true, data.addOredit = 'add'">添加</el-button>
</div>
<div class="input_model">
<el-dialog v-model="data.dialogOverflowVisible" :title="data.addOredit == 'add' ? '添加' : '修改'" width="400" draggable
overflow>
<el-input class="input" v-model="data.editOraddinput.name" style="width: 280px" placeholder="姓名" />
<el-input class="input" v-model="data.editOraddinput.ip" style="width: 280px" placeholder="IP" />
<el-input class="input" v-model="data.editOraddinput.dep" style="width: 280px" placeholder="部门" />
<el-input class="input" v-model="data.editOraddinput.project" style="width: 280px" placeholder="项目" />
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogOverflowVisible = false">Cancel</el-button>
<el-button type="primary" @click="commit">
Confirm
</el-button>
</div>
</template>
</el-dialog>
</div>
<div class="pvb_tables">
<el-table :data="data.tableData" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="用户名称" width="180" />
<el-table-column prop="ip" label="Ip" width="180" />
<el-table-column prop="dep" label="部门" />
<el-table-column prop="project" label="项目">
<template v-slot="scope">
<el-button type="primary" @click="deldata(scope.row)">删除</el-button>
<el-button type="success" @click="update(scope.row)">修改</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pvb_pages">
<el-pagination small background layout="prev, pager, next" :total="50" class="mt-4" />
</div>
</div>
</template>
<script setup>
import { reactive, ref } from "vue";
import { IpControl_listApi, IpControl_CreateApi, IpControl_OneApi, IpControl_UpdateApi, IpControl_DelApi } from "@/apis/ip_control_api"
const data = reactive({
current_row_id: '',
addOredit: '',
page_total: 1,
dialogOverflowVisible: false,
tableData: [
{
name: 1,
ip: '2016-05-03',
dep: 'Tom',
project: 'No. 189, Grove St, Los Angeles',
},
],
editOraddinput:
{
name: 1,
ip: '2016-05-03',
dep: 'Tom',
project: 'No. 189, Grove St, Los Angeles',
},
}
)
//
getlist()
async function getlist() {
const res = await IpControl_listApi()
data.tableData = res.data
}
async function commit() {
data.dialogOverflowVisible = false
console.log(data.current_row_id)
if (data.addOredit == 'add') {
console.log(data.editOraddinput)
const res = await IpControl_CreateApi(data.editOraddinput)
}
else {
console.log(data.current_row_id)
await IpControl_UpdateApi(data.current_row_id, data.editOraddinput)
}
}
async function update(row) {
data.addOredit = 'update'
data.dialogOverflowVisible = true
data.current_row_id = row.id
const res = await IpControl_OneApi(row.id)
data.editOraddinput = res.data
}
async function deldata(row) {
await IpControl_DelApi(row.id)
}
</script>
<style lang="scss">
.pvb_container {
background-color: white;
.pvb_search {
padding: 10px;
border-bottom: 1px solid #fff;
}
.pvb_actions {
padding: 10px;
}
.input_model {
.input {
padding-left: 25%;
margin-bottom: 10px;
}
}
.pvb_tables {
padding: 10px;
height: 500px;
}
.pvb_pages {
display: flex;
justify-content: center;
padding: 10px;
margin-bottom: 20px;
}
}
</style>
ip_control_api.js
后端接口API
import httpInstance from "@/utils/http";
// 查所有
export function IpControl_listApi(){
return httpInstance.get("api/permission/")
}
// 创建
export function IpControl_CreateApi(data){
return httpInstance.post("api/permission/",data)
}
// 查单个
export function IpControl_OneApi(id){
return httpInstance.get("api/permission"+"/"+id+"/")
}
// 改
export function IpControl_UpdateApi(id,data){
return httpInstance.put("api/permission"+"/"+id+"/",data)
}
// 删除
export function IpControl_DelApi(id){
return httpInstance.delete("api/permission"+"/"+id+"/")
}
5.2.添加分页以及搜索功能
userlist.vue
<template>
<div class="pvb_container">
<div class="pvb_search">
<el-input v-model="search_.search_value" style="width: 240px" placeholder="Type something"
@input="handleSearch" />
</div>
<div class="pvb_actions">
<el-button type="danger">批量删除</el-button>
<el-button type="success" @click="data.dialogOverflowVisible = true, data.addOredit = 'add'">添加</el-button>
</div>
<div class="input_model">
<el-dialog v-model="data.dialogOverflowVisible" :title="data.addOredit == 'add' ? '添加' : '修改'" width="400"
draggable overflow>
<el-input class="input" v-model="data.editOraddinput.name" style="width: 280px" placeholder="姓名" />
<el-input class="input" v-model="data.editOraddinput.ip" style="width: 280px" placeholder="IP" />
<el-input class="input" v-model="data.editOraddinput.dep" style="width: 280px" placeholder="部门" />
<el-input class="input" v-model="data.editOraddinput.project" style="width: 280px" placeholder="项目" />
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogOverflowVisible = false">Cancel</el-button>
<el-button type="primary" @click="commit">
Confirm
</el-button>
</div>
</template>
</el-dialog>
</div>
<div class="pvb_tables">
<el-table :data="data.tableData" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="用户名称" width="180" />
<el-table-column prop="ip" label="Ip" width="180" />
<el-table-column prop="dep" label="部门" />
<el-table-column prop="project" label="项目" />
<el-table-column prop="actions" label="操作">
<template v-slot="scope">
<el-button type="warning" @click="update(scope.row)">修改</el-button>
<el-button type="danger" @click="deldata(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pvb_pages">
<el-pagination small background layout="prev, pager, next" :total="page_.page_toal" class="mt-4"
@current-change="handelCurrentChenge" />
</div>
</div>
</template>
<script setup>
import { reactive, ref, inject } from "vue";
import { IpControl_listApi, IpControl_CreateApi, IpControl_OneApi, IpControl_UpdateApi, IpControl_DelApi, IpControl_SearchApi } from "@/apis/ip_control_api"
const reload = inject("reload");
const page_ = reactive({
page_toal: 0,
page_data: [],
page_Size: 11,
current_page: 0
})
const search_ = reactive({
search_flag: false,
search_data: [],
search: '',
})
const data = reactive({
current_row_id: '',
addOredit: '',
dialogOverflowVisible: false,
tableData: [
{
name: 1,
ip: '',
dep: '',
project: '',
actions: ''
},
],
editOraddinput:
{
name: 1,
ip: '',
dep: '',
project: '',
},
}
)
getlist()
async function getlist() {
const res = await IpControl_listApi()
if (res.code == 200) {
search_.search_flag = false
data.tableData = res.data
page_.page_data = res.data
page_.page_toal = res.total
}
else {
ElMessage.error('获取失败')
}
}
async function commit() {
data.dialogOverflowVisible = false
console.log(data.current_row_id)
if (data.addOredit == 'add') {
const res = await IpControl_CreateApi(data.editOraddinput)
if (res.code == 200) {
ElMessage.success('添加成功')
reload()
}
else {
ElMessage.error('添加失败')
}
}
else {
const res = await IpControl_UpdateApi(data.current_row_id, data.editOraddinput)
if (res.code == 200) {
ElMessage.success('修改成功')
reload()
}
else {
ElMessage.error('修改失败')
return
}
}
}
async function update(row) {
data.addOredit = 'update'
data.dialogOverflowVisible = true
data.current_row_id = row.id
const res = await IpControl_OneApi(row.id)
data.editOraddinput = res.data
}
async function deldata(row) {
const res = await IpControl_DelApi(row.id)
if (res.code == 200) {
ElMessage.success('删除成功')
reload()
}
else {
ElMessage.error('删除失败')
return
}
}
async function handleSearch() {
search_.search_flag = true
const res = await IpControl_SearchApi(search_.search_value)
search_.search_data = res.data
if (res.total != 0) {
data.tableData = res.data
}
}
const handelCurrentChenge = page => {
page_.current_page = page
const index = page_.page_Size * (page - 1)
const end = page_.page_Size * page
var tables = []
for (let i = index; i < end; i++) {
if (page_.page_data[i] && search_.search_flag == false) {
tables.push(page_.page_data[i])
}
else if (search_.search_data[i] && search_.search_flag) {
tables.push(search_.search_data[i])
}
data.tableData = tables
}
}
</script>
<style lang="scss">
.pvb_container {
background-color: white;
.pvb_search {
padding: 10px;
border-bottom: 1px solid #fff;
}
.pvb_actions {
padding: 10px;
}
.input_model {
.input {
padding-left: 25%;
margin-bottom: 10px;
}
}
.pvb_tables {
height: 710px;
overflow: hidden;
margin-left: 10%;
width: 1243px;
}
.pvb_pages {
display: flex;
justify-content: center;
margin-top: 30px;
}
}
</style>
5.2.1新增后端数据接口
import httpInstance from "@/utils/http";
// 查所有
export function IpControl_listApi(){
return httpInstance.get("api/permission/")
}
// 创建
export function IpControl_CreateApi(data){
return httpInstance.post("api/permission/",data)
}
// 查单个
export function IpControl_OneApi(id){
return httpInstance.get("api/permission"+"/"+id+"/")
}
// 改
export function IpControl_UpdateApi(id,data){
return httpInstance.put("api/permission"+"/"+id+"/",data)
}
// 删除
export function IpControl_DelApi(id){
return httpInstance.delete("api/permission"+"/"+id+"/")
}
//搜索
export function IpControl_SearchApi(search){
return httpInstance.get("api/permission/"+"?search="+search)
}
5.2.2新增刷新方法
在APP.vue
<template>
<!-- //一级路由出口 -->
<RouterView v-if="isRouter"></RouterView>
</template>
<script setup>
import { provide,nextTick,ref } from 'vue';
//刷新
const isRouter = ref(true);
const reload=()=>{
isRouter.value = false
nextTick(()=>{
isRouter.value = true
})
}
provide('reload',reload)
</script>
<style scoped lang="scss">
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
</style>
效果展示
6图片管理
7标签管理
8文章管理
9接入md组件
9-1引入组件
参考文档:MdEditorV3 Documentation (imzbf.github.io)https://imzbf.github.io/md-editor-v3/zh-CN/demo
npm install md-editor-v3
deomo
<template>
<MdEditor v-model="text" />
</template>
<script setup>
import { ref } from 'vue';
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
const text = ref('Hello Editor!');
</script>