從0開始使用 vue-cli 搭建一個項目

1. 安裝 vue-cli & 創建項目

# 安裝 vue-cli 因爲是全局安裝,安裝過一次之後再創建項目就不用安裝了;
> npm i -g @vue/cli

# 創建項目
> vue create vue-web

# 進入項目目錄
> cd vue-web

# 啓動項目
> npm run serve

# 訪問:
http://localhost:8080/

2. 安裝 iview

安裝 iview

> npm i iview

引入 iview main.js

import Vue from 'vue'
import iview from 'iview'
import 'iview/dist/styles/iview.css'
Vue.use(iview)

3. 安裝 less

# less-loader
> npm i less-loader

# less
> npm i less -D

4. 安裝並配置路由 vue-router

安裝 vue-router

npm i vue-router

引入 & 配置 vue-router

import Vue from 'vue'
import vueRouter from 'vue-router'

// 安裝 vue-router 插件
Vue.use(vueRouter);

// vue-router 實例化對象
const router = new vueRouter({
    mode: 'history',
    routes: []
})

// 暴露出去
export default router;

vue-router 官網

https://router.vuejs.org/zh/

5. 使用路由

創建頁面:

登錄(Login.vue) ,後臺(Admin.vue),
後臺-歡迎頁面(admin/Welcome.vue),後臺-用戶管理(admin/User.vue)等

導入路由:main.js

import router from './router/index'

new Vue({
  render: h => h(App),
  router
}).$mount('#app')

路由出口

main.js

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

配置路由

// vue-router 實例化對象
const router = new vueRouter({
    mode: 'history',
    routes: [
        {
            path: '/',
            redirect: '/login'
        },
        {
            path: '/login',
            component: () => import('../pages/Login.vue')
        },
        {
            path: '/sys',
            component: () => import('../pages/Admin.vue'),
            children: [
                {
                    path: '',
                    component: ()=> import('../pages/admin/Welcome.vue')
                },
                {
                    path: 'user',
                    component: () => import('../pages/admin/User.vue')
                }
            ]
        },
        {
            path: '/404',
            component: () => import('../pages/notFind.vue')
        },
        {
            path: '*',
            redirect: '/404'
        }
    ]
})

子路由出口 pages/Admin.vue

<div>
    admin
    <router-view></router-view>
</div>

6. 使用 iview 搭建頁面

  1. 登錄頁面
  2. 後臺框架
  3. 後臺歡迎頁面

7. 登錄 & 菜單列表

詳細的登錄流程見如下博客:

https://blog.csdn.net/qq_39125684/article/details/91126413






管理員部分
頁面大體如下:

8. 請求管理員數據

新建接口 /api/getUserInfo.js

* 注意:GET 請求的參數是一個對象,通過這個對象的 params屬性傳遞的!!

/**
 * 管理員列表
 */
import http from './http'

function getUserInfo(data) {
    return http.get('/sys/user/list', {
        params: data
    })
}

export default getUserInfo;

User.vue 頁面請求數據 beforeCreate()

將要用的數據在 data選項中定義好,然後將請求到的數據存儲起來

import getUserInfo from '../../api/getUserInfo'

data(){
    return {
        // 表體數據
        userList: [],
        // 當前頁碼
        currPage: 1,
        // 每頁條數
        pageSize: 10,
        // 總條數
        totalCount: 1,
        // 總頁數
        totalPage: 1
    }
},
beforeCreate() {
    getUserInfo({
        page: 1,
        limit: 1,
        sidx: 'username',
        order: 'desc',
        username: ''
    }).then((res)=>{
        const {code, msg, page} = res.data;
        if(code === 0){
            const {currPage, list, pageSize, totalCount, totalPage} = page;
            // 將請求到的數據存儲起來
            this.currPage = currPage;
            this.userList = list;
            this.pageSize = pageSize;
            this.totalCount = totalCount;
            this.totalPage = totalPage;
        }else{
            this.$Message.error(msg)
        }
    })
}

9. Table 表部分

iview中的 Table 組件循環是提供一個表頭數組,提供一個表體數組;
表體數組我們已經得到了,根據表體數據寫一個表頭數組循環生成 Table

data(){
    return {
        // 表頭
        userHeader: [
            /**
            * 表格前邊的複選框  type: 'selection',
            * 還可以設置每一列的寬度,這一列是否固定在左側或者右側等屬性
            */
            {
                type: 'selection',
                width: 50
            },
            {
                title: '#',
                key: 'userId'
            },
            {
                title: '用戶名',
                key: 'username'
            },
            {
                title: '郵箱',
                key: 'email'
            },
            {
                title: '手機號碼',
                key: 'mobile'
            },
            {
                title: '創建時間',
                key: 'createTime'
            }
        ]
    }
}
<Table :columns="userHeader" :data="userList"></Table>

因爲我們請求到的數據並沒有操作按鈕部分,所以需要我們使用 Table組件的 render函數生成操作按鈕;

就在表頭數組中;

  1. 方法一,使用 render函數生成結構
{
    title: '操作',
    align: 'center',
    render: (h) => h('div', [
        h('Button','編輯'),
        h('Button','刪除')
    ])
}
  1. 方法二,引用外部組件

創建組件 components/UserTools.vue

<template>
    <div>
        <Button> 編輯 </Button>
        <Button> 刪除 </Button>
    </div>
</template>

導入組件(局部組件,不過在 render函數中,局部組件和全局組件都可以使用)

import UserTools from '../../components/UserTools.vue'

在表頭數組中,通過render 函數使用導入的組件

{
    title: '操作',
    align: 'center',
    render: (h) => h(UserTools)
}

觸發事件:在組件內部觸發,render函數中處理

<Button @click="$emit('edit')"> 編輯 </Button>
<Button @click="$emit('del')"> 刪除 </Button>
{
    title: '操作',
    align: 'center',
    render: (h) => h(UserTools, {
        // 使用箭頭函數,使用普通函數的話獲取不到 this指向
        on: {
            edit: ()=> {
                console.log('編輯')
            },
            del: ()=> {
                console.log('刪除');
            }
        }
    })
}

但是新的問題又來了,我們怎麼知道是點擊的哪一行的操作按鈕呢?

通過 render函數中的第二個參數(params),可以知道點擊的是哪一行的按鈕

{
    title: '操作',
    align: 'center',
    render: (h, params) => h(UserTools, {
        on: {
            edit: ()=> {
                console.log('編輯', params)
            },
            del: ()=> {
                console.log('刪除', params);
            }
        }
    })
}

10. 搜索功能

  1. 在搜索輸入框綁定數據
<Input 
    search 
    v-model="usernameSearch"
/>
data() {
    return {
        usernameSearch: ''
    }
}
  1. 將我們 data中的數據和請求用戶信息的時候傳入的數據綁定在一起

這樣我們操控 data中的數據之後,再次請求到的就是我們想要的數據了

data() {
    return {
        // 表頭數據
        userHeader: [ ... ],
        // 表體數據
        userList: [],
        // 當前頁碼
        currPage: 1,
        // 每頁條數
        pageSize: 10,
        // 總條數
        totalCount: 1,
        // 總頁數
        totalPage: 1,
        // 搜索框的輸入
        usernameSearch: ''
    }
}
beforeCreate(){
    // 解構 this,簡化下方操作
    const {currPage, pageSize, usernameSearch} = this;
    // 請求的參數,綁定 data中的數據
    getUserInfo({
        page: currPage,
        limit: pageSize,
        sidx: 'username',
        order: 'desc',
        username: usernameSearch
    }).then((res)=>{
        const {code, msg, page} = res.data;
        if(code === 0){
            const {currPage, list, pageSize, totalCount, totalPage} = page;
            // 將請求返回的數據,和 data中的數據再次綁定
            this.currPage = currPage;
            this.userList = list;
            this.pageSize = pageSize;
            this.totalCount = totalCount;
            this.totalPage = totalPage;
        }else{
            this.$Message.error(msg)
        }
    })
}
  1. 不管是 搜索 還是 分頁,都是我們改變 data中的數據,然後再次執行 getUserInfo()函數,請求新的數據;

我們之前是在 beforeCreate()鉤子中請求初始數據,因爲需要在不少地方調用請求數據的方法,就不能在 beforeCreate()中直接請求了,要封裝成一個方法;

下面我們就創建一個方法(getUserInfoList()),用來請求數據;

created()鉤子中調用一下請求數據的方法,用來初始化,因爲 beforeCreate() 鉤子中得不到這個方法

created(){
    this.getUserInfoList();
},
methods: {
    getUserInfoList(){
        const {currPage, pageSize, usernameSearch} = this;
        getUserInfo({
            page: currPage,
            limit: pageSize,
            sidx: 'username',
            order: 'desc',
            username: usernameSearch
        }).then((res)=>{
            const {code, msg, page} = res.data;
            if(code === 0){
                const {currPage, list, pageSize, totalCount, totalPage} = page;
                this.currPage = currPage;
                this.userList = list;
                this.pageSize = pageSize;
                this.totalCount = totalCount;
                this.totalPage = totalPage;
            }else{
                this.$Message.error(msg)
            }
        })
    }
}
  1. 自動搜索,輸入後直接進行搜索

直接監聽搜索框綁定的數據usernameSearch 的變化,如果變化就再次請求數據就好了

watch: {
    usernameSearch() {
        this.getUserInfoList();
    }
}
  1. 手動搜索,可以解決多人同時搜索的時候併發量太大的問題

通過 <Input> 控件的 on-search 事件解決;

事件名 說明 返回值
on-search 開啓 search 時可用,點擊搜索或按下回車鍵時觸發 value

<Input >中的 v-model="usernameSearch" 去掉;添加上 @on-search事件

<Input 
    search 
    @on-search="handleSearch"
/>
data() {
    return {
        // 搜索框的輸入
        usernameSearch: '',
    }
},
methods: {
    /**
     * 搜索
     * value 搜索框中的內容
     */
    handleSearch(value){
        this.usernameSearch = value
    }
},
watch: {
    usernameSearch() {
        // 請求數據
        this.getUserInfoList();
    },
}
  1. 分頁也是如此,直接監聽當前頁碼currPage這個數據的變化就好了
watch: {
    currPage() {
        this.getUserInfoList();
    }
}
  1. 給表格的數據切換添加 loading…

Table控件添加 loading屬性,並綁定數據

<Table 
    :columns="userHeader" 
    :data="userList"
    :loading="tableLoading"
></Table>
data() {
    return {
        // 表格加載狀態
        tableLoading: false
    }
}

在請求數據的時候,將 tableLoading變成 true,請求完成之後,不管是請求成功還是請求失敗,都把 tableLoading變成 false

11. 分頁功能

  1. 改變分頁,直接監聽當前頁碼currPage這個數據的變化,如果有改變請求一次數據就好了
watch: {
    currPage() {
        this.getUserInfoList();
    }
}
  1. 每頁展示條數的控制
<!-- 
    total: 總條數
    current: 當前頁碼,支持 .sync 修飾符
    page-size: 每頁條數
    page-size-opts: Array 條數控制選項
    @on-page-size-change: 切換每頁條數時的回調,返回切換後的每頁條數
 -->
<Page 
    show-sizer 
    :total="totalCount" 
    :current.sync="currPage"
    :page-size="pageSize"
    :page-size-opts="[1, 5, 10, 20]"
    @on-page-size-change="pageSizeChange"
/>
methods: {
    /**
     * 每頁條數改變
     * num 當前選中的條數
     */
    pageSizeChange(num){
        this.pageSize = num;
    }
}
watch: {
    // 當頁碼發生改變的時候,重新請求數據
    pageSize() {
        this.getUserInfoList();
    }
}

12. 添加 & 編輯管理員

  1. 搭建佈局

使用模態框組件<Modal>搭建如下佈局

其中開關組件<Switch>需要注意: 直接使用是不會顯示的,需要加上前綴 <i-Switch>

開關這裏,因爲我們後臺需要的不是 true false 而是 1 0,所以通過 true-value false-value 屬性設置;

<Modal v-model="modalShow" :title="modalTitle" >
    <Form
        :label-width="40"
        label-position="left"
    >
        <FormItem label="賬號" >
            <Input prefix="md-person" placeholder="username" />
        </FormItem>

        <FormItem label="密碼" >
            <Input prefix="md-lock" type="password" placeholder="password" />
        </FormItem>

        <FormItem label="郵箱" >
            <Input prefix="md-mail" placeholder="email" />
        </FormItem>

        <FormItem label="手機" >
            <Input prefix="md-phone-portrait" placeholder="mobile" />
        </FormItem>

        <FormItem label="狀態" >
            <i-Switch
                size="large"
                :true-value="1"
                :false-value="0"
            >
                <span slot="open">激活</span>
                <span slot="close">禁用</span>
            </i-Switch>
        </FormItem>
    </Form>
</Modal>
data() {
    return {
        modalShow: true,
        modalTitle: '添加管理員'
    }
}
  1. 綁定數據
<Form :model="modalForm" >
    <FormItem>
        <Input v-model="modalForm.username" />
    </FormItem>
    <!--
        ...
    -->
</Form>
data() {
    return {
        // 模態框表單
        modalForm: {
            userId: 0,
            username: '',
            password: '',
            email: '',
            mobile: '',
            status: 0,
            roleIdList: []
        },
    }
}
  1. 表單數據驗證

iview表單驗證:https://blog.csdn.net/qq_39125684/article/details/91125528

# 安裝validator 庫,方便自定義驗證
> npm i validator
// 導入
import validator from 'validator'
<Form :rules="modalRules" >
    <FormItem
        label="賬號"
        prop="username"
    >
        <Input v-model="modalForm.username" />
    </FormItem>
</Form>
data() {
    return {
        // 模態框 表單驗證
        modalRules: {
            username: [
                {required: true, message: '用戶名不能爲空'}
            ],
            email: [
                {required: true, message: '郵箱不能爲空'},
                {validator: (rule, value, callback)=>{
                    if(validator.isEmail(value)){
                        callback()
                    }else{
                        callback(new Error('郵箱格式不正確'))
                    }
                }}
            ],
            mobile: [
                {required: true, message: '手機號碼不能爲空'},
                {validator: (rule, value, callback)=>{
                    if(validator.isMobilePhone(value, 'zh-CN')) {
                        callback()
                    }else{
                        callback(new Error('手機號碼格式不正確'))
                    }
                }}
            ]
        }
    }
}
  1. 添加管理員按鈕 & 編輯管理員
<Button @click="modalTitle='添加管理員',modalShow=true">添加管理員</Button>
{
    title: '操作',
    align: 'center',
    render: (h, params) => h(UserTools, {
        on: {
            edit: ()=> {
                this.modalShow = true;
                this.modalTitle = "編輯管理員"
            },
            del: ()=> {
                console.log('刪除', params);
            }
        }
    })
}
  1. 編輯管理員,模態框的表單中要有當前行的數據
{
    title: '操作',
    align: 'center',
    render: (h, params) => h(UserTools, {
        on: {
            edit: ()=> {
                this.modalShow = true;
                this.modalTitle = "編輯管理員";
                // 獲取到當前行的數據,賦值給模態框表單
                const {userId, email, mobile, username, password, status, roleIdList} = params.row;
                this.modalForm = {
                    userId,
                    username,
                    password: '',
                    email,
                    mobile,
                    status,
                    roleIdList,
                }
            },
            del: ()=> {
                console.log('刪除', params);
            }
        }
    })
}
  1. 但是由此帶來新的問題

當我們點擊完編輯某個用戶的信息之後,再點擊添加,則添加對話框中會有剛纔用戶的信息

所以我們要知道模態框是什麼時候關閉的,在關閉的時候,我們把模態框表單中的內容清空就好了;

Modal 事件

事件名 說明 返回值
on-ok 點擊確定的回調
on-cancel 點擊取消的回調

Form方法

方法名 說明 參數
resetFields 對整個表單進行重置,將所有字段值重置爲空並移除校驗結果

iview 提供瞭如上事件和方法,做起來就很簡單了

首先 給 <Modal> 對話框的關閉按鈕添加事件

<Modal
    @on-cancel="modalCancel"
>

然後 給將要清空的 <Form>表單添加 ref,方便後面可以通過 this.$refs找到

<Form
    ref="modalForm"
>

最後 在modalCancel函數中找到 <Form>,執行一下 resetFields(),清空掉就好了

methods: {
    /**
     * 模態框點擊取消時 執行
     */
    modalCancel(){
        this.$refs['modalForm'].resetFields();
    },
}
  1. 添加 & 編輯 管理員的業務邏輯

編寫接口

addUser.js 文件

/**
 * 添加管理員
 */
import http from './http'

function addUser(data){
    return http.post('/sys/user/save', data)
}

export default addUser;

editUser.js 文件

/**
 * 編輯管理員信息
 */
import http from './http'

function editUser(data) {
    return http.post('/sys/user/update', data)
}

export default editUser;

在頁面中導入接口

import addUser from '../../api/addUser'
import editUser from '../../api/editUser'

因爲添加管理員和編輯管理員使用的是同一個表單,所以在點擊確定按鈕,提交數據的時候要做一下判斷;

<Modal
    @on-ok="modalSubmit"
>

點擊確定按鈕的時候進行一下表單驗證,驗證通過則調用接口,請求數據

methods: {
    /**
     * 模態框點擊確定的時候執行
     */
    modalSubmit(){
        // 先做一下驗證
        this.$refs['modalForm'].validate((result)=>{
            if(result){
                // 驗證通過 判斷是添加還是編輯
                if(this.modalTitle == '添加管理員'){
                    addUser({
                        ...this.modalForm
                    }).then((res)=>{
                        const {code, msg} = res.data;
                        if(code === 0){
                            this.$Message.success('恭喜您,數據添加成功');
                            this.getUserInfoList();
                        }else{
                            this.$Message.error('錯誤:'+ msg)
                        }
                    });

                }else if(this.modalTitle == '編輯管理員'){
                    editUser({
                        ...this.modalForm
                    }).then((res)=> {
                        const {code, msg} = res.data;
                        if(code === 0){
                            this.$Message.success('恭喜您,數據更新成功');
                            this.getUserInfoList();
                        }else{
                            this.$Message.error('錯誤:'+ msg)
                        }
                    })
                }
            }
        })
    }
}

13. 刪除管理員

  1. 定義接口
    deleteUser.js
/**
 * 刪除用戶
 */
import http from './http'

function deleteUser(data){
    return http.post('/sys/user/delete', data)
}

export default deleteUser;
  1. 刪除單項

在表頭數據部分:

del: ()=> {
    // 拿到當前行的用戶的 ID
    const { userId } = params.row;

    // 模態框詢問一下是否確定刪除
    this.$Modal.confirm({
        title: '刪除',
        content: '<p>您確定要刪除該數據嗎?</p>',
        onOk: () => {
            deleteUser([userId]).then((res)=> {
                if(res.data.code === 0){
                    this.$Message.success('刪除成功');
                    this.getUserInfoList();
                }else{
                    this.$Message.error('刪除失敗:' + res.data.msg)
                }
            })
        },
        onCancel: () => {
            this.$Message.info('取消刪除');
        }
    });
}
  1. 批量刪除

Table 事件

事件名 說明 返回值
on-selection-change 在多選模式下有效,只要選中項發生變化時就會觸發 selection:已選項數據

找到選中的項

<Table 
    @on-selection-change="selectionChange"
></Table>
// 存儲將要刪除的用戶的ID
data() {
    return {
        selections: []
    }
}
/**
 * 拿到選中數據的 userId
 */
selectionChange(selection){
    this.selections = selection.map((item, index, arr)=>{
        return item.userId;
    })
},

點擊刪除按鈕,刪除選中的數據

<Button @click="deleteSome">批量刪除</Button>
/**
 * 批量刪除 按鈕點擊事件
 * 注意:先判斷一下是否有選中的數據
 */
deleteSome(){
    // 判斷一下是否有選中的數據
    if(this.selections.length > 0){
        // 模態框詢問一下是否確定刪除
        this.$Modal.confirm({
            title: '刪除',
            content: '<p>您確定要刪除選中的數據嗎?</p>',
            onOk: () => {
                // 刪除
                deleteUser([
                    ...this.selections
                    ]).then((res)=> {
                    if(res.data.code === 0){
                        this.$Message.success('刪除成功');
                        this.getUserInfoList();
                    }else{
                        this.$Message.error('刪除失敗:' + res.data.msg)
                    }
                })
            },
            onCancel: () => {
                this.$Message.info('取消刪除');
            }
        });
    }else {
        this.$Message.info('請選擇將要刪除的數據')
    }
}

至此,一個 vue-cli項目的,登錄流程,增刪改查已經完成了;








積跬步,而至千里;
共勉;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章