1. 程式人生 > 實用技巧 >商品列表和新增商品(五)

商品列表和新增商品(五)

實現的功能

功能 詳述
商品列表 渲染元件和子路由;佈局;獲取和渲染商品列表;自定義格式化時間全域性過濾器;搜尋和清空;刪除商品
新增商品頁 渲染元件和子路由;佈局;步驟條縱向Tab頁;步驟條和標籤頁聯動
基本資訊表單 商品分類使用級聯選擇器,只允許選中三級阻止標籤頁切換
動態引數頁 複選框tag;根據不同tab頁執行不同操作
圖片上傳 渲染元件;圖片上傳圖片刪除圖片預覽
富文字編輯器
新增商品 深拷貝

使用到的Element-ui元件

元件名稱_EN 註冊 備註
Steps Vue.use(Steps) 步驟條
Step Vue.use(Step)
CheckboxGroup Vue.use(CheckboxGroup) 多選框
Checkbox Vue.use(Checkbox)
Upload Vue.use(Upload) 上傳

使用到的依賴

執行依賴,富文字編輯器vue-quill-editor

// [main.js]
// 匯入富文字編輯器
import VueQuillEditor from 'vue-quill-editor'
// 匯入富文字編輯器對應樣式
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme

// 註冊富文字編輯器
Vue.use(VueQuillEditor)

執行依賴,lodash深拷貝lodash

// [Add.vue -> methods]
import _ from 'lodash'

一、商品列表

1.渲染元件和子路由

2.佈局

①麵包屑導航

②卡片檢視

③柵格系統 搜尋框 新增按鈕

④表格

⑤分頁

3.獲取渲染商品列表

①獲取商品列表

②渲染商品列表

1️⃣自定義格式化時間全域性過濾器

// [main.js]
Vue.filter('dataFormat', function (originVal) {
  const dt = new Date(originVal)
  const y = dt.getFullYear()
  const m = (dt.getMonth() + 1 + '').padStart(2, '0')
  const d = (dt.getDate() + '').padStart(2, '0')

  const hh = (dt.getHours() + '').padStart(2, '0')
  const mm = (dt.getMinutes() + '').padStart(2, '0')
  const ss = (dt.getSeconds() + '').padStart(2, '0')

  return `${y}-${m}-${d} ${hh}:${mm}:${ss}`

})
<!-- [GoodsList.vue] -->
<el-table-column label="建立時間" width="140px">
    <template slot-scope="scope">{{scope.row.add_time | dataFormat}}</template>
</el-table-column>

4、搜尋和清空

5、根據Id刪除商品

6、新增商品頁跳轉

1️⃣程式設計式導航

// [GoodsList.vue]
goAddpage(){
    this.$router.push('/goods/add')
}
// [index.js]
const routes = [
    // ......
    {path:'/goods/add',component:Add},
    // ......
]

二、新增商品頁

1.渲染元件和子路由

2.佈局

①麵包屑導航

②卡片檢視

③警告

④步驟條Steps

:active="activeIndex-0":啟用項

<!-- [Add.vue] -->
<!-- 步驟條 -->
<el-steps :space="200" :active="activeIndex-0" finish-status="success" align-center>
    <el-step title="基本資訊"></el-step>
    <el-step title="商品引數"></el-step>
    <el-step title="商品屬性"></el-step>
    <el-step title="商品圖片"></el-step>
    <el-step title="商品內容"></el-step>
    <el-step title="完成"></el-step>
</el-steps>
// [Add.vue -> data]
activeIndex: '0'
/* [assets/css/global.css] */
.el-steps{
    margin: 15px 0;
}
.el-step__title{
    font-size: 13px;
}

⑤縱向標籤頁Tabs

<!-- [Add.vue] -->
<el-tabs
          :tab-position="'left'"
          v-model="activeIndex"
          :before-leave="beforeTabLeave"
          @tab-click="tabClicked"
        >
          <el-tab-pane label="基本資訊" name="0"></el-tab-pane>
          <el-tab-pane label="商品引數" name="1"></el-tab-pane>
          <el-tab-pane label="商品圖片" name="3"></el-tab-pane>
          <el-tab-pane label="商品內容" name="4"></el-tab-pane>
</el-tabs>

⑥步驟條和標籤頁聯動

activeIndex: '0',

步驟條:active

​ 轉換activeIndex為數值

標籤頁:v-model

​ 啟用的name會繫結到v-model上

⑦表單

使用form包裹tabs,el-tabs必須和el-tab-pane巢狀

3、基本資訊

①渲染基本資訊表單

1️⃣商品分類使用級聯選擇器,只允許選中三級

// [Add.vue -> methods]
// 級聯選擇器選中項變化
handleChange() {
    // 選中的不是三級分類
    if (this.addForm.goods_cat.length !== 3) {
        this.addForm.goods_cat = []
    }
}

2️⃣阻止標籤頁切換

:before-leave="beforeTabLeave":標籤頁屬性,離開時觸發

oldActiveName:離開的標籤頁name

activeName:進入的標籤頁name

// [Add.vue -> methods]
beforeTabLeave(activeName, oldActiveName) {
    if (oldActiveName === '0' && this.addForm.goods_cat.length !== 3) {
        this.$message.error('請先選擇商品分類')
        return false
    }
}

②獲取商品分類資料

4、動態引數

①獲取動態引數

@tab-click="tabClicked":標籤頁屬性,點選時觸發,可以獲取activeIndex訪問的tab面板

// [Add.vue -> methods]
async tabClicked() {
      if (this.activeIndex === '1') {
          // 訪問動態引數面板
          // ......
      } else if (this.activeIndex === '2') {
          // 訪問靜態屬性面板
          // ......
      }
    },

②渲染動態引數

// [Add.vue -> methods]
async tabClicked() {
    if (this.activeIndex === '1') {
        // ......
        res.data.forEach((item) => {
            item.attr_vals = 
                item.attr_vals.length === 0 ? [] : item.attr_vals.split(',')
        })
    }
    // ......
}
<!-- [Add.vue] -->
<el-tab-pane label="商品引數" name="1">
    <el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id">
        <!-- 複選框組 -->
        <el-checkbox-group v-model="item.attr_vals">
            <el-checkbox :label="cb" v-for="(cb,i) in item.attr_vals" :key="i" border></el-checkbox>
        </el-checkbox-group>
    </el-form-item>
</el-tab-pane>

5、靜態屬性

①獲取靜態屬性

②渲染靜態屬性

6、圖片上傳

action:圖片上傳的api地址

:on-preview:圖片預覽

:on-remove:刪除圖片

:headers:上傳的請求頭

:on-success:圖片上傳成功後處理操作

①渲染圖片+上傳

<!-- [Add.vue] -->
<el-tab-pane label="商品圖片" name="3">
    <!-- 上傳 -->
    <el-upload
               :action="uploadURl"
               :on-preview="handlePreview"
               :on-remove="handleRemove"
               list-type="picture"
               :headers="headerObj"
               :on-success="handleSuccess"
               >
        <el-button size="small" type="primary">點選上傳</el-button>
    </el-upload>
</el-tab-pane>
// [Add.vue -> data]
uploadURl: 'http://127.0.0.1:8888/api/private/v1/upload',
// 圖片上傳的headers請求頭物件
headerObj: {
    Authorization: window.sessionStorage.getItem('token'),
},

// [Add.vue -> methods]    
// 監聽圖片上傳成功
handleSuccess(response) {
    // 1,拼接得到一個圖片資訊物件
    const picInfo = { pic: response.data.tmp_path }
    //2,將圖片資訊物件,push 到pics陣列中
    this.addForm.pics.push(picInfo)
},

②圖片移除

// 處理移除圖片
handleRemove(file) {
    // 1,獲取將要刪除的圖片的臨時路徑
    const filePath = file.response.data.tmp_path
    // 2.從pics陣列中,找到這個圖片對應的索引值
    const i = this.addForm.pics.findIndex((x) => x.pic === filePath)
    // 3.呼叫陣列的 splice方法,把圖片資訊物件,從pics陣列中移除
    this.addForm.pics.splice(i, 1)
},

③圖片預覽

<!-- 圖片預覽 -->
<el-dialog title="圖片預覽" :visible.sync="previewDialogVisible" width="50%">
    <img :src="previewPath" alt class="previewImg" />
</el-dialog>
// [Add.vue -> data]
previewPath: '',
previewDialogVisible: false

// [Add.vue -> methods]    
// 處理圖片預覽
handlePreview(file) {
    this.previewPath = file.response.data.url
    this.previewDialogVisible = true
}

7、富文字編輯器

<!-- [Add.vue] -->
<el-tab-pane label="商品內容" name="4">
    <!-- 富文字編輯器 -->
    <quill-editor v-model="addForm.goods_introduce"></quill-editor>
    <el-button type="primary" class="btnAdd" @click="add">新增商品</el-button>
</el-tab-pane>
/* [assets/css/global.css] */
.ql-editor{
    min-height: 300px;
}

8、新增商品

表單預校驗,校驗通過發起請求

①預校驗

// [Add.vue -> methods]  
// 新增商品
async add() {
    // 預校驗
    this.$refs.addFormRulesRef.validate((valid) => {
        if (!valid) return this.$message.error('請填寫必要的表單項')
    })
    // ......
}

②深拷貝

級聯選擇器要求陣列,請求要求字串---->深拷貝

// [Add.vue -> methods]  
// 新增商品
async add() {
    // ......
    
    // 執行新增的業務邏輯
    // 深拷貝 lodash  this.addForm雙向繫結級聯選擇器會報錯
    const form = _.cloneDeep(this.addForm)
    form.goods_cat = form.goods_cat.join(',')
    // ......
}

③商品引數(動態引數+靜態屬性)

// [Add.vue -> methods]  
// 新增商品
async add() {
    // ......
    // 處理動態引數
    this.manyTableData.forEach((item) => {
        const newInfo = {
            attr_id: item.attr_id,
            attr_value: item.attr_vals.join(','),
        }
        this.addForm.attrs.push(newInfo)
    })
    // 處理靜態屬性
    this.onlyTableData.forEach((item) => {
        const newInfo = {
            attr_id: item.attr_id,
            attr_value: item.attr_vals,
        }
        this.addForm.attrs.push(newInfo)
    })
    form.attrs = this.addForm.attrs
    // ......
}

④發起請求

// [Add.vue -> methods]  
// 新增商品
async add() {
    // ......
    // 發起請求
    const { data: res } = await this.$http.post('goods', form)
    if (res.meta.status !== 201) {
        return this.$message.error('新增商品失敗')
    }
    this.$message.success('新增商品成功')
    this.$router.push('/goods')
}