This commit is contained in:
lukaijie
2026-04-24 10:22:43 +08:00
parent 3491a08bf6
commit 848598cc3d
28 changed files with 707 additions and 12133 deletions

View File

@@ -0,0 +1,27 @@
# change-summary
## 需求与计划摘要
本次为本地迭代,仓库内无对应 `plan.md`。改动聚焦:秒杀商品挂件数据映射字段补全与命名对齐,以及子包中文案 i18n 词条批量入库。
## 改动范围
| 文件 | 说明 |
|------|------|
| `src/doc/goods.js` | `WGT_SPEEDKILL_GOODS`:新增 `marketPrice`(分转元);`discount_var` 重命名为 `discountRate`,与 `compact-card.jsx``hero-card.jsx``info.discountRate` 消费侧一致。 |
| `src/subpages/i18n/lang/index.json` | 新增多条 `zh-cn` 文案键值(企业购、活动口令、限购、亲友购、同城配送等场景),其他语言暂为空字符串。 |
## 关键逻辑或接口变化
- **marketPrice**:从接口 `market_price` 映射为元(除以 100供展示划线价等用途。
- **discountRate**:仍为十分位折扣字符串(`discount_rate/10` 保留一位小数);仅字段名从 `discount_var` 改为与渲染层一致的 `discountRate`
## 测试与验收情况
- 未运行自动化测试;建议在首页/秒杀挂件场景手动确认价格、折扣标签展示正常。
- 新增 i18n 键需在引用处校验 key 与业务一致(本次仅更新语言包文件)。
## 遗留问题与后续建议
- 新增词条的 `en``zh-tw``ar` 为空,若上线多语言需补译。
- 建议在后续需求中补齐 `.f2e-ai/requirements/<id>/plan.md`,便于与提交技能流程对齐。

View File

@@ -2,15 +2,6 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [4.5.1](https://git.ishopex.cn/ecshopx/ecshopx-vshop/compare/v4.3.7...v4.5.1) (2026-04-22)
### Features
* activity records link, coupon login flow, hot zone contact, i18n ([320da55](https://git.ishopex.cn/ecshopx/ecshopx-vshop/commit/320da55a3f965e5fc3c05360089b1a4ed5773b19))
* i18n coverage, trade/store flows, and home widgets ([2e8b65b](https://git.ishopex.cn/ecshopx/ecshopx-vshop/commit/2e8b65b414107b023b67673f79dbc9cbf31619ac))
* store UI, item subpackage move, location/home updates, and tooling ([6864c46](https://git.ishopex.cn/ecshopx/ecshopx-vshop/commit/6864c466f0b1448c1e6745218238eda8b8ed6893))
## [4.5.0](https://git.ishopex.cn/ecshopx/ecshopx-vshop/compare/v4.4.2...v4.5.0) (2026-04-17)
### [4.4.2](https://git.ishopex.cn/ecshopx/ecshopx-vshop/compare/v4.4.1...v4.4.2) (2026-04-10)

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ecshopx-vshop",
"version": "4.5.1",
"version": "4.5.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ecshopx-vshop",
"version": "4.5.1",
"version": "4.5.0",
"dependencies": {
"@babel/runtime": "^7.14.5",
"@lucky-canvas/taro": "^0.0.15",

View File

@@ -1,7 +1,7 @@
{
"name": "ecshopx-vshop",
"app_name": "ecshopx",
"version": "4.5.1",
"version": "4.5.0",
"private": true,
"description": "taro app for bbc",
"scripts": {

View File

@@ -92,7 +92,7 @@ function SpDeliver(props, ref) {
} = state
const formRef = useRef()
const $instance = getCurrentInstance() || {}
const { cart_type } = $instance?.router?.params || {}
const { cart_type, type: checkoutOrderType = 'distributor' } = $instance?.router?.params || {}
// useEffect(() => {
// fetch()
// }, [])
@@ -435,7 +435,7 @@ function SpDeliver(props, ref) {
className='ziti-title'
onClick={() => {
Taro.navigateTo({
url: `/subpages/store/ziti-picker?distributor_id=${distributor_id}&cart_type=${cart_type}`
url: `/subpages/store/ziti-picker?distributor_id=${distributor_id}&cart_type=${cart_type}&type=${checkoutOrderType}`
})
}}
>

View File

@@ -504,6 +504,7 @@ export const WGT_SPEEDKILL_GOODS = {
distributorId: 'distributor_id',
pic: ({ pics, main_img }) => main_img || pics[0] || '',
mainPrice: ({ price, activity_price }) => (activity_price || price) / 100,
marketPrice: ({ market_price }) => market_price / 100,
price: ({ price }) => price / 100, // 销售价
activityPrice: ({ activity_price }) => activity_price / 100, // 秒杀价、内购价
sales: 'sales',
@@ -549,7 +550,7 @@ export const WGT_SPEEDKILL_GOODS = {
productPrice_var: ({ activity_price, price }) => {
return (activity_price || price) / 100
},
discount_var: ({ discount_rate }) =>
discountRate: ({ discount_rate }) =>
discount_rate ? String((discount_rate / 10).toFixed(1)) : null,
// 积分商品data_type=pointsmall_items 时接口返回,用于挂件展示积分不展示价格
point: ({ point, point_exchange }) =>

View File

@@ -1,30 +0,0 @@
/**
* 仅给 `app.config.js` 等 Node 端编译期使用:不初始化 i18next / 不引用 Taro避免 `window is not defined`。
* 语言与 `instance.js` 中 `APP_DEFAULT_LANGUAGE`zhcn | en | ar一致。
*/
import zhCN from './locales/zh-CN.json'
import en from './locales/en.json'
import ar from './locales/ar.json'
const BY_STORAGE_LANG = {
zhcn: zhCN,
en,
ar
}
function pickDict() {
const env =
typeof process !== 'undefined' && process.env && process.env.APP_DEFAULT_LANGUAGE
? String(process.env.APP_DEFAULT_LANGUAGE).trim()
: ''
if (env && BY_STORAGE_LANG[env]) {
return BY_STORAGE_LANG[env]
}
return en
}
/** @param {string} key */
export function appConfigT(key) {
const d = pickDict()
return (d && d[key]) != null ? d[key] : key
}

View File

@@ -1,42 +0,0 @@
/**
* Copyright © ShopeX http://www.shopex.cn. All rights reserved.
* See LICENSE file for license details.
*
* 统一出口:`app.config.js`、组件等 `import { $t, useTranslation, i18n } from '@/i18n'`
*/
import { useTranslation } from 'react-i18next'
import i18n from './instance'
export { useTranslation }
export {
syncI18nLanguage,
STORAGE_TO_I18N,
I18N_TO_STORAGE,
SUPPORTED_STORAGE_LANGS
} from './instance'
export { i18n }
export default i18n
/** 平铺文案 */
export function $t(key, options) {
return i18n.t(key, options)
}
/**
* 占位符为 ${0}、${1}… 的文案(与 locales JSON 中写法一致)
* @param {string} key
* @param {Array<string|number>} values
*/
export function ti(key, values = []) {
let s = i18n.t(key)
if (!Array.isArray(values) || values.length === 0) {
return s
}
values.forEach((v, i) => {
s = String(s)
.split('${' + i + '}')
.join(v != null ? String(v) : '')
})
return s
}

View File

@@ -1,90 +0,0 @@
/**
* Copyright © ShopeX http://www.shopex.cn. All rights reserved.
* See LICENSE file for license details.
*
* i18next 单例:资源来自 src/i18n/locales语言与 Taro 存储 / Redux user.langzhcn|en|ar同步。
*/
/* eslint-disable import/no-named-as-default-member -- i18next 默认导出即实例,需调用 .use / .changeLanguage */
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import Taro from '@tarojs/taro'
import zhCN from './locales/zh-CN.json'
import en from './locales/en.json'
import ar from './locales/ar.json'
/** Taro 存储 / Redux 使用的语言码 → i18next lng */
export const STORAGE_TO_I18N = {
zhcn: 'zh-CN',
en: 'en',
ar: 'ar'
}
/** i18next lng → 存储用语言码 */
export const I18N_TO_STORAGE = {
'zh-CN': 'zhcn',
en: 'en',
ar: 'ar'
}
/** 可选语言顺序(与 Taro 存储 lang 一致) */
export const SUPPORTED_STORAGE_LANGS = ['zhcn', 'en', 'ar']
function readStorageLang() {
try {
return Taro.getStorageSync('lang') || ''
} catch {
return ''
}
}
function resolveInitialLng() {
const stored = readStorageLang()
if (stored && STORAGE_TO_I18N[stored]) {
return STORAGE_TO_I18N[stored]
}
const env =
typeof process !== 'undefined' && process.env && process.env.APP_DEFAULT_LANGUAGE
? process.env.APP_DEFAULT_LANGUAGE
: ''
if (env && STORAGE_TO_I18N[env]) {
return STORAGE_TO_I18N[env]
}
return 'en'
}
if (!i18n.isInitialized) {
i18n.use(initReactI18next).init({
resources: {
'zh-CN': { translation: zhCN },
en: { translation: en },
ar: { translation: ar }
},
lng: resolveInitialLng(),
fallbackLng: ['en', 'zh-CN'],
interpolation: {
escapeValue: false
},
react: {
useSuspense: false
},
returnNull: false
})
}
/**
* 与 Taro 存储语言对齐zhcn / en / ar
* @param {string} storageLang
*/
export function syncI18nLanguage(storageLang) {
const lng = STORAGE_TO_I18N[storageLang]
if (!lng) {
return Promise.resolve()
}
if (i18n.language === lng) {
return Promise.resolve()
}
return i18n.changeLanguage(lng)
}
export default i18n

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@
* See LICENSE file for license details.
*/
import Taro from '@tarojs/taro'
import { syncI18nLanguage } from '@/i18n/instance'
class Lang {
constructor() {
@@ -11,7 +10,6 @@ class Lang {
}
init() {
const self = this
// 定义翻译函数
let $t = function (key, val, nameSpace) {
// 获取指定命名空间下的语言包
@@ -67,43 +65,6 @@ class Lang {
// 返回语言对象
return langObj
}
/**
* 必须在首屏即可用:`setLanguagePackage` 在 useLaunch 里异步执行,
* 用户在语言包就绪前进「设置 → 切换语言」会导致 Taro.$changeLang 未定义。
*/
Taro.$changeLang = (lang) => {
const apply = () => {
globalThis._localStorage.setItem('lang', lang)
globalThis.$t.locale(globalThis.langMap[lang], 'lang')
if (Taro.eventCenter) {
Taro.eventCenter.trigger('languageChanged', { lang, langMap: globalThis.langMap[lang] })
}
Promise.resolve(syncI18nLanguage(lang)).catch(() => {})
}
if (
globalThis.langMap &&
typeof globalThis.langMap === 'object' &&
Object.keys(globalThis.langMap).length > 0
) {
apply()
return
}
import('@/subpages/i18n/index')
.then(() => {
const langJSON = Taro['langJSON']
if (!langJSON) {
console.error('[Lang] Taro.langJSON missing, cannot switch language')
return
}
self.setLanguagePackage(langJSON)
apply()
})
.catch((e) => {
console.error('[Lang] Failed to load language bundle', e)
})
}
}
setLanguagePackage(langJSON) {
@@ -185,7 +146,15 @@ class Lang {
}
// 根据当前语言设置翻译函数的语言包
globalThis.$t.locale(globalThis.langMap[lang], 'lang')
Promise.resolve(syncI18nLanguage(lang)).catch(() => {})
Taro.$changeLang = (lang) => {
globalThis._localStorage.setItem('lang', lang)
globalThis.$t.locale(globalThis.langMap[lang], 'lang')
// 触发语言变化事件,让其他地方能够监听到
if (Taro.eventCenter) {
Taro.eventCenter.trigger('languageChanged', { lang, langMap: globalThis.langMap[lang] })
}
}
}
}

View File

@@ -90,7 +90,7 @@ function CompDeliver(props, ref) {
} = state
const formRef = useRef()
const $instance = getCurrentInstance() || {}
const { cart_type } = $instance?.router?.params || {}
const { cart_type, type: checkoutOrderType = 'distributor' } = $instance?.router?.params || {}
// useEffect(() => {
// fetch()
// }, [])
@@ -435,7 +435,7 @@ function CompDeliver(props, ref) {
className='ziti-title'
onClick={() => {
Taro.navigateTo({
url: `/subpages/store/ziti-picker?distributor_id=${distributor_id}&cart_type=${cart_type}`
url: `/subpages/store/ziti-picker?distributor_id=${distributor_id}&cart_type=${cart_type}&type=${checkoutOrderType}`
})
}}
>

View File

@@ -1,255 +0,0 @@
/**
* Copyright © ShopeX http://www.shopex.cn. All rights reserved.
* See LICENSE file for license details.
*/
import React, { Component } from 'react'
import Taro, { getCurrentInstance } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import { SpImg, SpImage } from '@/components'
import { connect } from 'react-redux'
// import { Tracker } from '@/service'
import S from '@/spx'
import entry from '@/utils/entry'
import { getDistributorId, VERSION_STANDARD, classNames, isAlipay } from '@/utils'
import api from '@/api'
import './goods.scss'
@connect(
({ cart, user, shop }) => ({
cart,
favs: user.favs,
shop
}),
(dispatch) => ({
onFastbuy: (item) => dispatch({ type: 'cart/fastbuy', payload: { item } }),
onAddCart: (item) => dispatch({ type: 'cart/add', payload: { item } }),
onAddFav: ({ item_id }) => dispatch({ type: 'member/addFav', payload: { item_id } }),
onDelFav: ({ item_id }) => dispatch({ type: 'member/delFav', payload: { item_id } }),
onUpdateCartCount: (count) => dispatch({ type: 'cart/updateCartNum', payload: count })
})
)
export default class WgtGoods extends Component {
static options = {
addGlobalClass: true
}
static defaultProps = {
info: null
}
constructor(props) {
super(props)
this.state = {
curIdx: 0,
is_fav: false,
count: 0
}
}
handleClickItem = (item) => {
// const { info } = this.props
/*if(info.data) {
let onsale = true
info.data.map(item => {
if(id === item.item_id){
if(!item.isOnsale){
onsale = false
}
}
})
if(!onsale){
return false
}
}*/
// try {
const { distributor_id, item_id } = item
// const dtid = distributor_id ? distributor_id : getDistributorId()
Taro.navigateTo({
url: `/subpages/item/espier-detail?id=${item_id}&dtid=${distributor_id || 0}`
})
// }
// catch (error) {
// console.log(error)
// Taro.navigateTo({
// url: `/pages/iwp/item-detail?id=${id}`
// })
// }
}
handleSwiperChange = (e) => {
const { current } = e.detail
this.setState({
curIdx: current
})
}
handleClickOperate = async (item_data, type, e) => {
const { info } = this.props
e.stopPropagation()
if (!S.getAuthToken()) {
Taro.showToast({
icon: 'none',
title: '请登录'
})
return
}
/*if(info.data) {
let onsale = true
info.data.map(item => {
if(item_data.item_id === item.item_id){
if(!item.isOnsale){
onsale = false
}
}
})
if(!onsale){
return false
}
}*/
if (type === 'collect') {
/*if(this.state.count === 0) {
let is_fav = Boolean(this.props.favs[item_data.item_id])
this.setState({
count: 1,
is_fav
})
}
if(!this.state.is_fav) {
await api.member.addFav(item_data.item_id)
this.props.onAddFav(item_data)
Taro.showToast({
title: '已加入收藏',
icon: 'none'
})
} else {
await api.member.delFav(item_data.item_id)
this.props.onDelFav(item_data)
Taro.showToast({
title: '已移出收藏',
icon: 'none'
})
}
this.setState({
is_fav: !this.state.is_fav
})*/
if (!item_data.favStatus) {
await api.member.addFav(item_data.item_id)
// !isAlipay && Tracker.dispatch('GOODS_COLLECT', info)
this.props.onAddFav(item_data)
Taro.showToast({
title: '已加入收藏',
icon: 'none'
})
} else {
await api.member.delFav(item_data.item_id)
this.props.onDelFav(item_data)
Taro.showToast({
title: '已移出收藏',
icon: 'none'
})
}
this.props.onClick()
}
if (type === 'buy') {
try {
const isOpenStore = await entry.getStoreStatus()
const { distributor_id, store_id } = Taro.getStorageSync('curStore')
let id = Number(item_data.distributor_id) || 0
// if (VERSION_STANDARD && !id) {
// id = isOpenStore ? store_id : distributor_id
// }
await api.cart.add({
item_id: item_data.item_id,
distributor_id: this.props?.shop?.shopInfo?.distributor_id,
num: 1,
shop_type: 'distributor'
})
Taro.showToast({
title: '成功加入购物车',
icon: 'success'
})
this.fetchCartcount()
} catch (error) {
console.log(error)
}
}
}
async fetchCartcount() {
try {
const { item_count } = await api.cart.count({ shop_type: 'distributor' })
this.props.onUpdateCartCount(item_count)
} catch (e) {
console.error(e)
}
}
render() {
const { info } = this.props
const { curIdx, is_fav } = this.state
if (!info) {
return null
}
const { config, base, data } = info
const curContent = (data[curIdx] || {}).content
return (
<View className={`wgt wgt-goods ${base.padded ? 'wgt__padded' : null}`}>
{base.title && (
<View className='wgt__header'>
<View className='wgt__title'>{base.title}</View>
<View className='wgt__subtitle'>{base.subtitle}</View>
</View>
)}
<View className='slider-wrap'>
{data.map((item) => (
<View
className='goods'
key={item.item_id}
onClick={this.handleClickItem.bind(this, item)}
>
<View className='goods-info'>
<SpImage className='goods-img' src={item.img_url} />
<View className='goods-name'>
<View className='name'>{item.item_name}</View>
<View className='desc'>
{item.itemStatus ? '点击查看产品详情' : '该商品已下架'}
</View>
</View>
</View>
<View className='goods-btns'>
<View
className={classNames(`btn-text`, {
'disabled': !item.itemStatus
})}
onClick={this.handleClickOperate.bind(this, item, 'collect')}
>
{item.favStatus ? '移除心愿' : '加入心愿'}
</View>
<Text>|</Text>
<View
className={classNames(`btn-text`, {
'disabled': !item.itemStatus
})}
onClick={this.handleClickOperate.bind(this, item, 'buy')}
>
加入购买
</View>
</View>
</View>
))}
</View>
</View>
)
}
}

View File

@@ -1,38 +0,0 @@
/**
* Copyright © ShopeX http://www.shopex.cn. All rights reserved.
* See LICENSE file for license details.
*/
.wgt-goods {
.goods {
margin: 0 20px;
box-sizing: border-box;
background: #ffffff;
border-radius: 8px;
box-shadow: 2px 2px 6px 6px rgba(120, 120, 120, 0.1);
.goods-info {
padding: 20px;
display: flex;
}
.goods-img {
width: 200px;
}
.goods-name {
margin-left: 10px;
.desc {
color: #888;
margin-top: 6px;
}
}
.goods-btns {
display: flex;
justify-content: space-around;
padding: 0 20px 20px;
.btn-text {
color: $color-brand-primary;
&.disabled {
color: $color-brand-accent-light;
}
}
}
}
}

View File

@@ -0,0 +1,234 @@
/**
* Copyright © ShopeX http://www.shopex.cn. All rights reserved.
* See LICENSE file for license details.
*/
import React, { Component } from 'react'
import Taro from '@tarojs/taro'
import { View } from '@tarojs/components'
import { SpImage, SpPrice } from '@/components'
import { connect } from 'react-redux'
// import { Tracker } from '@/service'
import S from '@/spx'
import { classNames, styleNames, showToast, pxToUnitRpx } from '@/utils'
import api from '@/api'
import { getGlobalBaseStyle } from './helper'
import './goodscard.scss'
@connect(
({ cart, member, shop }) => ({
cart,
favs: member.favs,
shop
}),
(dispatch) => ({
onFastbuy: (item) => dispatch({ type: 'cart/fastbuy', payload: { item } }),
onAddCart: (item) => dispatch({ type: 'cart/add', payload: { item } }),
onAddFav: ({ item_id }) => dispatch({ type: 'member/addFav', payload: { item_id } }),
onDelFav: ({ item_id }) => dispatch({ type: 'member/delFav', payload: { item_id } }),
onUpdateCartCount: (count) => dispatch({ type: 'cart/updateCartNum', payload: count })
})
)
export default class WgtGoodsCard extends Component {
static options = {
addGlobalClass: true
}
static defaultProps = {
info: null
}
handleClickItem = (item) => {
// const { info } = this.props
/*if(info.data) {
let onsale = true
info.data.map(item => {
if(id === item.item_id){
if(!item.isOnsale){
onsale = false
}
}
})
if(!onsale){
return false
}
}*/
// try {
if (item.itemStatus === false) {
showToast({
title: '该商品已下架',
icon: 'none'
})
return
}
const { distributor_id, item_id } = item
// const dtid = distributor_id ? distributor_id : getDistributorId()
Taro.navigateTo({
url: `/subpages/item/espier-detail?id=${item_id}&dtid=${distributor_id || 0}`
})
// }
// catch (error) {
// console.log(error)
// Taro.navigateTo({
// url: `/pages/iwp/item-detail?id=${id}`
// })
// }
}
handleClickOperate = async (item_data, type, e) => {
e.stopPropagation()
if (!S.getAuthToken()) {
Taro.showToast({
icon: 'none',
title: '请登录'
})
return
}
if (item_data.itemStatus === false) {
showToast({
title: '该商品已下架',
icon: 'none'
})
return
}
if (type === 'collect') {
const isFav = Boolean(item_data.favStatus ?? this.props.favs?.[item_data.item_id])
if (!isFav) {
await api.member.addFav(item_data.item_id)
this.props.onAddFav(item_data)
Taro.showToast({
title: '已加入收藏',
icon: 'none'
})
} else {
await api.member.delFav(item_data.item_id)
this.props.onDelFav(item_data)
Taro.showToast({
title: '已移出收藏',
icon: 'none'
})
}
if (typeof this.props.onClick === 'function') {
this.props.onClick()
}
}
if (type === 'buy') {
try {
await api.cart.add({
item_id: item_data.item_id,
distributor_id: this.props?.shop?.shopInfo?.distributor_id,
num: 1,
shop_type: 'distributor'
})
Taro.showToast({
title: '成功加入购物车',
icon: 'success'
})
this.fetchCartcount()
} catch (error) {
console.log(error)
}
}
}
async fetchCartcount() {
try {
const { item_count } = await api.cart.count({ shop_type: 'distributor' })
this.props.onUpdateCartCount(item_count)
} catch (e) {
console.error(e)
}
}
render() {
const { info, favs } = this.props
if (!info) {
return null
}
const { base, data: dataList = [] } = info
const outerStyle = getGlobalBaseStyle(base.outerMargin)
const innerPaddingStyle = getGlobalBaseStyle(base.innerPadding)
const useOuterLayout = Boolean(base.outerMargin && typeof base.outerMargin === 'object')
const goodsCardInnerStyle = (() => {
const style = {
...innerPaddingStyle,
// 与后台 Vue 一致borderRadius 8px小程序端用 pxToUnitRpx 做 ×2 适配
'border-radius': pxToUnitRpx(8)
}
const hasBgColor = Boolean(style['background-color'])
const hasBgImage = Boolean(style['background-image'])
if (!hasBgColor && !hasBgImage) {
style['background-color'] = '#fff'
}
return style
})()
return (
<View
className={classNames('wgt wgt-goods-card', {
padded: base.padded && !useOuterLayout
})}
style={styleNames(outerStyle)}
>
<View className='wgt-bd'>
{dataList.map((item, index) => {
const key = item.item_id != null ? String(item.item_id) : `goods-card__${index}`
const onSale = item.itemStatus !== false
const isFav = Boolean(item.favStatus ?? favs?.[item.item_id])
return (
<View key={key} className='goods-card' style={styleNames(goodsCardInnerStyle)}>
<View
className='goods-card__header'
onClick={this.handleClickItem.bind(this, item)}
>
<SpImage src={item.img_url} width={160} height={160} isOss />
<View className='goods-card__info'>
<View className='goods-card__info-title'>{item.item_name}</View>
<View className='goods-card__info-price'>
<SpPrice unit='cent' value={item.price} size={28} primary noDecimal />
</View>
{item.sales > 0 && (
<View className='goods-card__info-sales'>销量{item.sales}</View>
)}
</View>
</View>
<View className='goods-card__footer'>
<View
className={classNames('goods-card__btn', {
'goods-card__btn--disabled': !onSale
})}
onClick={this.handleClickOperate.bind(this, item, 'collect')}
>
{isFav ? '移除心愿' : '加入心愿'}
</View>
<View className='gap-line' />
<View
className={classNames('goods-card__btn', {
'goods-card__btn--disabled': !onSale
})}
onClick={this.handleClickOperate.bind(this, item, 'buy')}
>
加入购买
</View>
</View>
</View>
)
})}
</View>
</View>
)
}
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright © ShopeX http://www.shopex.cn. All rights reserved.
* See LICENSE file for license details.
*/
.wgt-goods-card {
.wgt-bd {
.goods-card {
&:not(:last-child) {
margin-bottom: 20px;
}
&__header {
display: flex;
align-items: center;
padding-bottom: 20px;
}
&__info {
padding-left: 10px;
height: 160px;
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
}
&__info-title {
font-size: 28px;
font-weight: 500;
color: #333;
@include multi-ellipsis(2);
}
&__info-price {
margin-top: 8px;
}
&__info-sales {
font-size: 24px;
color: #666;
margin-top: 8px;
}
&__footer {
height: 80px;
display: flex;
justify-content: space-around;
align-items: center;
.goods-card__btn {
display: flex;
align-items: center;
justify-content: center;
padding: 0 20px;
height: 64px;
font-size: 28px;
color: $color-brand-primary;
background: #ffffff;
border: 1px solid #dcdfe6;
border-radius: 12px;
}
.goods-card__btn--disabled {
color: $color-brand-accent-light;
}
.gap-line {
width: 1px;
height: 40px;
background-color: #e5e5e5;
}
}
}
}
}

View File

@@ -26,6 +26,7 @@ export { default as WgtImgHotZone } from './imghot-zone/index'
export { default as WgtMarquees } from './marquees/index'
export { default as WgtStore } from './store'
export { default as WgtHeadline } from './headline'
export { default as WgtGoodsCard } from './goodscard'
// export { default as WgtImgGif } from './img-gif'
export { default as WgtFloorImg } from './floor-img'
export { default as WgtHomeHeader } from './home-header'

View File

@@ -15,7 +15,7 @@ import doc from '@/doc'
import S from '@/spx'
import { pickBy, log, isWeixin, showToast, buildSharePath } from '@/utils'
import { withPageWrapper } from '@/hocs'
import { WgtFilm, WgtSlider, WgtWriting, WgtGoods, WgtHeading } from '../home/wgts'
import { WgtFilm, WgtSlider, WgtWriting, WgtGoodsCard, WgtHeading } from '../home/wgts'
import './detail.scss'
const initialState = {
@@ -124,8 +124,6 @@ function GuideRecommendDetail(props) {
}
}
const handleClickGoods = async () => {}
return (
<SpPage
className='pages-recommend-detail'
@@ -159,7 +157,6 @@ function GuideRecommendDetail(props) {
<View className='article-title'>{title}</View>
<View className='article-info'>
<Text className='update-time'>{updated}</Text>
<Text className='focus-num'>{`${articleFocusNum}关注`}</Text>
</View>
</View>
<View className='article-bd'>
@@ -170,7 +167,7 @@ function GuideRecommendDetail(props) {
{item.name === 'slider' && <WgtSlider info={item} />}
{item.name === 'writing' && <WgtWriting info={item} />}
{item.name === 'heading' && <WgtHeading info={item} />}
{item.name === 'goods' && <WgtGoods onClick={handleClickGoods} info={item} />}
{item.name === 'goodsCard' && <WgtGoodsCard info={item} />}
</View>
))}
</View>

View File

@@ -13,7 +13,7 @@ import { connect } from 'react-redux'
import { formatTime, log, isWeixin, entryLaunch } from '@/utils'
import S from '@/spx'
// import { Tracker } from '@/service'
import { WgtFilm, WgtSlider, WgtWriting, WgtGoods, WgtHeading } from '../../../pages/home/wgts'
import { WgtFilm, WgtSlider, WgtWriting, WgtGoodsCard, WgtHeading } from '../../../pages/home/wgts'
import './detail.scss'
@connect(({ colors }) => ({
@@ -226,10 +226,6 @@ export default class recommendDetail extends Component {
<Text className={`iconfont icon-31shijian ${info.is_like ? '' : ''}`}> </Text>
{info.updated_str}
</View>
<View className='recommend-detail-info__time'>
<Text className={`iconfont icon-eye ${info.is_like ? '' : ''}`}> </Text>
{info.articleFocusNum.count ? info.articleFocusNum.count : 0}关注
</View>
</View>
<View className='recommend-detail__content' scrollY>
<View className='wgts-wrap__cont'>
@@ -240,8 +236,8 @@ export default class recommendDetail extends Component {
{item.name === 'slider' && <WgtSlider info={item} width={screenWidth} />}
{item.name === 'writing' && <WgtWriting info={item} />}
{item.name === 'heading' && <WgtHeading info={item} />}
{item.name === 'goods' && (
<WgtGoods onClick={this.handleClickGoods.bind('goods')} info={item} />
{item.name === 'goodsCard' && (
<WgtGoodsCard onClick={this.handleClickGoods.bind(this)} info={item} />
)}
</View>
)

View File

@@ -36,8 +36,7 @@
.in-icon-xingzhuang {
font-size: 22px;
}
.icon-31shijian,
.icon-eye {
.icon-31shijian {
font-size: 26px;
}
}

View File

@@ -13,7 +13,8 @@ import { SpPage } from '@/components'
import api from '@/api'
import doc from '@/doc'
import { pickBy, log, buildSharePath } from '@/utils'
import { WgtFilm, WgtSlider, WgtWriting, WgtGoods, WgtHeading } from '../components/wgts'
import { WgtGoodsCard } from '@/pages/home/wgts'
import { WgtFilm, WgtSlider, WgtWriting, WgtHeading } from '../components/wgts'
import './detail.scss'
const initialState = {
@@ -78,8 +79,6 @@ function GuideRecommendDetail(props) {
})
}
const handleClickGoods = async () => {}
return (
<SpPage
className='guide-recommend-detail'
@@ -95,7 +94,6 @@ function GuideRecommendDetail(props) {
<View className='article-title'>{title}</View>
<View className='article-info'>
<Text className='update-time'>{updated}</Text>
<Text className='focus-num'>{`${articleFocusNum}关注`}</Text>
</View>
</View>
<View className='article-bd'>
@@ -106,9 +104,7 @@ function GuideRecommendDetail(props) {
{item.name === 'slider' && <WgtSlider info={item} />}
{item.name === 'writing' && <WgtWriting info={item} />}
{item.name === 'heading' && <WgtHeading info={item} />}
{item.name === 'goods' && (
<WgtGoods onClick={handleClickGoods.bind(this, 'goods')} info={item} />
)}
{item.name === 'goodsCard' && <WgtGoodsCard info={item} />}
</View>
))}
</View>

View File

@@ -16522,5 +16522,323 @@
"en": "",
"zh-tw": "",
"ar": ""
},
"byn843": {
"zh-cn": "企业购",
"en": "",
"zh-tw": "",
"ar": ""
},
"c78gnd5": {
"zh-cn": "缺少口令码",
"en": "",
"zh-tw": "",
"ar": ""
},
"bzskof9": {
"zh-cn": "请先阅读并同意协议",
"en": "",
"zh-tw": "",
"ar": ""
},
"82tld46": {
"zh-cn": "活动尚未开始",
"en": "",
"zh-tw": "",
"ar": ""
},
"awuose4": {
"zh-cn": "即将开始",
"en": "",
"zh-tw": "",
"ar": ""
},
"ddiqvl4": {
"zh-cn": "敬请期待",
"en": "",
"zh-tw": "",
"ar": ""
},
"fu6pw44": {
"zh-cn": "立即参与",
"en": "",
"zh-tw": "",
"ar": ""
},
"s1yfsh5": {
"zh-cn": "开始时间:",
"en": "",
"zh-tw": "",
"ar": ""
},
"c4xods5": {
"zh-cn": "结束时间:",
"en": "",
"zh-tw": "",
"ar": ""
},
"fry8o94": {
"zh-cn": "确认订单",
"en": "",
"zh-tw": "",
"ar": ""
},
"cx3q0d4": {
"zh-cn": "提交中…",
"en": "",
"zh-tw": "",
"ar": ""
},
"b400fd4": {
"zh-cn": "商品信息",
"en": "",
"zh-tw": "",
"ar": ""
},
"wj3lfs4": {
"zh-cn": ",含运费",
"en": "",
"zh-tw": "",
"ar": ""
},
"b43y6y4": {
"zh-cn": "商品数量",
"en": "",
"zh-tw": "",
"ar": ""
},
"g9k3g58": {
"zh-cn": "此订单商品来自「",
"en": "",
"zh-tw": "",
"ar": ""
},
"6gbsr24": {
"zh-cn": "」导购「",
"en": "",
"zh-tw": "",
"ar": ""
},
"6k13b34": {
"zh-cn": "」的推荐",
"en": "",
"zh-tw": "",
"ar": ""
},
"1w4jop9": {
"zh-cn": "商品总额不超过 ¥",
"en": "",
"zh-tw": "",
"ar": ""
},
"7uf0jyd": {
"zh-cn": "内购商品非质量问题不予退货",
"en": "",
"zh-tw": "",
"ar": ""
},
"xbjdcy5": {
"zh-cn": "限购数量:",
"en": "",
"zh-tw": "",
"ar": ""
},
"molwj3": {
"zh-cn": "限购 ",
"en": "",
"zh-tw": "",
"ar": ""
},
"x507db5": {
"zh-cn": "限购金额:",
"en": "",
"zh-tw": "",
"ar": ""
},
"7z6yhu5": {
"zh-cn": "选择规格:",
"en": "",
"zh-tw": "",
"ar": ""
},
"ielu7q5": {
"zh-cn": "配送地址:",
"en": "",
"zh-tw": "",
"ar": ""
},
"x839az5": {
"zh-cn": "优惠活动:",
"en": "",
"zh-tw": "",
"ar": ""
},
"j2ylfn8": {
"zh-cn": "亲友购开始时间:",
"en": "",
"zh-tw": "",
"ar": ""
},
"aa0oqx4": {
"zh-cn": "亲友名额",
"en": "",
"zh-tw": "",
"ar": ""
},
"coyok3": {
"zh-cn": "可分享",
"en": "",
"zh-tw": "",
"ar": ""
},
"n322x76": {
"zh-cn": "暂无亲友数据",
"en": "",
"zh-tw": "",
"ar": ""
},
"atd3beb": {
"zh-cn": "当前活动不支持分享亲友",
"en": "",
"zh-tw": "",
"ar": ""
},
"6rlnnq6": {
"zh-cn": "活动剩余额度",
"en": "",
"zh-tw": "",
"ar": ""
},
"q993776": {
"zh-cn": "价格从低到高",
"en": "",
"zh-tw": "",
"ar": ""
},
"py76r76": {
"zh-cn": "价格从高到低",
"en": "",
"zh-tw": "",
"ar": ""
},
"u7b3fc6": {
"zh-cn": "请先进入活动",
"en": "",
"zh-tw": "",
"ar": ""
},
"3zfjysa": {
"zh-cn": "请在活动首页查看额度",
"en": "",
"zh-tw": "",
"ar": ""
},
"duq72": {
"zh-cn": "共 ",
"en": "",
"zh-tw": "",
"ar": ""
},
"f1kb2": {
"zh-cn": "品牌",
"en": "",
"zh-tw": "",
"ar": ""
},
"ue33nee": {
"zh-cn": "限购金额:商品总额不超过 ¥",
"en": "",
"zh-tw": "",
"ar": ""
},
"6gmzyd8": {
"zh-cn": "限购数量:限购 ",
"en": "",
"zh-tw": "",
"ar": ""
},
"cvdke44": {
"zh-cn": "我的额度",
"en": "",
"zh-tw": "",
"ar": ""
},
"7m2b1i6": {
"zh-cn": "请输入入口码",
"en": "",
"zh-tw": "",
"ar": ""
},
"cd7o0f4": {
"zh-cn": "开始选购",
"en": "",
"zh-tw": "",
"ar": ""
},
"iynby9b": {
"zh-cn": "请输入活动口令码以继续",
"en": "",
"zh-tw": "",
"ar": ""
},
"5i8gi07": {
"zh-cn": "请完成身份校验",
"en": "",
"zh-tw": "",
"ar": ""
},
"y1g7t79": {
"zh-cn": "请授权手机号以继续",
"en": "",
"zh-tw": "",
"ar": ""
},
"nkyvbn5": {
"zh-cn": "授权手机号",
"en": "",
"zh-tw": "",
"ar": ""
},
"ogwh2": {
"zh-cn": "账号",
"en": "",
"zh-tw": "",
"ar": ""
},
"g9ob2": {
"zh-cn": "密码",
"en": "",
"zh-tw": "",
"ar": ""
},
"dp1ljd5": {
"zh-cn": "邮箱验证码",
"en": "",
"zh-tw": "",
"ar": ""
},
"7w2nq79": {
"zh-cn": "隐私声明及使用条款",
"en": "",
"zh-tw": "",
"ar": ""
},
"az6zo64": {
"zh-cn": "同城配送",
"en": "",
"zh-tw": "",
"ar": ""
},
"cd9r0z4": {
"zh-cn": "当前企业",
"en": "",
"zh-tw": "",
"ar": ""
},
"dd1tdf4": {
"zh-cn": "更多活动",
"en": "",
"zh-tw": "",
"ar": ""
}
}
}

View File

@@ -42,7 +42,6 @@ import {
} from '@/store/slices/purchase'
import { useLogin, useLocation } from '@/hooks'
import { updateDeliveryPersonnel } from '@/store/slices/cart'
import { useTranslation, $t } from '@/i18n'
import CompMenu from './comps/comp-menu'
import './index.scss'
@@ -116,7 +115,6 @@ const initialState = {
}
function MemberIndex(props) {
useTranslation()
// console.log('===>getCurrentPages==>', getCurrentPages(), getCurrentInstance())
const pageRef = useRef(null)
const $instance = getCurrentInstance() || {}
@@ -359,21 +357,21 @@ function MemberIndex(props) {
Taro.navigateTo({ url: link })
} else {
const { confirm } = await Taro.showModal({
title: $t('3009e832.75716c'),
content: $t('3009e832.7785f6'),
title: '邀请推广',
content: '确定申请成为推广员?',
showCancel: true,
cancelText: $t('3009e832.625fb2'),
confirmText: $t('3009e832.e83a25'),
cancelText: '取消',
confirmText: '确认',
confirmColor: '#0b4137'
})
if (!confirm) return
const { status } = await api.distribution.become()
if (status) {
Taro.showModal({
title: $t('3009e832.a1651e'),
content: $t('3009e832.44fafb'),
title: '恭喜',
content: '已成为推广员',
showCancel: false,
confirmText: $t('3009e832.ac2c8f')
confirmText: '好'
})
}
}
@@ -433,7 +431,7 @@ function MemberIndex(props) {
<Text>
{
{
true: vipInfo.grade_name || $t('3009e832.4d9dd5'),
true: vipInfo.grade_name || '会员',
false: userInfo?.gradeInfo?.grade_name || ''
}[vipInfo.isVip]
}
@@ -460,7 +458,7 @@ function MemberIndex(props) {
showpoweredBy={false}
pageConfig={state.pageData?.base || {}}
renderFooter={<SpTabbar />}
title={$t('75e226b0.12688a')}
title='会员中心'
onReady={({ gNavbarH, footerHeight }) => {
setState((draft) => {
draft.bodyHeight = `calc(100vh - ${
@@ -517,7 +515,7 @@ function MemberIndex(props) {
</View>
</>
) : (
<Text className='login-text font-medium text-34'>{$t('3009e832.a1f629')}</Text>
<Text className='login-text font-medium text-34'>点击登录</Text>
)}
</View>
@@ -558,7 +556,7 @@ function MemberIndex(props) {
onClick={() => onLoginChange('/subpages/marketing/coupon')}
>
<Text className='stat-value'>{isLogin ? state.couponCount || 0 : '···'}</Text>
<Text className='stat-label'>{$t('3009e832.2f3635')}</Text>
<Text className='stat-label'>优惠券</Text>
</View>
<View

View File

@@ -72,7 +72,7 @@ function CompDeliver(props, ref) {
} = state
const formRef = useRef()
const $instance = getCurrentInstance() || {}
const { cart_type } = $instance?.router?.params || {}
const { cart_type, type: checkoutOrderType = 'distributor' } = $instance?.router?.params || {}
// useEffect(() => {
// fetch()
// }, [])
@@ -289,7 +289,7 @@ function CompDeliver(props, ref) {
className='ziti-title'
onClick={() => {
Taro.navigateTo({
url: `/subpages/store/ziti-picker?distributor_id=${distributor_id}&cart_type=${cart_type}`
url: `/subpages/store/ziti-picker?distributor_id=${distributor_id}&cart_type=${cart_type}&type=${checkoutOrderType}`
})
}}
>

View File

@@ -407,7 +407,7 @@ function StoreIndex() {
className='switchs'
style={{
background: `${pageData?.base?.pageBackgroundColor || '#fff'}`,
top: `${statusBarHeight + 40}px`
top: `0px`
}}
>
<Text

View File

@@ -3,13 +3,12 @@
* See LICENSE file for license details.
*/
import React, { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useDispatch } from 'react-redux'
import { useImmer } from 'use-immer'
import Taro, { getCurrentInstance } from '@tarojs/taro'
import api from '@/api'
import doc from '@/doc'
import { View } from '@tarojs/components'
import { VERSION_PLATFORM, VERSION_STANDARD } from '@/utils'
import { VERSION_STANDARD } from '@/utils'
import { changeZitiAddress } from '@/store/slices/cart'
import { SpPage, SpCheckboxNew } from '@/components'
import './ziti-picker.scss'
@@ -24,20 +23,25 @@ function StoreZitiPicker(props) {
const [state, setState] = useImmer(initialState)
const { zitiList, zitiId, isDefault } = state
const dispatch = useDispatch()
const { entryStoreByLBS } = useSelector((state) => state.sys)
useEffect(() => {
fetchZitiList()
}, [])
const fetchZitiList = async () => {
const { distributor_id, zitiId, cart_type } = $instance?.router?.params
const {
distributor_id,
zitiId,
cart_type,
type: orderType = 'distributor'
} = $instance?.router?.params || {}
let _params = {
cart_type,
distributor_id
}
// 与 pages/cart/espier-checkout.js getParamsInfo 中 isNostores 规则一致:云店 distributor 为 0
if (VERSION_STANDARD) {
_params['isNostores'] = entryStoreByLBS ? 0 : 1
_params['isNostores'] = orderType == 'distributor' ? 0 : 1
}
const { list } = await api.cart.getZitiList(_params)