Compare commits

...

33 Commits

Author SHA1 Message Date
c9aa4c75ae chore:template==99左侧一级分类选中时的指示器是一个红色的垂直竖条 2026-07-21 12:00:08 +08:00
a1db706072 chore:一级标题选中时加粗字体 2026-07-21 11:55:48 +08:00
276120162a chore:二级分类按钮是一致的颜色 2026-07-21 11:45:12 +08:00
f523122889 chore:99的视图按钮单列商品正常显示立即咨询 2026-07-21 11:40:55 +08:00
cea0e36d5d chore:视图改变 2026-07-21 10:42:21 +08:00
0169f25ca5 chore:H5的二级分类按钮下框对齐 2026-07-21 10:31:46 +08:00
3939d15f2a style(diy-category-item): 调整不同端的分类按钮容器内边距
针对H5和非H5平台分别适配二级分类按钮的内边距样式,保证页面布局在各端显示一致
2026-07-21 10:30:41 +08:00
fd4edc177d chore(config): update local dev config and env entries
1. add local-18 artificial crystal dev environment config
2. update domain for 2482 low-altitude economy env
3. adjust default active env to 2821 and add local-18 comment option
2026-07-21 09:54:13 +08:00
f96fee60d3 chore: 更新本地配置并补全页面导航栏标题配置
1.  调整.local.config.js中的测试平台配置项,替换原有54号配置为18号并新增48号配置,同时修改默认启用的低空经济项目id为2482
2.  在pages.json的所有页面配置中统一添加navigationBarTitleText空配置项,修正部分页面的APP端导航样式适配
2026-07-21 09:52:51 +08:00
d57b69d7cb Merge branch 'dev/1.0' of https://git.aigc-quickapp.com/Uniapp/lucky_shop into dev/1.0 2026-07-21 09:43:31 +08:00
d875642d99 feat(diy-category): 新增模板1和模板4分类页面样式与功能
1. 新增模板4:顶部图片搜索框、左侧一级分类列表、底部购物车栏
2. 新增模板1:仿京东淘宝分类布局,支持三级分类分组展示商品
3. 优化模板1样式:调整分类栏、商品列表、标签样式
4. 修复模板切换时的搜索框重复渲染问题
5. 新增模板1、3、4的专属UI样式和交互逻辑
2026-07-21 09:41:25 +08:00
0bcb5e7f4a fix(common/js/diy.js): 修正错误处理中使用的错误key函数
将diyErrorKey替换为httpErrorKey来适配HTTP状态码错误处理逻辑
2026-07-21 09:33:48 +08:00
65013a35f0 chore: 2026-07-21 09:20:48 +08:00
31daebc83b Merge remote-tracking branch 'origin/dev/1.0' into yuanlaide6 2026-07-21 08:50:19 +08:00
b8894ea2dc chore: 2026-07-20 18:00:04 +08:00
ff130bbf15 chore:改了二级分类的下边框 2026-07-20 17:29:15 +08:00
3a88941828 build(vue.config): 修复生产构建门禁的node执行路径问题
将执行test-gate的命令从硬编码的"node"改为使用process.execPath,避免依赖PATH环境变量查找node二进制,解决HBuilderX等IDE下PATH无node条目导致的构建失败问题,同时打印当前使用的node版本
2026-07-20 15:38:15 +08:00
8e31b9e327 chore:模板4的二级分类按钮正常 2026-07-18 11:26:27 +08:00
460dbcb447 chore:二级分类按钮显示正常 2026-07-18 11:15:58 +08:00
81ecd45de4 chore:二级分类按钮显示 2026-07-18 11:13:34 +08:00
d8816bbe42 chore:还原到模板3二级分类按钮正常 2026-07-18 10:31:35 +08:00
b410597d9c chore 2026-07-18 10:15:27 +08:00
987e75c65a Merge branch 'dev/1.0' of http://git.aigc-quickapp.com/Uniapp/lucky_shop into yuanlaide3 2026-07-18 09:07:28 +08:00
ac8c461546 chore:二级分类按钮点击一会能切一会不能切 2026-07-18 08:58:52 +08:00
a4d8023e1f chore:二级分类按钮正常 2026-07-17 18:17:45 +08:00
a4c218fecc chore: 2026-07-17 16:06:07 +08:00
0d820b04f7 chore:template == 4页面后台选择二级分类正常 2026-07-17 15:53:09 +08:00
6d1bdb5d8b chore:删除右侧上面的和一级标题一样的字 2026-07-17 15:07:19 +08:00
73b7b2a8df chore:template == 3的模板正常 2026-07-17 14:54:44 +08:00
b2063418fb chore:template == 1的模板正常 2026-07-17 14:01:40 +08:00
21c342e389 chore: 2026-07-17 11:56:44 +08:00
185d4914a6 chore:页面显示正常1模板 2026-07-17 10:21:40 +08:00
e3bbb7c77a chore:商品分类页面符合template==1 2026-07-17 09:58:25 +08:00
6 changed files with 4700 additions and 3539 deletions

View File

@@ -63,6 +63,10 @@ const localDevConfig = ({
uniacid: 11,
domain: 'http://localhost:8050/',
},
'local-18': { // 人工晶体
uniacid: 18,
domain: 'http://localhost:8050/',
},
'100': {
uniacid: 100,
domain: 'https://5g.aigc-quickapp.com/',
@@ -117,7 +121,7 @@ const localDevConfig = ({
},
'2482': { // 低空经济
uniacid: 2482,
domain: 'https://xcx6.aigc-quickapp.com/',
domain: 'https://xcx3.aigc-quickapp.com/',
},
'2485': { // 验证电子表单前后端数据一致性
uniacid: 2485,
@@ -139,11 +143,15 @@ const localDevConfig = ({
uniacid: 48,
domain: 'https://test.aigc-quickapp.com',
},
'54': { // 测试平台
uniacid: 54,
domain: 'https://test.aigc-quickapp.com',
},
// })['5g-237']; // 商品分类,没有立即咨询文字
// })['5g-97']; // 大宗商品贸易,支持自定义字体大小、颜色
// })['5g-50']; // 燃气安全产品机器人,购物车无反应
// })['2821']; // 离型纸硅油纸
})['2482']; // 低空经济
})['2821']; // 离型纸硅油纸
// })['2482']; // 低空经济
// })['48']; // 离型纸硅油纸
// })['2485']; // 验证电子表单前后端数据一致性
// })['5g-11']; // 数码
@@ -151,6 +159,7 @@ const localDevConfig = ({
// })['2811']; // 选择要使用的环境配置
// })['local-11']; // 选择要使用的环境配置
// })['local-1']; // 本地
// })['local-18']; // 本地-人工晶体-有商品
// })['local-2']; // 本地
// })['local-11']; // 本地
// })['1-test']; // test

View File

@@ -332,7 +332,7 @@ export default {
// 服务端明确返回的业务消息(字符串):直接透传
if (info.type === 'business') return info.raw;
const key = diyErrorKey(info.type);
const key = httpErrorKey(info.type);
let reason = this.$lang(key);
// HTTP 状态码类错误:把 statusCode 拼到 reason 后面

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,15 @@
<template>
<view data-component-name="diy-category" :class="['category-page-wrap', 'category-template-' + value.template]"
:style="{ height: 'calc(100vh - ' + tabBarHeight + ')' }">
<!-- 模板4: 顶部搜索框(图片样式) -->
<view class="search-box search-box-template-4" v-if="value.template == 4 && value.search"
@tap.stop="onClickSearch()">
<view class="search-content">
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('components-diy.diy-search.searchBtn')"
confirm-type="search" @tap.stop="onClickSearch()" disabled="true" />
<text class="iconfont icon-sousuo3"></text>
</view>
</view>
<!-- #ifdef MP-WEIXIN -->
<!-- <block v-if="value.template == 4">
<view class="search-box" v-if="value.search" @tap.stop="$util.redirectTo('/pages_tool/goods/search')" :style="navbarInnerStyle">
@@ -28,7 +37,7 @@
<!-- #endif -->
<!-- #ifdef H5 || MP-HARMONY -->
<view class="search-box" v-if="value.search" @tap.stop="onClickSearch()">
<view class="search-box" v-if="value.search && value.template != 4" @tap.stop="onClickSearch()">
<view class="search-content">
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('components-diy.diy-search.searchBtn')" confirm-type="search"
@tap.stop="onClickSearch()" disabled="true" />
@@ -95,12 +104,25 @@
<block v-if="categoryTree.length">
<scroll-view scroll-y="true" class="tree-wrap">
<view class="category-item-wrap">
<view class="category-item" v-for="(item, index) in categoryTree" :key="index" :class="[
{ select: select == index },
{ 'border-bottom': value.template == 4 && select + 1 === index },
{ 'border-top': value.template == 4 && select - 1 === index }
]" @tap.stop="switchOneCategory(index)">
<view class="">{{ item.category_name }}</view>
<!-- 模板4: 左侧显示一级分类(顶部轮播中的分类) -->
<view v-if="value.template == 4">
<view class="category-item" v-for="(item, index) in templateFourData" :key="'one-' + index" :class="[
{ select: oneCategorySelect == index },
{ 'border-bottom': oneCategorySelect + 1 === index },
{ 'border-top': oneCategorySelect - 1 === index }
]" @tap.stop="templateFourOneFn(index)">
<view class="">{{ item.category_name }}</view>
</view>
</view>
<!-- 其他模板: 左侧显示二级分类 -->
<view v-else>
<view class="category-item" v-for="(item, index) in categoryTree" :key="index" :class="[
{ select: select == index },
{ 'border-bottom': value.template == 4 && select + 1 === index },
{ 'border-top': value.template == 4 && select - 1 === index }
]" @tap.stop="switchOneCategory(index)">
<view class="">{{ item.category_name }}</view>
</view>
</view>
</view>
</scroll-view>
@@ -116,23 +138,36 @@
:lastSecondCategoryName="lastSecondCategoryName"
@tologin="toLogin" @selectsku="selectSku($event, 0)" @addCart="addCartPoint"
@saveSecondCategory="saveSecondCategoryName"></diy-category-item>
</scroll-view>
</scroll-view>
<scroll-view scroll-y="true" class="content-wrap" v-else-if="value.template == 1 || value.template == 99 || loadType == 'all'"
ref="contentWrap" :scroll-into-view="categoryId" :scroll-with-animation="true"
@scroll="listenScroll" @touchstart="touchStart" :refresher-enabled="true"
refresher-default-style="none" :refresher-triggered="triggered" @refresherrefresh="onRefresh"
@refresherrestore="onRestore">
<view class="child-category" v-for="(item, index) in categoryTree" :key="index"
:id="'category-' + index" :style="{ display: value.template == 99 && select != index ? 'none' : 'block' }">
<diy-category-item :category="item" :value="value" ref="categoryItem" :index="index"
:select="select" :oneCategorySelect="oneCategorySelect" :isList="isList"
:lastSecondCategoryName="value.template == 99 ? lastSecondCategoryName : ''"
@tologin="toLogin" @selectsku="selectSku($event, index)" @addCart="addCartPoint"
@loadfinish="getHeightArea" @saveSecondCategory="saveSecondCategoryName"></diy-category-item>
</view>
<view class="end-tips" ref="endTips" :style="{ opacity: endTips }">{{ $lang('components-diy.diy-category.reachedBottom') }}</view>
</scroll-view>
<!-- 模板1: 仿京东/淘宝商品分类 - 按 template 2 的 part-load 逻辑, 切换一级分类时只渲染选中分类的子分类网格 -->
<scroll-view scroll-y="true" class="content-wrap" v-else-if="value.template == 1"
:refresher-enabled="true" refresher-default-style="none"
:refresher-triggered="triggered" @refresherrefresh="onRefresh" @refresherrestore="onRestore">
<view class="child-category-wrap" v-for="(item, index) in categoryTree" :key="index"
:id="'category-' + index" :style="{ display: select == index ? 'block' : 'none' }">
<diy-category-item :category="item" :value="value" ref="categoryItem" :index="index"
:select="select" :oneCategorySelect="oneCategorySelect" :isList="isList"
@tologin="toLogin" @selectsku="selectSku($event, index)" @addCart="addCartPoint"
@saveSecondCategory="saveSecondCategoryName"></diy-category-item>
</view>
</scroll-view>
<scroll-view scroll-y="true" class="content-wrap" v-else-if="value.template == 99 || loadType == 'all'"
ref="contentWrap" :scroll-into-view="categoryId" :scroll-with-animation="true"
@scroll="listenScroll" @touchstart="touchStart" :refresher-enabled="true"
refresher-default-style="none" :refresher-triggered="triggered" @refresherrefresh="onRefresh"
@refresherrestore="onRestore">
<view class="child-category" v-for="(item, index) in categoryTree" :key="index"
:id="'category-' + index" :style="{ display: value.template == 99 && select != index ? 'none' : 'block' }">
<diy-category-item :category="item" :value="value" ref="categoryItem" :index="index"
:select="select" :oneCategorySelect="oneCategorySelect" :isList="isList"
:lastSecondCategoryName="value.template == 99 ? lastSecondCategoryName : ''"
@tologin="toLogin" @selectsku="selectSku($event, index)" @addCart="addCartPoint"
@loadfinish="getHeightArea" @saveSecondCategory="saveSecondCategoryName"></diy-category-item>
</view>
<view class="end-tips" ref="endTips" :style="{ opacity: endTips }">{{ $lang('components-diy.diy-category.reachedBottom') }}</view>
</scroll-view>
<view class="content-wrap"
v-if="(value.template == 2 || value.template == 3 || value.template == 4) && loadType == 'part'">
@@ -141,6 +176,7 @@
<diy-category-item :category="item" :value="value" ref="categoryItem" :index="index"
:last="index == categoryTree.length - 1 ? true : false" :select="select"
:oneCategorySelect="oneCategorySelect" :isList="isList" @tologin="toLogin"
:currentOneCategory="value.template == 4 && templateFourData[oneCategorySelect] ? templateFourData[oneCategorySelect] : null"
@selectsku="selectSku($event, index)" @addCart="addCartPoint"
@switch="switchOneCategory"></diy-category-item>
</view>
@@ -153,9 +189,12 @@
</view>
</view>
<!-- <view class="cart-box" v-if="(value.template == 2 || value.template == 4) && value.quickBuy && storeToken && categoryTree && categoryTree.length">
<!-- 模板4: 底部购物车栏 -->
<view class="cart-box cart-box-template-4"
v-if="value.template == 4 && value.quickBuy && storeToken && categoryTree && categoryTree.length">
<view class="left-wrap">
<view class="cart-icon" ref="cartIcon" :animation="cartAnimation" @tap.stop="$util.redirectTo('/pages_goods/cart')">
<view class="cart-icon" ref="cartIcon" :animation="cartAnimation"
@tap.stop="$util.redirectTo('/pages_goods/cart')">
<text class="iconfont icon-ziyuan1"></text>
<view class="num" v-if="cartNumber">{{ cartNumber < 99 ? cartNumber : '99+' }}</view>
</view>
@@ -166,9 +205,10 @@
<text class="unit font-size-tag price-font">.{{ cartTotalMoney[1] ? cartTotalMoney[1] : '00' }}</text>
</view>
</view>
<view class="right-wrap"><button type="primary" class="settlement-btn" @tap.stop="settlement">去结算</button>
<view class="right-wrap"><button type="primary" class="settlement-btn"
@tap.stop="settlement">去结算</button>
</view>
</view> -->
</view>
<view class="cart-point" v-for="(item, index) in carIconList" :key="index"
:style="{ left: item.left + 'px', top: item.top + 'px' }"></view>
@@ -229,6 +269,10 @@ export default {
created() {
this.getCategoryTree();
this.loadType = this.value.goodsLevel == 1 && this.value.loadType == 'all' ? 'all' : 'part';
// 模板4: 默认单列列表布局(图片样式)
if (this.value.template == 4) {
this.isList = true;
}
},
mounted() {
query = uni.createSelectorQuery().in(this);
@@ -1016,6 +1060,137 @@ export default {
// 风格四
.category-template-4 {
// 模板4 顶部搜索框(图片样式)
.search-box-template-4 {
padding: 20rpx 30rpx 16rpx;
background: #fff;
.search-content {
height: 64rpx;
background-color: #f5f5f5;
border-radius: 40rpx;
input {
height: 64rpx;
line-height: 64rpx;
padding: 0 30rpx 0 30rpx;
background: #f5f5f5;
border-radius: 40rpx;
font-size: 26rpx;
color: #333;
}
.iconfont {
font-size: 32rpx;
color: #89899a;
width: 70rpx;
text-align: center;
right: 10rpx;
}
}
}
// 模板4 底部购物车栏(图片样式)
.cart-box-template-4 {
height: 110rpx;
padding: 0 30rpx;
background: #fff;
border-top: 1rpx solid #f0f0f0;
position: relative;
z-index: 10;
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.04);
.left-wrap {
display: flex;
align-items: center;
}
.cart-icon {
width: 90rpx;
height: 90rpx;
position: relative;
margin-top: -10rpx;
.iconfont {
color: var(--btn-text-color, #fff);
width: 80rpx;
height: 80rpx;
background-color: $base-color;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.35);
}
.num {
position: absolute;
top: 0;
right: 0;
transform: translate(20%, 0);
display: inline-block;
box-sizing: border-box;
color: #fff;
line-height: 1.2;
text-align: center;
font-size: 24rpx;
padding: 0 8rpx;
min-width: 32rpx;
border-radius: 18rpx;
background-color: var(--price-color, #FF6B35);
border: 2rpx solid #fff;
}
}
.price {
margin-left: 16rpx;
display: flex;
align-items: baseline;
line-height: 1;
.title {
color: #333;
font-size: 28rpx;
}
.unit,
.money {
color: var(--price-color, #FF6B35);
font-weight: bold;
}
.unit {
font-size: 24rpx;
}
.money {
font-size: 36rpx;
}
}
.right-wrap {
display: flex;
align-items: center;
}
.settlement-btn {
margin: 0;
width: 220rpx;
font-weight: bold;
border-radius: 50rpx;
height: 80rpx;
line-height: 80rpx;
font-size: 30rpx;
background: $base-color;
color: var(--btn-text-color, #fff);
&::after {
border: none;
}
}
}
.search-box {
.search-content input {
background-color: #f1f1f1;
@@ -1243,6 +1418,125 @@ export default {
}
}
// ===================== 模板1 仿京东/淘宝商品分类样式 =====================
.category-template-1 {
.search-box {
padding: 20rpx 24rpx;
background: #fff;
.search-content {
background-color: #f5f5f5;
border-radius: 40rpx;
input {
background: #f5f5f5;
border-radius: 40rpx;
padding: 0 30rpx;
font-size: 26rpx;
color: #333;
}
.iconfont {
right: 16rpx;
color: #89899a;
font-size: 32rpx;
}
}
}
.content-box {
background: #fff;
.tree-wrap {
width: 170rpx;
background-color: #fff;
}
.right-flex-wrap {
background: #fff;
.content-wrap {
background: #fff;
}
}
}
// 穿透到子组件 diy-category-item: 调整 banner 横向内边距 + 分类网格容器为透明
::v-deep .category-adv {
padding: 0 20rpx;
margin-top: 0;
}
::v-deep .category-list {
margin: 0 20rpx;
border-radius: 0;
background: transparent;
padding: 0;
}
::v-deep .category-title-with-lines {
padding: 30rpx 0 20rpx;
}
// 穿透修复: 图片不需要灰色背景框,与目标图保持一致
::v-deep .img-box {
background: transparent !important;
border-radius: 0 !important;
width: 70% !important;
padding-bottom: 70% !important;
margin-bottom: 16rpx !important;
}
::v-deep .img-box image {
width: 100% !important;
height: 100% !important;
left: 0 !important;
top: 0 !important;
transform: none !important;
}
// 左侧分类栏样式
.tree-wrap .category-item {
padding: 28rpx 16rpx;
background-color: #fff;
border-radius: 0;
view {
color: #666;
font-size: 26rpx;
line-height: 1.3;
}
&.border-top,
&.border-bottom {
border-radius: 0;
}
&.select {
background: #fff;
view {
color: var(--base-color, #FF6B35);
font-weight: bold;
font-size: 28rpx;
}
&::before {
content: ' ';
width: 6rpx;
height: 32rpx;
background: var(--base-color, #FF6B35);
display: block;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
border-radius: 0 6rpx 6rpx 0;
}
}
}
}
// ===================== 模板99 商品分类样式 =====================
.category-template-99 {
.search-box {
@@ -1307,7 +1601,7 @@ export default {
background: #fff;
view {
color: #FF6B35;
color: #333;
font-weight: bold;
font-size: 28rpx;
}
@@ -1316,7 +1610,7 @@ export default {
content: ' ';
width: 6rpx;
height: 36rpx;
background: #FF6B35;
background: #FF4646;
display: block;
position: absolute;
left: 0;
@@ -1330,6 +1624,14 @@ export default {
.content-wrap {
background: #f5f5f5;
padding: 0;
// #ifdef H5
::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
background: transparent;
}
// #endif
}
.child-category {

View File

@@ -21,12 +21,18 @@ function _isProductionRelease() {
* 生产构建门禁: 同步执行 test-gate.js, 失败立即终止构建 (零产物)。
* 与 package.json 的 "prebuild": "npm run test" 互补 —— 那条只覆盖 npm run build,
* 本函数覆盖所有会 require vue.config.js 的入口 (HBuilderX / vue-cli-service / uni-cli)。
*
* 关键: 使用 process.execPath 而非字符串 "node",避免依赖 PATH 查找 node。
* 原因: HBuilderX 等 IDE 启动子进程时,PATH 中可能根本没有 node 条目 (本项目
* 就曾因 Machine PATH 残留指向 v14.21.3 的 Huawei node 而触发问题)。
* process.execPath 始终是"当前正在运行 vue.config.js 的那个 node 二进制",
* HBuilderX 自带 node (v22.22.2) ≥ 18,天然满足 test-gate 的版本要求。
*/
function _enforceTestGate() {
if (!_isProductionRelease()) return; // 开发态 / 运行态不拦截
console.log('\n🛡️ [test-gate] 检测到生产构建,先执行测试门禁...');
console.log(`\n🛡️ [test-gate] 检测到生产构建,先执行测试门禁 (${process.versions.node})...`);
try {
execSync('node scripts/test-gate.js', {
execSync(`"${process.execPath}" scripts/test-gate.js`, {
cwd: __dirname,
stdio: 'inherit',
});