Compare commits

...

8 Commits

11 changed files with 102 additions and 46 deletions

View File

@@ -244,7 +244,7 @@ const localDevConfig = ({
// })['2811']; // 本地
// })['5g-317']; // 电磁兼容
// })['5g-351']; //
})['1']; // 本地
})['5g-351']; // 本地
// })['local-2']; // 本地
// })['local-11']; // 本地
// })['1-test']; // test

View File

@@ -187,8 +187,9 @@
<!-- 三级分类(及更深层级): 一级分类标题 + 二级分类网格(图3) -->
<view :class="['category-title', '']">{{ category.category_name }}</view>
<view class="category-list">
<!-- 点二级分类进入商品列表: 带 list_style=grid 让列表页默认用竖版(双列宫格)视图, hide_sort=1 隐藏排序栏 -->
<view class="category-item" v-for="(one, oneIndex) in category.child_list" :key="oneIndex"
@tap.stop="$util.redirectTo('/pages_goods/list', { category_id: one.category_id })">
@tap.stop="$util.redirectTo('/pages_goods/list', { category_id: one.category_id, list_style: 'grid', hide_sort: 1 })">
<view class="img-box">
<image :src="categoryImg(one.image)" mode="aspectFit" @error="categoryImgError(2, oneIndex, -1)" show-menu-by-longpress />
</view>

View File

@@ -64,6 +64,13 @@
<diy-rich-text :value="item"></diy-rich-text>
</template>
<!-- #ifdef H5 -->
<template v-if="['Webview', 'WebView'].includes(item.componentName)">
<!-- 网页容器 -->
<diy-webview :value="item"></diy-webview>
</template>
<!-- #endif -->
<template v-if="item.componentName == 'HorzLine'">
<!-- 辅助线 -->
<diy-horz-line :value="item"></diy-horz-line>

View File

@@ -1,7 +1,7 @@
<template>
<!-- Vue 2 / uni-app 单根约束:用 view 包一层 -->
<view>
<view v-if="pageCount === 1 || need" class="fixed-box"
<view v-if="(pageCount === 1 || need) && hasVisibleFixBtn" class="fixed-box"
:style="[customContainerStyle, {
height: containerHeight,
bottom: containerBottom,
@@ -74,10 +74,11 @@
<text class="icox icox-kefu" v-if="!kefuimg"></text>
</button>
<!-- 电话按钮 -->
<!-- 电话按钮 (已隐藏: 仅 display:none, DOM/事件/逻辑全部保留, 把 hidePhoneBtn 改回 false 即恢复) -->
<view
v-if="fixBtnShow"
class="btn-item common-bg"
:class="{ 'btn-item-hide': hidePhoneBtn }"
:style="[btnItemStyle, { backgroundImage: phoneimg ? `url(${phoneimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]"
@click="call"
>
@@ -178,6 +179,9 @@ export default {
langDisplayMap: { ...LANG_DISPLAY_NAMES }, // 后端 langDisplayNames 会覆盖默认值
showLangPopup: false, // 语言选择弹窗显隐
customerService: null,
// 是否隐藏「电话」悬浮按钮: true = 隐藏(只隐藏不移除, DOM/点击事件/拨打逻辑都保留)
// 需要恢复时改回 false 即可, 或按需接后端配置
hidePhoneBtn: true,
// ============================================================
// 布局参数(从 App.vue 的 :root CSS 变量读取,权威源在 App.vue)
// 关键设计:
@@ -295,6 +299,12 @@ export default {
const customerService = createCustomerService(this);
return customerService.hasValidKefuConfig();
},
// 是否还有需要展示的浮动按钮:
// 电话按钮被隐藏、且没有语言切换/客服按钮时, 整个浮动按钮容器都不渲染,
// 避免留下一个透明的空容器挡住下面的页面内容点击
hasVisibleFixBtn() {
return !this.hidePhoneBtn || this.isLanguageSwitchEnabled || this.hasValidKefuConfig;
},
//根据显示的按钮数量动态计算容器高度
// 计算明细(buttonCount 按钮场景):
@@ -309,7 +319,8 @@ export default {
containerHeight() {
if (!this.fixBtnShow) return '320rpx';
let buttonCount = 1;
// 电话按钮隐藏时不再占用容器高度, 避免底部留白
let buttonCount = this.hidePhoneBtn ? 0 : 1;
if (this.isLanguageSwitchEnabled) buttonCount++;
if (this.showWeappSystemKefu) buttonCount++;
buttonCount++;
@@ -864,6 +875,11 @@ export default {
background-color: var(--hover-nav-bg-color);
}
/* 电话悬浮按钮隐藏态: 只隐藏不移除(DOM、点击事件、拨打逻辑都还在), 去掉该类即恢复 */
.btn-item-hide {
display: none !important;
}
.btn-item text {
font-size: 28rpx;
}

View File

@@ -13,7 +13,8 @@
"zion-uniapp-mp-load-package": "^1.0.13"
},
"dependencies": {
"jweixin-module": "^1.6.0"
"jweixin-module": "^1.6.0",
"playwright-core": "^1.63.0"
},
"uni-app": {
"scripts": {
@@ -89,4 +90,4 @@
}
}
}
}
}

View File

@@ -90,16 +90,17 @@ export default {
}
},
// 跟随 goodsSkuDetail 变化重新决定默认 Tab
// 解决 refreshGoodsSkuDetail 后续合并 pdf_url 时,Tab 已显示但 detailTab 仍为 'html' 的问题
// 默认展示排在前面的「商品详情」, 只有在没有图文详情、仅有说明书(pdf_url)时才默认选中说明书;
// 同时解决 refreshGoodsSkuDetail 后续合并数据时,Tab 已显示但 detailTab 未跟上的问题
goodsSkuDetail: {
deep: true,
immediate: true,
handler(newVal) {
if (!newVal) return;
if (newVal.pdf_url) {
this.detailTab = 'pdf';
} else if (newVal.goods_content) {
if (this.hasHtmlTab) {
this.detailTab = 'html';
} else if (newVal.pdf_url) {
this.detailTab = 'pdf';
}
}
}

View File

@@ -386,14 +386,15 @@
<!-- 商品详情 -->
<view v-if="hasDetailContent" class="goods-detail-tab">
<view class="detail-tab">
<view v-if="goodsSkuDetail.pdf_url" class="tab-item"
:class="{ active: detailTab === 'pdf' }" @click="switchDetailTab('pdf')">
<text class="tab-label">{{ $lang('goods.detail.document') }}</text>
</view>
<!-- 商品详情 在前, 产品说明书(文档详情) 在后 -->
<view v-if="hasHtmlTab" class="tab-item"
:class="{ active: detailTab === 'html' }" @click="switchDetailTab('html')">
<text class="tab-label">{{ $lang('goods.detail.details') }}</text>
</view>
<view v-if="goodsSkuDetail.pdf_url" class="tab-item"
:class="{ active: detailTab === 'pdf' }" @click="switchDetailTab('pdf')">
<text class="tab-label">{{ $lang('goods.detail.document') }}</text>
</view>
</view>
<view class="detail-content active">
<!-- PDF文档 -->

View File

@@ -1,6 +1,6 @@
<template>
<view class="content" :style="themeColor">
<view class="head-wrap">
<view class="head-wrap" :class="{ 'head-wrap-extend': hideSort }">
<!-- 搜索区域 -->
<view class="search-wrap uni-flex uni-row">
<view class="flex-item input-wrap">
@@ -12,8 +12,8 @@
@click="changeListStyle()"></view>
</view>
<!-- 排序 -->
<view class="sort-wrap">
<!-- 排序 (商品分类模板5进入时隐藏, 只隐藏不移除) -->
<view class="sort-wrap" :class="{ 'sort-wrap-hide': hideSort }">
<view class="comprehensive-wrap" :class="{ 'color-base-text': orderType === '' }"
@click="sortTabClick('')">
<text :class="{ 'color-base-text': orderType === '' }">{{ $lang('goods.list.Random') }}</text>
@@ -45,7 +45,7 @@
</view>
</view>
<mescroll-uni top="180" ref="mescroll" @getData="getGoodsList">
<mescroll-uni :top="hideSort ? 120 : 180" ref="mescroll" @getData="getGoodsList">
<block slot="list">
<view v-if="isList" class="goods-list single-column" :class="{ show: isList }">
<view class="goods-item margin-bottom" v-for="(item, index) in goodsList" :key="item.id"

View File

@@ -5,6 +5,13 @@
left: 0;
z-index: 1;
// 商品分类模板5进入时排序栏被隐藏, 让白色背景继续向下延伸(搜索框位置不变),
// 一直覆盖到第一行商品卡片顶部, 避免搜索框下方出现灰色空白条
&.head-wrap-extend {
// 26rpx = mescroll top(100rpx) - 搜索区高度(94rpx) + 列表 padding-top($margin-updown: 20rpx)
padding-bottom: 20rpx;
}
.search-wrap {
flex: 0.5;
padding: 30rpx 30rpx 0;
@@ -60,6 +67,12 @@
.sort-wrap {
display: flex;
padding: 10rpx 20rpx 10rpx 0;
// 商品分类模板5进入商品列表时隐藏排序栏(综合/销量/价格/筛选), 只隐藏不移除
&.sort-wrap-hide {
display: none;
}
> view {
flex: 1;
text-align: center;

View File

@@ -19,6 +19,7 @@ export default {
coupon: 0,
emptyShow: false,
isList: true, //列表样式
hideSort: false, //是否隐藏排序栏(综合/销量/价格/筛选)
//分享所需标题
share_title: '',
//搜索到多少件商品
@@ -82,6 +83,15 @@ export default {
});
this.categoryId = options.category_id || 0;
// 商品分类(模板5)点二级分类进入时带 list_style=grid, 默认展示竖版(双列宫格)视图;
// 其它入口(搜索/优惠券/品牌等)保持原有的单列列表视图
if (options.list_style === 'grid') {
this.isList = false;
}
// 商品分类(模板5)进入时隐藏排序栏(综合/销量/价格/筛选), 只隐藏不移除
if (options.hide_sort == 1) {
this.hideSort = true;
}
this.keyword = options.keyword || '';
this.coupon = options.coupon || 0;
this.goods_id_arr = options.goods_id_arr || 0;

View File

@@ -2186,11 +2186,14 @@ _handleStreamError(msg) {
})
// 5) 行内代码 / 加粗 / 斜体 / 删除线 (此时链接已抽离, 不会被误改)
// PR-FIX-AI-TEXT-COLOR (2026-09-24): AI 回答统一配色 —— 正文/标题/加粗/行内代码/引用/删除线
// 一律黑色(TEXT), 只有"网址/链接"是蓝色(LINK)。
// 之前行内代码是粉色(#d63384)、删除线是灰色(#999), 回答里颜色太杂。
t = t
.replace(/`([^`]+)`/g, '<code style="background:#f2f3f5;padding:2rpx 10rpx;border-radius:6rpx;font-size:24rpx;font-family:monospace;color:#d63384;">$1</code>')
.replace(/`([^`]+)`/g, '<code style="background:#f2f3f5;padding:2rpx 10rpx;border-radius:6rpx;font-size:24rpx;font-family:monospace;color:' + TEXT + ';">$1</code>')
.replace(/\*\*(.+?)\*\*/g, '<strong style="font-weight:700;color:' + TEXT + ';">$1</strong>')
.replace(/\*(.+?)\*/g, '<em style="font-style:italic;">$1</em>')
.replace(/~~(.+?)~~/g, '<del style="text-decoration:line-through;color:#999;">$1</del>')
.replace(/\*(.+?)\*/g, '<em style="font-style:italic;color:' + TEXT + ';">$1</em>')
.replace(/~~(.+?)~~/g, '<del style="text-decoration:line-through;color:' + TEXT + ';">$1</del>')
return t
}
@@ -2208,7 +2211,7 @@ _handleStreamError(msg) {
const safe = code.replace(/</g, '&lt;').replace(/>/g, '&gt;')
codeBlocks.push(
`<pre style="background:#f7f8fa;padding:20rpx;border-radius:8rpx;overflow-x:auto;font-size:24rpx;line-height:1.6;margin:12rpx 0;white-space:pre-wrap;">` +
`<code style="background:transparent;padding:0;font-family:monospace;color:#333;font-size:24rpx;">${safe}</code></pre>`
`<code style="background:transparent;padding:0;font-family:monospace;color:${TEXT};font-size:24rpx;">${safe}</code></pre>`
)
return `%%CODEBLOCK_${idx}%%`
})
@@ -2304,7 +2307,7 @@ _handleStreamError(msg) {
const bqMatch = line.match(/^>\s?(.+)$/)
if (bqMatch) {
flushBuf(); out.push(closeList())
out.push(`<blockquote style="border-left:4rpx solid #d0d7de;padding:8rpx 16rpx;margin:${BQ_MARGIN};background:#f6f8fa;color:#57606a;line-height:${BQ_LH};font-size:28rpx;border-radius:0 6rpx 6rpx 0;">${parseInline(bqMatch[1])}</blockquote>`)
out.push(`<blockquote style="border-left:4rpx solid #d0d7de;padding:8rpx 16rpx;margin:${BQ_MARGIN};background:#f6f8fa;color:${TEXT};line-height:${BQ_LH};font-size:28rpx;border-radius:0 6rpx 6rpx 0;">${parseInline(bqMatch[1])}</blockquote>`)
continue
}
@@ -3751,16 +3754,18 @@ _handleStreamError(msg) {
.markdown-body {
line-height: 1.8;
font-size: 26rpx;
color: $color-text;
/* PR-FIX-AI-TEXT-COLOR (2026-09-24): 统一配色 —— 正文/加粗/斜体/代码全部黑色,
只有链接(a)是蓝色; 之前正文是灰蓝($color-text)、代码是蓝灰($color-primary) */
color: #1a1a1a;
strong {
font-weight: 600;
color: $color-text;
color: #1a1a1a;
}
em {
font-style: italic;
color: $color-text-light;
color: #1a1a1a;
}
code {
@@ -3769,17 +3774,18 @@ _handleStreamError(msg) {
border-radius: $radius-sm;
font-family: monospace;
font-size: 24rpx;
color: $color-primary;
color: #1a1a1a;
}
a {
color: $color-primary;
/* 网址/链接: 蓝色 (与 parseMarkdown 里的 LINK 常量保持一致) */
color: #3a7afe;
text-decoration: underline;
text-underline-offset: 4rpx;
transition: color 0.2s ease;
&:hover {
color: #5a7cb0;
color: #2b6cf0;
}
}
}
@@ -4330,8 +4336,10 @@ _handleStreamError(msg) {
- 内边距加大 -> 气泡比文字大, 文字与气泡边缘留出间距
- 宽度 auto + inline-block -> 气泡宽度贴着内容, 不拉满整行
- 只调整气泡自身, 气泡内文字字号不变
- 用 :not() 排除图片/配文/文件/视频/音频气泡, 避免影响它们原有的紧凑样式 */
.user-message .message-content .message-bubble:not(.user-image-bubble):not(.image-caption-bubble):not(.user-file-bubble):not(.user-video-bubble):not(.user-audio-bubble) {
- 用 :not() 排除图片/文件/视频/音频气泡, 避免影响它们原有的紧凑样式
PR-FIX-CAPTION-BUBBLE-SIZE (2026-09-24): 不再排除 .image-caption-bubble ——
图文一起发时的配文气泡要和普通文字气泡一样大(之前被排除 → 内边距只有 30rpx) */
.user-message .message-content .message-bubble:not(.user-image-bubble):not(.user-file-bubble):not(.user-video-bubble):not(.user-audio-bubble) {
padding: 10rpx 30rpx !important;
border-radius: 16rpx !important;
display: inline-block !important;
@@ -5128,10 +5136,11 @@ _handleStreamError(msg) {
/* 用户消息气泡: 覆盖上面的 padding:0
- H5: 气泡要明显比文字大(与小程序一致), 只改气泡, 不改文字字号;
用 :not() 排除图片/配文/文件/视频/音频气泡
- 小程序: 保持原有浅灰气泡 + 加大内边距(不动) */
用 :not() 排除图片/文件/视频/音频气泡
- 小程序: 保持原有浅灰气泡 + 加大内边距(不动)
PR-FIX-CAPTION-BUBBLE-SIZE (2026-09-24): 同上层, 配文气泡不再排除, 与普通文字气泡同尺寸 */
/* #ifdef H5 */
.user-message .message-content .message-bubble:not(.user-image-bubble):not(.image-caption-bubble):not(.user-file-bubble):not(.user-video-bubble):not(.user-audio-bubble) {
.user-message .message-content .message-bubble:not(.user-image-bubble):not(.user-file-bubble):not(.user-video-bubble):not(.user-audio-bubble) {
padding: 20rpx 40rpx !important;
border-radius: 16rpx !important;
display: inline-block !important;
@@ -5623,19 +5632,16 @@ _handleStreamError(msg) {
}
}
/* 图片消息配文气泡 (图文一起发出时, 独立气泡显示在图片下方) */
/* 图片消息配文气泡 (图文一起发出时, 独立气泡显示在图片下方)
PR-FIX-CAPTION-BUBBLE-SIZE (2026-09-24): 这里原先单独定义了
background/border-radius/padding/max-width/字号, 使配文气泡比普通文字气泡小一圈
(H5 下又被 :not() 规则排除, 内边距只有 30rpx)。
现在只保留"与图片的间距 + 右对齐", 其余(内边距/圆角/背景/字号)全部复用普通文字气泡,
保证图文消息下方的问题气泡和纯文字消息的气泡一样大。 */
.image-caption-bubble {
margin-top: 10rpx;
background: #f5f5f5;
border-radius: 20rpx;
padding: 14rpx 22rpx;
max-width: 460rpx;
.message-text {
font-size: 28rpx;
color: #111827;
line-height: 1.5;
word-break: break-all;
}
align-self: flex-end;
width: auto;
}
/* 输入框内待发送图片缩略图: 位于文字输入行上方, 出现时输入框自动向上撑高 */