diff --git a/api/request.js b/api/request.js
index a9bc839..5ee239f 100644
--- a/api/request.js
+++ b/api/request.js
@@ -11,7 +11,14 @@
*/
// 基础路径:修改此处即可切换后端地址
-const BASE_URL = 'https://official.inkreach.cc/api/';
+// #ifdef H5
+// H5 端走 devServer 代理(manifest.json h5.devServer.proxy),规避浏览器 CORS 限制
+const BASE_URL = '/';
+// #endif
+// #ifndef H5
+// 小程序端直连后端 v2 环境
+const BASE_URL = 'https://official.inkreach.cc/v2-api/';
+// #endif
export { BASE_URL };
// 默认配置
@@ -25,7 +32,16 @@ export function buildUrl(url) {
if (/^https?:\/\//i.test(url)) return url;
const base = BASE_URL.replace(/\/+$/, '');
const path = url.replace(/^\/+/, '');
- return path ? `${base}/${path}` : base;
+ let full = path ? `${base}/${path}` : base;
+ // #ifdef H5
+ // uni-app H5 的 image 组件会将以 / 开头的路径重写为 routerBase + path
+ // (如 /assets/x.png → /h5/assets/x.png),导致 devServer 代理匹配不到而 404。
+ // 这里拼成完整绝对地址(https://host/assets/x.png),image 组件对 http(s):// 开头的路径原样放行
+ if (typeof window !== 'undefined' && full.charAt(0) === '/') {
+ full = window.location.origin + full;
+ }
+ // #endif
+ return full;
}
// 统一取 header(注入 token)
diff --git a/manifest.json b/manifest.json
index f723e92..88efd2a 100644
--- a/manifest.json
+++ b/manifest.json
@@ -69,8 +69,34 @@
"vueVersion" : "2",
"h5" : {
"devServer" : {
- "https" : false,
- "host" : "192.168.124.19"
+ "https" : true,
+ "host" : "192.168.124.19",
+ "proxy" : {
+ "/public" : {
+ "target" : "https://official.inkreach.cc",
+ "changeOrigin" : true,
+ "pathRewrite" : {
+ "^/public" : "/v2-api/public"
+ }
+ },
+ "/assets" : {
+ "target" : "https://official.inkreach.cc",
+ "changeOrigin" : true,
+ "pathRewrite" : {
+ "^/assets" : "/v2-api/assets"
+ }
+ }
+ }
+ },
+ "router" : {
+ "mode" : "history",
+ "base" : "/h5/"
+ },
+ "title" : "印美达POD供应链",
+ "optimization" : {
+ "treeShaking" : {
+ "enable" : true
+ }
}
}
}
diff --git a/pages/index/components/CompanyIntro.vue b/pages/index/components/CompanyIntro.vue
index 6abd06f..2a2f392 100644
--- a/pages/index/components/CompanyIntro.vue
+++ b/pages/index/components/CompanyIntro.vue
@@ -222,7 +222,8 @@ export default {
line-height: 1.5;
}
-/* 核心能力卡片 - 自适应2列(避免宽度+间距同时固定导致挤压) */
+/* 核心能力卡片 - 固定2列(345rpx×2 + 间距20rpx + 容器内边距40rpx = 750rpx)
+ 注意:不要用 flex-basis + calc(50%),部分手机浏览器解析失败会导致单列 */
.ability-grid {
padding: 0 20rpx;
display: flex;
@@ -231,7 +232,7 @@ export default {
margin-bottom: 60rpx;
}
.ability-card {
- flex: 1 1 calc(50% - 10rpx);
+ width: 345rpx;
min-width: 0;
height: 180rpx;
background: #ffffff;
diff --git a/pages/index/components/HeroBanner.vue b/pages/index/components/HeroBanner.vue
index 013e083..aca75da 100644
--- a/pages/index/components/HeroBanner.vue
+++ b/pages/index/components/HeroBanner.vue
@@ -165,7 +165,7 @@ export default {
display: block;
margin: 12rpx auto 0;
width: fit-content;
- max-width: calc(100% - 80rpx);
+ max-width: 670rpx; /* 750 - 两侧留白 80rpx,避免用 calc(部分手机浏览器解析失败) */
font-size: 20rpx;
color: #000000;
line-height: 1.4;
diff --git a/pages/index/components/PodCatalog.vue b/pages/index/components/PodCatalog.vue
index b587d5d..6b0d803 100644
--- a/pages/index/components/PodCatalog.vue
+++ b/pages/index/components/PodCatalog.vue
@@ -127,7 +127,8 @@ export default {
color: #ffffff;
}
-/* 产品网格 - 自适应2列(避免宽度+间距同时固定导致挤压) */
+/* 产品网格 - 固定2列(345rpx×2 + 间距20rpx + 容器内边距40rpx = 750rpx)
+ 注意:不要用 flex-basis + calc(50%),部分手机浏览器解析失败会导致单列 */
.pod-grid {
padding: 0 20rpx;
display: flex;
@@ -135,7 +136,7 @@ export default {
gap: 20rpx;
}
.pod-card {
- flex: 1 1 calc(50% - 10rpx);
+ width: 345rpx;
min-width: 0;
background: #ffffff;
border-radius: 16rpx;
diff --git a/pages/index/components/WhyChoose.vue b/pages/index/components/WhyChoose.vue
index 5d3fe4d..0ddf274 100644
--- a/pages/index/components/WhyChoose.vue
+++ b/pages/index/components/WhyChoose.vue
@@ -46,7 +46,8 @@ export default {
margin-bottom: 64rpx;
}
-/* 2x2 网格 - 自适应2列(避免宽度+间距同时固定导致挤压) */
+/* 2x2 网格 - 固定2列(345rpx×2 + 间距20rpx + 容器内边距40rpx = 750rpx)
+ 注意:不要用 flex-basis + calc(50%),部分手机浏览器解析失败会导致单列 */
.why-grid {
padding: 0 20rpx;
display: flex;
@@ -54,7 +55,7 @@ export default {
gap: 20rpx;
}
.why-card {
- flex: 1 1 calc(50% - 10rpx);
+ width: 345rpx;
min-width: 0;
height: 160rpx;
background: #ffffff;
diff --git a/pages/product-detail/components/OptionSelector.vue b/pages/product-detail/components/OptionSelector.vue
new file mode 100644
index 0000000..e84ce2e
--- /dev/null
+++ b/pages/product-detail/components/OptionSelector.vue
@@ -0,0 +1,86 @@
+
+
+
+ {{ title }}
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
diff --git a/pages/product-detail/components/ProductInfo.vue b/pages/product-detail/components/ProductInfo.vue
index cc93152..802cd4a 100644
--- a/pages/product-detail/components/ProductInfo.vue
+++ b/pages/product-detail/components/ProductInfo.vue
@@ -1,19 +1,19 @@
-
+
{{ info.name }}
-
+
-
- SKU:
- {{ info.sku }}
-
国家:
{{ info.country }}
+
+ 发货时效:
+ {{ info.shipTime }}
+
@@ -22,17 +22,6 @@
{{ info.price.integer }}
{{ info.price.decimal }}
-
-
-
-
-
-
-
- {{ item.label }}
- {{ item.value }}
-
-
@@ -63,7 +52,7 @@ export default {
line-height: 50rpx; /* 26px */
}
-/* SKU / 国家 同行 */
+/* 国家 / 发货时效 同行 */
.meta-row {
display: flex;
margin-top: 10rpx;
@@ -104,34 +93,4 @@ export default {
font-weight: 700;
line-height: 1;
}
-
-/* 分隔线 */
-.divider {
- height: 1rpx; /* 0.5px */
- background: #f0f0f0;
- margin-top: 26rpx; /* 13px 间隙 */
-}
-
-/* 4 列快捷信息 */
-.quick-info {
- display: flex;
- margin-top: 20rpx; /* 10px */
-}
-.quick-col {
- flex: 1;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 8rpx;
-}
-.quick-label {
- font-size: 19rpx; /* 10px */
- color: #979797;
- line-height: 27rpx;
-}
-.quick-value {
- font-size: 25rpx; /* 13px */
- color: #000000;
- line-height: 33rpx;
-}
diff --git a/pages/product-detail/components/SizeSelector.vue b/pages/product-detail/components/SizeSelector.vue
index f9c869d..18175c4 100644
--- a/pages/product-detail/components/SizeSelector.vue
+++ b/pages/product-detail/components/SizeSelector.vue
@@ -84,8 +84,10 @@ export default {
/* 单个尺码按钮 44x34,圆角 4px,背景 #F7F7F7 */
.size-item {
- width: 85rpx; /* 44px */
+ min-width: 85rpx; /* 44px */
height: 65rpx; /* 34px */
+ padding: 0 20rpx;
+ box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
diff --git a/pages/product-detail/product-detail.vue b/pages/product-detail/product-detail.vue
index cacde54..447a220 100644
--- a/pages/product-detail/product-detail.vue
+++ b/pages/product-detail/product-detail.vue
@@ -4,7 +4,7 @@
-
+
@@ -29,6 +29,42 @@
@select="selectSize"
/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -54,6 +90,7 @@ import DetailSwiper from './components/DetailSwiper.vue'
import ProductInfo from './components/ProductInfo.vue'
import ColorSelector from './components/ColorSelector.vue'
import SizeSelector from './components/SizeSelector.vue'
+import OptionSelector from './components/OptionSelector.vue'
import ParamsTable from './components/ParamsTable.vue'
import SizeChart from './components/SizeChart.vue'
import PackagingSpec from './components/PackagingSpec.vue'
@@ -66,6 +103,7 @@ export default {
ProductInfo,
ColorSelector,
SizeSelector,
+ OptionSelector,
ParamsTable,
SizeChart,
PackagingSpec
@@ -77,10 +115,9 @@ export default {
loading: false,
product: {
name: '',
- sku: '',
country: '',
- price: { symbol: '¥', integer: '0', decimal: '' },
- quickInfo: []
+ shipTime: '',
+ price: { symbol: '¥', integer: '0', decimal: '' }
},
productImages: [],
colors: [],
@@ -95,7 +132,12 @@ export default {
mediaByColor: {},
// 默认轮播图(无颜色或 mediaByColor 缺失时使用,来自 detail.media.images)
defaultImages: [],
- packaging: null
+ packaging: null,
+ // 产品族价格矩阵(来自 detail.family,含 尺码×颜色×工艺×物流×印花 五维)
+ family: null,
+ selectedLogistics: '',
+ selectedPrintCount: '',
+ selectedCraft: ''
}
},
onLoad(options) {
@@ -129,21 +171,12 @@ export default {
* 将接口详情数据映射为各组件所需结构
*/
applyDetail(detail) {
- // 1. 产品信息:名称 / SKU / 国家 / 价格 / 4 列快捷信息
+ // 1. 产品信息:名称 / 国家 / 发货时效 / 价格
this.product = {
name: detail.goodName || '—',
- sku: detail.productCode || '—',
country: (detail.country && detail.country.countryName) || '—',
- price: this.parsePrice(detail.price),
- quickInfo: [
- { label: '生产工艺', value: (detail.details && detail.details.productionProcess) || '—' },
- {
- label: '发货时效',
- value: detail.productionCycleHours ? detail.productionCycleHours + '小时' : '—'
- },
- { label: '印花', value: (detail.details && detail.details.designArea) || '—' },
- { label: '产品编码', value: detail.productCode || '—' }
- ]
+ shipTime: detail.productionCycleHours ? detail.productionCycleHours + '小时' : '—',
+ price: this.parsePrice(detail.price)
}
// 2. 图片画廊:
@@ -195,10 +228,17 @@ export default {
// 8. 包装规格(从 variants 按尺码去重生成)
this.packaging = this.buildPackaging(this.variants)
- // 9. 初始价格(按默认选中的 尺码+颜色 匹配变体价格)
+ // 9. 产品族价格矩阵:解析 family 五维选项(物流/印花/工艺),默认选中第一项
+ const family = detail.family && detail.family.priceMatrix ? detail.family : null
+ this.family = family
+ this.selectedLogistics = family && family.logistics && family.logistics.length ? family.logistics[0] : ''
+ this.selectedPrintCount = family && family.printCounts && family.printCounts.length ? family.printCounts[0] : ''
+ this.selectedCraft = family && family.crafts && family.crafts.length ? family.crafts[0] : ''
+
+ // 10. 初始价格(family 优先按价格矩阵取,否则按默认选中的 尺码+颜色 匹配变体价格)
this.syncPriceByVariant()
- // 10. 初始轮播图(按默认选中颜色取 mediaByColor,无则回退 defaultImages)
+ // 11. 初始轮播图(按默认选中颜色取 mediaByColor,无则回退 defaultImages)
this.syncImagesByColor()
},
@@ -372,7 +412,41 @@ export default {
) || null
)
},
+ /**
+ * 在 family.priceMatrix.rows 中查找当前选中组合(尺码+颜色+物流+印花+工艺)的价格行
+ * 未选全或无 family 时返回 null
+ */
+ findMatrixRow() {
+ const f = this.family
+ if (!f || !f.priceMatrix || !Array.isArray(f.priceMatrix.rows) || !f.priceMatrix.rows.length) {
+ return null
+ }
+ const sizeName = this.selectedSize
+ const colorId = this.selectedColorId
+ return (
+ f.priceMatrix.rows.find(
+ (row) =>
+ row &&
+ (!sizeName || String(row.sizeName) === String(sizeName)) &&
+ (!colorId || String(row.colorId) === String(colorId)) &&
+ (!this.selectedLogistics || String(row.logistics) === String(this.selectedLogistics)) &&
+ (!this.selectedPrintCount || String(row.printCount) === String(this.selectedPrintCount)) &&
+ (!this.selectedCraft || String(row.craft) === String(this.selectedCraft))
+ ) || null
+ )
+ },
+
+ /**
+ * 根据当前选中组合同步价格:
+ * - 有 family 价格矩阵:按 尺码+颜色+物流+印花+工艺 匹配矩阵行
+ * - 否则回退:按 尺码+颜色 匹配 variant
+ */
syncPriceByVariant() {
+ const row = this.findMatrixRow()
+ if (row && row.price != null) {
+ this.$set(this.product, 'price', this.parsePrice(row.price))
+ return
+ }
const v = this.findVariant()
if (v && v.price != null) {
this.$set(this.product, 'price', this.parsePrice(v.price))
@@ -395,20 +469,62 @@ export default {
selectSize(size) {
this.selectedSize = size
this.syncPriceByVariant()
+ },
+
+ /**
+ * 物流/印花/工艺选择:来自 family 价格矩阵维度,选中后联动价格
+ */
+ selectLogistics(value) {
+ this.selectedLogistics = value
+ this.syncPriceByVariant()
+ },
+ selectPrintCount(value) {
+ this.selectedPrintCount = value
+ this.syncPriceByVariant()
+ },
+ selectCraft(value) {
+ this.selectedCraft = value
+ this.syncPriceByVariant()
}
},
computed: {
+ /**
+ * 可用性判断数据源:有 family 价格矩阵时用矩阵行,否则用 variants
+ * 两者都含 sizeName / colorId 字段
+ */
+ availabilitySource() {
+ const f = this.family
+ if (f && f.priceMatrix && Array.isArray(f.priceMatrix.rows) && f.priceMatrix.rows.length) {
+ return f.priceMatrix.rows
+ }
+ return this.variants
+ },
+
+ /**
+ * 物流/印花/工艺选项列表(来自 family)
+ */
+ logisticsOptions() {
+ return this.family && Array.isArray(this.family.logistics) ? this.family.logistics : []
+ },
+ printCountOptions() {
+ return this.family && Array.isArray(this.family.printCounts) ? this.family.printCounts : []
+ },
+ craftOptions() {
+ return this.family && Array.isArray(this.family.crafts) ? this.family.crafts : []
+ },
+
/**
* 颜色列表 + 可用态标记(给 ColorSelector 用,用于 disabled 样式)
* - 如果没选尺码:所有已 enabled 的颜色都可选
- * - 如果已选尺码:仅保留存在 (colorId + sizeName) 组合且 variant.enabled 的颜色
+ * - 如果已选尺码:仅保留存在 (colorId + sizeName) 组合的项
*/
colorListWithAvailability() {
const sizeName = this.selectedSize
+ const source = this.availabilitySource
return this.colors.map((c) => {
let enabled = c.enabled !== false
- if (sizeName && this.variants.length) {
- enabled = this.variants.some(
+ if (sizeName && source.length) {
+ enabled = source.some(
(v) =>
v &&
v.enabled !== false &&
@@ -425,10 +541,11 @@ export default {
*/
sizeListWithAvailability() {
const colorId = this.selectedColorId
+ const source = this.availabilitySource
return this.sizes.map((s) => {
let enabled = true
- if (colorId && this.variants.length) {
- enabled = this.variants.some(
+ if (colorId && source.length) {
+ enabled = source.some(
(v) =>
v &&
v.enabled !== false &&
diff --git a/pages/products/components/ProductGrid.vue b/pages/products/components/ProductGrid.vue
index edf50bb..81809b7 100644
--- a/pages/products/components/ProductGrid.vue
+++ b/pages/products/components/ProductGrid.vue
@@ -35,21 +35,13 @@
class="product-image"
:src="item.image"
mode="aspectFill"
+ lazy-load
v-if="item.image"
>
{{ item.name }}
-
-
- {{ tag.text }}
-
¥{{ item.price }}
@@ -188,19 +180,6 @@ export default {
width: 100%;
height: 100%;
}
-/* 标签 - 位于商品名下方、价格上方,横向排列 */
-.product-badges {
- display: flex;
- flex-wrap: wrap;
- gap: 8rpx;
- margin-top: 8rpx;
-}
-.product-badge {
- padding: 4rpx 12rpx;
- border-radius: 8rpx;
- font-size: 18rpx;
-}
-
/* 商品信息 - 内边距 10px */
.product-info {
padding: 20rpx;
diff --git a/pages/products/products.vue b/pages/products/products.vue
index 8c34e3e..8c82bde 100644
--- a/pages/products/products.vue
+++ b/pages/products/products.vue
@@ -145,9 +145,12 @@ export default {
} catch (e) {
this.windowHeight = 667
}
+ // 首屏商品请求与基础数据并行发出(goods 无筛选时不依赖国家/分类数据),
+ // 避免串行等待拖慢首屏(goods 是最慢的接口,应最先发出)
+ const goodsPromise = this.loadGoods(1)
await this.initData()
this.loadSubCategories('all')
- await this.loadGoods(1)
+ await goodsPromise
// 首屏数据渲染完成后,子组件会通过 mounted 自行测量并 emit 高度
// 这里兜底触发一次重算(防止某些端 mounted 比父组件 onLoad 晚)
this.recomputeScrollHeight()
@@ -252,14 +255,7 @@ export default {
id: item.goodId,
name: truncateProductTitle(item.goodName),
price: item.price,
- image: item.image,
- tags: (item.tags || []).map(function (t) {
- return {
- text: t.tagName,
- bg: t.tagColor,
- color: t.tagFontColor
- }
- })
+ image: item.image
}))
const total = data.total || 0
diff --git a/static/images/home/Rectangle_178_1426_1248.png b/static/images/home/Rectangle_178_1426_1248.png
index 2d6d3d8..9bfcdf5 100644
Binary files a/static/images/home/Rectangle_178_1426_1248.png and b/static/images/home/Rectangle_178_1426_1248.png differ
diff --git a/static/images/home/Rectangle_181_1455_1450.jpg b/static/images/home/Rectangle_181_1455_1450.jpg
index 4ccddd7..a7a1e8c 100644
Binary files a/static/images/home/Rectangle_181_1455_1450.jpg and b/static/images/home/Rectangle_181_1455_1450.jpg differ
diff --git a/static/images/home/Rectangle_181_1455_1456.jpg b/static/images/home/Rectangle_181_1455_1456.jpg
index 4ccddd7..a7a1e8c 100644
Binary files a/static/images/home/Rectangle_181_1455_1456.jpg and b/static/images/home/Rectangle_181_1455_1456.jpg differ
diff --git a/static/images/home/Rectangle_187_1470_1653.jpg b/static/images/home/Rectangle_187_1470_1653.jpg
index 96957bc..29dac9a 100644
Binary files a/static/images/home/Rectangle_187_1470_1653.jpg and b/static/images/home/Rectangle_187_1470_1653.jpg differ
diff --git a/static/images/home/Rectangle_63_1446_1437.jpg b/static/images/home/Rectangle_63_1446_1437.jpg
index 4ccddd7..a7a1e8c 100644
Binary files a/static/images/home/Rectangle_63_1446_1437.jpg and b/static/images/home/Rectangle_63_1446_1437.jpg differ
diff --git a/static/images/home/Rectangle_63_1455_1461.jpg b/static/images/home/Rectangle_63_1455_1461.jpg
index 4ccddd7..a7a1e8c 100644
Binary files a/static/images/home/Rectangle_63_1455_1461.jpg and b/static/images/home/Rectangle_63_1455_1461.jpg differ
diff --git a/static/images/home/contact_us.png b/static/images/home/contact_us.png
index 7f41f14..daa5501 100644
Binary files a/static/images/home/contact_us.png and b/static/images/home/contact_us.png differ
diff --git a/static/images/home/feat_auto.jpg b/static/images/home/feat_auto.jpg
index 9bb6de6..d839f64 100644
Binary files a/static/images/home/feat_auto.jpg and b/static/images/home/feat_auto.jpg differ
diff --git a/static/images/home/feat_design.jpg b/static/images/home/feat_design.jpg
index 133872f..5654a73 100644
Binary files a/static/images/home/feat_design.jpg and b/static/images/home/feat_design.jpg differ
diff --git a/static/images/home/feat_global.jpg b/static/images/home/feat_global.jpg
index 7ee7107..9c573f3 100644
Binary files a/static/images/home/feat_global.jpg and b/static/images/home/feat_global.jpg differ
diff --git a/static/images/home/feat_store.jpg b/static/images/home/feat_store.jpg
index 2216392..0281d5c 100644
Binary files a/static/images/home/feat_store.jpg and b/static/images/home/feat_store.jpg differ
diff --git a/static/images/home/image_26_1496_1761.jpg b/static/images/home/image_26_1496_1761.jpg
index 0a63ab6..e001c84 100644
Binary files a/static/images/home/image_26_1496_1761.jpg and b/static/images/home/image_26_1496_1761.jpg differ
diff --git a/static/images/home/image_28_1498_1767.png b/static/images/home/image_28_1498_1767.png
index 535a61a..46ebf68 100644
Binary files a/static/images/home/image_28_1498_1767.png and b/static/images/home/image_28_1498_1767.png differ
diff --git a/static/images/home/step_01_select.jpg b/static/images/home/step_01_select.jpg
index 9ae0411..96ab872 100644
Binary files a/static/images/home/step_01_select.jpg and b/static/images/home/step_01_select.jpg differ
diff --git a/static/images/home/step_02_upload.jpg b/static/images/home/step_02_upload.jpg
index 74da055..6487049 100644
Binary files a/static/images/home/step_02_upload.jpg and b/static/images/home/step_02_upload.jpg differ
diff --git a/static/images/home/step_04_produce.jpg b/static/images/home/step_04_produce.jpg
index f548abe..f11a44d 100644
Binary files a/static/images/home/step_04_produce.jpg and b/static/images/home/step_04_produce.jpg differ