小程序模板網(wǎng)

詩(shī)和遠(yuǎn)方:旅行小賬本云開發(fā)實(shí)戰(zhàn)

發(fā)布時(shí)間:2018-12-04 12:02 所屬欄目:小程序開發(fā)教程

最近沉迷小程序開發(fā),發(fā)現(xiàn)了一款功能、界面、體驗(yàn)俱佳的小程序“旅行小賬本”。該小程序由騰訊旅游操刀制作,簡(jiǎn)約大氣,功能性強(qiáng)。借著最近云開發(fā)的熱潮,著手做了個(gè)簡(jiǎn)約版——"旅行小賬本"。效果比較滿意,畢竟前后臺(tái)一人單干。

Talk is cheap!

show~

 

IDE

  • 微信開發(fā)者工具
  • VSCode

小程序開發(fā)必然少不了微信開發(fā)者工具,再加上其對(duì)云開發(fā)的全面支持,再好不過的開發(fā)利器。但熟悉微信開發(fā)者工具的朋友們應(yīng)該知道,它不支持Emmet縮寫語(yǔ)法,并且wxml的屬性值默認(rèn)用單引號(hào)表示(強(qiáng)迫癥表示很難受)。

而VSCode很好的補(bǔ)足了微信開發(fā)者工具的不足之處,并且支持多元化插件開發(fā),輕量好用。

所以這里推薦采用微信開發(fā)者工具+VSCode配合開發(fā)。微信開發(fā)者工具負(fù)責(zé)調(diào)試、模擬小程序運(yùn)行情況,VSCode負(fù)責(zé)代碼編輯工作。二者各司其職,會(huì)使開發(fā)更加的高效、便捷。

總體架構(gòu)

該項(xiàng)目基于小程序云開發(fā),使用的模板是云開發(fā)快速啟動(dòng)模板

由于是個(gè)全棧項(xiàng)目,前端使用小程序所支持的wxml + wxss + js開發(fā)模式,命名采用BEM命名規(guī)范。后臺(tái)則是借助云數(shù)據(jù)庫(kù)+云儲(chǔ)存進(jìn)行數(shù)據(jù)管理。

項(xiàng)目總體結(jié)構(gòu)

|-travelbook  項(xiàng)目名
    |-cloudfunctions  云函數(shù)模塊
        |-deleteItems 級(jí)聯(lián)刪除--云函數(shù)
        |-getTime     獲取時(shí)間--云函數(shù)
    |-miniprogram  項(xiàng)目模塊
        |-components  自定義組件
            |-accountCover  賬本封面組件
            |-spendDetail   支出細(xì)節(jié)組件
        |-pages  頁(yè)面
            |-accountBooks     總賬本頁(yè)
            |-accountCalendar  賬本日歷頁(yè)
            |-accountDetail    支出細(xì)節(jié)頁(yè)
            |-accountList      支出明細(xì)頁(yè)
            |-accountPage      選定賬本頁(yè)
            |-editAccount      賬本編輯頁(yè)
            |-index            首頁(yè)
        |-vant-weapp   有贊vant框架組件庫(kù)
            |-···      系列組件...
        app.js         全局js
        app.json       全局json配置
        app.wxss       全局wxss
復(fù)制代碼

逆向工程

在做該小程序之前,有必要進(jìn)行項(xiàng)目的逆向工程,進(jìn)一步解構(gòu)每一個(gè)頁(yè)面,從而深入了解這款小程序的交互細(xì)節(jié)。那么現(xiàn)在我假設(shè)自己為騰訊旅游的產(chǎn)品設(shè)計(jì)師,在繪制完界面原型后,撰寫了相應(yīng)的交互文檔。當(dāng)然解構(gòu)過程中可能有些細(xì)節(jié)處理并沒有那么仔細(xì)到位...

以下是我繪制的界面原型

接下來對(duì)每個(gè)頁(yè)面的細(xì)節(jié)進(jìn)行解構(gòu),并完成簡(jiǎn)單的wxml結(jié)構(gòu)

<!--switchList使用定位布局-->
<view bindtap="switchList" class="list"></view>

<!--newAccount使用flex布局-->
<view class="newAccount" bindtap="createNewAccount">
    <view class="desc">旅行中的每一筆開支都有獨(dú)特的意義!</view>
    <image src="{{}}"></image>
    <view class="title">創(chuàng)建一個(gè)新賬本</view>
</view>
復(fù)制代碼

<!--整體用flex + 百分比布局-->
<input type="text" class="accuntName" placeholder="旅行賬本名稱" bindinput="getInput" />
  
<van-panel title="選擇封面" class="panel">
    <van-row class="imageBox">
        <!--使用wx:for遍歷數(shù)據(jù)庫(kù)賬本圖片信息-->
        <van-col span="8" class="imgCol" bindtap="selectThis">
            <image class="select" src="{{}}"></image>
        </van-col>
        
        <van-col span="8">
            <view class="addBox" bindtap="useMore">更多封面</view>
        </van-col>
    </van-row>
</van-panel>

<button type="primary" bindtap="save">保存</button>
<button type="warn" bindtap="delete">刪除</button>
復(fù)制代碼

<view class="accountDesc" bindtap="viewDetail">
    <!--使用wx:for遍歷數(shù)據(jù)庫(kù)賬本信息-->
    <view class="accountName">
        <view>{{}}</view>
        <view class="accountTime">{{}}</view>
    </view>
    
    <!--絕對(duì)定位-->
    <image class="updateImg" catchtap="editAccount" src="{{}}"></image>
</view>
復(fù)制代碼

<!--switchList使用定位布局-->
<view bindtap="switchList" class="list"></view>

<view class="account__list-year">{{}}</view>
<view class="account__list-new account__list-public" bindtap="createNewAccount">
    <!--日期小圓點(diǎn)-->
    <view class="account__list-point"></view>
    <view class="account__list-time">{{}}</view>
    <image src="{{}}"></image>
    <view class="account__list-title">創(chuàng)建一個(gè)新賬本</view>
</view>

<!--使用wx:for遍歷數(shù)據(jù)庫(kù)賬本信息-->
<view class="account__list-item account__list-public" bindtap="viewDetail">
    <!--日期小圓點(diǎn)-->
    <view class="account__list-point"></view>
    <image src="{{}}" mode="aspectFill"></image>
    <view class="account__list-name">{{}}</view>
    <view class="account__list-time">{{}}</view>
    <image class="account__list-update" catchtap="editAccount" src="{{}}"></image>
 </view>

<view class="account__spend">
    <image bindtap="getCalendar" class="account__spend-calendar" src="{{}}"></image>
    <view class="account__spend-text">
        <view class="account__spend-total">總花費(fèi)(元)</view>
        <view class="account__spend-num">{{}}</view>
    </view>
    <image bindtap="accountAnalyze" class="account__spend-detail" src="{{}}"></image>
</view>

<view class="account__show-time">今天</view>
    <view class="account__show-detail">
        <view class="account__show-income account__show-public">
        <view class="account__show-title">收入(元)</view>
        <text class="account__show-in">+{{}}</text>
    </view>
    <view class="account__show-spend account__show-public">
        <view class="account__show-title">支出(元)</view>
        <text class="account__show-out">-{{}}</text>
    </view>
</view>

<!--使用wx:for遍歷數(shù)據(jù)庫(kù)賬本信息-->
<view class="account__show-items-spend">
    <view>
        <image src="{{}}"></image>
    </view>
    <text>{{}}</text>
    <text class="account__show-items-money">{{}}</text>
</view>

<!--日歷使用極點(diǎn)日歷的插件-->
<!--json中做配置-->
"usingComponents": {
    "calendar": "plugin://calendar/calendar"
}

<!--js改變樣式-->
days_style.push({
  month: 'current',
  day: new Date().getDate(),
  color: 'white',
  background: '#e0a58e'
})

<!--wxml中引用-->
<calendar weeks-type="cn" cell-size="50" next="{{true}}" prev="{{true}}"
    show-more-days="{{true}}" calendar-style="demo6-calendar"
    header-style="calendar-header"board-style="calendar-board" active-type="rounded" 
    lunar="true" header-style="header"calendar-style="calendar"days-color="{{days_style}}">
</calendar>

<!--頂欄日期及收支結(jié)構(gòu)-->
<view class="account__title">
    <text class="account__title-time">{{}}</text>
    <text class="account__title-spend">支出{{}}元 收入{{}}元</text>
</view>

<!--收支細(xì)節(jié)結(jié)構(gòu) 使用flex彈性布局-->
<view class="account__detail">
    <image src="{{}}"></image>
    <view class="account__detail-name">{{}}</view>
    <view class="account__detail-money">{{}}</view>
</view>

<!--使用vant框架的van-tabs組件-->
<!--并封裝自定義組件復(fù)用收支頁(yè),自定義組件后面會(huì)詳細(xì)說明-->
<van-tabs active="{{ active }}" bind:change="onChange">
  <van-tab title="支出">
    <spendDetail detail="{{detail}}" accountKey="{{accountKey}}"></spendDetail>
  </van-tab>
  <van-tab title="收入">
    <spendDetail detail="{{income}}" accountKey="{{accountKey}}"></spendDetail>
  </van-tab>
</van-tabs>
復(fù)制代碼

云開發(fā)

在做完逆向工程的解構(gòu),頁(yè)面基礎(chǔ)結(jié)構(gòu)基本搭建完成。但頁(yè)面依舊是靜態(tài)的,需要數(shù)據(jù)來填充。所以第二步就是數(shù)據(jù)庫(kù)的設(shè)計(jì)。而小程序的云控制臺(tái)恰好提供了數(shù)據(jù)的操作功能,為數(shù)據(jù)驅(qū)動(dòng)提供基石。

云數(shù)據(jù)庫(kù)設(shè)計(jì)

云數(shù)據(jù)庫(kù)是一種NoSQL數(shù)據(jù)庫(kù)。每一張表是一個(gè)集合。值得注意的是在設(shè)計(jì)數(shù)據(jù)庫(kù)時(shí), _id和 _openid 這兩個(gè)字段需要帶上。 _id 是表的主鍵,而 _openid 是用戶標(biāo)識(shí),每個(gè)用戶都有不同的 _openid ,可區(qū)分不同用戶。

以下是項(xiàng)目中的數(shù)據(jù)表設(shè)計(jì)

cover_photos 賬本封面表  用于存儲(chǔ)創(chuàng)建賬本時(shí)需要的封面信息
    - _id
    - _openid
    - cover_index 封面索引
    - cover_url   封面url
    - isSelected  封面是否選中
復(fù)制代碼
accounts 賬本表   用于存儲(chǔ)用戶創(chuàng)建的賬本
    - _id
    - _openid
    - accountKey  賬本唯一標(biāo)識(shí)
    - coverUrl    賬本封面
    - i           賬本索引
    - inputValue  賬本名字
    - now         賬本創(chuàng)建時(shí)間
    - spend       賬本總花費(fèi)
復(fù)制代碼
account_detail 支出類型表   用于存儲(chǔ)消費(fèi)類型
    - _id
    - _openid
    - detail       類型細(xì)節(jié)
    - pic_index    消費(fèi)類型索引
    - pic_url      未點(diǎn)擊時(shí)的圖片
    - pic_url_act  點(diǎn)擊后的圖片
    - type         消費(fèi)類型
復(fù)制代碼
account_income 收入類型表   用于存儲(chǔ)收入類型
    - _id
    - _openid
    - pic_index    收入類型索引
    - pic_url      未點(diǎn)擊時(shí)的圖片
    - pic_url_act  點(diǎn)擊后的圖片
    - type         收入類型
復(fù)制代碼
spend_items   消費(fèi)明細(xì)表
    - _id
    - _openid
    - accountKey   賬本唯一標(biāo)識(shí)
    - address      消費(fèi)地點(diǎn)
    - desc         消費(fèi)描述
    - fullDate     消費(fèi)時(shí)間
    - money        消費(fèi)金額
    - pic_type     消費(fèi)類型
    - pic_url      消費(fèi)類型圖片
復(fù)制代碼

云儲(chǔ)存管理

這是個(gè)非常實(shí)用的板塊。類似于百度云盤,它提供了文件存儲(chǔ)、上傳與下載功能。

除此之外,它還會(huì)將你所上傳的資源自動(dòng)進(jìn)行壓縮操作,并生成一個(gè)地址供你引用。該項(xiàng)目中的一些圖片資源就是存在于此,然后在云數(shù)據(jù)庫(kù)的字段中引用這些資源地址即可,十分方便,不必在本地存儲(chǔ),占用小程序內(nèi)存。

云函數(shù)設(shè)計(jì)

云函數(shù)簡(jiǎn)單來說就是在云后端(Node.js)運(yùn)行的代碼,本地看不到這些代碼的執(zhí)行過程,全封閉式只暴露接口供本地調(diào)用執(zhí)行,本地只需等待云端代碼執(zhí)行完畢后返回結(jié)果。這也是面向接口編程的思想體現(xiàn)。

項(xiàng)目中的云函數(shù)設(shè)計(jì)

// getTime  獲取當(dāng)前時(shí)間并格式化為 yyyy-mm-dd

// 云函數(shù)入口文件
const cloud = require('wx-server-sdk')

// 初始化云函數(shù)
cloud.init()

// 云函數(shù)入口函數(shù)
exports.main = async (event, context) => {
  var date = new Date()
  var seperator1 = "-"
  var year = date.getFullYear()
  var month = date.getMonth() + 1
  var strDate = date.getDate()
  if (month >= 1 && month <= 9) {
    month = "0" + month
  }
  if (strDate >= 0 && strDate <= 9) {
    strDate = "0" + strDate
  }
  // 格式化當(dāng)前時(shí)間
  var currentdate = year + seperator1 + month + seperator1 + strDate
  return currentdate
}
復(fù)制代碼
// deleteItems  批量刪除,云數(shù)據(jù)庫(kù)的批量刪除只允許在云函數(shù)中執(zhí)行

// 云函數(shù)入口文件
const cloud = require('wx-server-sdk')

// 初始化云函數(shù)
cloud.init()

// 連接云數(shù)據(jù)庫(kù)
const db = cloud.database()
const _ = db.command


// 云函數(shù)入口函數(shù)
exports.main = async (event, context) => {
  try {
    return await db.collection('spend_items')
      .where({
        accountKey: event.accountKey
      })
      .remove()
  } catch (e) {
    console.error(e)
  }
}

MVVM

界面有了,數(shù)據(jù)有了。萬事俱備,只欠東風(fēng)!所以下一步就是MVVM的設(shè)計(jì)。小程序本質(zhì)就是基于MVVM所設(shè)計(jì)的,在MVVM的世界里,數(shù)據(jù)是靈魂,一切都由數(shù)據(jù)來驅(qū)動(dòng)。

賬本頁(yè)顯示

賬本頁(yè)有兩種顯示的風(fēng)格,左上角的按鈕可以來回切換風(fēng)格,下拉可刷新頁(yè)面,顯示accounts數(shù)據(jù)表中存儲(chǔ)的賬本信息。顯示時(shí)有個(gè)小細(xì)節(jié),需要根據(jù)創(chuàng)建的時(shí)間先后來顯示,越晚創(chuàng)建的越先顯示。

// 頁(yè)面數(shù)據(jù)設(shè)計(jì), 在wxml中使用{{}}符號(hào)引用數(shù)據(jù),數(shù)據(jù)就動(dòng)態(tài)顯示到了頁(yè)面上
data: {
    isList: false, // 轉(zhuǎn)換頁(yè)面風(fēng)格的標(biāo)識(shí) true為豎向風(fēng)格 false為橫向風(fēng)格
    accounts: [],  // 存儲(chǔ)查詢的賬本數(shù)據(jù)
    now: null,     // 存儲(chǔ)當(dāng)日時(shí)間
    year: null     // 存儲(chǔ)年份
}

 // 轉(zhuǎn)換顯示風(fēng)格
switchList() {
    // 設(shè)置頁(yè)面風(fēng)格樣式
    let isList = !this.data.isList
    this.setData({
      isList
    })
    wx.setStorage({
      key: "isList",
      data: isList
    })
}

// 獲取頁(yè)面風(fēng)格轉(zhuǎn)換標(biāo)識(shí)
var isList = wx.getStorageSync('isList')
    
// 查詢賬本
db.collection('accounts')
  .get({
    success: res => {
      this.setData({
        accounts: res.data.reverse(),  // 反轉(zhuǎn)數(shù)組,優(yōu)先顯示創(chuàng)建早的賬本
        isList
      })
      wx.hideLoading()
    }
  })

// 調(diào)用云函數(shù)接口 獲取當(dāng)前日期
wx.cloud.callFunction({
    // 云函數(shù)接口名就是創(chuàng)建的云函數(shù)名字,這里是'getTime'
    name: 'getTime',
    success: (res) => {
    let year = res.result.split('-')[0]
    this.setData({
      now: res.result,
      year
    })
    },
    fail: console.error
})

賬本頁(yè)增刪改

賬本頁(yè)通過調(diào)用相應(yīng)的云數(shù)據(jù)庫(kù)API,可進(jìn)行一系列的增刪改操作。值得一提的是,修改時(shí)需要表單回顯,刪除時(shí)需要級(jí)聯(lián)刪除。因?yàn)橐粋€(gè)賬本中有許多收支情況,spend_items表就是進(jìn)行收支記錄,所以刪除賬本時(shí)需要級(jí)聯(lián)刪除對(duì)應(yīng)的spend_items表中的收支信息。

一些重要的邏輯

  • 封面單選邏輯
    data: {
        images: [],      // 封面數(shù)組
        selectImg: null, // 選擇其它封面
        isSelected: {},  // 選中的圖片
        inputValue: '',  // 賬本名字
        now: null,       // 當(dāng)前時(shí)間
        account: {}      // 傳入賬本信息
    }
    
      // 單選邏輯 通過構(gòu)造{'0': isSelected}來實(shí)現(xiàn)
    selectThis(e) {
        let index = e.currentTarget.dataset.index
        let coverUrl = e.currentTarget.dataset.coverurl
        let is = this.data.isSelected[index]
        let obj = {
            coverUrl
        }
        // obj[index] 屬性動(dòng)態(tài)改變
        obj[index] = !is
        obj.i = index
        this.setData({
            isSelected: obj
        })
    }
    復(fù)制代碼
    表單回顯邏輯
    // 頁(yè)面加載時(shí)先通過對(duì)應(yīng)的accountKey, 得到回顯信息
    let { i, id, value, url, accountKey } = options
    photos.get({
        success: res => {
        this.setData({
          images: res.data,
          account: {
            id,
            value,
            url,
            i,
            accountKey
          },
          isSelected: obj
        })
        wx.hideLoading()
      }
    })
    // 修改
    save() {
        let { id } = this.data.account
        let { i, coverUrl, value } = this.data.isSelected
        // 若沒修改 則為之前的value
        let inputValue = this.data.inputValue || value
        
        db.collection('accounts')
          .doc(id)
          .update({
            data: {
                inputValue,
                coverUrl,
                i
            }
        })
    }
    復(fù)制代碼
    級(jí)聯(lián)刪除邏輯
    db.collection('accounts')
        .doc(this.data.account.id)
        .remove()
        .then(() => {
          wx.hideLoading()
          wx.showToast({
            title: '刪除成功'
          })
          setTimeout(() => {
            wx.reLaunch({
              url: '../accountBooks/accountBooks'
            })
          }, 400)
        })
      // 調(diào)用deleteItems云函數(shù), 傳入對(duì)應(yīng)accountKey主鍵, 通過云函數(shù)批量刪除
      wx.cloud.callFunction({
        name: 'deleteItems',
        data: {
          accountKey
        }
      })
    復(fù)制代碼

賬本頁(yè)收支

因?yàn)槭杖肱c支出頁(yè)面基本類似,所以使用自定義組件封裝,可以復(fù)用。

// 封裝spendDetail組件
// 注冊(cè)組件
properties: {
    detail: {
      type: Object
    },
    accountKey: {
      type: Number
    },
    isSpend: {
      type: Boolean
    }
}

// 引用組件
<van-tab title="支出">
    <spendDetail detail="{{detail}}" accountKey="{{accountKey}}" isSpend="{{isSpend}}"></spendDetail>
  </van-tab>
  <van-tab title="收入">
    <spendDetail detail="{{income}}" accountKey="{{accountKey}}" isSpend="{{isSpend}}"></spendDetail>
</van-tab>

收入與支出類型icon選擇使用兩個(gè)view來存放,通過選擇不同類型,跳轉(zhuǎn)不同的icon

// js
data: {
    address: '',
    money: 0,
    desc: '',
    selectPicIndex: 0,
    selectIndex: 0
}
// 選擇消費(fèi)類別
selectSpend(e) {
  let { index } = e.currentTarget.dataset
  let { selectPicIndex } = this.data
  selectPicIndex = index
  this.setData({
    selectPicIndex
  })
},

// 選擇消費(fèi)類別中的細(xì)節(jié)
selectSpendDetail(e) {
  let { index } = e.currentTarget.dataset
  let { selectIndex } = this.data
  selectIndex = index
  this.setData({
    selectIndex
  })
}

// wxml
// 消費(fèi)類型
<view class="expense">
  <block wx:for="{{detail}}" wx:key="index">
    <view class="expense__type" bindtap="selectSpend" data-index="{{index}}">
      <block wx:if="{{selectPicIndex == item.pic_index}}">
        <view class="expense__type-icon" style="background-color: #e64343">
          <image src="{{item.pic_url_act}}"></image>
        </view>
      </block>
      <block wx:else>
        <view class="expense__type-icon">
          <image src="{{item.pic_url}}"></image>
        </view>
      </block>
      <view class="expense__type-name">{{item.type}}</view>
    </view>
  </block>
</view>

// 消費(fèi)子類型
<view class="detail">
  <block wx:for="{{detail[selectPicIndex].detail}}" wx:key="index">
    <view class="detail__type" bindtap="selectSpendDetail" data-index="{{index}}">
      <image class="detail__type-icon" src="{{item.detail_url}}"></image>
      <block wx:if="{{selectIndex == item.detail_index}}">
        <view class="detail__type-name" style="color: #f86319; border-bottom: 1rpx solid #f86319;">
          {{item.detail_type}}
        </view>
      </block>
      <block wx:else>
        <view class="detail__type-name" style="border-bottom: 1rpx solid #e4e2e2;">
          {{item.detail_type}}
        </view>
      </block>
    </view>
  </block>
</view>

賬本頁(yè)明細(xì)

因?yàn)槭罩骷?xì)中需要顯示每一天的消費(fèi)信息,所以需要將數(shù)據(jù)表中的數(shù)據(jù)通過時(shí)間來分類,分成若干個(gè)數(shù)組,頁(yè)面從而使用wx:for來遍歷這些數(shù)組。在顯示之前,首先需要判斷有無收支信息。

// 通過時(shí)間分類算法  {} => [ [{時(shí)間1}], [{時(shí)間2}], [{時(shí)間3}] ]
arr.forEach(item => {
  if (!_this.isExist(item.fullDate, dateArr)) {
    dateArr.push([item])
  } else {
    dateArr.forEach(res => {
      if (res[0].fullDate == item.fullDate) {
        res.push(item)
      }
    })
  }
})

// 使用map 方法構(gòu)造 [{}, {}, {}, ...] 類型數(shù)組
dateArr = dateArr.map((item) => {
  let spend = 0
  let income = 0
  item.forEach(res => {
    if (res.money > 0) {
      spend += res.money
    } else {
      income += (-res.money)
    }
  })
  return {
    item,
    spend,
    income
  }
})

// 判斷自身是否存在數(shù)組中
isExist(item, arr) {
    for (let i = 0; i < arr.length; i++) {
      if (item == arr[i][0].fullDate)
        return true
    }
    return false
  }

以上是小程序中比較復(fù)雜的邏輯實(shí)現(xiàn)。

一點(diǎn)感悟

 

之前做項(xiàng)目時(shí),只是在github提交時(shí)草草寫一句話當(dāng)做提交日志。這次做了一個(gè)比較正式提交日志,做這個(gè)的初衷其實(shí)是為了監(jiān)督自己不要偷懶,堅(jiān)持每天完成項(xiàng)目一部分,并總結(jié)不足之處。學(xué)而時(shí)習(xí)之才能成長(zhǎng)的更快!

篇幅有限,奉上項(xiàng)目 如果你喜歡這篇文章或是這個(gè)項(xiàng)目,不妨進(jìn)去點(diǎn)個(gè)Star支持下,有興趣的朋友歡迎Fork,一起探討知識(shí)或是旅行~~當(dāng)然也希望您能留下一些寶貴的建議。感激不盡!

生活不止眼前的茍且,還有詩(shī)和遠(yuǎn)方。最后要感謝騰訊旅游的各位大大設(shè)計(jì)出一個(gè)這么簡(jiǎn)潔美觀大方的小程序產(chǎn)品,實(shí)屬良心之作!



易優(yōu)小程序(企業(yè)版)+靈活api+前后代碼開源 碼云倉(cāng)庫(kù):starfork
本文地址:http://u-renovate.com/wxmini/doc/course/24994.html 復(fù)制鏈接 如需定制請(qǐng)聯(lián)系易優(yōu)客服咨詢:800182392 點(diǎn)擊咨詢
QQ在線咨詢