卖家端多语言

This commit is contained in:
李志鹏
2026-05-04 11:18:56 +08:00
parent 596bc75b83
commit b3d9bce440
10 changed files with 262 additions and 93 deletions

View File

@@ -2,10 +2,10 @@
<div class="brand-info">
<a-form :model="formData" :rules="isEdit ? formRules : {}" layout="vertical" ref="formRef">
<div class="form-group">
<a-form-item label="Store Name" name="shopName">
<a-form-item :label="$t('Seller.storeName')" name="shopName">
<a-input
v-model:value="formData.shopName"
placeholder="Enter the store name"
:placeholder="$t('Seller.storeNameDesc')"
:maxlength="80"
:readonly="!isEdit"
/>
@@ -13,34 +13,34 @@
>{{ formData.shopName.length }}/80</span
>
</a-form-item>
<a-form-item label="Owners Full Name" name="ownerName">
<a-form-item :label="$t('Seller.ownerName')" name="ownerName">
<a-input
v-model:value="formData.ownerName"
placeholder="Enter store owner's full name"
:placeholder="$t('Seller.ownerNameDesc')"
:readonly="!isEdit"
/>
</a-form-item>
</div>
<div class="form-group">
<a-form-item label="Email" name="email">
<a-form-item :label="$t('Seller.email')" name="email">
<a-input
type="email"
v-model:value="formData.email"
placeholder="Enter email"
:placeholder="$t('Seller.emailDesc')"
:readonly="!isEdit"
/>
</a-form-item>
<a-form-item label="Phone Number" name="mobile">
<a-form-item :label="$t('Seller.mobile')" name="mobile">
<a-input
type="tel"
v-model:value="formData.mobile"
placeholder="Enter phone number"
:placeholder="$t('Seller.mobileDesc')"
:readonly="!isEdit"
/>
</a-form-item>
</div>
<div class="form-group">
<a-form-item label="Portfoilo/Social Media Links">
<a-form-item :label="$t('Seller.links')">
<a-input
placeholder="https://"
v-for="(v, i) in formData.socialLinks"
@@ -48,7 +48,7 @@
v-model:value="formData.socialLinks[i]"
:readonly="!isEdit"
>
<template #prefix>Link {{ i + 1 }}</template>
<template #prefix>{{ $t("Seller.link", { index: i + 1 }) }}</template>
</a-input>
<a-input
placeholder="https://"
@@ -63,10 +63,10 @@
</template>
</a-input>
</a-form-item>
<a-form-item label="Store Description" name="description">
<a-form-item :label="$t('Seller.storeDescription')" name="description">
<a-textarea
v-model:value="formData.description"
placeholder="Briefly describe your design style and store features..."
:placeholder="$t('Seller.storeDescriptionDesc')"
:maxlength="500"
:readonly="!isEdit"
/>
@@ -83,6 +83,9 @@
import { ref, reactive, watch } from "vue"
import { useRoute, useRouter } from "vue-router"
import { useStore } from "vuex"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const store = useStore()
const designerInfo = computed(() => store.state.seller.designerInfo)
const route = useRoute()
@@ -94,11 +97,11 @@
}
})
const formRules = {
shopName: [{ required: true, message: "Enter the store name" }],
ownerName: [{ required: true, message: "Enter store owner's full name" }],
email: [{ required: true, message: "Enter email" }],
mobile: [{ required: true, message: "Enter phone number" }],
description: [{ required: true, message: "Enter store description" }]
shopName: [{ required: true, message: t("Seller.storeNameDesc") }],
ownerName: [{ required: true, message: t("Seller.ownerNameDesc") }],
email: [{ required: true, message: t("Seller.emailDesc") }],
mobile: [{ required: true, message: t("Seller.mobileDesc") }],
description: [{ required: true, message: t("Seller.storeDescriptionErr") }]
}
const formRef = ref(null)

View File

@@ -16,28 +16,28 @@
<div class="title">{{ data.title }}</div>
<div class="right flex">
<div v-if="coverOrigin.length > 1" class="origin-container flex align-center">
<span>Crop from: </span>
<span>{{ $t("Seller.cropFrom") }}</span>
<div class="origin-select flex align-center">
<div
class="origin-item sketch"
:class="{ selected: currentOrigin === 'sketch' }"
@click="handleChangeOrigin('sketch')"
>
Sketch
{{ $t("Seller.sketch") }}
</div>
<div
class="origin-item product"
:class="{ selected: currentOrigin === 'mainProducImage' }"
@click="handleChangeOrigin('mainProductImage')"
>
Main product image
{{ $t("Seller.mainProductImage") }}
</div>
</div>
</div>
<div class="submit" v-if="!data.isPreview" @click="onSubmit">
<svg-icon name="seller-dui" size="24" />
</div>
<button @click="onCancel">Cancel</button>
<button @click="onCancel">{{ $t("Seller.cancel") }}</button>
</div>
</div>
<div class="content" :class="{ 'is-product': data.isProduct }">
@@ -64,7 +64,7 @@
>
<div class="title">
<span class="icon"><svg-icon name="seller-preview" size="24" /></span>
<span class="label">Crop Preview</span>
<span class="label">{{ $t("Seller.cropPreview") }}</span>
</div>
<div class="preview-image">
<img :src="data.preview_url" />
@@ -81,6 +81,10 @@
<script setup>
import { ref, reactive, computed } from "vue"
import ImageClip from "./image-clip.vue"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const props = defineProps({
type: {
@@ -95,16 +99,16 @@ const props = defineProps({
const tips = computed(() => {
if (props.type === "cover") {
return "Align crown to top, mid-thigh to bottom for best results."
return t("Seller.imageClipCoverTip")
}
if (props.type === "mainProductImage") {
return "Align crown to top, foot base to bottom for best results."
return t("Seller.imageClipMainProductImageTip")
}
if (props.type === "sketch") {
return "Align crown to top, foot base to bottom for best results."
return t("Seller.imageClipSketchTip")
}
if (props.type === "apparel") {
return "Trim whitespace and center your apparel sketch."
return t("Seller.imageClipApparelTip")
}
})

View File

@@ -5,9 +5,9 @@
<img v-if="banner" :src="banner" />
<div v-else class="null">
<span class="icon"><svg-icon name="seller-picture" size="60" /></span>
<span class="tip">Your brand banner has not been set up yet.</span>
<span class="tip">{{ $t("Seller.brandProfileBgNullTip") }}</span>
</div>
<button @click="onChangeBanner">Change Brand Banner</button>
<button @click="onChangeBanner">{{ $t("Seller.changeBrandBanner") }}</button>
</div>
<!-- 头像 -->
<div class="avatar">
@@ -25,14 +25,14 @@
<div class="and-profile-footer">
<template v-if="isEdit">
<div class="btns">
<button class="cancel" @click="onCancel()">Cancel</button>
<button class="submit" @click="onSubmit()">Save Change</button>
<button class="cancel" @click="onCancel()">{{ $t("Seller.cancel") }}</button>
<button class="submit" @click="onSubmit()">{{ $t("Seller.saveChange") }}</button>
</div>
<p class="tip">Changes will be reflected on your Stylish Parade brand page.</p>
<p class="tip">{{ $t("Seller.brandProfileEditTip") }}</p>
</template>
<template v-else>
<div class="btns">
<button class="edit" @click="onEdit">Edit</button>
<button class="edit" @click="onEdit">{{ $t("Seller.edit") }}</button>
</div>
<p class="tip">&nbsp;</p>
</template>
@@ -46,6 +46,8 @@
import BrandInfo from "./brand-info.vue"
import ImageClipDialog from "./image-clip-dialog.vue"
import { useStore } from "vuex"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const store = useStore()
store.dispatch("seller/get_designerInfo")
const designerInfo = computed(() => store.state.seller.designerInfo)
@@ -90,7 +92,7 @@
onSubmit({ brandBanner: res })
store.commit("set_loading", false)
},
{ ratio: [40, 7], isPreview: false, title: "Crop Brand Banner" }
{ ratio: [40, 7], isPreview: false, title: t("Seller.cropBrandBanner") }
)
})
}
@@ -104,7 +106,7 @@
onSubmit({ avatar: res })
store.commit("set_loading", false)
},
{ ratio: [1, 1], isPreview: true, title: "Crop Avatar" }
{ ratio: [1, 1], isPreview: true, title: t("Seller.cropAvatar") }
)
})
}

View File

@@ -11,8 +11,8 @@
</div>
<div class="filter-box">
<div class="left">
<div class="title">All Invoice</div>
<div class="tip">A summary of all completed transactions.</div>
<div class="title">{{ t("Seller.allInvoice") }}</div>
<div class="tip">{{ t("Seller.myOrdersTip") }}</div>
</div>
<div class="right">
<div class="input">
@@ -22,7 +22,7 @@
<input
type="text"
v-model="nameOrId"
placeholder="Search by item name or order ID"
:placeholder="t('Seller.myOrdersSearchPlaceholder')"
@keydown.enter.prevent="getList(true)"
/>
</div>
@@ -30,11 +30,11 @@
</div>
<div class="table">
<div class="header">
<div class="order-id">Order ID</div>
<div class="item">Item</div>
<div class="price">Price</div>
<div class="buyer-username">Buyer Username</div>
<div class="date">Date</div>
<div class="order-id">{{ t("Seller.orderId") }}</div>
<div class="item">{{ t("Seller.item") }}</div>
<div class="price">{{ t("Seller.price") }}</div>
<div class="buyer-username">{{ t("Seller.buyerUsername") }}</div>
<div class="date">{{ t("Seller.date") }}</div>
</div>
<div class="body">
<div class="item" v-for="v in list" :key="v.orderId">
@@ -79,6 +79,8 @@
<script setup>
import { ref, onMounted, onBeforeUnmount, computed } from "vue"
import { Https } from "@/tool/https"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const totals_obj = ref({
totalRevenue: "--",
totalPurchases: "--",
@@ -87,17 +89,17 @@
const totals = computed(() => [
{
icon: "seller-qiandaizi",
title: "Total Revenue",
title: t("Seller.totalRevenue"),
value: `HK$ ${totals_obj.value.totalRevenue}`
},
{
icon: "seller-gouwudai",
title: "Total Purchases",
title: t("Seller.totalPurchases"),
value: totals_obj.value.totalPurchases
},
{
icon: "seller-eye",
title: "Total Views",
title: t("Seller.totalViews"),
value: totals_obj.value.totalViews
}
])

View File

@@ -2,11 +2,11 @@
<div class="settings-index">
<div>
<div class="notification">
<div class="header">Notifications</div>
<div class="header">{{ $t("Seller.notifications") }}</div>
<div class="content">
<div class="left">
<div class="title">New order notification</div>
<div class="tip">Receive an inbox message when a new order is placed.</div>
<div class="title">{{ $t("Seller.notificationsTitle") }}</div>
<div class="tip">{{ $t("Seller.notificationsTip") }}</div>
</div>
<div class="right">
<a-switch v-model:checked="checked" />
@@ -14,29 +14,29 @@
</div>
</div>
<div class="payout">
<div class="header">Payout</div>
<div class="header">{{ $t("Seller.payout") }}</div>
<div class="content">
<div class="header">
<div class="title">Payment Providers</div>
<div class="tip">Select how you want to receive payments.</div>
<div class="title">{{ $t("Seller.payoutTitle") }}</div>
<div class="tip">{{ $t("Seller.payoutTip") }}</div>
</div>
<div class="pay-item" v-for="v in payList" :key="v.type">
<div class="left">
<img :src="v.icon" />
<div class="value">{{ v.value || "Unbound" }}</div>
<div class="value">{{ v.value || $t("Seller.unbound") }}</div>
</div>
<div class="right">
<button v-if="v.value" class="manage">Manage</button>
<button v-else class="bind-now">Bind Now</button>
<button v-if="v.value" class="manage">{{ $t("Seller.manage") }}</button>
<button v-else class="bind-now">{{ $t("Seller.bindNow") }}</button>
</div>
</div>
<div class="footer">
<div class="left">
<div class="title">Payment Currency</div>
<div class="tip">HKD - Hong Kong Dollar</div>
<div class="title">{{ $t("Seller.paymentCurrency") }}</div>
<div class="tip">{{ $t("Seller.HKD") }}</div>
</div>
<div class="right">
<button>Fixed</button>
<button>{{ $t("Seller.fixed") }}</button>
</div>
</div>
</div>
@@ -44,40 +44,32 @@
</div>
<div>
<div class="data-privacy">
<div class="header">Data & Privacy</div>
<div class="header">{{ $t("Seller.dataPrivacy") }}</div>
<div class="content">
<div class="title">Copyright licence</div>
<div class="tip">
A licence certificate is automatically included with every purchase
download. View the default licensing terms applied to your listings.
</div>
<div class="tip">
This licence is issued by Code-Create and is legally binding upon purchase.
It certifies the buyer's right to use the purchased design asset in
accordance with the terms below.
</div>
<div class="tip">
For custom licensing arrangements, <span>contact us</span>.
</div>
<div class="title">{{ $t("Seller.dataPrivacyTitle") }}</div>
<div class="tip">{{ $t("Seller.dataPrivacyTip1") }}</div>
<div class="tip">{{ $t("Seller.dataPrivacyTip2") }}</div>
<div
class="tip"
v-html="
$t('Seller.dataPrivacyTip3', { click: 'onSellerSettingsContactUs' })
"
></div>
<div class="btns">
<button>
<button @click="onDownloadDataPrivacy">
<span class="icon"><svg-icon name="seller-download" size="14" /></span>
<span class="label">Download to View</span>
<span class="label">{{ $t("Seller.downloadToView") }}</span>
</button>
</div>
</div>
</div>
<div class="stop">
<div class="header">Stop Selling</div>
<div class="header">{{ $t("Seller.stopSelling") }}</div>
<div class="content">
<div class="title">Deactivate seller account</div>
<div class="tip">
Permanently deactivate your seller account. All listings and invoice records
will be deleted. You may re-register as a seller in the future, but your
previous sales data cannot be recovered.
</div>
<div class="title">{{ $t("Seller.stopSellingTitle") }}</div>
<div class="tip">{{ $t("Seller.stopSellingTip") }}</div>
<div class="btns">
<button>Deactivate</button>
<button @click="onStopSelling">{{ $t("Seller.deactivate") }}</button>
</div>
</div>
</div>
@@ -87,10 +79,14 @@
<script setup>
import { ref } from "vue"
import { Modal } from "ant-design-vue"
import paypal from "@/assets/images/seller/setting/paypal.png"
import stripe from "@/assets/images/seller/setting/stripe.png"
import alipayHk from "@/assets/images/seller/setting/alipay-hk.png"
import alipayChinese from "@/assets/images/seller/setting/alipay-chinese.png"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const checked = ref(true)
const payList = ref([
{
@@ -114,6 +110,24 @@
value: "123123"
}
])
window.onSellerSettingsContactUs = () => {
console.log("contact us")
}
const onDownloadDataPrivacy = () => {
console.log("download data privacy")
}
const onStopSelling = () => {
Modal.confirm({
title: t("Seller.stopSellingTitle"),
content: t("Seller.stopSellingTip"),
okText: t("Seller.confirm"),
cancelText: t("Seller.cancel"),
centered: true,
onOk() {
console.log("stop selling")
}
})
}
</script>
<style scoped lang="less">
.settings-index {
@@ -281,7 +295,7 @@
font-size: 1.4rem;
color: #999;
margin-bottom: 3rem;
> span {
&:deep(*) {
cursor: pointer;
text-decoration: underline;
color: #0080ed;

View File

@@ -28,6 +28,9 @@
import toolTipBox from "./toolTipBox.vue"
import myEvent from "@/tool/myEvents.js"
import { useStore } from "vuex"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const props = defineProps({
cachedRoutes: {
type: Array,
@@ -35,7 +38,7 @@
}
})
const store = useStore()
const isSeller = computed(() => store.state.seller.isSeller)
const isSeller = computed(() => store.state.seller.isSeller)
// store.dispatch("seller/get_designerInfo")
const route = useRoute()
const router = useRouter()
@@ -43,22 +46,22 @@
const list = ref([
{
icon: "seller-brandProfile",
layer: "Brand Profile",
layer: t("Seller.brandProfile"),
path: "/home/seller/brandProfile"
},
{
icon: "seller-myListings",
layer: "My Listings",
layer: t("Seller.myListings"),
path: "/home/seller/myListings"
},
{
icon: "seller-myOrders",
layer: "My Orders",
layer: t("Seller.myOrders"),
path: "/home/seller/myOrders"
},
{
icon: "seller-settings",
layer: "Settings",
layer: t("Seller.settings"),
path: "/home/seller/settings"
}
])