Compare commits
15 Commits
merge/tmp-
...
test/test-
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e480d35df | |||
| bc6d243362 | |||
| a285706112 | |||
| 9c04c031ab | |||
| 0ce929f8a1 | |||
| 61ab9f11bd | |||
| 2747cb49ae | |||
| 9c8ebf8d74 | |||
| e19fef785f | |||
| 92ae114c5c | |||
| b25c366a5b | |||
| b23c21d05b | |||
| 65a24f25bb | |||
| 2a0935b581 | |||
| b31197a8b4 |
@@ -41,19 +41,13 @@ const localDevConfig = ({
|
||||
'local-2': { // 测试平台
|
||||
uniacid: 2,
|
||||
domain: 'http://localhost:8050/',
|
||||
},
|
||||
'100': {
|
||||
uniacid: 100,
|
||||
domain: 'https://5g.aigc-quickapp.com/',
|
||||
},
|
||||
'1': {
|
||||
uniacid: 1,
|
||||
domain: 'https://5g.aigc-quickapp.com/',
|
||||
},
|
||||
'115': {
|
||||
uniacid: 115,
|
||||
domain: 'https://5g.aigc-quickapp.com/',
|
||||
},
|
||||
})['1']; // 选择要使用的环境配置
|
||||
|
||||
'47': { // 修改弹窗
|
||||
uniacid: 47,
|
||||
domain: 'https://5g.aigc-quickapp.com/',
|
||||
},
|
||||
'2075': { //
|
||||
uniacid: 2075,
|
||||
domain: 'https://xcx.aigc-quickapp.com/',
|
||||
},
|
||||
})['47']; // 选择要使用的环境配置
|
||||
export default localDevConfig;
|
||||
@@ -5,17 +5,6 @@ import store from '@/store/index.js'
|
||||
let currentConversationId = null;
|
||||
const CONVERSATION_KEY = 'ai_conversation_id';
|
||||
|
||||
/**
|
||||
* 获取域名配置参数(避免重复代码)
|
||||
*/
|
||||
function getDomainParams() {
|
||||
return {
|
||||
imgDomain: Config.imgDomain,
|
||||
h5Domain: Config.h5Domain,
|
||||
baseUrl: Config.baseUrl
|
||||
};
|
||||
}
|
||||
|
||||
// 初始化时从本地读取会话ID
|
||||
try {
|
||||
const saved = uni.getStorageSync(CONVERSATION_KEY);
|
||||
@@ -27,28 +16,12 @@ try {
|
||||
}
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取uniacid
|
||||
* @returns {number} uniacid
|
||||
*/
|
||||
getUniacid() {
|
||||
return uni.getStorageSync('uniacid') || Config.uniacid; // 从缓存中获取uniacid,或使用默认uniacid
|
||||
},
|
||||
|
||||
/**
|
||||
* 发送普通消息(blocking 模式)
|
||||
*/
|
||||
async sendMessage(message, options = {}) {
|
||||
try {
|
||||
// 调试:打印关键状态
|
||||
console.log('[AI Service] sendMessage 开始执行');
|
||||
console.log('[AI Service] store.state.memberInfo:', store.state.memberInfo);
|
||||
console.log('[AI Service] uniacid:', this.getUniacid());
|
||||
console.log('[AI Service] currentConversationId:', this.getConversationId());
|
||||
|
||||
const aiConfig = store.getters.globalAIKefuConfig;
|
||||
console.log('[AI Service] aiConfig:', aiConfig);
|
||||
|
||||
const params = {
|
||||
url: '/api/kefu/chat',
|
||||
data: {
|
||||
@@ -58,38 +31,28 @@ export default {
|
||||
query: message,
|
||||
response_mode: 'blocking', // 强制 blocking
|
||||
user: store.state.memberInfo?.id || 'anonymous',
|
||||
conversation_id: this.getConversationId() || '',
|
||||
...getDomainParams()
|
||||
conversation_id: this.getConversationId() || ''
|
||||
},
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
|
||||
console.log('[AI Service] 发送请求到:', params.url);
|
||||
console.log('[AI Service] 请求参数:', JSON.stringify(params.data, null, 2));
|
||||
console.log('Sending blocking request to:', params.url);
|
||||
console.log('Request data:', params.data);
|
||||
|
||||
const response = await http.sendRequest({
|
||||
...params,
|
||||
async: false
|
||||
});
|
||||
|
||||
console.log('[AI Service] 收到响应:', response);
|
||||
|
||||
const result = this.handleResponse(response);
|
||||
if (result.conversationId) {
|
||||
this.setConversationId(result.conversationId);
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('[AI Service] Dify API请求失败,详细错误:', error);
|
||||
console.error('[AI Service] 错误堆栈:', error.stack);
|
||||
// 打印更多信息帮助调试
|
||||
console.error('[AI Service] store 状态:', {
|
||||
memberInfo: store.state.memberInfo,
|
||||
uniacid: this.getUniacid(),
|
||||
token: store.state.token
|
||||
});
|
||||
console.error('Dify API请求失败:', error);
|
||||
throw new Error('AI服务暂时不可用,请稍后重试');
|
||||
}
|
||||
},
|
||||
@@ -100,14 +63,10 @@ export default {
|
||||
async sendStreamMessage(message, onChunk, onComplete) {
|
||||
// #ifdef MP-WEIXIN
|
||||
return new Promise((resolve, reject) => {
|
||||
const match = Config.baseUrl.match(/^(?:https?:\/\/|wss?:\/\/)([^\/\s:]+)/);
|
||||
const domain = match ? match[1] : '';
|
||||
const wssUrl = `wss://${domain}/ws/aikefu`;
|
||||
console.log(`wssUrl = ${wssUrl}`);
|
||||
const socketTask = wx.connectSocket({
|
||||
url: wssUrl,
|
||||
header: {}
|
||||
});
|
||||
const socketTask = wx.connectSocket({
|
||||
url: 'wss://dev.aigc-quickapp.com/ws/aikefu',
|
||||
header: {}
|
||||
});
|
||||
|
||||
let content = '';
|
||||
let conversationId = '';
|
||||
@@ -118,7 +77,7 @@ export default {
|
||||
socketTask.send({
|
||||
data: JSON.stringify({
|
||||
action: 'auth',
|
||||
uniacid: this.getUniacid(),
|
||||
uniacid: store.state.uniacid || '1',
|
||||
token: store.state.token || 'test_token',
|
||||
user_id: store.state.memberInfo?.id || 'anonymous'
|
||||
})
|
||||
@@ -136,11 +95,10 @@ export default {
|
||||
socketTask.send({
|
||||
data: JSON.stringify({
|
||||
action: 'chat',
|
||||
uniacid: this.getUniacid(),
|
||||
uniacid: store.state.uniacid || '1',
|
||||
query: message,
|
||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||
conversation_id: this.getConversationId() || '',
|
||||
...getDomainParams()
|
||||
conversation_id: this.getConversationId() || ''
|
||||
})
|
||||
});
|
||||
} else if (data.type === 'auth_failed') {
|
||||
@@ -152,20 +110,20 @@ export default {
|
||||
}
|
||||
|
||||
// 处理流式消息块
|
||||
else if (data.event === 'message') {
|
||||
else if (data.type === 'message' || data.event === 'message') {
|
||||
const text = data.answer || data.content || data.text || '';
|
||||
content += text;
|
||||
if (onChunk) onChunk(text);
|
||||
}
|
||||
|
||||
// 处理流结束
|
||||
else if (data.event === 'message_end') {
|
||||
else if (data.event === 'message_end' || data.type === 'message_end') {
|
||||
conversationId = data.conversation_id || '';
|
||||
if (conversationId) {
|
||||
this.setConversationId(conversationId);
|
||||
}
|
||||
if (onComplete) {
|
||||
onComplete({ content, conversation_id: conversationId });
|
||||
onComplete({ content, conversation_id: conversationId });
|
||||
}
|
||||
resolve({ content, conversation_id: conversationId });
|
||||
socketTask.close();
|
||||
@@ -217,14 +175,12 @@ export default {
|
||||
'Accept': 'text/event-stream',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
uniacid: this.getUniacid(),
|
||||
token: store.state.token || 'test_token',
|
||||
uniacid: store.state.uniacid || '1',
|
||||
inputs: {},
|
||||
query: message,
|
||||
response_mode: 'streaming',
|
||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||
conversation_id: this.getConversationId() || '',
|
||||
...getDomainParams()
|
||||
conversation_id: this.getConversationId() || ''
|
||||
})
|
||||
});
|
||||
|
||||
@@ -358,19 +314,17 @@ export default {
|
||||
* 生成新会话(如需)
|
||||
*/
|
||||
async generateConversationId() {
|
||||
const params = {
|
||||
url: '/api/kefu/createConversation',
|
||||
data: {
|
||||
uniacid: this.getUniacid(),
|
||||
token: store.state.token || 'test_token',
|
||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||
member_id: store.state.memberInfo?.id || '',
|
||||
...getDomainParams()
|
||||
},
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
const params = {
|
||||
url: '/api/kefu/createConversation',
|
||||
data: {
|
||||
uniacid: store.state.uniacid,
|
||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||
member_id: store.state.memberInfo?.id || '',
|
||||
},
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
const response = await http.sendRequest({
|
||||
...params,
|
||||
async: false
|
||||
@@ -388,7 +342,6 @@ export default {
|
||||
async: false,
|
||||
data: {}
|
||||
});
|
||||
console.log('/api/kefu/health response', response);
|
||||
const available = [response?.data?.status, response?.data?.components?.ai_service_config?.status]
|
||||
.filter(item => item === 'healthy').length > 0;
|
||||
return {
|
||||
@@ -410,10 +363,7 @@ export default {
|
||||
try {
|
||||
const response = await http.sendRequest({
|
||||
url: '/api/kefu/clear-conversation',
|
||||
data: {
|
||||
conversation_id: conversationId,
|
||||
...getDomainParams()
|
||||
},
|
||||
data: { conversation_id: conversationId },
|
||||
async: false
|
||||
});
|
||||
return response.success;
|
||||
@@ -432,14 +382,13 @@ export default {
|
||||
throw new Error('会话ID(conversation_id)是必填参数');
|
||||
}
|
||||
const requestData = {
|
||||
uniacid: params.uniacid || this.getUniacid(),
|
||||
uniacid: params.uniacid || store.state.uniacid || '1',
|
||||
conversation_id: params.conversation_id,
|
||||
user_id: params.user_id || store.state.memberInfo?.id || 'anonymous',
|
||||
limit: params.limit || 20,
|
||||
offset: params.offset || 0,
|
||||
member_id: params.member_id || store.state.memberInfo?.id || '',
|
||||
token: params.token || '',
|
||||
...getDomainParams()
|
||||
token: params.token || ''
|
||||
};
|
||||
const response = await http.sendRequest({
|
||||
url: '/api/kefu/getHistory',
|
||||
@@ -478,15 +427,13 @@ export default {
|
||||
if (window.currentEventSource) {
|
||||
window.currentEventSource.close();
|
||||
}
|
||||
const paramsObj = {
|
||||
uniacid: this.getUniacid(),
|
||||
user_id: store.state.memberInfo?.id,
|
||||
const params = new URLSearchParams({
|
||||
uniacid: store.state.uniacid || '1',
|
||||
user_id: store.state.memberInfo?.id || '123456',
|
||||
query: message,
|
||||
conversation_id: conversationId || '',
|
||||
stream: 'true',
|
||||
...getDomainParams()
|
||||
};
|
||||
const params = new URLSearchParams(paramsObj);
|
||||
stream: 'true'
|
||||
});
|
||||
const url = `${Config.baseUrl}/api/kefu/chat?${params.toString()}`;
|
||||
try {
|
||||
const eventSource = new EventSource(url);
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
* 客服统一处理服务
|
||||
* 整合各种客服方式,提供统一的调用接口
|
||||
*/
|
||||
import IMAGE_CONFIG from './image-config.js';
|
||||
|
||||
export class CustomerService {
|
||||
constructor(vueInstance, externalConfig = null) {
|
||||
this.vm = vueInstance;
|
||||
@@ -473,11 +471,13 @@ export class CustomerService {
|
||||
const shopInfo = this.vm.shopInfo || {};
|
||||
const config = this.getPlatformConfig();
|
||||
|
||||
// 优先使用指定的智能客服头像
|
||||
if (config?.type === 'aikefu' || config?.type === 'wxwork' || config?.type === 'qyweixin') {
|
||||
return this.vm.$util.img(IMAGE_CONFIG.aiAvatar);
|
||||
if (config?.type === 'aikefu') {
|
||||
return shopInfo.aiAgentimg || '';
|
||||
} else if (config?.type === 'wxwork' || config?.type === 'qyweixin') {
|
||||
return shopInfo.aiAgentimg || '';
|
||||
} else if (config?.type === 'third') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return shopInfo.kefuimg || this.vm.$util?.getDefaultImage()?.kefu || '';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* 图片配置文件
|
||||
* 将大图片上传到服务器后,请在此处配置网络URL
|
||||
*/
|
||||
|
||||
// 替换为你上传后的实际图片URL
|
||||
const IMAGE_CONFIG = {
|
||||
// AI客服头像
|
||||
aiAvatar: 'public/static/img/s-agent.jpg',
|
||||
|
||||
// 用户默认头像
|
||||
userAvatar: 'public/static/img/s-user.jpg'
|
||||
};
|
||||
|
||||
export default IMAGE_CONFIG;
|
||||
@@ -30,12 +30,12 @@ function processRoutePath(route) {
|
||||
}
|
||||
}
|
||||
// ---- 处理页面目录映射 <end>----
|
||||
|
||||
|
||||
// 去掉pages目录,只保留子目录
|
||||
if (routeParts[0] === 'pages') {
|
||||
routeParts = routeParts.slice(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return routeParts.join("/");
|
||||
}
|
||||
|
||||
@@ -54,11 +54,14 @@ function loadLangPackSync(lang, path) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前本地语言
|
||||
* @returns
|
||||
*/
|
||||
function getCurrentLocale() {
|
||||
return uni.getStorageSync('lang') || "zh-cn";
|
||||
return uni.getStorageSync('lang') || "zh-cn";
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
langList: langConfig.langList,
|
||||
|
||||
@@ -77,18 +80,18 @@ export default {
|
||||
if (!_page) return;
|
||||
|
||||
const locale = getCurrentLocale(); // 获得当前本地语言
|
||||
|
||||
|
||||
let value = ''; // 存放解析后的语言值
|
||||
let langPath = ''; // 存放当前页面语言包路径
|
||||
|
||||
try {
|
||||
//公共语言包(同步加载)
|
||||
var lang = loadLangPackSync(locale, 'common');
|
||||
|
||||
|
||||
//当前页面语言包(同步加载)
|
||||
let route = _page.route;
|
||||
langPath = processRoutePath(route);
|
||||
|
||||
|
||||
// 加载当前页面语言包
|
||||
let currentPageLang = loadLangPackSync(locale, langPath);
|
||||
|
||||
@@ -113,7 +116,7 @@ export default {
|
||||
} else {
|
||||
value = mergedLang[field] !== undefined ? mergedLang[field] : field;
|
||||
}
|
||||
|
||||
|
||||
} catch (e) {
|
||||
console.error('解析语言包失败:', e, { langPath, field, locale });
|
||||
value = field;
|
||||
@@ -129,7 +132,7 @@ export default {
|
||||
|
||||
// 多语言调试,注释后可以关闭控制台输出
|
||||
if (field == value) {
|
||||
console.warn(`警告: 字段 ${field} 在语言包 ${langPath} 中未找到对应值,使用默认值 ${field} 当前语言: ${locale}`);
|
||||
console.warn(`警告: 字段 ${field} 在语言包 ${langPath} 中未找到对应值,使用默认值 ${field} 当前语言: ${locale}`);
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -165,7 +168,7 @@ export default {
|
||||
if (!_page) return;
|
||||
|
||||
const locale = getCurrentLocale();
|
||||
|
||||
|
||||
this.title(this.lang("title"));
|
||||
|
||||
//设置tabbar的文字语言
|
||||
@@ -215,4 +218,4 @@ export default {
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,10 +82,10 @@
|
||||
<block v-if="goodsList.length">
|
||||
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
||||
<view class="goods-img" @tap.stop="toDetail(item)">
|
||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" :show-menu-by-longpress="true" />
|
||||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}
|
||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" />
|
||||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-wrap">
|
||||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||||
@@ -99,6 +99,9 @@
|
||||
<text class="unit price-style small">.{{
|
||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||
</block>
|
||||
<block v-else>
|
||||
<text class="unit price-style small">{{ $lang('Make') ? $lang('Make') : '询底价' }}</text>
|
||||
</block>
|
||||
</view>
|
||||
<view class="member-price-tag"
|
||||
v-if="item.member_price && item.member_price == showPrice(item)">
|
||||
@@ -117,12 +120,6 @@
|
||||
<text>{{ showMarketPrice(item) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
|
||||
<view class="ai-chat-btn" @tap.stop="sendToAIChat(item)">
|
||||
立即咨询
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- <view class="right-wrap" v-if="value.template == 2 || value.template == 4">
|
||||
<block v-if="item.is_virtual">
|
||||
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
|
||||
@@ -177,24 +174,28 @@
|
||||
:data-template="value.template">
|
||||
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
||||
<view class="goods-img" @tap.stop="toDetail(item)">
|
||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"
|
||||
:lazy-load="true" :show-menu-by-longpress="true" />
|
||||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
||||
</view>
|
||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"
|
||||
:lazy-load="true" />
|
||||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
||||
</view>
|
||||
<view class="info-wrap">
|
||||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||||
</view>
|
||||
<view class="price-wrap">
|
||||
<view class="discount-price">
|
||||
<block v-if="item.isinformation == 0">
|
||||
<text class="unit price-style small">¥</text>
|
||||
<text class="price price-style large">{{
|
||||
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||||
<text class="unit price-style small">.{{
|
||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||
</block>
|
||||
</view>
|
||||
<block v-if="item.isinformation == 0">
|
||||
<text class="unit price-style small">¥</text>
|
||||
<text class="price price-style large">{{
|
||||
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||||
<text class="unit price-style small">.{{
|
||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||
</block>
|
||||
<block v-else>
|
||||
<text class="unit price-style small">{{ $lang('Make') ? $lang('Make') : '询底价' }}</text>
|
||||
</block>
|
||||
|
||||
</view>
|
||||
<view class="member-price-tag"
|
||||
v-if="item.member_price && item.member_price == showPrice(item)">
|
||||
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" />
|
||||
@@ -211,12 +212,6 @@
|
||||
<text>{{ showMarketPrice(item) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
|
||||
<view class="ai-chat-btn" @tap.stop="sendToAIChat(item)">
|
||||
立即咨询
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="right-wrap" v-if="value.template == 2">
|
||||
<block v-if="item.is_virtual">
|
||||
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
|
||||
@@ -723,15 +718,6 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 跳转到商品详情
|
||||
* @param {Object} item 商品信息
|
||||
*/
|
||||
sendToAIChat(item) {
|
||||
this.$util.redirectTo('/pages_goods/detail', {
|
||||
goods_id: item.goods_id
|
||||
});
|
||||
},
|
||||
diyRedirectTo(link) {
|
||||
if (!link) return;
|
||||
this.$util.diyRedirectTo(JSON.parse(link));
|
||||
@@ -1398,25 +1384,4 @@ export default {
|
||||
color: var(--main-color);
|
||||
}
|
||||
}
|
||||
|
||||
.ai-chat-btn-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: auto;
|
||||
padding-top: 10rpx;
|
||||
}
|
||||
|
||||
.ai-chat-btn {
|
||||
background-color: #ff0000;
|
||||
color: #fff;
|
||||
padding: 12rpx 30rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -8,8 +8,7 @@
|
||||
<image class="goods-img"
|
||||
:src="$util.img(item.goods_image, { size: goodsValue.template == 'large-mode' ? 'big' : 'mid' })"
|
||||
mode="widthFix" @error="imgError(index)"
|
||||
:style="{ borderRadius: goodsValue.imgAroundRadius * 2 + 'rpx' }"
|
||||
:show-menu-by-longpress="true" />
|
||||
:style="{ borderRadius: goodsValue.imgAroundRadius * 2 + 'rpx' }" />
|
||||
<view class="sell-out" v-if="item.stock <= 0">
|
||||
<text class="iconfont icon-shuqing"></text>
|
||||
</view>
|
||||
@@ -118,7 +117,7 @@
|
||||
<view class="goods-img-wrap">
|
||||
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix" @error="imgError(index)"
|
||||
:lazy-load="true" :show-menu-by-longpress="true" />
|
||||
:lazy-load="true" />
|
||||
<view class="sell-out" v-if="item.stock <= 0">
|
||||
<text class="iconfont icon-shuqing"></text>
|
||||
</view>
|
||||
@@ -185,7 +184,7 @@
|
||||
<view class="goods-img-wrap">
|
||||
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
|
||||
@error="imgError(dataIndex)" :lazy-load="true" :show-menu-by-longpress="true" />
|
||||
@error="imgError(dataIndex)" :lazy-load="true" />
|
||||
<view class="sell-out" v-if="dataItem.stock <= 0">
|
||||
<text class="iconfont icon-shuqing"></text>
|
||||
</view>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="goods-img-wrap">
|
||||
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
|
||||
@error="imgError(index, dataIndex)" :lazy-load="true" :show-menu-by-longpress="true" />
|
||||
@error="imgError(index, dataIndex)" :lazy-load="true" />
|
||||
<view class="sell-out" v-if="dataItem.stock <= 0">
|
||||
<text class="iconfont icon-shuqing"></text>
|
||||
</view>
|
||||
|
||||
@@ -913,7 +913,7 @@ export default {
|
||||
// ===== 主容器样式 =====
|
||||
.tab-diy-component-preview-container {
|
||||
width: 100%;
|
||||
min-height: 0px;
|
||||
min-height: 300px;
|
||||
display: flex;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -53,19 +53,9 @@
|
||||
|
||||
<!-- 视频播放弹窗 -->
|
||||
<uni-popup ref="videoPopup" type="center" style="background:transparent;width:100%;height:100%;">
|
||||
<view class="video-container" :style="popupVideoStyle">
|
||||
<video
|
||||
ref="popupVideoRef"
|
||||
class="adaptive-video"
|
||||
:autoPauseIfNavigate="true"
|
||||
:autoPauseIfOpenNative="true"
|
||||
:autoplay="false"
|
||||
:enableAutoRotation="true"
|
||||
id="myVideo"
|
||||
:src="video_url"
|
||||
:controls="true"
|
||||
@loadedmetadata="onPopupVideoLoaded"
|
||||
></video>
|
||||
<view class="video-container" style="position:fixed;top:30%;width:100%;left:0;">
|
||||
<video class="adaptive-video" :autoPauseIfNavigate="true" :autoPauseIfOpenNative="true"
|
||||
:autoplay="false" :enableAutoRotation="true" id="myVideo" :src="video_url" :controls="true"></video>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
@@ -136,38 +126,12 @@ export default {
|
||||
isIndicatorDots() {
|
||||
return this.value.carousel.type != 'hide' &&
|
||||
1 != Math.ceil(this.value.list.length / (this.value.pageCount * this.value.rowCount));
|
||||
},
|
||||
|
||||
// 弹窗视频容器样式
|
||||
popupVideoStyle() {
|
||||
let style = 'position:fixed;width:100%;left:0;';
|
||||
|
||||
if (this.popupVideoRatio) {
|
||||
// 根据视频比例设置位置
|
||||
if (this.popupVideoRatio === 'portrait') {
|
||||
// 竖屏视频:居中显示,使用9/16比例
|
||||
style += 'top:50%;transform:translateY(-50%);';
|
||||
style += 'aspect-ratio: 9 / 16;';
|
||||
} else {
|
||||
// 横屏视频:靠近顶部显示,使用16/9比例
|
||||
style += 'top:20%;';
|
||||
style += 'aspect-ratio: 16 / 9;';
|
||||
}
|
||||
} else {
|
||||
// 默认:居中显示,使用竖屏比例
|
||||
style += 'top:50%;transform:translateY(-50%);';
|
||||
style += 'aspect-ratio: 9 / 16;';
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 显示视频播放弹窗
|
||||
showVideo(item) {
|
||||
this.video_url = item.videoUrl;
|
||||
// 重置视频比例为null,等待新的检测结果
|
||||
this.popupVideoRatio = null;
|
||||
this.$refs.videoPopup.open();
|
||||
},
|
||||
|
||||
@@ -175,39 +139,6 @@ export default {
|
||||
imgError(index) {
|
||||
// 图片加载失败的处理逻辑
|
||||
console.log('图片加载失败:', index);
|
||||
},
|
||||
|
||||
// 弹窗视频加载完成事件
|
||||
onPopupVideoLoaded(event) {
|
||||
this.$nextTick(() => {
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
|
||||
// H5 端:获取真实视频宽高
|
||||
// #ifdef H5
|
||||
const video = this.$refs.popupVideoRef;
|
||||
if (video) {
|
||||
width = video.videoWidth || 0;
|
||||
height = video.videoHeight || 0;
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 微信小程序端:通过事件对象获取视频宽高
|
||||
// #ifdef MP-WEIXIN
|
||||
if (event && event.detail) {
|
||||
width = event.detail.width || 0;
|
||||
height = event.detail.height || 0;
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (width && height) {
|
||||
this.popupVideoRatio = width >= height ? 'landscape' : 'portrait';
|
||||
console.log(`弹窗视频宽高比检测: ${width}x${height}, 比例: ${this.popupVideoRatio}`);
|
||||
} else {
|
||||
// 获取失败,默认竖屏
|
||||
this.popupVideoRatio = 'portrait';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,7 @@
|
||||
<template>
|
||||
<view class="video-container" :style="containerStyle">
|
||||
<!-- H5端使用原生video标签 -->
|
||||
<!-- #ifdef H5 -->
|
||||
<video
|
||||
ref="videoRef"
|
||||
data-component-name="diy-video"
|
||||
class="diy-video h5-video"
|
||||
:src="videoUrl"
|
||||
:poster="posterUrl"
|
||||
:style="videoWarpCss"
|
||||
controls
|
||||
preload="metadata"
|
||||
@loadedmetadata="onVideoLoaded"
|
||||
@click="handlerClick(videoUrl)"
|
||||
></video>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 微信小程序端使用uni-app的video组件 -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<video
|
||||
ref="videoRef"
|
||||
data-component-name="diy-video"
|
||||
class="diy-video mp-video"
|
||||
:src="videoUrl"
|
||||
:poster="posterUrl"
|
||||
:style="videoWarpCss"
|
||||
controls
|
||||
@loadedmetadata="onVideoLoaded"
|
||||
@error="onVideoError"
|
||||
></video>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<video data-component-name="diy-video" class="diy-video" :src="$util.img(value.videoUrl)"
|
||||
:poster="$util.img(value.imageUrl)" :style="videoWarpCss" objectFit="cover"
|
||||
@tap="handlerClick(value.videoUrl)"></video>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -44,160 +15,27 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
videoRatio: null, // 视频比例:'landscape'(横屏)或 'portrait'(竖屏)
|
||||
videoUrl: '',
|
||||
posterUrl: ''
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initVideoData();
|
||||
return {};
|
||||
},
|
||||
created() { },
|
||||
mixins: [DiyMinx],
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.initVideoData();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 组件刷新监听
|
||||
componentRefresh: function (nval) { }
|
||||
},
|
||||
computed: {
|
||||
videoWarpCss: function () {
|
||||
var obj = '';
|
||||
if (this.value && this.value.componentAngle == 'round') {
|
||||
if (this.value.componentAngle == 'round') {
|
||||
obj += 'border-top-left-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
|
||||
obj += 'border-top-right-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
|
||||
obj += 'border-bottom-left-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
||||
obj += 'border-bottom-right-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
// 动态计算容器比例
|
||||
containerStyle() {
|
||||
let style = '';
|
||||
|
||||
if (this.videoRatio === 'landscape') {
|
||||
// 横屏视频:16/9比例
|
||||
style += 'aspect-ratio: 16 / 9;';
|
||||
} else if (this.videoRatio === 'portrait') {
|
||||
// 竖屏视频:9/16比例
|
||||
style += 'aspect-ratio: 9 / 16;';
|
||||
} else {
|
||||
// 未加载完成 → 默认使用竖屏比例
|
||||
style += 'aspect-ratio: 9 / 16;';
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 初始化视频数据
|
||||
initVideoData() {
|
||||
this.videoUrl = this.getVideoUrl();
|
||||
this.posterUrl = this.getPosterUrl();
|
||||
// H5端:预加载视频获取宽高
|
||||
// #ifdef H5
|
||||
this.preloadVideoForRatio();
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 获取视频URL
|
||||
getVideoUrl() {
|
||||
if (!this.value || !this.value.videoUrl) return '';
|
||||
const videoUrl = this.value.videoUrl;
|
||||
// 如果是完整的http/https链接,直接使用
|
||||
if (videoUrl.indexOf('http://') === 0 || videoUrl.indexOf('https://') === 0) {
|
||||
return videoUrl;
|
||||
}
|
||||
// 否则使用$util.img()处理
|
||||
return this.$util.img(videoUrl);
|
||||
},
|
||||
|
||||
// 获取封面图URL
|
||||
getPosterUrl() {
|
||||
if (!this.value || !this.value.imageUrl) return '';
|
||||
return this.$util.img(this.value.imageUrl);
|
||||
},
|
||||
|
||||
// H5端:预加载视频获取宽高比例
|
||||
preloadVideoForRatio() {
|
||||
const videoUrl = this.videoUrl;
|
||||
if (!videoUrl) return;
|
||||
|
||||
console.log('H5端预加载视频获取比例:', videoUrl);
|
||||
|
||||
const video = document.createElement('video');
|
||||
video.preload = 'metadata';
|
||||
video.src = videoUrl;
|
||||
video.muted = true;
|
||||
video.playsInline = true;
|
||||
|
||||
video.addEventListener('loadedmetadata', () => {
|
||||
const width = video.videoWidth;
|
||||
const height = video.videoHeight;
|
||||
console.log('H5端预加载获取视频宽高:', width, height);
|
||||
|
||||
if (width && height) {
|
||||
this.videoRatio = width >= height ? 'landscape' : 'portrait';
|
||||
console.log('H5端视频比例判定:', this.videoRatio, width >= height ? '横屏' : '竖屏');
|
||||
}
|
||||
});
|
||||
|
||||
video.addEventListener('error', (e) => {
|
||||
console.error('H5端预加载视频失败:', e);
|
||||
});
|
||||
|
||||
// 触发加载
|
||||
video.load();
|
||||
},
|
||||
|
||||
// 视频加载完成事件(组件内video标签)
|
||||
onVideoLoaded(event) {
|
||||
console.log('视频组件 loadedmetadata 事件触发');
|
||||
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
|
||||
// #ifdef H5
|
||||
if (event && event.target) {
|
||||
width = event.target.videoWidth || 0;
|
||||
height = event.target.videoHeight || 0;
|
||||
console.log('H5端组件视频宽高:', width, height);
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
if (event && event.detail) {
|
||||
width = event.detail.width || 0;
|
||||
height = event.detail.height || 0;
|
||||
console.log('微信小程序端视频宽高:', width, height);
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (width && height) {
|
||||
this.videoRatio = width >= height ? 'landscape' : 'portrait';
|
||||
console.log('视频比例最终判定:', this.videoRatio);
|
||||
}
|
||||
},
|
||||
|
||||
// 视频播放事件
|
||||
onVideoPlay() {
|
||||
console.log('视频开始播放');
|
||||
},
|
||||
|
||||
// 视频暂停事件
|
||||
onVideoPause() {
|
||||
console.log('视频暂停');
|
||||
},
|
||||
|
||||
// 视频错误事件
|
||||
onVideoError(event) {
|
||||
console.error('视频加载失败:', event);
|
||||
},
|
||||
|
||||
async handlerClick(videoUrl) {
|
||||
await this.__$emitEvent({
|
||||
eventName: 'video-tap', data: videoUrl, promiseCallback: (event, handler, awaitedResult) => {
|
||||
@@ -210,32 +48,11 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.video-container {
|
||||
video {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
/* 基础样式 */
|
||||
.diy-video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
.diy-video>>>.uni-video-container {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* H5端样式 */
|
||||
/* #ifdef H5 */
|
||||
.h5-video {
|
||||
object-fit: cover;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* 微信小程序端样式 */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
.mp-video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
||||
</style>
|
||||
@@ -18,6 +18,7 @@
|
||||
v-if="fixBtnShow && hasValidKefuConfig && isWeappOfficialKefu"
|
||||
class="btn-item common-bg"
|
||||
open-type="contact"
|
||||
:style="{ backgroundImage: currentKefuImg ? `url(${currentKefuImg})` : '', backgroundSize: '100% 100%' }"
|
||||
>
|
||||
<text class="icox icox-kefu" v-if="!kefuimg"></text>
|
||||
</button>
|
||||
@@ -27,13 +28,8 @@
|
||||
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && !showWeappSystemKefu"
|
||||
class="btn-item common-bg"
|
||||
@click="handleUnifiedKefuClick"
|
||||
:style="{ backgroundImage: currentKefuImg ? `url(${currentKefuImg})` : '', backgroundSize: '100% 100%' }"
|
||||
>
|
||||
<image
|
||||
v-if="currentKefuImg"
|
||||
:src="currentKefuImg"
|
||||
mode="aspectFill"
|
||||
class="kefu-img"
|
||||
></image>
|
||||
<text class="ai-icon" v-if="isAIKefu && !currentKefuImg">🤖</text>
|
||||
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
|
||||
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
|
||||
@@ -44,13 +40,8 @@
|
||||
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && showWeappSystemKefu"
|
||||
class="btn-item common-bg"
|
||||
@click="handleUnifiedKefuClick"
|
||||
:style="{ backgroundImage: currentKefuImg ? `url(${currentKefuImg})` : '', backgroundSize: '100% 100%' }"
|
||||
>
|
||||
<image
|
||||
v-if="currentKefuImg"
|
||||
:src="currentKefuImg"
|
||||
mode="aspectFill"
|
||||
class="kefu-img"
|
||||
></image>
|
||||
<text class="ai-icon" v-if="isAIKefu && !currentKefuImg">🤖</text>
|
||||
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
|
||||
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
|
||||
@@ -61,6 +52,7 @@
|
||||
v-if="fixBtnShow && hasValidKefuConfig && showWeappSystemKefu"
|
||||
class="btn-item common-bg"
|
||||
open-type="contact"
|
||||
:style="{ backgroundImage: currentKefuImg ? `url(${currentKefuImg})` : '', backgroundSize: '100% 100%' }"
|
||||
>
|
||||
<text class="icox icox-kefu" v-if="!kefuimg"></text>
|
||||
</button>
|
||||
@@ -312,13 +304,6 @@ export default {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.kefu-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.ai-icon {
|
||||
font-size: 40rpx;
|
||||
width: 100%;
|
||||
|
||||
@@ -53,15 +53,8 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 检查配置,决定是否显示隐私协议
|
||||
const shopInfo = uni.getStorageSync('shopInfo');
|
||||
if (shopInfo?.ischina == 1 && shopInfo?.index_login_auth == 1) {
|
||||
this.listenPrivacySettingChange()
|
||||
this.curPageShow()
|
||||
} else {
|
||||
// 如果不需要显示隐私协议,直接触发同意事件
|
||||
this.$emit('agree');
|
||||
}
|
||||
this.listenPrivacySettingChange()
|
||||
this.curPageShow()
|
||||
},
|
||||
created() {
|
||||
try {
|
||||
@@ -159,14 +152,6 @@ export default {
|
||||
},
|
||||
// 主动查询用户隐私政策授权状态
|
||||
proactive() {
|
||||
// 检查配置,决定是否显示隐私协议
|
||||
const shopInfo = uni.getStorageSync('shopInfo');
|
||||
if (!(shopInfo?.ischina == 1 && shopInfo?.index_login_auth == 1)) {
|
||||
// 如果不需要显示隐私协议,直接触发同意事件
|
||||
this.$emit('agree');
|
||||
return;
|
||||
}
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
if (wx.getPrivacySetting) {
|
||||
wx.getPrivacySetting({
|
||||
|
||||
@@ -3,5 +3,5 @@ export const lang = {
|
||||
title: '商品分类',
|
||||
search:'商品搜索',
|
||||
seeMore : '查看更多',
|
||||
Make: "立即咨询"
|
||||
Make: "询底价"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ export const lang = {
|
||||
//title为每个页面的标题
|
||||
title: '',
|
||||
select: '选择',
|
||||
details:'商品详情',
|
||||
details:'资源详情',
|
||||
params: '参数',
|
||||
service: '商品服务',
|
||||
allGoods: '全部商品',
|
||||
@@ -11,7 +11,7 @@ export const lang = {
|
||||
home:'首页',
|
||||
cart:'购物车',
|
||||
leave:'立即留言',
|
||||
make:'立即咨询',
|
||||
make:'联系我们',
|
||||
|
||||
send:'配送',
|
||||
express:'快递发货',
|
||||
@@ -22,7 +22,7 @@ export const lang = {
|
||||
status:'该商品已下架',
|
||||
sellout:'库存不足',
|
||||
max:'已达最大限购数量',
|
||||
makebtn:'立即咨询',
|
||||
makebtn:'价格电议',
|
||||
sales:'销量',
|
||||
stock:'库存',
|
||||
kefu:'客服',
|
||||
|
||||
@@ -6,6 +6,6 @@ export const lang = {
|
||||
Price:'价格',
|
||||
Filter:'筛选',
|
||||
Search:'请输入您要搜索的商品',
|
||||
Make:'立即咨询',
|
||||
Make:'立即询底价',
|
||||
|
||||
}
|
||||
|
||||
@@ -71,14 +71,11 @@
|
||||
"scope.userLocation" : {
|
||||
"desc" : "为了更好地为您提供服务"
|
||||
},
|
||||
"scope.writePhotosAlbum" : {
|
||||
"desc" : "为了更好地为您提供服务"
|
||||
},
|
||||
"scope.userClipboard" : {
|
||||
"desc" : "为了方便您复制信息"
|
||||
},
|
||||
"scope.camera" : {
|
||||
"desc" : "用于拍照上传头像"
|
||||
},
|
||||
"scope.writePhotosAlbum" : {
|
||||
"desc" : "用于从相册选择和保存头像"
|
||||
}
|
||||
},
|
||||
"requiredPrivateInfos" : [ "chooseLocation", "getLocation", "chooseAddress" ],
|
||||
@@ -107,7 +104,7 @@
|
||||
},
|
||||
"router" : {
|
||||
"mode" : "history",
|
||||
"base" : "/hwappx/test/"
|
||||
"base" : "/hwappx/common/"
|
||||
},
|
||||
"title" : "",
|
||||
"devServer" : {
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
{
|
||||
"path": "detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品详情"
|
||||
"navigationBarTitleText": "资源详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
}}</view>
|
||||
</view>
|
||||
|
||||
<!-- <view class="page-header" v-if="diyData.global && diyData.global.navBarSwitch" :style="{ backgroundImage: bgImg }">
|
||||
<ns-navbar :title-color="textNavColor" :data="diyData.global" :scrollTop="scrollTop" :isBack="false"/>
|
||||
</view> -->
|
||||
|
||||
<diy-index-page v-if="topIndexValue" ref="indexPage" :value="topIndexValue" :bgUrl="bgUrl"
|
||||
:scrollTop="scrollTop" :diyGlobal="diyData.global" class="diy-index-page">
|
||||
<template v-slot:components>
|
||||
@@ -43,35 +39,17 @@
|
||||
<view @touchmove.prevent.stop>
|
||||
<uni-popup ref="uniPopupWindow" type="center" class="wap-floating" :maskClick="false">
|
||||
<view class="small-bot">
|
||||
<!-- <view class="adver-time" wx:if="{{startadv.params.style=='default'&&startadv.params.canclose==1}}">
|
||||
<view bindtap="adverclose">跳过</view>
|
||||
<view class="time">{{clock}}s</view>
|
||||
</view>
|
||||
<view class="adver-time" wx:if="{{startadv.params.style=='default'&&startadv.params.canclose==0}}">
|
||||
<view class="time" style="line-height: 64rpx;">{{clock}}s</view>
|
||||
</view> -->
|
||||
<swiper autoplay="true" :circular="true" indicator-active-color="#fff"
|
||||
indicator-color="rgba(255,255,255,0.6)" :indicator-dots="true" interval="3000">
|
||||
<swiper-item v-for="(item, index) in adv.list" :key="index">
|
||||
<image class="slide-image" @click="$util.diyRedirectTo(item.link)" height="100%"
|
||||
:src="$util.img(item.imageUrl)" width="100%"></image>
|
||||
<!-- <image bindtap="adverclose" class="slide-image" height="100%" src="{{item.imgurl}}" width="100%" wx:if="{{item.click==1}}"></image> -->
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view bindtap="adverclose" class="small-bot-close" @click="closePopupWindow">
|
||||
<i class="iconfont icon-round-close" style="color:#fff"></i>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="image-wrap">
|
||||
<swiper class="swiper" style="width:100%;height: 1200rpx;" :autoplay="true" interval="3000" circular="true" :indicator-dots="true" indicator-color="#000" indicator-active-color="red">
|
||||
<swiper-item class="swiper-item" v-for="(item, index) in adv.list" :key="index" v-if="item.imageUrl" @click="$util.diyRedirectTo(item.link)">
|
||||
<view class="item">
|
||||
<image :src="$util.img(item.imageUrl)" mode="aspectFit" :show-menu-by-longpress="true"/>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
<text class="iconfont icon-round-close" @click="closePopupWindow"></text> -->
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
@@ -101,7 +79,7 @@
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<!-- 选择门店弹出框,定位当前位置,展示最近的一个门店 -->
|
||||
<!-- 选择门店弹出框 -->
|
||||
<view @touchmove.prevent.stop class="choose-store">
|
||||
<uni-popup ref="chooseStorePopup" type="center" :maskClick="false" class="choose-store">
|
||||
<view class="choose-store-popup">
|
||||
@@ -136,8 +114,27 @@
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
|
||||
<hover-nav :need="true"></hover-nav>
|
||||
<!-- 隐私协议 -->
|
||||
|
||||
<!-- 隐私协议弹窗 UI (新增) -->
|
||||
<view v-if="showPrivacyModal" class="privacy-modal-mask" @touchmove.prevent>
|
||||
<view class="privacy-modal-container">
|
||||
<view class="modal-header">用户隐私保护提示</view>
|
||||
<view class="modal-body">
|
||||
<text>感谢您使用本应用,在使用前您应当阅读并同意</text>
|
||||
<text class="link" @click="viewPrivacy('privacy')">《隐私条款》</text>
|
||||
<text class="link" @click="viewPrivacy('agreement')">《用户服务协议》</text>
|
||||
<text>,当点击同意并继续时,即表示您已理解并同意该条款内容,该条款将对您产生法律约束力;如您不同意,将无法继续使用相关功能。</text>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<view class="btn btn-disagree" @click="handleDisagree">不同意</view>
|
||||
<view class="btn btn-agree" @click="handleAgree">同意并继续</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 隐私协议组件 (原有) -->
|
||||
<privacy-popup ref="privacyPopup"></privacy-popup>
|
||||
<to-top v-if="showTop" @toTop="scrollToTopNative()"></to-top>
|
||||
<ns-login ref="login"></ns-login>
|
||||
@@ -146,14 +143,132 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
import diyJs from '@/common/js/diy.js';
|
||||
import scroll from '@/common/js/scroll-view.js';
|
||||
import indexJs from './public/js/index.js';
|
||||
|
||||
export default {
|
||||
mixins: [diyJs, scroll, indexJs]
|
||||
mixins: [diyJs, scroll, indexJs],
|
||||
data() {
|
||||
return {
|
||||
minScrollTop: 100,
|
||||
wechatQrcode: '',
|
||||
diyRoute: '/pages/index/index',
|
||||
showPrivacyModal: false // 新增:控制隐私弹窗显示
|
||||
};
|
||||
},
|
||||
onLoad(option) {
|
||||
// #ifdef H5
|
||||
if (option.module && option.module == 'locationPicker') {
|
||||
option.name = '';
|
||||
}
|
||||
// #endif
|
||||
this.name = option.name || 'DIY_VIEW_INDEX';
|
||||
},
|
||||
onShow() {
|
||||
this.getFollowQrcode();
|
||||
// 页面显示时检查隐私设置
|
||||
this.checkPrivacySetting();
|
||||
},
|
||||
mounted() {
|
||||
this.unsubscribe = this.$eventBus?.onMany([
|
||||
'rubik-cube-tap',
|
||||
'picture-tap',
|
||||
'text-tap',
|
||||
'goods-list-tap',
|
||||
'presale-tap',
|
||||
'notes-tap',
|
||||
'merch-list-tap',
|
||||
'map-tap',
|
||||
'img-ads-tap',
|
||||
'many-goods-list-tap',
|
||||
'search-tap',
|
||||
], this._handleDiyGroupInteractionEvent);
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.unsubscribe) this.unsubscribe();
|
||||
},
|
||||
methods: {
|
||||
// 检查隐私弹窗逻辑
|
||||
checkPrivacySetting() {
|
||||
// 模拟检查本地存储,看用户是否已经同意过
|
||||
// 实际项目中请替换为你真实的存储逻辑
|
||||
const hasAgreed = uni.getStorageSync('HAS_AGREED_PRIVACY');
|
||||
if (!hasAgreed) {
|
||||
// 延迟显示,防止遮挡页面渲染
|
||||
setTimeout(() => {
|
||||
this.showPrivacyModal = true;
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
// 用户点击同意
|
||||
handleAgree() {
|
||||
// 保存同意状态
|
||||
uni.setStorageSync('HAS_AGREED_PRIVACY', true);
|
||||
this.showPrivacyModal = false;
|
||||
},
|
||||
|
||||
// 用户点击不同意
|
||||
handleDisagree() {
|
||||
// 微信小程序环境下,不同意通常退出小程序
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.exitMiniProgram();
|
||||
// #endif
|
||||
// H5 或 App 下可以提示或直接不显示内容
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.showToast({
|
||||
title: '您已拒绝协议,无法使用',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
// 这里可以选择关闭页面或保持弹窗
|
||||
// this.showPrivacyModal = false;
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 查看具体条款(可选功能)
|
||||
viewPrivacy(type) {
|
||||
// 这里可以跳转到具体的条款页面,或者使用你现有的 privacy-popup 组件
|
||||
// 假设你有现成的组件逻辑:
|
||||
if (this.$refs.privacyPopup) {
|
||||
// 假设 privacy-popup 组件有一个 show 方法
|
||||
// this.$refs.privacyPopup.show(type);
|
||||
}
|
||||
uni.showToast({
|
||||
title: '请在此处实现查看条款逻辑',
|
||||
icon: 'none'
|
||||
});
|
||||
},
|
||||
|
||||
getFollowQrcode() {
|
||||
if (!this.$util.isWeiXin()) return;
|
||||
this.$api.sendRequest({
|
||||
url: '/wechat/api/wechat/followqrcode',
|
||||
success: res => {
|
||||
if (res.code >= 0 && res.data) {
|
||||
this.wechatQrcode = res.data.qrcode;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
officialAccountsOpen() {
|
||||
this.$refs.officialAccountsPopup.open();
|
||||
},
|
||||
officialAccountsClose() {
|
||||
this.$refs.officialAccountsPopup.close();
|
||||
},
|
||||
_handleDiyGroupInteractionEvent(payload) {
|
||||
// #ifdef H5
|
||||
return true;
|
||||
// #endif
|
||||
if (this.storeToken) {
|
||||
return true;
|
||||
}
|
||||
this.$refs.login?.open();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -199,6 +314,74 @@ export default {
|
||||
.small-bot-close i {
|
||||
font-size: 60rpx;
|
||||
}
|
||||
|
||||
/* 新增:隐私弹窗样式 */
|
||||
.privacy-modal-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.6); // 半透明背景
|
||||
z-index: 9999; // 确保层级最高
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.privacy-modal-container {
|
||||
width: 80%;
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
text-align: left;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: #007aff; // 蓝色链接
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 30rpx;
|
||||
text-align: center;
|
||||
margin: 0 15rpx;
|
||||
}
|
||||
|
||||
.btn-disagree {
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-agree {
|
||||
background-color: #07c160; // 微信绿
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
.swiper ::v-deep .uni-swiper-dots-horizontal {
|
||||
|
||||
@@ -1,98 +1,95 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
minScrollTop: 100, // 设置回到顶端按钮显示要求,最小页面滚动距离
|
||||
wechatQrcode: '', // 公众号二维码
|
||||
diyRoute: '/pages/index/index',
|
||||
shopInfo: null // 店铺信息
|
||||
};
|
||||
},
|
||||
onLoad(option) {
|
||||
// #ifdef H5
|
||||
// H5地图选择位置回调数据
|
||||
if (option.module && option.module == 'locationPicker') {
|
||||
option.name = ''; // 清空地址
|
||||
}
|
||||
// #endif
|
||||
this.name = option.name || 'DIY_VIEW_INDEX'; // 根据店铺运营模式,打开平台(shop)或者连锁门店(stroe)页面,接口会自行判断
|
||||
data() {
|
||||
return {
|
||||
minScrollTop: 100, // 设置回到顶端按钮显示要求,最小页面滚动距离
|
||||
wechatQrcode: "", // 公众号二维码
|
||||
diyRoute: "/pages/index/index",
|
||||
};
|
||||
},
|
||||
onLoad(option) {
|
||||
// #ifdef H5
|
||||
// H5地图选择位置回调数据
|
||||
if (option.module && option.module == "locationPicker") {
|
||||
option.name = ""; // 清空地址
|
||||
}
|
||||
// #endif
|
||||
this.name = option.name || "DIY_VIEW_INDEX"; // 根据店铺运营模式,打开平台(shop)或者连锁门店(stroe)页面,接口会自行判断
|
||||
},
|
||||
onShow() {
|
||||
// 获取关注公众号二维码
|
||||
this.getFollowQrcode();
|
||||
// 检查隐私设置并显示弹窗
|
||||
this.checkPrivacySetting();
|
||||
},
|
||||
mounted() {
|
||||
// 监听图片、文字、商品列表等组件的点击事件
|
||||
this.unsubscribe = this.$eventBus?.onMany(
|
||||
[
|
||||
"rubik-cube-tap",
|
||||
"picture-tap",
|
||||
"text-tap",
|
||||
"goods-list-tap",
|
||||
"presale-tap",
|
||||
"notes-tap",
|
||||
"merch-list-tap",
|
||||
"map-tap",
|
||||
"img-ads-tap",
|
||||
"many-goods-list-tap",
|
||||
"search-tap",
|
||||
],
|
||||
this._handleDiyGroupInteractionEvent,
|
||||
);
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 移除图片点击事件监听
|
||||
if (this.unsubscribe) this.unsubscribe();
|
||||
},
|
||||
methods: {
|
||||
// 检查隐私设置
|
||||
checkPrivacySetting() {
|
||||
// 延迟执行,确保组件已经挂载
|
||||
setTimeout(() => {
|
||||
if (this.$refs.privacyPopup) {
|
||||
this.$refs.privacyPopup.proactive();
|
||||
}
|
||||
}, 100);
|
||||
},
|
||||
// 关注公众号
|
||||
getFollowQrcode() {
|
||||
if (!this.$util.isWeiXin()) return;
|
||||
this.$api.sendRequest({
|
||||
url: "/wechat/api/wechat/followqrcode",
|
||||
success: (res) => {
|
||||
if (res.code >= 0 && res.data) {
|
||||
this.wechatQrcode = res.data.qrcode;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
officialAccountsOpen() {
|
||||
this.$refs.officialAccountsPopup.open();
|
||||
},
|
||||
officialAccountsClose() {
|
||||
this.$refs.officialAccountsPopup.close();
|
||||
},
|
||||
// 统一处理点击及触摸事件
|
||||
_handleDiyGroupInteractionEvent(payload) {
|
||||
// #ifdef H5
|
||||
// 如果是 H5 环境,直接返回 true 继续传播
|
||||
return true;
|
||||
// #endif
|
||||
|
||||
// 获取店铺信息
|
||||
this.shopInfo = uni.getStorageSync('shopInfo');
|
||||
},
|
||||
onShow() {
|
||||
// 获取关注公众号二维码
|
||||
this.getFollowQrcode();
|
||||
|
||||
// 重新获取店铺信息
|
||||
this.shopInfo = uni.getStorageSync('shopInfo');
|
||||
},
|
||||
mounted() {
|
||||
// 监听图片、文字、商品列表等组件的点击事件
|
||||
this.unsubscribe = this.$eventBus?.onMany([
|
||||
'rubik-cube-tap',
|
||||
'picture-tap',
|
||||
'text-tap',
|
||||
'goods-list-tap',
|
||||
'presale-tap',
|
||||
'notes-tap',
|
||||
'merch-list-tap',
|
||||
'map-tap',
|
||||
'img-ads-tap',
|
||||
'many-goods-list-tap',
|
||||
'search-tap',
|
||||
], this._handleDiyGroupInteractionEvent);
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 移除图片点击事件监听
|
||||
if (this.unsubscribe) this.unsubscribe();
|
||||
},
|
||||
methods: {
|
||||
// 关注公众号
|
||||
getFollowQrcode() {
|
||||
if (!this.$util.isWeiXin()) return;
|
||||
this.$api.sendRequest({
|
||||
url: '/wechat/api/wechat/followqrcode',
|
||||
success: res => {
|
||||
if (res.code >= 0 && res.data) {
|
||||
this.wechatQrcode = res.data.qrcode;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
officialAccountsOpen() {
|
||||
this.$refs.officialAccountsPopup.open();
|
||||
},
|
||||
officialAccountsClose() {
|
||||
this.$refs.officialAccountsPopup.close();
|
||||
},
|
||||
// 判断是否需要登录授权
|
||||
_needLoginAuth() {
|
||||
// 检查 index_login_auth 都开启时才需要登录授权
|
||||
return this.shopInfo?.index_login_auth == 1;
|
||||
},
|
||||
// 统一处理点击及触摸事件
|
||||
_handleDiyGroupInteractionEvent(payload) {
|
||||
// #ifdef H5
|
||||
// 如果是 H5 环境,直接返回 true 继续传播
|
||||
return true;
|
||||
// #endif
|
||||
// 如果已登录,直接返回 true 继续传播
|
||||
if (this.storeToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果不需要登录授权,直接返回 true 继续传播
|
||||
if (!this._needLoginAuth()) {
|
||||
return true;
|
||||
}
|
||||
// 打开登录弹窗
|
||||
// console.log('==> _handleDiyGroupInteractionEvent, 未登录,打开登录弹窗', { payload })
|
||||
this.$refs.login?.open();
|
||||
|
||||
// 如果已登录,直接返回 true 继续传播
|
||||
if (this.storeToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 打开登录弹窗
|
||||
// console.log('==> _handleDiyGroupInteractionEvent, 未登录,打开登录弹窗', { payload })
|
||||
this.$refs.login?.open();
|
||||
|
||||
// 阻止事件继续传播
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 阻止事件继续传播
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
<view>
|
||||
<view scroll-y="true" class="goods-detail" :class="isIphoneX ? 'active' : ''">
|
||||
<view class="goods-container">
|
||||
<!-- AI客服悬浮按钮 -->
|
||||
<view class="ai-chat-float-btn" @tap.stop="$emit('aiChatClick')" v-if="showAIChatBtn">
|
||||
<image :src="aiAvatarUrl" mode="aspectFill" class="ai-avatar-icon" />
|
||||
</view>
|
||||
|
||||
<!-- 弹幕 -->
|
||||
<pengpai-fadein-out v-if="goodsSkuDetail.barrage_show && goodsSkuDetail.barrageData" ref="pengpai"
|
||||
:duration="1600" :wait="1900" :top="200" :left="0" :radius="60" :loop="true"
|
||||
@@ -22,7 +17,7 @@
|
||||
:item-id="'goods_id_' + index">
|
||||
<view class="item" @click="previewMedia(index)">
|
||||
<image :src="$util.img(item, { size: 'big' })" @error="swiperImageError(index)"
|
||||
mode="aspectFit" :show-menu-by-longpress="true" />
|
||||
mode="aspectFit" />
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
@@ -463,8 +458,6 @@
|
||||
// 商品详情视图
|
||||
import scroll from '@/common/js/scroll-view.js';
|
||||
import detail from './detail.js';
|
||||
import IMAGE_CONFIG from '@/common/js/image-config.js';
|
||||
import { createCustomerService } from '@/common/js/customer-service.js';
|
||||
|
||||
import pengpaiFadeinOut from '../pengpai-fadein-out/pengpai-fadein-out.vue';
|
||||
|
||||
@@ -481,15 +474,6 @@ export default {
|
||||
components: {
|
||||
pengpaiFadeinOut
|
||||
},
|
||||
computed: {
|
||||
aiAvatarUrl() {
|
||||
return this.$util.img(IMAGE_CONFIG.aiAvatar);
|
||||
},
|
||||
showAIChatBtn() {
|
||||
const customerService = createCustomerService(this);
|
||||
return customerService.hasValidKefuConfig() && customerService.isAIKefu();
|
||||
}
|
||||
},
|
||||
mixins: [scroll, detail]
|
||||
};
|
||||
</script>
|
||||
@@ -497,36 +481,4 @@ export default {
|
||||
<style lang="scss">
|
||||
@import '@/common/css/goods_detail.scss';
|
||||
</style>
|
||||
<style scoped>
|
||||
.ai-chat-float-btn {
|
||||
position: fixed;
|
||||
right: 20rpx;
|
||||
top: 100rpx;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.15);
|
||||
z-index: 9999;
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ai-chat-float-btn:active {
|
||||
transform: scale(0.9);
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.ai-chat-float-btn .iconfont {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.ai-chat-float-btn .ai-avatar-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
<style scoped></style>
|
||||
@@ -26,8 +26,7 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
diyData: null,
|
||||
mpShareData: null //小程序分享数据
|
||||
diyData: null
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
@@ -39,25 +38,6 @@ export default {
|
||||
onShow() {
|
||||
this.$util.hideHomeButton();
|
||||
if (this.$refs.category) this.$refs.category[0].pageShow();
|
||||
|
||||
//小程序分享
|
||||
// #ifdef MP-WEIXIN
|
||||
this.$util.getMpShare('/pages_goods/category').then(res => {
|
||||
this.mpShareData = res;
|
||||
}).catch(err => {
|
||||
// 如果后台没配置,用默认值
|
||||
this.mpShareData = {
|
||||
appMessage: {
|
||||
title: '发现超多好物,快来看看',
|
||||
path: '/pages_goods/category'
|
||||
},
|
||||
timeLine: {
|
||||
title: '发现超多好物,快来看看',
|
||||
query: '/pages_goods/category'
|
||||
}
|
||||
};
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
onUnload() {
|
||||
if (!this.storeToken && this.$refs.login) this.$refs.login.cancelCompleteInfo();
|
||||
@@ -89,24 +69,6 @@ export default {
|
||||
onPullDownRefresh() {
|
||||
uni.hideTabBar();
|
||||
this.getDiyInfo();
|
||||
},
|
||||
onShareAppMessage() {
|
||||
return this.mpShareData?.appMessage || {
|
||||
title: '发现超多好物,快来看看',
|
||||
path: '/pages_goods/category'
|
||||
};
|
||||
},
|
||||
onShareTimeline() {
|
||||
return this.mpShareData?.timeLine || {
|
||||
title: '发现超多好物,快来看看',
|
||||
query: '/pages_goods/category'
|
||||
};
|
||||
},
|
||||
onAddToFavorites() {
|
||||
return {
|
||||
title: '发现超多好物,快来看看',
|
||||
path: '/pages_goods/category'
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</view> -->
|
||||
<!-- #endif -->
|
||||
|
||||
<goods-detail-view :goodsSkuDetail="goodsSkuDetail" ref="goodsDetailView" @aiChatClick="sendToAIChat(goodsSkuDetail)">
|
||||
<goods-detail-view :goodsSkuDetail="goodsSkuDetail" ref="goodsDetailView">
|
||||
<!-- 价格区域 -->
|
||||
<template v-slot:price>
|
||||
<!-- 限时折扣 -->
|
||||
@@ -77,6 +77,7 @@
|
||||
</block>
|
||||
<block v-else>
|
||||
<template>
|
||||
<text class="price price-font" style="font-weight: 600;">{{$lang('makebtn')}}</text>
|
||||
</template>
|
||||
</block>
|
||||
</view>
|
||||
@@ -460,12 +461,11 @@
|
||||
<ns-goods-action-icon :text="$lang('follow')" :icon="whetherCollection == 1 ? 'icon-likefill color-base-text' : 'icon-guanzhu'" @click="editCollection" />
|
||||
<!-- #endif -->
|
||||
<block v-if="goodsSkuDetail.isinformation >= 1">
|
||||
|
||||
<ns-goods-action-button v-if="goodsSkuDetail.isinformation == 1" class="goods-action-button active3" disabled-text="" backgroundColor="#54c952" @click="phoneClick(goodsSkuDetail.goods_mobile)" :text="$lang('make')" :disabled="false" />
|
||||
<ns-goods-action-button v-if="goodsSkuDetail.isinformation == 2" class="goods-action-button active3" disabled-text="" backgroundColor="#54c952" @click="showinformation" :text="$lang('leave')" :disabled="false" />
|
||||
<ns-goods-action-button v-if="goodsSkuDetail.isinformation == 3" class="goods-action-button active3" disabled-text="" backgroundColor="#54c952" @click="goToAIChat" text="智能客服" :disabled="false" />
|
||||
<!-- <ns-goods-action-button v-if="goodsSkuDetail.isinformation == 2" class="goods-action-button active3" disabled-text="" backgroundColor="#54c952" @click="buyNow" text="商品留言" :disabled="false" /> -->
|
||||
|
||||
<ns-goods-action-button v-if="goodsSkuDetail.isinformation == 1" class="goods-action-button active3" disabled-text="" backgroundColor="#54c952" @click="phoneClick(goodsSkuDetail.goods_mobile)" :text="$lang('make')" :disabled="false" />
|
||||
<ns-goods-action-button v-if="goodsSkuDetail.isinformation == 2" class="goods-action-button active3" disabled-text="" backgroundColor="#54c952" @click="showinformation" :text="$lang('leave')" :disabled="false" />
|
||||
<!-- <ns-goods-action-button v-if="goodsSkuDetail.isinformation == 2" class="goods-action-button active3" disabled-text="" backgroundColor="#54c952" @click="buyNow" text="商品留言" :disabled="false" /> -->
|
||||
|
||||
</block>
|
||||
<block v-else>
|
||||
<block v-if="goodsSkuDetail.stock == 0 && !goodsSkuDetail.sku_spec_format">
|
||||
@@ -515,19 +515,6 @@
|
||||
|
||||
<!-- 隐私弹窗 -->
|
||||
<privacy-popup ref="privacyPopup"></privacy-popup>
|
||||
|
||||
<!-- 联系电话弹窗 -->
|
||||
<uni-popup ref="contactPopup" type="center">
|
||||
<view class="contact-popup">
|
||||
<view class="contact-content">
|
||||
<text class="contact-text">可在手机端拨打 021-51815181</text>
|
||||
</view>
|
||||
<view class="contact-buttons">
|
||||
<button class="contact-btn cancel-btn" @click="closeContactPopup">取消</button>
|
||||
<button class="contact-btn copy-btn" @click="copyPhoneNumber">复制到剪切板</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -544,61 +531,9 @@ export default {
|
||||
goodsDetailView,
|
||||
nsGoodsPromotion
|
||||
},
|
||||
mixins: [base, detail, scroll],
|
||||
methods: {
|
||||
/**
|
||||
* 发送商品信息到智能客服
|
||||
* @param {Object} item 商品信息
|
||||
*/
|
||||
sendToAIChat(item) {
|
||||
this.$refs.contactPopup.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* 跳转到智能客服页面
|
||||
*/
|
||||
goToAIChat() {
|
||||
const productInfo = {
|
||||
goods_id: this.goodsSkuDetail.goods_id || '',
|
||||
goods_name: this.goodsSkuDetail.goods_name || '',
|
||||
price: this.goodsSkuDetail.discount_price || this.goodsSkuDetail.member_price || this.goodsSkuDetail.price || '',
|
||||
image: this.goodsSkuDetail.sku_image || '',
|
||||
category_id: this.goodsSkuDetail.category_id || ''
|
||||
};
|
||||
|
||||
uni.setStorageSync('selectedProduct', productInfo);
|
||||
|
||||
uni.navigateTo({
|
||||
url: '/pages_tool/ai-chat/index'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭联系电话弹窗
|
||||
*/
|
||||
closeContactPopup() {
|
||||
this.$refs.contactPopup.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* 复制电话号码到剪切板
|
||||
*/
|
||||
copyPhoneNumber() {
|
||||
uni.setClipboardData({
|
||||
data: '021-51815181',
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: '复制成功',
|
||||
icon: 'success'
|
||||
});
|
||||
this.closeContactPopup();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
mixins: [base, detail, scroll]
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/common/css/goods_detail.scss';
|
||||
@import './public/css/detail.scss';
|
||||
@@ -610,57 +545,6 @@ export default {
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.contact-popup {
|
||||
width: 600rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.contact-content {
|
||||
padding: 60rpx 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contact-text {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.contact-buttons {
|
||||
display: flex;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.contact-btn {
|
||||
flex: 1;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
border: none;
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.contact-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
color: #333333;
|
||||
background: #ffffff;
|
||||
border-right: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
color: #ffffff;
|
||||
background: #54c952;
|
||||
}
|
||||
|
||||
</style>
|
||||
<style scoped>
|
||||
::v-deep .action-icon-wrap .iconfont.icon-shouye1 {
|
||||
@@ -756,5 +640,4 @@ export default {
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<view class="goods-item margin-bottom" v-for="(item, index) in goodsList" :key="index"
|
||||
@click="toDetail(item)">
|
||||
<view class="goods-img">
|
||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" :show-menu-by-longpress="true"></image>
|
||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"></image>
|
||||
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}
|
||||
</view>
|
||||
<view class="sell-out" v-if="item.goods_stock <= 0">
|
||||
@@ -75,23 +75,68 @@
|
||||
<text class="unit price-style small">.{{
|
||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||
</view>
|
||||
<view class="discount-price" v-else>
|
||||
<text class="price price-style large">{{ langstatus ? $lang('Make') : '询底价' }}</text>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="member-price-tag"
|
||||
v-if="item.member_price && item.member_price == showPrice(item)">
|
||||
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix"></image>
|
||||
</view>
|
||||
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
|
||||
<image :src="$util.img('public/uniapp/index/discount.png')" mode="widthFix"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="pro-info" v-if="item.isinformation == 0">
|
||||
<view class="delete-price color-tip price-font" v-if="showMarketPrice(item)">
|
||||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||||
<text>{{ showMarketPrice(item) }}</text>
|
||||
</view>
|
||||
<view class="block-wrap">
|
||||
<view class="sale" v-if="item.sale_show">已售{{ item.sale_num }}{{ item.unit
|
||||
<view class="sale color-tip" v-if="item.sale_show">已售{{ item.sale_num }}{{ item.unit
|
||||
? item.unit : '件' }}</view>
|
||||
</view>
|
||||
<view class="cart-action-wrap" v-if="config.control && item.is_virtual == 0">
|
||||
<view class="cart buy-btn click-wrap" :id="'goods-' + item.id"
|
||||
<!-- 购物车图标 -->
|
||||
<view v-if="config.style == 'icon-cart'" :style="{
|
||||
color: config.theme == 'diy' ? config.textColor : '',
|
||||
borderColor: config.theme == 'diy' ? config.textColor : ''
|
||||
}" class="cart shopping-cart-btn iconfont icon-gouwuche click-wrap" :id="'goods-' + item.id"
|
||||
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
|
||||
购买
|
||||
<view class="click-event"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
|
||||
<view class="ai-chat-btn" @click.stop="sendToAIChat(item)">
|
||||
立即咨询
|
||||
|
||||
<!--加号图标 -->
|
||||
<view v-else-if="config.style == 'icon-add'" :style="{
|
||||
color: config.theme == 'diy' ? config.textColor : '',
|
||||
borderColor: config.theme == 'diy' ? config.textColor : ''
|
||||
}" class="cart plus-sign-btn iconfont icon-add1 click-wrap" :id="'goods-' + item.id"
|
||||
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
|
||||
<view class="click-event"></view>
|
||||
</view>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<view v-else-if="config.style == 'button'" :style="{
|
||||
backgroundColor: config.theme == 'diy' ? config.bgColor : '',
|
||||
color: config.theme == 'diy' ? config.textColor : '',
|
||||
fontWeight: config.theme == 'diy' ? (config.fontWeight ? 'bold' : 'normal') : '',
|
||||
padding: config.theme == 'diy' ? '12rpx ' + config.padding * 2 + 'rpx' : ''
|
||||
}" class="cart buy-btn click-wrap" :id="'goods-' + item.id"
|
||||
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
|
||||
{{ config.text }}
|
||||
<view class="click-event"></view>
|
||||
</view>
|
||||
|
||||
<!--自定义图标 -->
|
||||
<view v-else-if="config.style == 'icon-diy'" :style="{
|
||||
color: config.theme == 'diy' ? config.textColor : ''
|
||||
}" class="icon-diy click-wrap" :id="'goods-' + item.id"
|
||||
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
|
||||
<view class="click-event"></view>
|
||||
<diy-icon :icon="config.iconDiy.icon"
|
||||
:value="config.iconDiy.style ? config.iconDiy.style : null"></diy-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -102,7 +147,7 @@
|
||||
@click="toDetail(item)"
|
||||
:style="{ left: listPosition[index] ? listPosition[index].left : '', top: listPosition[index] ? listPosition[index].top : '' }">
|
||||
<view class="goods-img">
|
||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" :show-menu-by-longpress="true"></image>
|
||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"></image>
|
||||
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}
|
||||
</view>
|
||||
<view class="sell-out" v-if="item.stock <= 0">
|
||||
@@ -123,23 +168,66 @@
|
||||
<text class="unit price-style small">.{{
|
||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||
</view>
|
||||
<view class="discount-price" v-else>
|
||||
<text class="price price-style large">{{ langstatus ? $lang('Make') : '询底价' }}</text>
|
||||
</view>
|
||||
<view class="member-price-tag"
|
||||
v-if="item.member_price && item.member_price == showPrice(item)">
|
||||
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix"></image>
|
||||
</view>
|
||||
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
|
||||
<image :src="$util.img('public/uniapp/index/discount.png')" mode="widthFix"></image>
|
||||
</view>
|
||||
<view class="delete-price color-tip price-font" v-if="showMarketPrice(item)">
|
||||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||||
<text>{{ showMarketPrice(item) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="pro-info" v-if="item.isinformation == 0">
|
||||
<view class="block-wrap">
|
||||
<view class="sale" v-if="item.sale_show">已售{{ item.sale_num }}{{ item.unit
|
||||
<view class="sale color-tip" v-if="item.sale_show">已售{{ item.sale_num }}{{ item.unit
|
||||
? item.unit : '件' }}</view>
|
||||
</view>
|
||||
<view class="cart-action-wrap" v-if="config.control && item.is_virtual == 0">
|
||||
<view class="cart buy-btn click-wrap" :id="'goods-' + item.id"
|
||||
<!-- 购物车图标 -->
|
||||
<view v-if="config.style == 'icon-cart'" :style="{
|
||||
color: config.theme == 'diy' ? config.textColor : '',
|
||||
borderColor: config.theme == 'diy' ? config.textColor : ''
|
||||
}" class="cart shopping-cart-btn iconfont icon-gouwuche click-wrap" :id="'goods-' + item.id"
|
||||
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
|
||||
购买
|
||||
<view class="click-event"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
|
||||
<view class="ai-chat-btn" @click.stop="sendToAIChat(item)">
|
||||
立即咨询
|
||||
|
||||
<!--加号图标 -->
|
||||
<view v-else-if="config.style == 'icon-add'" :style="{
|
||||
color: config.theme == 'diy' ? config.textColor : '',
|
||||
borderColor: config.theme == 'diy' ? config.textColor : ''
|
||||
}" class="cart plus-sign-btn iconfont icon-add1 click-wrap" :id="'goods-' + item.id"
|
||||
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
|
||||
<view class="click-event"></view>
|
||||
</view>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<view v-else-if="config.style == 'button'" :style="{
|
||||
backgroundColor: config.theme == 'diy' ? config.bgColor : '',
|
||||
color: config.theme == 'diy' ? config.textColor : '',
|
||||
fontWeight: config.theme == 'diy' ? (config.fontWeight ? 'bold' : 'normal') : '',
|
||||
padding: config.theme == 'diy' ? '12rpx ' + config.padding * 2 + 'rpx' : ''
|
||||
}" class="cart buy-btn click-wrap" :id="'goods-' + item.id"
|
||||
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
|
||||
{{ config.text }}
|
||||
<view class="click-event"></view>
|
||||
</view>
|
||||
|
||||
<!--自定义图标 -->
|
||||
<view v-else-if="config.style == 'icon-diy'" :style="{
|
||||
color: config.theme == 'diy' ? config.textColor : ''
|
||||
}" class="icon-diy click-wrap" :id="'goods-' + item.id"
|
||||
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
|
||||
<view class="click-event"></view>
|
||||
<diy-icon :icon="config.iconDiy.icon"
|
||||
:value="config.iconDiy.style ? config.iconDiy.style : null"></diy-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -223,14 +311,7 @@ export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
mixins: [list],
|
||||
methods: {
|
||||
sendToAIChat(item) {
|
||||
this.$util.redirectTo('/pages_goods/detail', {
|
||||
goods_id: item.goods_id
|
||||
});
|
||||
}
|
||||
}
|
||||
mixins: [list]
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -246,24 +327,4 @@ export default {
|
||||
::v-deep .sku-layer .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
||||
max-height: unset !important;
|
||||
}
|
||||
|
||||
.ai-chat-btn-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: auto;
|
||||
padding-top: 10rpx;
|
||||
}
|
||||
|
||||
.ai-chat-btn {
|
||||
background-color: #ff0000;
|
||||
color: #fff;
|
||||
padding: 12rpx 30rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
@@ -295,8 +295,7 @@
|
||||
|
||||
.lineheight-clear {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
// 商品列表单列样式
|
||||
@@ -387,30 +386,46 @@
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
margin-top: 0;
|
||||
margin-top: 16rpx;
|
||||
|
||||
background-color: var(--main-color);
|
||||
border-radius: 35rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
|
||||
|
||||
.unit {
|
||||
margin-right: 6rpx;
|
||||
color: #ff4444;
|
||||
color: var(--price-color);
|
||||
}
|
||||
.price {
|
||||
color: #ff4444;
|
||||
font-size: 32rpx !important;
|
||||
color: var(--price-color);
|
||||
color: #fff !important;
|
||||
font-size: 27rpx !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
}
|
||||
|
||||
.pro-info {
|
||||
margin-top: 10rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: auto;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
.delete-price {
|
||||
text-decoration: line-through;
|
||||
font-size: $font-size-tag !important;
|
||||
flex: 1;
|
||||
|
||||
.unit {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.block-wrap {
|
||||
flex: 1;
|
||||
line-height: 1;
|
||||
margin-right: 20rpx;
|
||||
.sale {
|
||||
font-size: $font-size-tag !important;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -528,9 +543,6 @@
|
||||
|
||||
.lineheight-clear {
|
||||
margin-top: 16rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.discount-price {
|
||||
@@ -540,26 +552,24 @@
|
||||
|
||||
.unit {
|
||||
margin-right: 6rpx;
|
||||
color: #ff4444;
|
||||
color: var(--price-color);
|
||||
}
|
||||
.price {
|
||||
color: #ff4444;
|
||||
font-size: 32rpx !important;
|
||||
font-weight: bold !important;
|
||||
color: var(--price-color);
|
||||
}
|
||||
}
|
||||
|
||||
.pro-info {
|
||||
margin-top: 10rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: auto;
|
||||
align-items: center;
|
||||
|
||||
.block-wrap {
|
||||
flex: 1;
|
||||
line-height: 1;
|
||||
margin-right: 20rpx;
|
||||
.sale {
|
||||
font-size: $font-size-tag !important;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,9 +644,9 @@
|
||||
}
|
||||
|
||||
.buy-btn {
|
||||
background-color: #ff4444;
|
||||
color: #fff;
|
||||
border-radius: 30rpx;
|
||||
background-color: $base-color;
|
||||
color: var(--btn-text-color);
|
||||
border-radius: 50rpx;
|
||||
font-size: $font-size-tag;
|
||||
padding: 12rpx 30rpx;
|
||||
line-height: 1;
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||
</view>
|
||||
<view class="discount-price" v-else>
|
||||
<text class="price price-style large">{{ $lang('Make') ? $lang('Make') : '立即咨询'
|
||||
<text class="price price-style large">{{ $lang('Make') ? $lang('Make') : '询底价'
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||
</view>
|
||||
<view class="discount-price" v-else>
|
||||
<text class="price price-style large">{{ $lang('Make') ? $lang('Make') : '立即咨询'
|
||||
<text class="price price-style large">{{ $lang('Make') ? $lang('Make') : '询底价'
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="member-price-tag"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,25 @@
|
||||
<template>
|
||||
<view class="container" :style="wrapperPageStyle">
|
||||
<!--自定义导航头部 - 只在微信小程序显示,H5端隐藏 -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!--自定义导航头部 -->
|
||||
<view class="custom-navbar" ref="pageHeader" v-if="showCustomNavbar">
|
||||
<view class="header-left">
|
||||
<button class="back-btn" v-show="showBackButton" @click="goBack">
|
||||
<text class="iconfont icon-back"></text>
|
||||
</button>
|
||||
<!-- 占位元素,确保布局平衡 -->
|
||||
<view v-show="!showBackButton" class="placeholder"></view>
|
||||
</view>
|
||||
<view class="header-center">
|
||||
<text class="header-subtitle">在线为您服务</text>
|
||||
</view>
|
||||
<view class="header-right">
|
||||
<button class="menu-btn" v-show="showMenuButton" @click="showMenu">
|
||||
<text>⚙️</text>
|
||||
</button>
|
||||
<!-- 占位元素,确保布局平衡 -->
|
||||
<view v-show="!showMenuButton" class="placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 聊天内容区域 -->
|
||||
<view class="chat-content"
|
||||
@@ -79,7 +87,6 @@ import { mapGetters, mapMutations } from 'vuex'
|
||||
import navigationHelper from '@/common/js/navigation';
|
||||
import { EventSafety } from '@/common/js/event-safety';
|
||||
import aiChatMessage from './ai-chat-message.vue';
|
||||
import IMAGE_CONFIG from '@/common/js/image-config.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -94,6 +101,10 @@ export default {
|
||||
type: 'text',
|
||||
content: '您好!我是AI智能客服,很高兴为您服务!\n\n我可以帮您:\n• 解答产品相关问题\n• 处理订单问题\n• 提供技术支持\n• 推荐相关商品\n\n请告诉我您需要什么帮助?',
|
||||
timestamp: Date.now() - 60000,
|
||||
actions: [
|
||||
{ type: 'like', icon: 'icon-dianzan1', text: '喜欢', count: 0 },
|
||||
{ type: 'dislike', icon: 'icon-dianzan1', text: '不喜欢', count: 0 }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -110,9 +121,6 @@ export default {
|
||||
// --- 聊天内容区域 ---
|
||||
scrollViewHeight: '0px',
|
||||
|
||||
// --- 键盘高度 ---
|
||||
keyboardHeight: 0,
|
||||
|
||||
// 事件处理器引用(用于清理)
|
||||
safeEventHandlers: new Map()
|
||||
}
|
||||
@@ -125,10 +133,10 @@ export default {
|
||||
|
||||
/// ---- 关于AI客服的配置,支持远程配置 ----
|
||||
userAvatar() {
|
||||
return this.globalAIKefuConfig?.userAvatar || this.$util.img(IMAGE_CONFIG.userAvatar)
|
||||
return this.globalAIKefuConfig?.userAvatar || '/static/images/user-avatar.png'
|
||||
},
|
||||
aiAvatar() {
|
||||
return this.globalAIKefuConfig?.aiAvatar || this.$util.img(IMAGE_CONFIG.aiAvatar)
|
||||
return this.globalAIKefuConfig?.aiAvatar || '/static/images/ai-avatar.png'
|
||||
},
|
||||
/// ---- others ----
|
||||
containerHeight() {
|
||||
@@ -146,21 +154,10 @@ export default {
|
||||
return ``
|
||||
},
|
||||
wrapperChatContentStyle() {
|
||||
// #ifdef H5
|
||||
// H5端没有自定义导航栏,不需要paddingTop
|
||||
return {
|
||||
height: this.containerHeight,
|
||||
paddingTop: '0'
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序端根据导航栏显示情况决定
|
||||
return {
|
||||
height: this.containerHeight,
|
||||
paddingTop: this.showCustomNavbar ? '0' : (this.navBarHeight + 'px')
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
|
||||
@@ -181,23 +178,11 @@ export default {
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 监听键盘高度变化
|
||||
uni.onKeyboardHeightChange((res) => {
|
||||
console.log('键盘高度变化:', res.height);
|
||||
this.keyboardHeight = res.height;
|
||||
if (this.$refs.chat && this.$refs.chat.updateKeyboardHeight) {
|
||||
this.$refs.chat.updateKeyboardHeight(res.height);
|
||||
}
|
||||
});
|
||||
// 可在此补充逻辑(如刷新未读数)
|
||||
},
|
||||
|
||||
onHide() {
|
||||
// 取消监听键盘高度变化
|
||||
uni.offKeyboardHeightChange();
|
||||
this.keyboardHeight = 0;
|
||||
if (this.$refs.chat && this.$refs.chat.updateKeyboardHeight) {
|
||||
this.$refs.chat.updateKeyboardHeight(0);
|
||||
}
|
||||
// 页面隐藏时可暂停音频等
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
@@ -272,20 +257,6 @@ export default {
|
||||
// ========== 初始化页面 ==========
|
||||
initChat() {
|
||||
console.log('AI聊天页面初始化');
|
||||
|
||||
// 检查是否有从商品分类传递过来的商品信息
|
||||
const selectedProduct = uni.getStorageSync('selectedProduct')
|
||||
if (selectedProduct) {
|
||||
console.log('检测到选择的商品:', selectedProduct)
|
||||
|
||||
// 清除存储的商品信息,避免重复发送
|
||||
uni.removeStorageSync('selectedProduct')
|
||||
|
||||
// 延迟发送商品信息,确保聊天组件已经初始化
|
||||
setTimeout(() => {
|
||||
this.sendProductToAI(selectedProduct)
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
|
||||
async initNavigation() {
|
||||
@@ -511,20 +482,6 @@ export default {
|
||||
onVideoPause(message) {
|
||||
console.log('视频暂停:', message);
|
||||
},
|
||||
|
||||
sendProductToAI(product) {
|
||||
if (!this.$refs.chat) {
|
||||
console.error('聊天组件未初始化')
|
||||
return
|
||||
}
|
||||
|
||||
const productContent = `我想了解以下商品的详细信息:\n商品名称:${product.goods_name}\n价格:¥${product.price}\n商品ID:${product.goods_id}`
|
||||
|
||||
console.log('商品信息已发送到AI客服:', productContent)
|
||||
|
||||
// 使用sendMessageWithContent方法发送商品信息
|
||||
this.$refs.chat.sendMessageWithContent(productContent)
|
||||
},
|
||||
|
||||
onLinkOpen(message) {
|
||||
uni.showModal({
|
||||
@@ -614,8 +571,8 @@ export default {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
background-color: white; /* 白色 */
|
||||
border-bottom: 1rpx solid #e5e5e5;
|
||||
background-color: #c4e0ff; /* 浅蓝色 */
|
||||
border-bottom: 2rpx solid #eeeeee;
|
||||
|
||||
.header-left {
|
||||
flex: 0 0 auto;
|
||||
|
||||
@@ -248,8 +248,7 @@ export default {
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
},
|
||||
personnel_bg: '',
|
||||
mpShareData: null //小程序分享数据
|
||||
personnel_bg: ''
|
||||
};
|
||||
},
|
||||
onLoad(option) {
|
||||
@@ -304,24 +303,6 @@ export default {
|
||||
});
|
||||
},
|
||||
onShow() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.$util.getMpShare('/pages_tool/contact/contact').then(res => {
|
||||
this.mpShareData = res;
|
||||
}).catch(err => {
|
||||
// 如果后台没配置,用默认值
|
||||
this.mpShareData = {
|
||||
appMessage: {
|
||||
title: '联系我们',
|
||||
path: '/pages_tool/contact/contact'
|
||||
},
|
||||
timeLine: {
|
||||
title: '联系我们',
|
||||
query: ''
|
||||
}
|
||||
};
|
||||
});
|
||||
// #endif
|
||||
|
||||
this.$util.hideHomeButton();
|
||||
},
|
||||
methods: {
|
||||
@@ -468,28 +449,9 @@ export default {
|
||||
longitude: parseFloat(this.shop.longitude),
|
||||
name: this.$lang('oneClickNavigation'),
|
||||
});
|
||||
},
|
||||
onShareAppMessage() {
|
||||
return this.mpShareData?.appMessage || {
|
||||
title: '联系我们',
|
||||
path: '/pages_tool/contact/contact'
|
||||
};
|
||||
},
|
||||
onShareTimeline() {
|
||||
return this.mpShareData?.timeLine || {
|
||||
title: '联系我们',
|
||||
query: ''
|
||||
};
|
||||
},
|
||||
onAddToFavorites() {
|
||||
return {
|
||||
title: '联系我们',
|
||||
path: '/pages_tool/contact/contact'
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -134,8 +134,7 @@ export default {
|
||||
levelId: 0,
|
||||
levelInfo: {
|
||||
bg_color: '#333'
|
||||
},
|
||||
mpShareData: null //小程序分享数据
|
||||
}
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
@@ -162,26 +161,7 @@ export default {
|
||||
});
|
||||
this.levelInfo = levelInfo;
|
||||
},
|
||||
onShow() {
|
||||
//小程序分享
|
||||
// #ifdef MP-WEIXIN
|
||||
this.$util.getMpShare('/pages_tool/member/card').then(res => {
|
||||
this.mpShareData = res;
|
||||
}).catch(err => {
|
||||
// 如果后台没配置,用默认值
|
||||
this.mpShareData = {
|
||||
appMessage: {
|
||||
title: '专属会员权益,快来体验',
|
||||
path: '/pages_tool/member/card'
|
||||
},
|
||||
timeLine: {
|
||||
title: '专属会员权益,快来体验',
|
||||
query: '/pages_tool/member/card'
|
||||
}
|
||||
};
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
onShow() { },
|
||||
methods: {
|
||||
headimgError() {
|
||||
this.memberInfo.headimg = this.$util.getDefaultImage().head;
|
||||
@@ -205,24 +185,6 @@ export default {
|
||||
}
|
||||
this.$util.redirectTo(this.$util.MEMBER_PAGE_URL);
|
||||
return true;
|
||||
},
|
||||
onShareAppMessage() {
|
||||
return this.mpShareData?.appMessage || {
|
||||
title: '专属会员权益,快来体验',
|
||||
path: '/pages_tool/member/card'
|
||||
};
|
||||
},
|
||||
onShareTimeline() {
|
||||
return this.mpShareData?.timeLine || {
|
||||
title: '专属会员权益,快来体验',
|
||||
query: '/pages_tool/member/card'
|
||||
};
|
||||
},
|
||||
onAddToFavorites() {
|
||||
return {
|
||||
title: '专属会员权益,快来体验',
|
||||
path: '/pages_tool/member/card'
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
@click.stop="toDetail(item)">
|
||||
<view class="pic">
|
||||
<image :src="$util.img(item.goods_image.split(',')[0], { size: 'mid' })" mode="aspectFill"
|
||||
@error="goodsImageError(index)" :show-menu-by-longpress="true"></image>
|
||||
@error="goodsImageError(index)"></image>
|
||||
</view>
|
||||
<view class="goods_info">
|
||||
<view class="goods_name font-size-base">{{ item.sku_name }}</view>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<text class="iconfont" :class="$util.inArray(item.id, idArr) != -1 ? 'icon-yuan_checked color-base-text' : 'icon-yuan_checkbox'"></text>
|
||||
</view>
|
||||
<view class="goods-img" @click="toDetail(item)">
|
||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" :show-menu-by-longpress="true"></image>
|
||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"></image>
|
||||
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}</view>
|
||||
</view>
|
||||
<view class="info-wrap" @click="toDetail(item)">
|
||||
|
||||
30
readme.md
30
readme.md
@@ -37,7 +37,7 @@
|
||||
*已有依赖可跳过此步骤*
|
||||
|
||||
##### 4.1.1.2 【发布构建】
|
||||
1. 使用 HBuilderX 打开项目(不用改manifest.json的web,只有H5要改 )
|
||||
1. 使用 HBuilderX 打开项目
|
||||
2. 选择菜单栏「发行」 → 「小程序-微信」→ 「发行」,等待构建完成
|
||||
⚠️ **注意**:底部控制台弹出"请在微信小程序开发者工具中点击上传"后再执行下一步
|
||||
3. 打开资源管理器→项目根目录,右键选择「在终端中打开」,执行命令:
|
||||
@@ -67,9 +67,7 @@
|
||||
- 确认 `site.js` 中的 `uniacid` 为客户编号
|
||||
- 用微信开发者工具导入解压后的代码目录
|
||||
- 编译验证无误后,上传发布
|
||||
##### 4.1.1.5 【本地设置】
|
||||

|
||||
#####
|
||||
|
||||
---
|
||||
|
||||
### 4.2 快应用发布
|
||||
@@ -80,22 +78,20 @@
|
||||
1. 使用 HBuilderX 打开项目
|
||||
2. 打开项目根目录的 `manifest.json` 文件,切换至可视化配置界面:
|
||||
1. 依次点击「Web 配置」→「运行的基础配置」
|
||||
2. 根据情况查看是否修改路径中/hwappx/????/的客户编号
|
||||
📌 **通用版**保留原有 `/hwappx/common/`
|
||||
📌 **测试版**保留原有 `/hwappx/test/`
|
||||
💡 **个性化定制示例**:`/hwappx/2811/`,其中 2811 为定制化版客户编号
|
||||
2. 修改路径中的客户编号
|
||||
3. 📌 **通用版**保留原有 `/hwappx/common/`
|
||||
|
||||
💡 **示例**:`/hwappx/2811/`,其中 2811 为定制化版客户编号
|
||||
|
||||
3. 选择菜单栏「发行」 → 「自定义发行」 →(「H5-xcx.aigc-quickapp.com」查看.local.config.js中站点的网址前缀,站点的网址前缀是什么就选自定义发行中相同的格式)
|
||||
例如:网址是5g.aigc-quickapp.com,自定义发行就选H5-5g.aigc-quickapp.com
|
||||
网址是dev.aigc-quickapp.com,自定义发行就选H5-xcx.aigc-quickapp.com
|
||||
4. 修改以下配置:
|
||||
3. 选择菜单栏「发行」 → 「自定义发行」 →「H5-xcx.aigc-quickapp.com」,修改以下配置:
|
||||
1. **网站标题**:快应用
|
||||
2. **网站域名**:当前客户域名(示例:xcx.aigc-quickapp.com或者5g.aigc-quickapp.com)
|
||||
2. **网站域名**:当前客户域名(示例:xcx.aigc-quickapp.com)
|
||||
3. 确认后点击「发行」等待构建完成
|
||||
|
||||
⚠️ **注意**:底部控制台弹出“项目 lucky_shop 导出Web成功,路径为:D:\项目文件\项目根目录\unpackage\dist\build\web”后再执行下一步
|
||||
💡 **示例**:`项目 lucky_shop 导出Web成功,路径为:D:\0.项目源码\lucky_shop\unpackage\dist\build\web`
|
||||
5. 按控制台提示的路径找到 `web` 目录,将该目录下所有文件手动打包成一个 `.zip` 压缩包
|
||||
|
||||
4. 按控制台提示的路径找到 `web` 目录,将该目录下所有文件手动打包成一个 `.zip` 压缩包
|
||||
*仅打包文件,不包含外层 web 目录*
|
||||
|
||||
##### 4.2.1.2 【重命名】
|
||||
@@ -109,16 +105,12 @@
|
||||
🚫 **禁止**:压缩包命名禁止包含 `/ \ : * ? " < > |` 等特殊字符
|
||||
|
||||
##### 4.2.1.3 【交付与最终发布】🔍
|
||||
|
||||
1. 将重命名后的压缩包发送给运维人员
|
||||
2. **运维人员操作流程**:
|
||||
1. 解压压缩包
|
||||
2. 打开快应用开发者工具,导入解压后的代码目录
|
||||
3. 验证代码无报错后,执行上传发布操作
|
||||
##### 4.2.1.3 【测试流程】
|
||||
H5:打开自己的浏览器输入网址(https://5g.aigc-quickapp.com/hwappx/common/?uniacid=75)改站点
|
||||
如果是test就把hwappx/common/改为hwappx/test/----------改站点
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 5. 📝 构建脚本说明
|
||||
|
||||
@@ -134,9 +134,8 @@ async function main() {
|
||||
// 2) 创建 ZIP 文件(如果未指定 --no-zip)
|
||||
if (!options.noZip) {
|
||||
const cwd = path.join(__dirname, '..');
|
||||
const buildDir = options.mode === 'production' ? 'build' : 'dev';
|
||||
const sourceDir = path.join(cwd, 'unpackage', 'dist', buildDir, 'mp-weixin');
|
||||
const destDir = path.join(cwd, 'unpackage', 'dist', buildDir);
|
||||
const sourceDir = path.join(cwd, 'unpackage', 'dist', 'build', 'mp-weixin');
|
||||
const destDir = path.join(cwd, 'unpackage', 'dist', 'build');
|
||||
const zipFilePath = await createZipWithSystemCommand(sourceDir, destDir);
|
||||
console.log(`ZIP 文件路径: ${zipFilePath}`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user