首頁  >  文章  >  web前端  >  超市場

超市場

王林
王林原創
2024-07-18 18:38:58582瀏覽

Mega Mart

這是 Wix Studio 挑戰賽的投稿內容。

背景

人工智慧正在改變商業格局時,為什麼還要遵循過時的方法?我創建這個電子商務是因為現在人工智慧是公司的趨勢。 Mega Mart 是一個不僅提供各種商品的平台,還透過 Wix Studio 中的高級功能增強了 UI/UX。


我建造了什麼

我開發了高級人工智慧搜尋選項來解決產品發現效率低下的問題,目標是為用戶創造更直觀和愉快的購物體驗。

你有沒有看過有人穿著你喜歡的T卹但不想問他們是什麼牌子的?透過 Mega Market,只需拍照並將其上傳到我們的平台;我們將在我們的產品資料庫中搜尋它。

當你可以像向商店員工詢問一樣搜尋你想要的產品時,為什麼還要去商場呢?只需用自然語言寫下您的查詢,我們就會向您展示您正在尋找的產品的範例。例如,試著輸入「我需要一件女士冬季夾克」或「我想要一部帶相機的黑色智慧型手機」。

最好的部分是網站不需要 Wix 訂閱來完成結帳流程因為它完全使用 Wix 元素和 Velo API 建立。


如何在大型市場購買

建立帳戶(或不建立帳戶...)

在 Mega Mart,您可以選擇使用或不使用帳戶購買產品
建立帳戶有多種好處

  • 結帳時,您的個人資料和以前的地址會自動填寫,節省您的時間。
  • 此外,您還可以獲得即時現金回饋,可用於將來的購買。
  • 喜歡某個產品但還不想購買?您的帳戶包含願望清單功能,讓您可以儲存產品以供日後使用。

搜尋產品項目

如前所述,您可以透過從裝置上傳圖片使用人工智慧來搜尋產品,或瀏覽所有商品以選擇您最喜歡的商品。

驗證您的購物車

結帳之前,請確保您已將所有想要的產品添加到您的購物車。 根據需要調整物品的數量刪除任何錯誤。此外,請檢查您的總交貨日期和預計交貨日期。

查看

如果您登錄,您的個人資料將自動填寫。您可以選擇先前儲存的地址之一或輸入一個新地址,該新地址將新增至您的帳戶以供將來訂購。
輸入您的信用卡或金融卡訊息,然後點擊最後按鈕完成結帳。

您的訂單已完成!

您將被重定向到訂單頁面,您可以在其中查看購買的詳細資訊及其狀態:是否正在處理、正在運送或已運送。您還將收到訂單確認電子郵件。


超級市場的好處

無密碼

現在誰想記得密碼?在Mega Mart,您不必擔心它們;每次您想要登入帳戶時,您都會透過電子郵件、簡訊或電話收到一次性數位代碼

現金回饋

誰不喜歡購物獲得獎勵呢?每次在Mega Mart購物,您將獲得1%返現,您可以用它來支付以後的訂單。

願望清單

曾經找到過您喜歡的產品,但還沒準備好購買嗎?在 Mega Mart,您可以將其保存到您的願望清單以供將來購買。

訂單狀態

您無需切換頁面即可查看貨件狀態。從訂單頁面,您可以輕鬆查看訂單是否正在處理中、運送或已交付。

全面回應

有沒有想過網站在您的電腦、平板電腦或手機上是否也能同樣流暢?在 Mega Mart,我們的網站在所有裝置上都能完全回應。

儲存您的地址

想知道購物有多方便嗎?在 Mega Mart,您的地址會被保存以供將來購買,您可以從帳戶頁面輕鬆管理它們。

無需帳戶

雖然許多電子商務網站需要建立帳戶才能購物,但在 Mega Mart,您可以以訪客身分購買。但是,請記住作為 Mega Mart 的常客可以享受的所有好處。


發展歷程

利用 Wix Studio 幫助我加速了前端開發,顯著減少了時間,同時確保了跨所有裝置的完全響應式設計。 JavaScript 在增強使用者與 Wix Studio 元素的互動、無縫實現動態功能方面發揮著重要作用。此外,我們的網站採用了強大的安全措施,包括資料庫、網路方法和路由器的安全性,以保護使用者資料並確保安全的瀏覽體驗。

在深入了解我如何創建 Mega Mart 之前,需要先說明的是,我沒有安裝 Wix Market 中的任何應用程式。雖然我使用 Wix Stores 將產品儲存到購物車,然後在某些頁面中檢索它們,但我選擇使用 Wix Elements 完全從頭開始建立產品頁面、購物車和結帳頁面。

免責聲明
由於這是比賽的演示,我在資料庫中只保存了 29 個產品。為了充分利用人工智慧圖像產品識別和人工智慧搜尋方法,請先熟悉商店中的可用產品。例如,搜尋印表機或鐵鎚可能不會產生結果,因為這些項目不在演示資料庫中。然而,如果這個項目擴大到像家得寶或沃爾瑪這樣的資料庫中擁有數千種產品的大公司,人工智慧功能將執行全面的搜尋並提供更廣泛的結果。


嘗試一下!

想知道 Mega Mart 能為您帶來什麼購物體驗嗎?點擊探索並發現可能性。
走吧!


我是如何建構它的

開發這個電子商務網站並不容易,特別是考慮到時間緊迫,但我很自豪能夠在短短 2 週內建立其所有功能。

我開發的第一個頁面是主頁,我的目標是整合其他電子商務網站中常見的功能,例如展示暢銷商品、新品以及強調在商店購物的好處。
以下是我如何在程式碼中檢索暢銷產品和最新產品資料的範例。

import wixData from 'wix-data';
import wixWindowFrontend from 'wix-window-frontend';
import wixLocationFrontend from 'wix-location-frontend';

const limit = wixWindowFrontend.formFactor === "Mobile" ? 6 : 5

const bestSellers = (await wixData.query("Products").descending("rating").limit(limit).find()).items
$w('#repeater2').data = bestSellers

$w('#repeater2').onItemReady(($item, itemData, index)=>{
    $item('#imageX9').src = itemData.imageUrl[0].src
    $item('#imageX9').alt = itemData.name

    $item('#text12').text = itemData.name
    $item('#text13').text = "$" + itemData.price

    $item('#box15').onClick(() => {
        wixLocationFrontend.to(`/product/${itemData._id}`)
    })
})

const newProucts = (await wixData.query("Products").descending("_createdDate").limit(limit).find()).items
$w('#repeater3').data = newProucts

$w('#repeater3').onItemReady(($item, itemData, index)=>{
    $item('#imageX10').src = itemData.imageUrl[0].src
    $item('#imageX10').alt = itemData.name

    $item('#text15').text = itemData.name
    $item('#text14').text = "$" + itemData.price

    $item('#box19').onClick(() => {
        wixLocationFrontend.to(`/product/${itemData._id}`)
    })
})

如您所見,程式碼還包含偵測使用者是否在行動裝置上瀏覽的邏輯。在行動裝置檢視中,中繼器顯示為兩列,而在平板電腦和電腦模式下,它們排列成單行五列。

登入並註冊

我認為註冊和登入頁面應該有一個直覺的 UI/UX 介面。如果任一流程對使用者來說很困難或令人沮喪,他們可能會離開網站,從而導致銷售損失。
為了防止這種情況,Mega Mart 使用無密碼系統。每次您想要登入時,都會透過簡訊、電話或電子郵件發送一次性代碼,無需記住多個密碼。

登記

import { verify, sendVerification } from "backend/twilio.web"
import { register } from "backend/members.web"

$w('#button1').onClick(async () => {
    $w('#imageX9').show()
    $w('#button1').disable()
    if ($w('#box37').isVisible) {
        if ($w('#input5').valid) {

            $w('#text35').collapse()

            const verifyEmailResponse = await verify($w('#input3').value, $w('#input5').value)
            const verifyPhoneResponse = await verify($w('#input4').value, $w('#input5').value)

            if (verifyEmailResponse || verifyPhoneResponse) {
                try {
                    const sessionToken = await register($w('#input1').value, $w('#input2').value, $w('#input3').value, $w('#input4').value)
                    await authentication.applySessionToken(sessionToken)
                } catch (error) {
                    $w('#text35').text = "Error registering: " + new Error(error).message
                    $w('#text35').expand()
                }

            } else {
                $w('#text35').text = "Wrong verification code."
                $w('#text35').expand()
            }
        }
    } else {
        const sendEmailResponse = await sendVerification("email", $w('#input3').value)
        const sendSMSResponse = await sendVerification("sms", $w('#input4').value)
        if (sendEmailResponse && sendSMSResponse) {
            $w('#input1,#input2,#box36').collapse()
            $w('#box37').expand()
            $w('#button1').label = "Register"
            $w('#text37').hide()
        }
    }
    $w('#imageX9').hide()
    $w('#button1').enable()
})

此程式碼處理點選按鈕時的註冊程序。如果驗證碼輸入框可見且輸入的驗證碼有效,則會使用提供的驗證碼驗證使用者的電子郵件或電話號碼。如果任一驗證成功,它會嘗試註冊用戶,然後應用會話令牌進行身份驗證。如果驗證失敗,則會顯示錯誤訊息。
如果該框不可見,它會向使用者的電子郵件和電話發送驗證碼,然後更新 UI 以允許使用者完成註冊。

登入

發送驗證碼給使用者

import { sendVerification } from "backend/twilio.web"
import { verifyEmailOrPhoneNumber} from "backend/members.web"

$w('#button2,#button3').onClick(async (rest) => {
    if ($w('#input7').valid || $w('#input8').valid) {
        $w('#imageX10').show()
        $w('#text34').collapse()
        $w('#button2,#button3').disable()

        let toSearch = $w('#input7').isVisible ? $w('#input7').value : $w('#input8').value
        if (!(await verifyEmailOrPhoneNumber(toSearch))) {
            $w('#text34').text = "There isn't an account with that email or phone number."
            $w('#text34').expand()
        } else {
            switch (rest.target) {
            case $w('#button2'):
                let response = false
                if ($w('#input7').isVisible) {
                    response = await sendVerification("email", $w('#input7').value)
                    verificationTypeAtLogin = "email"
                    $w('#text31').text = "Please check you email. It may be at junk mails."
                } else {
                    response = await sendVerification("sms", $w('#input8').value)
                    verificationTypeAtLogin = "sms"
                    $w('#text31').text = "Please check you SMS."
                }

                if (response) {
                    $w('#text33,#box41,#box47').collapse()
                    $w('#box46,#box48').expand()
                    $w('#text36').hide()
                } else {
                    $w('#text34').text = "We couldn't send the verification code. Try using another method."
                    $w('#text34').expand()
                }
                break;
            case $w('#button3'):
                verificationTypeAtLogin = "call"
                $w('#text31').text = "You will receive a phone call in a few moments."

                if (await sendVerification("call", $w('#input8').value)) {
                    $w('#text33,#box41,#box47').collapse()
                    $w('#box46,#box48').expand()
                    $w('#text36').hide()
                } else {
                    $w('#text34').text = "We couldn't send the verification code. Try using another method."
                    $w('#text34').expand()
                }
                break;
            }
        }
        $w('#imageX10').hide()
        $w('#button2,#button3').enable()
    }
})

它首先驗證輸入的電子郵件或電話號碼是否存在。
根據單擊的按鈕,它會透過電子郵件、簡訊或電話發送驗證碼。然後擴展驗證碼輸入,供用戶寫入接收到的代碼。
如果驗證失敗,則會顯示相應的錯誤訊息。最後,載入圖像被隱藏,按鈕被重新啟用。

正在驗證驗證碼

import { verify } from "backend/twilio.web"
import { login } from "backend/members.web"

if ($w('#input9').valid) {
    $w('#imageX11').show()
    $w('#text34').collapse()
    $w('#button4').disable()

    let verifyResponse = false
    if (verificationTypeAtLogin === "email") verifyResponse = await verify($w('#input7').value, $w('#input9').value)
    else verifyResponse = await verify($w('#input8').value, $w('#input9').value)

    if (verifyResponse) {
        try {
            const sessionToken = await login(verificationTypeAtLogin === "email" ? $w('#input7').value : "", verificationTypeAtLogin !== "email" ? $w('#input8').value : "")
            await authentication.applySessionToken(sessionToken)
        } catch (error) {
            $w('#text34').text = "Error registering: " + new Error(error).message
            $w('#text34').expand()
            $w('#imageX11').hide()
            $w('#button4').enable()
        }

    } else {
        $w('#text34').text = "Wrong verification code."
        $w('#text34').expand()
        $w('#imageX11').hide()
        $w('#button4').enable()
    }

}

它根據驗證類型(電子郵件或電話)驗證提供的代碼。如果驗證成功,它會嘗試讓使用者登入並應用會話令牌進行身份驗證。

重定向用戶

無論使用者是登入或註冊,成功後都會被重新導向到上次造訪的頁面。

import { authentication } from "wix-members"
import wixLocationFrontend from 'wix-location-frontend'

authentication.onLogin(() => {
    wixLocationFrontend.to(wixLocationFrontend.query.redirect)
})

搜尋欄視窗

為了讓使用者可以從任何頁面搜尋產品,我在燈箱內新增了搜尋欄。

Search type

First check what search type the user selected.

import wixWindowFrontend from 'wix-window-frontend';

$w('#imageX7').hide()
$w('#button2,#input1,#searchByImage,#searchByAI').enable()
const type = wixWindowFrontend.lightbox.getContext()

let instructions = ""
switch (type) {
case "text":
    $w('#searchByTextMobile').show("fade", { duration: 500 })
    instructions = "Enter keywords or product names into the search bar."
    break;
case "image":
    $w('#searchByImage').show("fade", { duration: 500 })
    instructions = "Upload an image of the product you are looking for."
    break;
case "AI":
    $w('#searchByAI').show("fade", { duration: 500 })
    instructions = "Type your query in natural language (e.g., 'I need a winter jacket for women')."
    break;
}

$w('#text21').text = `Search by ${type}`
$w('#text22').text = instructions

Handle search type

By image

import { getImageLabels } from "backend/googleVision.web"

$w('#searchByImage').onChange(async () => {
    $w('#searchByImage').disable()
    $w('#imageX7').show()
    const file = await $w('#searchByImage').uploadFiles()
    const labels = await getImageLabels(file[0].fileUrl)

    sendTo("tags", labels)
})

First, the image is uploaded to the Media Manager to obtain its URL. Then, the URL is sent to Google Vision to identify tags related to the image.

By AI

$w('#searchByAI').onKeyPress(async (rest) => {
    if ($w('#searchByAI').valid && rest.key === "Enter") {
        $w('#searchByAI').disable()
        $w('#imageX7').show()

        const labels = await getTextLabels($w('#searchByAI').value)
        sendTo("tags", labels)
    }
})

If the user presses Enter, the input text is tokenized to extract nouns.

Search redirection

Once one of the methods is completed, the user is redirected to the search router page with the queries included in the URL.

import wixLocationFrontend from 'wix-location-frontend';

function sendTo(type = "", value = []) {
    wixLocationFrontend.to(`/search?type=${type}&value=${JSON.stringify(value)}`)
}

Search page

This is a Wix Router page that helped me show the different types of search, and also the wishlist of the products the user saved.

Repeater display

import wixWindowFrontend from 'wix-window-frontend';
import { cart } from "wix-stores-frontend";

let originalItem = wixWindowFrontend.getRouterData()
let items = wixWindowFrontend.getRouterData()
initRepeater(items)

$w('#repeater1').onItemReady(async ($item, itemData, index) => {
    $item('#imageX9').src = itemData.imageUrl[0].src
    $item('#text12').text = itemData.name
    $item('#text13').text = "$" + itemData.price
    $item('#ratingsDisplay1').rating = itemData.rating

    $item('#button1').onClick(async () => {
        const product = [{
            productId: itemData._id,
            quantity: 1,
        }]

        await cart.addProducts(product)
        await showAlert("add")
    })

    $item('#box38').onClick(() => {
        wixLocationFrontend.to(`/product/${itemData._id}`)
    })

    if (authentication.loggedIn()) {
        const userID = await currentMember.getMember({ fieldsets: ["FULL"] })
        const item = await wixData.query("Wishlist").eq("product", itemData._id).eq("_owner", userID._id).find()
        if (item.totalCount === 0) $item('#button2').expand()
        else {
            itemData.wishList = item.items[0]._id
            $item('#button3').expand()
        }
    } else $item('#button2').expand()

    $item('#button2').onClick(async () => {
        if (authentication.loggedIn()) {
            const itemWishlist = await wixData.insert("Wishlist", { product: itemData._id })
            itemData.wishList = itemWishlist._id
            await showAlert("save")
            $item('#button2').collapse().then(() => {
                $item('#button3').expand()
            })
        } else wixWindowFrontend.openLightbox("loginToWishlist")
    })

    $item('#button3').onClick(async () => {
        await wixData.remove("Wishlist", itemData.wishList)
        await showAlert("remove")
        $item('#button3').collapse().then(() => {
            $item('#button2').expand()
        })
    })
})

function initRepeater(items = [], changeFilter = true) {
    $w('#repeater1').data = items

    if ($w('#repeater1').data.length === 0) {
        if (wixLocationFrontend.query.type === "wishlist") {
            $w('#text27').text = "You haven't add any item to your wishlist. Add your first product!"
        } else {
            $w('#text27').text = "Try searching with another method or include more details if you are using AI."
        }
        $w('#section5').collapse()
        $w('#section6').expand()
    } else {
        const htmlLinks = items[0].tags.slice(0, 5).map(word => `<span style="font-size: 10px;"><a href="/search?type=tags&value=[%22${word}%22]">${word}</a></span>`).join(' - ');
        $w('#text22').html = htmlLinks
        $w('#section6').collapse()
        $w('#section5').expand()

        $w('#switch1').checked = false

        if (changeFilter) {
            const maxPrice = sortByPrice(items, "desc")[0]

            $w('#slider1').max = maxPrice.price
            $w('#slider2').max = maxPrice.price

            $w('#slider2').value = maxPrice.price
        }

    }

}

As you can see, this includes the import { cart } from "wix-stores-frontend" statement, which allows the product to be added to the cart when the user clicks the button inside the repeater.

Sort products

$w('#switch1').onChange(() => {
    $w('#repeater1').data = []

    items = sortByPrice(items, $w('#switch1').checked ? "desc" : "asc")
    $w('#repeater1').data = items
})

function sortByPrice(products = [{}], order = 'asc') {
    return products.sort((a, b) => {
        if (order === 'asc') {
            return a.price - b.price;
        } else if (order === 'desc') {
            return b.price - a.price;
        } else {
            throw new Error("Order must be either 'asc' or 'desc'");
        }
    });
}

Filter products

$w('#slider1,#slider2').onChange(() => {
    items = filterByPriceRange(originalItem, $w('#slider1').value, $w('#slider2').value)
    initRepeater(items, false)
})

function filterByPriceRange(products, minPrice, maxPrice) {
    return products.filter(product => product.price >= minPrice && product.price <= maxPrice);
}

Wixlocation.onChange()

Since searching for new products redirects the user to the same page /search and only changes the URL queries, I needed to handle this with wixLocationFrontend.onChange() to update the repeater items.

Product page

This is also a router page /product/${productID} that fetches product information from the backend and displays it using wixWindowFrontend.getRouterData().

import wixWindowFrontend from 'wix-window-frontend';

const product = wixWindowFrontend.getRouterData()

$w('#gallery1').items = product.imageUrl
$w('#features').text = product.features
$w('#title').text = product.name
$w('#brand').text = product.brand
$w('#color').text = product.color
$w('#price').text = String(product.price)
$w('#description').text = product.description
$w('#ratings').rating = product.rating
$w('#stock').text = `${product.stock} available`

Since you can add the product to your cart from here, with the option to set a quantity, I used the following code to achieve this.

import { cart } from "wix-stores-frontend"

$w('#addCart').onClick(async () => {
    const productToSave = [{
        productId: product._id,
        quantity: Number($w('#inputQuantity').value),
    }]

    await cart.addProducts(productToSave)
    await showAlert("add")

})

$w('#quantityLess,#quantityMore').onClick((rest) => {
    let value = Number($w('#inputQuantity').value)

    switch (rest.target) {
    case $w('#quantityLess'):
        if (value > 1) value--
        break;
    case $w('#quantityMore'):
        value++
        break;
    }

    $w('#inputQuantity').value = String(value)
})

Also having the option to add the product to the wishlist or delete it.

$w('#addWishList').onClick(async () => {
    if (authentication.loggedIn()) {
        await wixData.insert("Wishlist", { product: product._id })
        await showAlert("save")
        $w('#addWishList').collapse().then(() => {
            $w('#deleteWishlist').expand()
        })
    } else wixWindowFrontend.openLightbox("loginToWishlist")
})

$w('#deleteWishlist').onClick(async () => {
    await wixData.remove("Wishlist", product.wishList)
    await showAlert("remove")

    $w('#deleteWishlist').collapse().then(() => {
        $w('#addWishList').expand()
    })
})

Cart page

Need to review your cart? This page allows you to check if you have added the desired quantities of the products you want to buy and also lets you delete any items if needed.

import { cart } from "wix-stores-frontend"

$w('#repeater1').onItemReady(($item, itemData, index) => {
$item('#quantityLess,#quantityMore').onClick(async (rest) => {
        $item('#quantityLess,#quantityMore,#button3').disable()
        let value = Number($item('#inputQuantity').value)

        switch (rest.target) {
        case $item('#quantityLess'):
            if (value > 1) value--
            break;
        case $item('#quantityMore'):
            value++
            break;
        }

        $item('#inputQuantity').value = String(value)
        $item('#text22').text = `$${(itemData.price * value).toFixed(2)}`

        const updadedCart = await cart.updateLineItemQuantity(Number(itemData._id), value)
        $item('#quantityLess,#quantityMore,#button3').enable()

        $w('#text25').text = `Total: $${updadedCart.totals.total.toFixed(2)}`
    })

    $item('#button3').onClick(async () => {
        $item('#quantityLess,#quantityMore,#button3').disable()
        await cart.removeProduct(Number(itemData._id))
        await initCart()
    })
})

Clicking the buttons #quantityLess and #quantityMore triggers an event to edit the product quantity. Clicking the #button3 removes the product from the cart.

Checkout

This is the final step for purchasing at Mega Market.
If the user is a Site Member, it retrieves their saved addresses and accumulated cashback from their account.

import { authentication, currentMember } from "wix-members-frontend"

if (authentication.loggedIn()) {
    $w('#text25').collapse()

    const user = await currentMember.getMember({ fieldsets: ["FULL"] })

    $w('#input1').value = user.contactDetails.firstName
    $w('#input2').value = user.contactDetails.lastName
    $w('#input3').value = user.loginEmail
    $w('#input4').value = user.contactDetails.phones[0]

    const addresses = await getUserAdderess(user._id)

    if (addresses.length === 0) {
        $w('#dropdown1').options = [{ value: "No addresses founded", label: "No addresses founded" }]
        $w('#dropdown1').value = "No addresses founded"
        $w('#dropdown1').disable()
    } else {
        const addressesOptions = addresses.map(address => ({
            label: `${address.addressLine} ${address.state}, ${address.country}`,
            value: JSON.stringify(address)
        }));

        $w('#dropdown1').options = addressesOptions
    }

    const totalCashback = await getTotalCashback(user._id)

    if (totalCashback === 0) {
        $w('#switch1').disable()
        $w('#switch1').checked = false
        $w('#text28').text = `You will earn $${Number(currentCart.totals.total * 0.01).toFixed(2)} in cashback with this order.`
    } else {
        cashback = totalCashback > currentCart.totals.total ? currentCart.totals.total : totalCashback;
        $w('#switch1').enable()
        $w('#text28').text = `Use my cashback: $${Number(cashback).toFixed(2)}`
    }

}

It also validates that the card number of the user is a Master Card, Visa or AMEX.

$w('#input10').onCustomValidation((value, reject) => {
        const cardsInfo = {
            "Unknown": {
                "image": "https://static.wixstatic.com/media/4c0a11_600a3b93f607463296a7c9149268800e~mv2.gif",
                "regex": /^\d+$/,
                "length": 16
            },
            "Master Credit": {
                "image": "https://www.mercadopago.com/org-img/MP3/API/logos/master.gif",
                "regex": ,
                "length": 16
            },
            "Visa Credit": {
                "image": "https://www.mercadopago.com/org-img/MP3/API/logos/visa.gif",
                "regex": ,
                "length": 16
            },
            "AMEX": {
                "image": "https://www.mercadopago.com/org-img/MP3/API/logos/amex.gif",
                "regex": ,
                "length": 15
            },
            "Visa Debit": {
                "image": "https://www.mercadopago.com/org-img/MP3/API/logos/debvisa.gif",
                "regex": ,
                "length": 16
            },
            "Master Debit": {
                "image": "https://www.mercadopago.com/org-img/MP3/API/logos/debmaster.gif",
                "regex": ,
                "length": 16
            }
        }

        const cardCategory = identifyCardCategory(value)
        $w('#imageX8').src = cardsInfo[cardCategory].image
        $w('#input10').maxLength = cardsInfo[cardCategory].length
        if (!cardsInfo[cardCategory].regex.test(value)) {
            reject("Invalid card number")
        }
    })

function identifyCardCategory(cardNumber) {
    const patterns = {
        masterCredit: ,
        visaCredit: ,
        amex: ,
        visaDebit: ,
        masterDebit: 
    };

    if (patterns.masterCredit.test(cardNumber)) return "Master Credit";
    if (patterns.visaCredit.test(cardNumber)) return "Visa Credit";
    if (patterns.amex.test(cardNumber)) return "AMEX";
    if (patterns.visaDebit.test(cardNumber)) return "Visa Debit";
    if (patterns.masterDebit.test(cardNumber)) return "Master Debit";

    return "Unknown";
}

After the user inputs all the required information, it's time to process the order.

$w('#button2').onClick(async () => {
    if ($w('#input1').valid && $w('#input2').valid && $w('#input3').valid && $w('#input4').valid && $w('#input5').valid && $w('#input6').valid && $w('#input7').valid && $w('#input8').valid && $w('#input9').valid && $w('#input10').valid && $w('#dropdown2').valid && $w('#dropdown3').valid && $w('#dropdown4').valid) {
        $w('TextInput,Dropdown').disable()
        $w('#box53').show()

        const productArray = currentCart.lineItems.map(item => ({
            quantity: item.quantity,
            name: item.name,
            priceData: {
                price: item.price,
            },
            mediaItem: {
                src: item.mediaItem.src
            }
        }));

        const insertedItem = await wixData.insert("Orders", {
            lineItems: productArray,
            totals: {
                subtotal: subTotal,
                total: total,
            },
            shippingInfo: {
                shipmentDetails: {
                    address: {
                        city: $w('#dropdown4').value,
                        country: $w('#dropdown2').value,
                        addressLine: $w('#input5').value,
                        postalCode: $w('#input6').value,
                        subdivision: $w('#dropdown3').value,
                    }
                }
            },
            billingInfo: {
                address: {
                    city: $w('#dropdown4').value,
                    country: $w('#dropdown2').value,
                    addressLine: $w('#input5').value,
                    postalCode: $w('#input6').value,
                    subdivision: $w('#dropdown3').value,
                },
                lastName: $w('#input2').value,
                firstName: $w('#input1').value,
                email: $w('#input3').value,
                phone: $w('#input4').value
            },
            cashback: $w('#switch1').checked
        })

        for (let lineItem of currentCart.lineItems) {
            await cart.removeProduct(lineItem.id)
        }

        wixLocationFrontend.to(`/order/${insertedItem._id}`)
    }
})

This wixData.insert() triggers a data hook that creates the order using wixStoresBackend.createOrder() which I'll talk of it later.

Order page

This page displays the order information passed though the wixWindowFrontend.getRouterData().

import wixWindowFrontend from 'wix-window-frontend';

const orderInfo = wixWindowFrontend.getRouterData()
const contact = orderInfo.billingInfo
const shipping = contact.address

$w('#text21').text = `Order #${orderInfo.orderNumber}`
$w('#text48').text = `Amount paid: $${orderInfo.totals.total}`
$w('#text47').text = `Subtotal: $${Number(orderInfo.totals.subtotal).toFixed(2)} | Discounts: $${Number(orderInfo.totals.subtotal - orderInfo.totals.total).toFixed(2)}`

$w('#text26').text = contact.firstName
$w('#text27').text = contact.lastName
$w('#text28').text = contact.phone
$w('#text29').text = contact.email

$w('#text30').text = shipping.addressLine
$w('#text31').text = shipping.city
$w('#text32').text = shipping.postalCode
$w('#text33').text = `${shipping.subdivision}, ${shipping.country}`

APIs

Velo APIs

API Method Location in Project
Wix Data 1. query() 2. insert() 3. remove() 4. get() 1. Home page 2. Search page 3. Checkout
Wix Window Frontend 1. formFactor 2. lightbox.getContext() 3. getRouterData() 4. openLightbox() 1. Home page 2. Login 3. Search bar 4. Search page 5. Product page
Wix Location Frontend 1. to() 2. onChange() 3. query 1. Home page 2. Login 3. Search bar 4. Search page 5. Cart page 6. Checkout 7. Order page
{authentication} from Wix Members 1. applySessionToken() 2. onLogin() 1. Login 2. Checkout
{ cart } from Wix Stores 1. addProducts() 1. Search page 2. Product page 3. Cart page 4. Checkout
{currentMember} from Wix Members 1. getMember() 1. Product page 2. Checkout
Wix Secrets getSecret() Backend

External services

country-state-city

Helped to get the countries, states of countries and cities of states.

import { Permissions, webMethod } from "wix-web-module";

export const getAllCountries = webMethod(
    Permissions.Anyone,
    async () => {
        const countries = await require('country-state-city').Country.getAllCountries();

        const toReturn = countries.map(state => ({
            label: state.name,
            value: state.isoCode
        }));

        return toReturn;
    }
);

export const getStatesOfCountry = webMethod(
    Permissions.Anyone,
    async (country) => {
        const states = await require('country-state-city').State.getStatesOfCountry(country);

        const toReturn = states.map(state => ({
            label: state.name,
            value: state.isoCode
        }));

        return toReturn;
    }
);

export const getCitiesOfStates = webMethod(
    Permissions.Anyone,
    async (country, state) => {
        const states = await require('country-state-city').City.getCitiesOfState(country, state);

        const toReturn = states.map(state => ({
            label: state.name,
            value: state.name
        }));

        return toReturn;
    }
);

google-cloud/vision

Reads the image and provides labels of what it identifies.

const vision = require('@google-cloud/vision');

export const getImageLabels = webMethod(
    Permissions.Anyone,
    async (imageUrl) => {
        const credentials = JSON.parse(await wixSecretsBackend.getSecret("googleVisionCredentials"))

        const client = new vision.ImageAnnotatorClient({ credentials });

        try {
            const [result] = await client.labelDetection(getPublicURL(imageUrl));
            const labels = result.labelAnnotations.map(label => label.description);

            const lowerCaseLabels = labels.map(element => element.toLowerCase());

            return lowerCaseLabels;
        } catch (error) {
            console.error('Error detecting labels:', error);
            return []
        }
    }
);

Compromise

Extracts the nouns from a text.

const nlp = require('compromise');

export const getTextLabels = webMethod(
    Permissions.Anyone,
    async (text) => {
        let doc = nlp(text);

        let categories = doc.match('#Noun').out('array');
        const lowerCaseCategories = categories.map(element => element.toLowerCase());

        return lowerCaseCategories
    }
);

Twilio

Used for sending the verification code via SMS, call or email and to verify the one-time digit code.

export const sendVerification = webMethod(
    Permissions.Anyone,
    async (type =  "email" || "sms" || "call", to = "diego_deap@outlook.com") => {
        try {
            const { accountSid, authToken, service } = await auth()

            const client = twilio(accountSid, authToken);

            await client.verify.v2
                .services(service)
                .verifications.create({
                    channel: type,
                    to: to,
                });

            return true
        } catch (error) {
            return false
        }
    }
);

export const verify = webMethod(
    Permissions.Anyone,
    async (to = "", code = "") => {
        const { accountSid, authToken, service } = await auth()

        const client = twilio(accountSid, authToken);

        const verificationCheck = await client.verify.v2
            .services(service)
            .verificationChecks.create({
                code: code,
                to: to,
            });
        const status = verificationCheck.status === "approved" ? true : false;

        return status
    }
);

以上是超市場的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn