Files
lgp-wx-plus/mixins/theme.js
2026-08-20 19:15:41 +08:00

344 lines
10 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
getActiveLayout,
getActiveSchemes,
getActiveVars,
getThemeFeatures,
getThemeMeta,
loadAndApplyTheme,
} from '@/utils/theme'
import { resolvePageTabBar, syncTabBarByRoute } from '@/utils/tabbar'
import {
PC_PAGE_MAX_WIDTH,
catColsByWidth,
getWindowWidth,
goodsColsByWidth,
isPcWeixin,
watchWindowWidth,
} from '@/utils/device'
/**
* 页面主题 mixin暴露色板 + layout + 进入动效 class
* 严禁按 themeMeta.code 特判业务结构
*/
export default {
data() {
return {
themeVars: getActiveVars(),
themeLayout: getActiveLayout(),
themeMeta: getThemeMeta(),
themeSchemes: getActiveSchemes(),
themeFeatures: getThemeFeatures(),
pcWindowWidth: getWindowWidth(),
}
},
computed: {
isPcWeixin() {
return isPcWeixin()
},
/** 首页 / 列表商品列数,随窗口宽度变(不限 PC */
pcGoodsCols() {
return goodsColsByWidth(this.pcWindowWidth)
},
/** 首页分类宫格列数,格子更小所以更早加列 */
pcCatCols() {
return catColsByWidth(this.pcWindowWidth)
},
themeEffect() {
return (this.themeLayout && this.themeLayout.effect) || {}
},
themePageStyle() {
const v = this.themeVars || {}
const style = {
backgroundColor: v['--color-bg'] || '#F7F8FA',
color: v['--color-text'] || '#1a1a1a',
}
// PC 大窗不要把手机版拉成一条超宽带,内容限宽居中
if (isPcWeixin()) {
style.maxWidth = PC_PAGE_MAX_WIDTH + 'px'
style.marginLeft = 'auto'
style.marginRight = 'auto'
style.width = '100%'
style.boxSizing = 'border-box'
}
return style
},
themePrimary() {
return (this.themeVars && this.themeVars['--color-primary']) || '#B08D57'
},
themeAccent() {
return (this.themeVars && this.themeVars['--color-accent']) || this.themePrimary
},
themeSurface() {
return (this.themeVars && this.themeVars['--color-surface']) || '#ffffff'
},
themeSurfaceSoft() {
return (this.themeVars && this.themeVars['--color-surface-soft']) || this.themeSurface
},
themeText() {
return (this.themeVars && this.themeVars['--color-text']) || '#1a1a1a'
},
themeTextSoft() {
return (this.themeVars && this.themeVars['--color-text-soft']) || '#666666'
},
themeBorder() {
return (this.themeVars && this.themeVars['--color-border']) || '#eeeeee'
},
themePrice() {
return (this.themeVars && this.themeVars['--color-price']) || this.themePrimary
},
themeRadius() {
return (this.themeVars && this.themeVars['--radius-md']) || '16rpx'
},
themeRadiusLg() {
return (this.themeVars && this.themeVars['--radius-lg']) || '20rpx'
},
themeShadow() {
return (this.themeVars && this.themeVars['--shadow-md']) || 'none'
},
themeIconColor() {
return this.themePrimary
},
themeCardMode() {
return this.themeEffect.card || 'elevated'
},
themeIconMode() {
return this.themeEffect.icon || 'line'
},
themeEnterClass() {
const t = this.themeEffect.transition || 'fade'
return `theme-enter theme-enter--${t}`
},
homeHero() {
return this.sectionVariant(
'home',
'hero',
(this.themeLayout.home && this.themeLayout.home.hero) || 'carousel',
)
},
homeCategory() {
return this.sectionVariant(
'home',
'category',
(this.themeLayout.home && this.themeLayout.home.category) || 'grid',
)
},
homeProduct() {
return this.sectionVariant(
'home',
'product',
(this.themeLayout.home && this.themeLayout.home.product) || 'grid',
)
},
homePackage() {
return this.sectionVariant(
'home',
'package',
(this.themeLayout.home && this.themeLayout.home.package) || 'card',
)
},
packageList() {
return this.sectionVariant(
'package',
'list',
(this.themeLayout.package && this.themeLayout.package.list) || 'card',
)
},
/** 套餐页搜索:优先本页 search 区块,没有则复用首页搜索样式 */
packageSearch() {
const own = this.sectionVariant('package', 'search', '')
if (own) return own
return this.homeSearch
},
packageEnabled() {
return !!(this.themeFeatures && this.themeFeatures.package_enabled)
},
productGallery() {
return this.sectionVariant(
'product',
'gallery',
(this.themeLayout.product && this.themeLayout.product.gallery) || 'swiper',
)
},
productPrice() {
return this.sectionVariant(
'product',
'price',
(this.themeLayout.product && this.themeLayout.product.price) || 'inline',
)
},
productAction() {
return this.sectionVariant(
'product',
'action',
(this.themeLayout.product && this.themeLayout.product.action) || 'fixed',
)
},
catalogFilter() {
return this.sectionVariant('catalog', 'filter', 'chip')
},
catalogList() {
return this.sectionVariant('catalog', 'list', 'waterfall')
},
searchList() {
return this.sectionVariant('search', 'list', 'waterfall')
},
listStyle() {
return this.sectionVariant(
'cart',
'list',
(this.themeLayout.list && this.themeLayout.list.style) || 'card',
)
},
mineHeader() {
return this.sectionVariant(
'mine',
'header',
(this.themeLayout.mine && this.themeLayout.mine.header) || 'gradient',
)
},
mineMenu() {
return this.sectionVariant(
'mine',
'menu',
(this.themeLayout.mine && this.themeLayout.mine.menu) || 'list',
)
},
themeSkeleton() {
return this.themeEffect.skeleton || 'shimmer'
},
themeCardScheme() {
return this.themeLayout.card_scheme || 'champagne-foil'
},
pageCardScheme() {
const meta = this.pageMeta(this.currentThemePage || '')
return (meta && meta.card_scheme) || this.themeCardScheme
},
pageCardCaption() {
const meta = this.pageMeta(this.currentThemePage || '')
return (meta && meta.card_caption) || ''
},
themeChrome() {
return (this.themeLayout && this.themeLayout.chrome) || {}
},
themeTabbar() {
return this.themeChrome.tabbar || 'plain'
},
themeNavbar() {
return this.themeChrome.navbar || 'solid'
},
homeSections() {
return this.pageSections('home')
},
homeSearch() {
return this.sectionVariant('home', 'search', 'bar')
},
/** 当前页皮肤:先读本页 pages.xxx.skin没有再回落到首页 */
pageSkin() {
const page = this.pageMeta(this.currentThemePage || 'home')
const home = this.pageMeta('home')
return page.skin || home.skin || 'shop'
},
pageDensity() {
const page = this.pageMeta(this.currentThemePage || 'home')
const home = this.pageMeta('home')
return page.density || home.density || 'regular'
},
pageFrame() {
const page = this.pageMeta(this.currentThemePage || 'home')
const home = this.pageMeta('home')
return page.frame || home.frame || 'none'
},
productInfo() {
return this.sectionVariant('product', 'info', 'plain')
},
productSpec() {
return this.sectionVariant('product', 'spec', 'plain')
},
},
methods: {
pageMeta(page) {
const pages = (this.themeLayout && this.themeLayout.pages) || {}
return pages[page] || {}
},
pagePreset(page) {
const map = (this.themeLayout && this.themeLayout.page_presets) || {}
return map[page] || ''
},
pageSections(page) {
const pages = (this.themeLayout && this.themeLayout.pages) || {}
const list = pages[page] && pages[page].sections
if (Array.isArray(list) && list.length) {
return list
}
return []
},
sectionVisible(page, type) {
const list = this.pageSections(page)
if (!list.length) {
return !(page === 'home' && type === 'product')
}
const found = list.find((s) => s.type === type)
if (!found) return !(page === 'home' && type === 'product')
return found.visible !== false
},
sectionOrder(page, type) {
const list = this.pageSections(page)
const index = list.findIndex((s) => s.type === type)
return index < 0 ? 50 : index
},
sectionVariant(page, type, fallback) {
const list = this.pageSections(page)
const found = list.find((s) => s.type === type)
return (found && found.variant) || fallback
},
/**
* 拉主题并按当前路由对齐底栏。
* 页面自己写了 onShow 时可能盖掉 mixin 钩子,所以抽成方法让页面显式调用。
*/
unbindWindowWidth() {
if (typeof this._unwatchWindowWidth === 'function') {
this._unwatchWindowWidth()
this._unwatchWindowWidth = null
}
},
async applyThemeOnShow() {
// 先对齐当前页底栏,不要等主题接口,否则邻项互切时 pending 还在栏已经画死
syncTabBarByRoute(this)
try {
const theme = await loadAndApplyTheme()
this.themeVars = (theme && theme.css_vars) || getActiveVars()
this.themeLayout = (theme && theme.layout) || getActiveLayout()
this.themeSchemes = (theme && theme.card_schemes) || getActiveSchemes()
this.themeMeta = {
code: (theme && theme.code) || '',
name: (theme && theme.name) || '',
style_tag: (theme && theme.style_tag) || '',
fallback: !!(theme && theme.fallback),
}
this.themeFeatures = (theme && theme.features) || getThemeFeatures()
} catch (e) {
this.themeVars = getActiveVars()
this.themeLayout = getActiveLayout()
this.themeMeta = getThemeMeta()
this.themeFeatures = getThemeFeatures()
}
const bar = resolvePageTabBar(this)
if (bar && typeof bar.pullTheme === 'function') bar.pullTheme()
},
},
created() {
this._unwatchWindowWidth = watchWindowWidth((width) => {
this.pcWindowWidth = width
})
},
beforeDestroy() {
this.unbindWindowWidth()
},
beforeUnmount() {
this.unbindWindowWidth()
},
async onShow() {
await this.applyThemeOnShow()
},
}