国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

記一次基于mpvue的小程序開發及上線實戰

miqt / 606人閱讀

摘要:一起打車吧微信小程序依然是一個玩具般的存在,僅供自己學習和探索,當然也歡迎各位讀者能夠貢獻代碼,參與開發

小程序名稱:一起打車吧

項目地址:

客戶端:https://github.com/jrainlau/t...

服務端:https://github.com/jrainlau/t...

小程序二維碼:

經過為期兩個晚上下班時間的努力,終于把我第一個小程序開發完成并發布上線了。整個過程還算順利,由于使用了mpvue方案進行開發,故可以享受和vue一致的流暢開發體驗;后臺系統使用了python3+flask框架進行,使用最少的代碼完成了小程序的后臺邏輯。除了開發之外,還實實在在地體驗了一把微信小程序的開發流程,包括開發者工具的使用、體驗版的發布、上線的申請等等。這些開發體驗都非常值得被記錄下來,于是便趁熱打鐵,寫下這篇文章。

一、需求&功能

由于公司里有相當多的同事都住在同一個小區,所以上下班的時候經常會在公司群里組織拼車。但是由于完全依賴聊天記錄,且上下班拼車的同事也很多,依賴群聊很容易把消息刷走,而且容易造成信息錯亂。既然如此,那么完全可以開發一個小工具把這些問題解決。

發起拼車的人把出發地點、目的地點、打車信息以卡片的形式分享出來,參與拼車的人點擊卡片就能選擇參加拼車,并且能看到同車拼友是誰,拼單的信息等等內容。

交互流程如下:

可以看到,邏輯是非常簡單的,我們只需要保證生成拼單、分享拼單、進入拼單和退出拼單這四個功能就好。

需求和功能已經確定好,首先按照小程序官網的介紹,注冊好小程序并拿到appId,接下來可以開始進行后臺邏輯的開發。

二、后臺邏輯開發

由于時間倉促,功能又簡單,所以并沒有考慮任何高并發等復雜場景,僅僅考慮功能的實現。從需求的邏輯可以知道,其實后臺只需要維護兩個列表,分別存儲當前所有拼車單以及當前所有參與了拼車的用戶即可,其數據結構如下:

當前所有拼單列表billsList

當前所有參與了拼車的用戶列表inBillUsers

當用戶確定并分享了一個拼單之后,會直接新建一個拼單,同時把該用戶添加到當前所有參與了拼車的用戶列表列表里面,并且添加到該拼單的成員列表當中:

只要維護好這兩個列表,接下來就是具體的業務邏輯了。

為了快速開發,這里我使用了python3+flask框架的方案。不懂python的讀者看到這里也不用緊張,代碼非常簡單且直白,看看也無妨。

首先新建一個BillController類:

class BillController:
    billsList = []
    inBillUsers = []

接下來會在這個類的內部添加創建拼單獲取拼單參與拼單退出拼單判斷用戶是否在某一拼單中圖片上傳的功能。

1、獲取拼單getBill()

該方法接收客戶端傳來的拼單ID,然后拿這個ID去檢索是否存在對應的拼單。若存在則返回對應的拼單,否則報錯給客戶端。

    def getBill(self, ctx):
        ctxBody = ctx.form
        billId = ctxBody["billId"]
        try: 
            return response([item for item in self.billsList if item["billId"] == billId][0])
        except IndexError:
            return response({
                "errMsg": "拼單不存在!",
                "billsList": self.billsList,
            }, 1)
2、創建拼單createBill()

該方法會接收來自客戶端的用戶信息拼單信息,分別添加到billsListinBillUsers當中。

    def createBill(self, ctx):
        ctxBody = ctx.form
        user = {
            "userId": ctxBody["userId"],
            "billId": ctxBody["billId"],
            "name": ctxBody["name"],
            "avatar": ctxBody["avatar"]
        }
        bill = {
            "billId": ctxBody["billId"],
            "from": ctxBody["from"],
            "to": ctxBody["to"],
            "time": ctxBody["time"],
            "members": [user]
        }

        if ctxBody["userId"] in [item["userId"] for item in self.inBillUsers]:
            return response({
                "errMsg": "用戶已經在拼單中!"
            }, 1)

        self.billsList.append(bill)
        self.inBillUsers.append(user)
        return response({
            "billsList": self.billsList,
            "inBillUsers": self.inBillUsers
        })

創建完成后,會返回當前的billsListinBillUsers到客戶端。

3、參與拼單joinBill()

接收客戶端傳來的用戶信息拼單ID,把用戶添加到拼單和inBillUsers列表中。

    def joinBill(self, ctx):
        ctxBody = ctx.form
        billId = ctxBody["billId"]
        user = {
            "userId": ctxBody["userId"],
            "name": ctxBody["name"],
            "avatar": ctxBody["avatar"],
            "billId": ctxBody["billId"]
        }
        if ctxBody["userId"] in [item["userId"] for item in self.inBillUsers]:
            return response({
                "errMsg": "用戶已經在拼單中!"
            }, 1)
        theBill = [item for item in self.billsList if item["billId"] == billId]
        if not theBill:
            return response({
                "errMsg": "拼單不存在"
            }, 1)
        theBill[0]["members"].append(user)
        self.inBillUsers.append(user)
        return response({
            "billsList": self.billsList,
            "inBillUsers": self.inBillUsers
        })
4、退出拼單leaveBill()

接收客戶端傳來的用戶ID拼單ID,然后刪除掉兩個列表里面的該用戶。

這個函數還有一個功能,如果判斷到這個拼單ID所對應的拼單成員為空,會認為該拼單已經作廢,會直接刪除掉這個拼單以及所對應的車輛信息圖片。

    def leaveBill(self, ctx):
        ctxBody = ctx.form
        billId = ctxBody["billId"]
        userId = ctxBody["userId"]
        indexOfUser = [i for i, member in enumerate(self.inBillUsers) if member["userId"] == userId][0]
        indexOfTheBill = [i for i, bill in enumerate(self.billsList) if bill["billId"] == billId][0]
        indexOfUserInBill = [i for i, member in enumerate(self.billsList[indexOfTheBill]["members"]) if member["userId"] == userId][0]
        # 刪除拼單里面的該用戶
        self.billsList[indexOfTheBill]["members"].pop(indexOfUserInBill)
        # 刪除用戶列表里面的該用戶
        self.inBillUsers.pop(indexOfUser)
        # 如果拼單里面用戶為空,則直接刪除這筆拼單
        if len(self.billsList[indexOfTheBill]["members"]) == 0:
            imgPath = "./imgs/" + self.billsList[indexOfTheBill]["img"].split("/getImg")[1]
            if os.path.exists(imgPath):
                os.remove(imgPath)
            self.billsList.pop(indexOfTheBill)
        return response({
            "billsList": self.billsList,
            "inBillUsers": self.inBillUsers
        })
5、判斷用戶是否在某一拼單中inBill()

接收客戶端傳來的用戶ID,接下來會根據這個用戶ID去inBillUsers里面去檢索該用戶所對應的拼單,如果能檢索到,會返回其所在的拼單。

    def inBill(self, ctx):
        ctxBody = ctx.form
        userId = ctxBody["userId"]
        if ctxBody["userId"] in [item["userId"] for item in self.inBillUsers]:
            return response({
                "inBill": [item for item in self.inBillUsers if ctxBody["userId"] == item["userId"]][0],
                "billsList": self.billsList,
                "inBillUsers": self.inBillUsers
            })
        return response({
            "inBill": False,
            "billsList": self.billsList,
            "inBillUsers": self.inBillUsers
        })
6、圖片上傳uploadImg()

接收客戶端傳來的拼單ID圖片資源,先存儲圖片,然后把該圖片的路徑寫入對應拼單ID的拼單當中。

    def uploadImg(self, ctx):
        billId = ctx.form["billId"]
        file = ctx.files["file"]
        filename = file.filename
        file.save(os.path.join("./imgs", filename))
        # 把圖片信息掛載到對應的拼單
        indexOfTheBill = [i for i, bill in enumerate(self.billsList) if bill["billId"] == billId][0]
        self.billsList[indexOfTheBill]["img"] = url_for("getImg", filename=filename)
        return response({
            "billsList": self.billsList
        })

完成了業務邏輯的功能,接下來就是把它們分發給不同的路由了:

@app.route("/create", methods = ["POST"])
def create():
    return controller.createBill(request)

@app.route("/join", methods = ["POST"])
def join():
    return controller.joinBill(request)

@app.route("/leave", methods = ["POST"])
def leave():
    return controller.leaveBill(request)

@app.route("/getBill", methods = ["POST"])
def getBill():
    return controller.getBill(request)

@app.route("/inBill", methods = ["POST"])
def inBill():
    return controller.inBill(request)

@app.route("/uploadImg", methods = ["POST"])
def uploadImg():
    return controller.uploadImg(request)

@app.route("/getImg/")
def getImg(filename):
  return send_from_directory("./imgs", filename)

完整的代碼可以直接到倉庫查看,這里僅展示關鍵的內容。

三、前端業務開發

前端借助vue-cli直接使用了mpvue的mpvue-quickstart來初始化項目,具體過程不再細述,直接進入業務開發部分。

首先,微信小程序的API都是callback風格,為了使用方便,我把用到的小程序API都包裝成了Promise,統一放在src/utils/wx.js內部,類似下面這樣:

export const request = obj => new Promise((resolve, reject) => {
  wx.request({
    url: obj.url,
    data: obj.data,
    header: { "content-type": "application/x-www-form-urlencoded", ...obj.header },
    method: obj.method,
    success (res) {
      resolve(res.data.data)
    },
    fail (e) {
      console.log(e)
      reject(e)
    }
  })
})
1、注冊全局Store

由于開發習慣,我喜歡把所有接口請求都放在store里面的actions當中,所以這個小程序也是需要用到Vuex。但由于小程序每一個Page都是一個新的Vue實例,所以按照Vue的方式,用全局Vue.use(Vuex)是不會把$store注冊到實例當中的,這一步要手動來。

src/目錄下新建一個store.js文件,然后在里面進行使用注冊:

import Vue from "vue"
import Vuex from "vuex"


Vue.use(Vuex)

export default new Vuex.Store({})

接下來在src/main.js當中,手動在Vue的原型里注冊一個$store

import Vue from "vue"
import App from "./App"
import Store from "./store"

Vue.prototype.$store = Store

這樣,以后在任何的Page里都可以通過this.$store來操作這個全局Store了。

2、構建好請求的API接口

和后臺系統的邏輯對應,前端也要構造好各個請求的API接口,這樣的做法能夠避免把API邏輯分散到頁面四處,具有清晰、易維護的優勢。

    /**
     * @param  {} {commit}
     * 獲取用戶公開信息
     */
    async getUserInfo ({ commit }) {
      const { userInfo } = await getUserInfo({
        withCredenitals: false
      })
      userInfo.avatar = userInfo.avatarUrl
      userInfo.name = userInfo.nickName
      userInfo.userId = encodeURIComponent(userInfo.nickName + userInfo.city + userInfo.gender + userInfo.country)
      commit("GET_USER_INFO", userInfo)
      return userInfo
    },
    /**
     * @param  {} {commit}
     * @param  { String } userId 用戶ID
     * 檢查用戶是否已經存在于某一拼單中
     */
    async checkInBill ({ commit }, userId) {
      const res = await request({
        method: "post",
        url: `${apiDomain}/inBill`,
        data: {
          userId
        }
      })
      return res
    },
    /**
     * @param  {} {commit}
     * @param  { String } userId 用戶ID
     * @param  { String } name   用戶昵稱
     * @param  { String } avatar 用戶頭像
     * @param  { String } time   出發時間
     * @param  { String } from   出發地點
     * @param  { String } to     目的地點
     * @param  { String } billId 拼單ID
     * 創建拼單
     */
    async createBill ({ commit }, { userId, name, avatar, time, from, to, billId }) {
      const res = await request({
        method: "post",
        url: `${apiDomain}/create`,
        data: {
          userId,
          name,
          avatar,
          time,
          from,
          to,
          billId
        }
      })
      commit("GET_BILL_INFO", res)
      return res
    },
    /**
     * @param  {} {commit}
     * @param  { String } billId 拼單ID
     * 獲取拼單信息
     */
    async getBillInfo ({ commit }, billId) {
      const res = await request({
        method: "post",
        url: `${apiDomain}/getBill`,
        data: {
          billId
        }
      })
      return res
    },
    /**
     * @param  {} {commit}
     * @param  { String } userId 用戶ID
     * @param  { String } name   用戶昵稱
     * @param  { String } avatar 用戶頭像
     * @param  { String } billId 拼單ID
     * 參加拼單
     */
    async joinBill ({ commit }, { userId, name, avatar, billId }) {
      const res = await request({
        method: "post",
        url: `${apiDomain}/join`,
        data: {
          userId,
          name,
          avatar,
          billId
        }
      })
      return res
    },
    /**
     * @param  {} {commit}
     * @param  { String } userId 用戶ID
     * @param  { String } billId 拼單ID
     * 退出拼單
     */
    async leaveBill ({ commit }, { userId, billId }) {
      const res = await request({
        method: "post",
        url: `${apiDomain}/leave`,
        data: {
          userId,
          billId
        }
      })
      return res
    },
    /**
     * @param  {} {commit}
     * @param  { String } filePath 圖片路徑
     * @param  { String } billId   拼單ID
     * 參加拼單
     */
    async uploadImg ({ commit }, { filePath, billId }) {
      const res = await uploadFile({
        url: `${apiDomain}/uploadImg`,
        header: {
          "content-type": "multipart/form-data"
        },
        filePath,
        name: "file",
        formData: {
          "billId": billId
        }
      })
      return res
    }
3、填寫拼單并實現分享功能實現

新建一個src/pages/index目錄,作為小程序的首頁。

該首頁的業務邏輯如下:

進入首頁的時候先獲取用戶信息,得到userId

然后用userId去請求判斷是否已經處于拼單

若是,則跳轉到對應拼單Id的詳情頁

若否,才允許新建拼單

onShow的生命周期鉤子中實現上述邏輯:

  async onShow () {
    this.userInfo = await this.$store.dispatch("getUserInfo")
    const inBill = await this.$store.dispatch("checkInBill", this.userInfo.userId)

    if (inBill.inBill) {
      wx.redirectTo(`../join/main?billId=${inBill.inBill.billId}&fromIndex=true`)
    }
  },

當用戶填寫完拼單后,會點擊一個帶有open-type="share"屬性的button,然后會觸發onShareAppMessage生命周期鉤子的邏輯把拼單構造成卡片分享出去。當分享成功后會跳轉到對應拼單ID的參加拼單頁。

  onShareAppMessage (result) {
    let title = "一起拼車"
    let path = "/pages/index"
    if (result.from === "button") {
      this.billId = "billId-" + new Date().getTime()
      title = "我發起了一個拼車"
      path = `pages/join/main?billId=${this.billId}`
    }
    return {
      title,
      path,
      success: async (res) => {
        await this.$store.dispatch("createBill", { ...this.userInfo, ...this.billInfo })

        // 上傳圖片
        await this.$store.dispatch("uploadImg", {
          filePath: this.imgSrc,
          billId: this.billId
        })
        
        // 分享成功后,會帶著billId跳轉到參加拼單頁
        wx.redirectTo(`../join/main?billId=${this.billId}`)
      },
      fail (e) {
        console.log(e)
      }
    }
  },
4、參與拼單&退出拼單功能實現

新建一個src/pages/join目錄,作為小程序的“參加拼單頁”。

該頁面的運行邏輯如下:

首先會獲取從url里面帶來的billId

其次會請求一次userInfo,獲取userId

然后拿這個userId去檢查該用戶是否已經處于拼單

如果已經處于拼單,那么就會獲取一個新的billId代替從url獲取的

拿當前的billId去查詢對應的拼單信息

如果billId都無效,則redirect到首頁

由于要獲取url攜帶的內容,親測onShow()是不行的,只能在onLoad()里面獲取:

  async onLoad (options) {
    // 1. 首先會獲取從url里面帶來的billId
    this.billId = options.billId
    // 2. 其次會請求一次userInfo,獲取userId
    this.userInfo = await this.$store.dispatch("getUserInfo")
    // 3. 然后拿這個userId去檢查該用戶是否已經處于拼單
    const inBill = await this.$store.dispatch("checkInBill", this.userInfo.userId)
    // 4. 如果已經處于拼單,那么就會有一個billId
    if (inBill.inBill) {
      this.billId = inBill.inBill.billId
    }
    // 5. 如果沒有處于拼單,那么將請求當前billId的拼單
    // 6. 如果billId都無效,則redirect到首頁,否則檢查當前用戶是否處于該拼單當中
    await this.getBillInfo()
  }

此外,當用戶點擊“參與拼車”后,需要重新請求拼單信息,以刷新視圖拼車人員列表;當用戶點擊“退出拼車”后,要重定向到首頁。

經過上面幾個步驟,客戶端的邏輯已經完成,可以進行預發布了。

四、預發布&申請上線

如果要發布預發布版本,需要運行npm run build命令,打包出一個生產版本的包,然后通過小程序開發者工具的上傳按鈕上傳代碼,并填寫測試版本號:

接下來可以在小程序管理后臺→開發管理→開發版本當中看到體驗版小程序的信息,然后選擇發布體驗版即可:

當確定預發布測試無誤之后,就可以點擊“提交審核”,正式把小程序提交給微信團隊進行審核。審核的時間非常快,在3小時內基本都能夠有答復。

值得注意的是,小程序所有請求的API,都必須經過域名備案使用https證書,同時要在設置→開發設置→服務器域名里面把API添加到白名單才可以正常使用。

五、后記

這個小程序現在已經發布上線了,算是完整體驗了一把小程序的開發樂趣。小程序得到了微信團隊的大力支持,以后的生態只會越來越繁榮。當初小程序上線的時候我也對它有一些抵觸,但后來想了想,這只不過是前端工程師所需面對的又一個“端“而已,沒有必要為它戴上有色眼鏡,多掌握一些總是好的。

“一起打車吧”微信小程序依然是一個玩具般的存在,僅供自己學習和探索,當然也歡迎各位讀者能夠貢獻代碼,參與開發~

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41556.html

相關文章

  • 一次基于mpvue小程開發上線實戰

    摘要:一起打車吧微信小程序依然是一個玩具般的存在,僅供自己學習和探索,當然也歡迎各位讀者能夠貢獻代碼,參與開發 小程序名稱:一起打車吧 項目地址:客戶端:https://github.com/jrainlau/t... 服務端:https://github.com/jrainlau/t... 小程序二維碼:showImg(https://segmentfault.com/img/bV80...

    freewolf 評論0 收藏0
  • 一次小程之旅

    摘要:用戶綁定的邏輯主要復雜在既需要考慮微信本身的接口在不同情況下提供的數據不同,另外一方面就是考慮本身用戶模塊的業務邏輯問題。針對每一節課以及每一節系列課程生成小程序太陽碼主要涉及到幾個細節問題。 感覺已經好久沒寫程序了,最近這段時間,一方面是學習了python,然后折騰了scrapy框架,用python寫了下守護進程程序監聽任務以及用redis做隊列任務通信,并開進程來處理爬蟲任務。以上...

    不知名網友 評論0 收藏0
  • 小程開發坑點總結

    摘要:整個小程序所有分包大小不超過單個分包主包大小不能超過微信小程序主流框架對比應該算是最早發布的小程序開發框架,提供了類的語法風格和特性,現階段應該也是應用最廣泛的框架吧。不過微信官方為了防止下載離線包的時間過程,也嚴格限制了小程序包的體積。 那些年我們踩過的坑css樣式不能引用本地圖片資源,只能引用線上資源(background-image),引用本地圖片資源只能用標簽。{{}}不能執行...

    lowett 評論0 收藏0
  • mpvue小程開發從零構建

    摘要:根據官方文檔,用搭建腳手架。全局安裝創建一個基于模板的新項目安裝依賴啟動構建生成的目錄結構如圖。示例圖小知識點,標簽模板既可以用里也可以用小程序里的,比如等,在輪播圖中運用方便高效。 1、根據官方文檔,用mpvue搭建腳手架。 # 全局安裝 vue-cli $ npm install --global vue-cli # 創建一個基于 mpvue-quickstart 模板的新項目 $...

    zhiwei 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<