Compare commits
81 Commits
test/test-
...
merge/tmp-
| Author | SHA1 | Date | |
|---|---|---|---|
| 50908a274b | |||
| 9e68562de5 | |||
| 8e5561c2a7 | |||
| 8d5c543a4f | |||
| cf411407dd | |||
| 86e4be302b | |||
| 2fd06796e1 | |||
| 60c51e551a | |||
| d0d3ac47c6 | |||
| af677f6565 | |||
| f889efeb16 | |||
| d026bb6180 | |||
| 82b856f5d9 | |||
| 7f33b6948a | |||
| 3489b89a0b | |||
| dc4f2769f9 | |||
| 5cf87e4d56 | |||
| c9ca63cff5 | |||
| a6072f4895 | |||
| 38947ddc62 | |||
| cfe83ada38 | |||
| 1e9e0ffea8 | |||
| 28acdfb621 | |||
| ad9f1c495b | |||
| 7c2a7468e7 | |||
| b2051dcf92 | |||
| e3c86de84a | |||
| 6e0eb5d4f3 | |||
| 9d186ff094 | |||
| 8611ec4dc3 | |||
| 89bfd94a07 | |||
| 5cf8b7b467 | |||
| 2cc0162435 | |||
| d8e75a0c9e | |||
| 8af3c0ccb9 | |||
| d8b78168a8 | |||
| 7675e8e04d | |||
| 88d05048a6 | |||
| 28a92c0635 | |||
| b5a47b8b4e | |||
| 4f99e8b5f2 | |||
| 87fc49146c | |||
| 826fcfb024 | |||
| 7174d87b79 | |||
| 4bc747cd9c | |||
| e046868665 | |||
| 19c3ec4f9f | |||
| ffbc15929d | |||
| 524280d816 | |||
| 6e736e0733 | |||
| 4891566793 | |||
| b02f5fbc21 | |||
| 4f5a300afa | |||
| aae6e76797 | |||
| 3162d6a9f2 | |||
| 8010ac45a6 | |||
| baebba4cef | |||
| 1be7155fe8 | |||
| 611c0b930b | |||
| ec1903519d | |||
| b1953a020d | |||
| 3ce26322e0 | |||
| 588ec60948 | |||
| 962ea124fa | |||
| 0ca0fe3b0d | |||
| cef2b6e02e | |||
| e018f9a8b9 | |||
| def8a6cf6f | |||
| 857f295923 | |||
| 50ee885b74 | |||
| 410f248510 | |||
| ba5fcfaab1 | |||
| 5a46bb8d69 | |||
| e33ad16d10 | |||
| d8ba7ace0f | |||
| 875072bb68 | |||
| 0912b9b031 | |||
| 7ffe4acc0f | |||
| 4bcf0cbd2d | |||
| fdf85dfec7 | |||
| 1d62b37060 |
@@ -42,6 +42,18 @@ const localDevConfig = ({
|
|||||||
uniacid: 2,
|
uniacid: 2,
|
||||||
domain: 'http://localhost:8050/',
|
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']; // 选择要使用的环境配置
|
})['1']; // 选择要使用的环境配置
|
||||||
|
|
||||||
export default localDevConfig;
|
export default localDevConfig;
|
||||||
@@ -5,6 +5,17 @@ import store from '@/store/index.js'
|
|||||||
let currentConversationId = null;
|
let currentConversationId = null;
|
||||||
const CONVERSATION_KEY = 'ai_conversation_id';
|
const CONVERSATION_KEY = 'ai_conversation_id';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取域名配置参数(避免重复代码)
|
||||||
|
*/
|
||||||
|
function getDomainParams() {
|
||||||
|
return {
|
||||||
|
imgDomain: Config.imgDomain,
|
||||||
|
h5Domain: Config.h5Domain,
|
||||||
|
baseUrl: Config.baseUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化时从本地读取会话ID
|
// 初始化时从本地读取会话ID
|
||||||
try {
|
try {
|
||||||
const saved = uni.getStorageSync(CONVERSATION_KEY);
|
const saved = uni.getStorageSync(CONVERSATION_KEY);
|
||||||
@@ -16,12 +27,28 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取uniacid
|
||||||
|
* @returns {number} uniacid
|
||||||
|
*/
|
||||||
|
getUniacid() {
|
||||||
|
return uni.getStorageSync('uniacid') || Config.uniacid; // 从缓存中获取uniacid,或使用默认uniacid
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送普通消息(blocking 模式)
|
* 发送普通消息(blocking 模式)
|
||||||
*/
|
*/
|
||||||
async sendMessage(message, options = {}) {
|
async sendMessage(message, options = {}) {
|
||||||
try {
|
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;
|
const aiConfig = store.getters.globalAIKefuConfig;
|
||||||
|
console.log('[AI Service] aiConfig:', aiConfig);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
url: '/api/kefu/chat',
|
url: '/api/kefu/chat',
|
||||||
data: {
|
data: {
|
||||||
@@ -31,28 +58,38 @@ export default {
|
|||||||
query: message,
|
query: message,
|
||||||
response_mode: 'blocking', // 强制 blocking
|
response_mode: 'blocking', // 强制 blocking
|
||||||
user: store.state.memberInfo?.id || 'anonymous',
|
user: store.state.memberInfo?.id || 'anonymous',
|
||||||
conversation_id: this.getConversationId() || ''
|
conversation_id: this.getConversationId() || '',
|
||||||
|
...getDomainParams()
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Sending blocking request to:', params.url);
|
console.log('[AI Service] 发送请求到:', params.url);
|
||||||
console.log('Request data:', params.data);
|
console.log('[AI Service] 请求参数:', JSON.stringify(params.data, null, 2));
|
||||||
|
|
||||||
const response = await http.sendRequest({
|
const response = await http.sendRequest({
|
||||||
...params,
|
...params,
|
||||||
async: false
|
async: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('[AI Service] 收到响应:', response);
|
||||||
|
|
||||||
const result = this.handleResponse(response);
|
const result = this.handleResponse(response);
|
||||||
if (result.conversationId) {
|
if (result.conversationId) {
|
||||||
this.setConversationId(result.conversationId);
|
this.setConversationId(result.conversationId);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Dify API请求失败:', 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
|
||||||
|
});
|
||||||
throw new Error('AI服务暂时不可用,请稍后重试');
|
throw new Error('AI服务暂时不可用,请稍后重试');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -63,8 +100,12 @@ export default {
|
|||||||
async sendStreamMessage(message, onChunk, onComplete) {
|
async sendStreamMessage(message, onChunk, onComplete) {
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
return new Promise((resolve, reject) => {
|
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({
|
const socketTask = wx.connectSocket({
|
||||||
url: 'wss://dev.aigc-quickapp.com/ws/aikefu',
|
url: wssUrl,
|
||||||
header: {}
|
header: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -77,7 +118,7 @@ export default {
|
|||||||
socketTask.send({
|
socketTask.send({
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
action: 'auth',
|
action: 'auth',
|
||||||
uniacid: store.state.uniacid || '1',
|
uniacid: this.getUniacid(),
|
||||||
token: store.state.token || 'test_token',
|
token: store.state.token || 'test_token',
|
||||||
user_id: store.state.memberInfo?.id || 'anonymous'
|
user_id: store.state.memberInfo?.id || 'anonymous'
|
||||||
})
|
})
|
||||||
@@ -95,10 +136,11 @@ export default {
|
|||||||
socketTask.send({
|
socketTask.send({
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
action: 'chat',
|
action: 'chat',
|
||||||
uniacid: store.state.uniacid || '1',
|
uniacid: this.getUniacid(),
|
||||||
query: message,
|
query: message,
|
||||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||||
conversation_id: this.getConversationId() || ''
|
conversation_id: this.getConversationId() || '',
|
||||||
|
...getDomainParams()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
} else if (data.type === 'auth_failed') {
|
} else if (data.type === 'auth_failed') {
|
||||||
@@ -110,14 +152,14 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理流式消息块
|
// 处理流式消息块
|
||||||
else if (data.type === 'message' || data.event === 'message') {
|
else if (data.event === 'message') {
|
||||||
const text = data.answer || data.content || data.text || '';
|
const text = data.answer || data.content || data.text || '';
|
||||||
content += text;
|
content += text;
|
||||||
if (onChunk) onChunk(text);
|
if (onChunk) onChunk(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理流结束
|
// 处理流结束
|
||||||
else if (data.event === 'message_end' || data.type === 'message_end') {
|
else if (data.event === 'message_end') {
|
||||||
conversationId = data.conversation_id || '';
|
conversationId = data.conversation_id || '';
|
||||||
if (conversationId) {
|
if (conversationId) {
|
||||||
this.setConversationId(conversationId);
|
this.setConversationId(conversationId);
|
||||||
@@ -175,12 +217,14 @@ export default {
|
|||||||
'Accept': 'text/event-stream',
|
'Accept': 'text/event-stream',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
uniacid: store.state.uniacid || '1',
|
uniacid: this.getUniacid(),
|
||||||
|
token: store.state.token || 'test_token',
|
||||||
inputs: {},
|
inputs: {},
|
||||||
query: message,
|
query: message,
|
||||||
response_mode: 'streaming',
|
response_mode: 'streaming',
|
||||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||||
conversation_id: this.getConversationId() || ''
|
conversation_id: this.getConversationId() || '',
|
||||||
|
...getDomainParams()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -317,9 +361,11 @@ export default {
|
|||||||
const params = {
|
const params = {
|
||||||
url: '/api/kefu/createConversation',
|
url: '/api/kefu/createConversation',
|
||||||
data: {
|
data: {
|
||||||
uniacid: store.state.uniacid,
|
uniacid: this.getUniacid(),
|
||||||
|
token: store.state.token || 'test_token',
|
||||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||||
member_id: store.state.memberInfo?.id || '',
|
member_id: store.state.memberInfo?.id || '',
|
||||||
|
...getDomainParams()
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@@ -342,6 +388,7 @@ export default {
|
|||||||
async: false,
|
async: false,
|
||||||
data: {}
|
data: {}
|
||||||
});
|
});
|
||||||
|
console.log('/api/kefu/health response', response);
|
||||||
const available = [response?.data?.status, response?.data?.components?.ai_service_config?.status]
|
const available = [response?.data?.status, response?.data?.components?.ai_service_config?.status]
|
||||||
.filter(item => item === 'healthy').length > 0;
|
.filter(item => item === 'healthy').length > 0;
|
||||||
return {
|
return {
|
||||||
@@ -363,7 +410,10 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const response = await http.sendRequest({
|
const response = await http.sendRequest({
|
||||||
url: '/api/kefu/clear-conversation',
|
url: '/api/kefu/clear-conversation',
|
||||||
data: { conversation_id: conversationId },
|
data: {
|
||||||
|
conversation_id: conversationId,
|
||||||
|
...getDomainParams()
|
||||||
|
},
|
||||||
async: false
|
async: false
|
||||||
});
|
});
|
||||||
return response.success;
|
return response.success;
|
||||||
@@ -382,13 +432,14 @@ export default {
|
|||||||
throw new Error('会话ID(conversation_id)是必填参数');
|
throw new Error('会话ID(conversation_id)是必填参数');
|
||||||
}
|
}
|
||||||
const requestData = {
|
const requestData = {
|
||||||
uniacid: params.uniacid || store.state.uniacid || '1',
|
uniacid: params.uniacid || this.getUniacid(),
|
||||||
conversation_id: params.conversation_id,
|
conversation_id: params.conversation_id,
|
||||||
user_id: params.user_id || store.state.memberInfo?.id || 'anonymous',
|
user_id: params.user_id || store.state.memberInfo?.id || 'anonymous',
|
||||||
limit: params.limit || 20,
|
limit: params.limit || 20,
|
||||||
offset: params.offset || 0,
|
offset: params.offset || 0,
|
||||||
member_id: params.member_id || store.state.memberInfo?.id || '',
|
member_id: params.member_id || store.state.memberInfo?.id || '',
|
||||||
token: params.token || ''
|
token: params.token || '',
|
||||||
|
...getDomainParams()
|
||||||
};
|
};
|
||||||
const response = await http.sendRequest({
|
const response = await http.sendRequest({
|
||||||
url: '/api/kefu/getHistory',
|
url: '/api/kefu/getHistory',
|
||||||
@@ -427,13 +478,15 @@ export default {
|
|||||||
if (window.currentEventSource) {
|
if (window.currentEventSource) {
|
||||||
window.currentEventSource.close();
|
window.currentEventSource.close();
|
||||||
}
|
}
|
||||||
const params = new URLSearchParams({
|
const paramsObj = {
|
||||||
uniacid: store.state.uniacid || '1',
|
uniacid: this.getUniacid(),
|
||||||
user_id: store.state.memberInfo?.id || '123456',
|
user_id: store.state.memberInfo?.id,
|
||||||
query: message,
|
query: message,
|
||||||
conversation_id: conversationId || '',
|
conversation_id: conversationId || '',
|
||||||
stream: 'true'
|
stream: 'true',
|
||||||
});
|
...getDomainParams()
|
||||||
|
};
|
||||||
|
const params = new URLSearchParams(paramsObj);
|
||||||
const url = `${Config.baseUrl}/api/kefu/chat?${params.toString()}`;
|
const url = `${Config.baseUrl}/api/kefu/chat?${params.toString()}`;
|
||||||
try {
|
try {
|
||||||
const eventSource = new EventSource(url);
|
const eventSource = new EventSource(url);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
* 客服统一处理服务
|
* 客服统一处理服务
|
||||||
* 整合各种客服方式,提供统一的调用接口
|
* 整合各种客服方式,提供统一的调用接口
|
||||||
*/
|
*/
|
||||||
|
import IMAGE_CONFIG from './image-config.js';
|
||||||
|
|
||||||
export class CustomerService {
|
export class CustomerService {
|
||||||
constructor(vueInstance, externalConfig = null) {
|
constructor(vueInstance, externalConfig = null) {
|
||||||
this.vm = vueInstance;
|
this.vm = vueInstance;
|
||||||
@@ -471,13 +473,11 @@ export class CustomerService {
|
|||||||
const shopInfo = this.vm.shopInfo || {};
|
const shopInfo = this.vm.shopInfo || {};
|
||||||
const config = this.getPlatformConfig();
|
const config = this.getPlatformConfig();
|
||||||
|
|
||||||
if (config?.type === 'aikefu') {
|
// 优先使用指定的智能客服头像
|
||||||
return shopInfo.aiAgentimg || '';
|
if (config?.type === 'aikefu' || config?.type === 'wxwork' || config?.type === 'qyweixin') {
|
||||||
} else if (config?.type === 'wxwork' || config?.type === 'qyweixin') {
|
return this.vm.$util.img(IMAGE_CONFIG.aiAvatar);
|
||||||
return shopInfo.aiAgentimg || '';
|
|
||||||
} else if (config?.type === 'third') {
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shopInfo.kefuimg || this.vm.$util?.getDefaultImage()?.kefu || '';
|
return shopInfo.kefuimg || this.vm.$util?.getDefaultImage()?.kefu || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
common/js/image-config.js
Normal file
15
common/js/image-config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* 图片配置文件
|
||||||
|
* 将大图片上传到服务器后,请在此处配置网络URL
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 替换为你上传后的实际图片URL
|
||||||
|
const IMAGE_CONFIG = {
|
||||||
|
// AI客服头像
|
||||||
|
aiAvatar: 'public/static/img/s-agent.jpg',
|
||||||
|
|
||||||
|
// 用户默认头像
|
||||||
|
userAvatar: 'public/static/img/s-user.jpg'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IMAGE_CONFIG;
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<block v-if="goodsList.length">
|
<block v-if="goodsList.length">
|
||||||
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
||||||
<view class="goods-img" @tap.stop="toDetail(item)">
|
<view class="goods-img" @tap.stop="toDetail(item)">
|
||||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" />
|
<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 }}
|
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -99,9 +99,6 @@
|
|||||||
<text class="unit price-style small">.{{
|
<text class="unit price-style small">.{{
|
||||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||||
</block>
|
</block>
|
||||||
<block v-else>
|
|
||||||
<text class="unit price-style small">{{ $lang('Make') ? $lang('Make') : '询底价' }}</text>
|
|
||||||
</block>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="member-price-tag"
|
<view class="member-price-tag"
|
||||||
v-if="item.member_price && item.member_price == showPrice(item)">
|
v-if="item.member_price && item.member_price == showPrice(item)">
|
||||||
@@ -120,6 +117,12 @@
|
|||||||
<text>{{ showMarketPrice(item) }}</text>
|
<text>{{ showMarketPrice(item) }}</text>
|
||||||
</view>
|
</view>
|
||||||
</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">
|
<!-- <view class="right-wrap" v-if="value.template == 2 || value.template == 4">
|
||||||
<block v-if="item.is_virtual">
|
<block v-if="item.is_virtual">
|
||||||
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
|
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
|
||||||
@@ -175,7 +178,7 @@
|
|||||||
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
||||||
<view class="goods-img" @tap.stop="toDetail(item)">
|
<view class="goods-img" @tap.stop="toDetail(item)">
|
||||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"
|
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"
|
||||||
:lazy-load="true" />
|
: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 class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="info-wrap">
|
<view class="info-wrap">
|
||||||
@@ -191,10 +194,6 @@
|
|||||||
<text class="unit price-style small">.{{
|
<text class="unit price-style small">.{{
|
||||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||||
</block>
|
</block>
|
||||||
<block v-else>
|
|
||||||
<text class="unit price-style small">{{ $lang('Make') ? $lang('Make') : '询底价' }}</text>
|
|
||||||
</block>
|
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
<view class="member-price-tag"
|
<view class="member-price-tag"
|
||||||
v-if="item.member_price && item.member_price == showPrice(item)">
|
v-if="item.member_price && item.member_price == showPrice(item)">
|
||||||
@@ -212,6 +211,12 @@
|
|||||||
<text>{{ showMarketPrice(item) }}</text>
|
<text>{{ showMarketPrice(item) }}</text>
|
||||||
</view>
|
</view>
|
||||||
</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">
|
<view class="right-wrap" v-if="value.template == 2">
|
||||||
<block v-if="item.is_virtual">
|
<block v-if="item.is_virtual">
|
||||||
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
|
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
|
||||||
@@ -718,6 +723,15 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 跳转到商品详情
|
||||||
|
* @param {Object} item 商品信息
|
||||||
|
*/
|
||||||
|
sendToAIChat(item) {
|
||||||
|
this.$util.redirectTo('/pages_goods/detail', {
|
||||||
|
goods_id: item.goods_id
|
||||||
|
});
|
||||||
|
},
|
||||||
diyRedirectTo(link) {
|
diyRedirectTo(link) {
|
||||||
if (!link) return;
|
if (!link) return;
|
||||||
this.$util.diyRedirectTo(JSON.parse(link));
|
this.$util.diyRedirectTo(JSON.parse(link));
|
||||||
@@ -1384,4 +1398,25 @@ export default {
|
|||||||
color: var(--main-color);
|
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>
|
</style>
|
||||||
@@ -8,7 +8,8 @@
|
|||||||
<image class="goods-img"
|
<image class="goods-img"
|
||||||
:src="$util.img(item.goods_image, { size: goodsValue.template == 'large-mode' ? 'big' : 'mid' })"
|
:src="$util.img(item.goods_image, { size: goodsValue.template == 'large-mode' ? 'big' : 'mid' })"
|
||||||
mode="widthFix" @error="imgError(index)"
|
mode="widthFix" @error="imgError(index)"
|
||||||
:style="{ borderRadius: goodsValue.imgAroundRadius * 2 + 'rpx' }" />
|
:style="{ borderRadius: goodsValue.imgAroundRadius * 2 + 'rpx' }"
|
||||||
|
:show-menu-by-longpress="true" />
|
||||||
<view class="sell-out" v-if="item.stock <= 0">
|
<view class="sell-out" v-if="item.stock <= 0">
|
||||||
<text class="iconfont icon-shuqing"></text>
|
<text class="iconfont icon-shuqing"></text>
|
||||||
</view>
|
</view>
|
||||||
@@ -117,7 +118,7 @@
|
|||||||
<view class="goods-img-wrap">
|
<view class="goods-img-wrap">
|
||||||
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix" @error="imgError(index)"
|
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix" @error="imgError(index)"
|
||||||
:lazy-load="true" />
|
:lazy-load="true" :show-menu-by-longpress="true" />
|
||||||
<view class="sell-out" v-if="item.stock <= 0">
|
<view class="sell-out" v-if="item.stock <= 0">
|
||||||
<text class="iconfont icon-shuqing"></text>
|
<text class="iconfont icon-shuqing"></text>
|
||||||
</view>
|
</view>
|
||||||
@@ -184,7 +185,7 @@
|
|||||||
<view class="goods-img-wrap">
|
<view class="goods-img-wrap">
|
||||||
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@error="imgError(dataIndex)" :lazy-load="true" />
|
@error="imgError(dataIndex)" :lazy-load="true" :show-menu-by-longpress="true" />
|
||||||
<view class="sell-out" v-if="dataItem.stock <= 0">
|
<view class="sell-out" v-if="dataItem.stock <= 0">
|
||||||
<text class="iconfont icon-shuqing"></text>
|
<text class="iconfont icon-shuqing"></text>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<div class="goods-img-wrap">
|
<div class="goods-img-wrap">
|
||||||
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@error="imgError(index, dataIndex)" :lazy-load="true" />
|
@error="imgError(index, dataIndex)" :lazy-load="true" :show-menu-by-longpress="true" />
|
||||||
<view class="sell-out" v-if="dataItem.stock <= 0">
|
<view class="sell-out" v-if="dataItem.stock <= 0">
|
||||||
<text class="iconfont icon-shuqing"></text>
|
<text class="iconfont icon-shuqing"></text>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -913,7 +913,7 @@ export default {
|
|||||||
// ===== 主容器样式 =====
|
// ===== 主容器样式 =====
|
||||||
.tab-diy-component-preview-container {
|
.tab-diy-component-preview-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 300px;
|
min-height: 0px;
|
||||||
display: flex;
|
display: flex;
|
||||||
border: 1px solid #e0e0e0;
|
border: 1px solid #e0e0e0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|||||||
@@ -53,9 +53,19 @@
|
|||||||
|
|
||||||
<!-- 视频播放弹窗 -->
|
<!-- 视频播放弹窗 -->
|
||||||
<uni-popup ref="videoPopup" type="center" style="background:transparent;width:100%;height:100%;">
|
<uni-popup ref="videoPopup" type="center" style="background:transparent;width:100%;height:100%;">
|
||||||
<view class="video-container" style="position:fixed;top:30%;width:100%;left:0;">
|
<view class="video-container" :style="popupVideoStyle">
|
||||||
<video class="adaptive-video" :autoPauseIfNavigate="true" :autoPauseIfOpenNative="true"
|
<video
|
||||||
:autoplay="false" :enableAutoRotation="true" id="myVideo" :src="video_url" :controls="true"></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>
|
</view>
|
||||||
</uni-popup>
|
</uni-popup>
|
||||||
</view>
|
</view>
|
||||||
@@ -126,12 +136,38 @@ export default {
|
|||||||
isIndicatorDots() {
|
isIndicatorDots() {
|
||||||
return this.value.carousel.type != 'hide' &&
|
return this.value.carousel.type != 'hide' &&
|
||||||
1 != Math.ceil(this.value.list.length / (this.value.pageCount * this.value.rowCount));
|
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: {
|
methods: {
|
||||||
// 显示视频播放弹窗
|
// 显示视频播放弹窗
|
||||||
showVideo(item) {
|
showVideo(item) {
|
||||||
this.video_url = item.videoUrl;
|
this.video_url = item.videoUrl;
|
||||||
|
// 重置视频比例为null,等待新的检测结果
|
||||||
|
this.popupVideoRatio = null;
|
||||||
this.$refs.videoPopup.open();
|
this.$refs.videoPopup.open();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -139,6 +175,39 @@ export default {
|
|||||||
imgError(index) {
|
imgError(index) {
|
||||||
// 图片加载失败的处理逻辑
|
// 图片加载失败的处理逻辑
|
||||||
console.log('图片加载失败:', 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,7 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<video data-component-name="diy-video" class="diy-video" :src="$util.img(value.videoUrl)"
|
<view class="video-container" :style="containerStyle">
|
||||||
:poster="$util.img(value.imageUrl)" :style="videoWarpCss" objectFit="cover"
|
<!-- H5端使用原生video标签 -->
|
||||||
@tap="handlerClick(value.videoUrl)"></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>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -15,27 +44,160 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {
|
||||||
|
videoRatio: null, // 视频比例:'landscape'(横屏)或 'portrait'(竖屏)
|
||||||
|
videoUrl: '',
|
||||||
|
posterUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.initVideoData();
|
||||||
},
|
},
|
||||||
created() { },
|
|
||||||
mixins: [DiyMinx],
|
mixins: [DiyMinx],
|
||||||
watch: {
|
watch: {
|
||||||
// 组件刷新监听
|
value: {
|
||||||
componentRefresh: function (nval) { }
|
immediate: true,
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.initVideoData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
videoWarpCss: function () {
|
videoWarpCss: function () {
|
||||||
var obj = '';
|
var obj = '';
|
||||||
if (this.value.componentAngle == 'round') {
|
if (this.value && this.value.componentAngle == 'round') {
|
||||||
obj += 'border-top-left-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
|
obj += 'border-top-left-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
|
||||||
obj += 'border-top-right-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-left-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
||||||
obj += 'border-bottom-right-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
obj += 'border-bottom-right-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
||||||
}
|
}
|
||||||
return obj;
|
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: {
|
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) {
|
async handlerClick(videoUrl) {
|
||||||
await this.__$emitEvent({
|
await this.__$emitEvent({
|
||||||
eventName: 'video-tap', data: videoUrl, promiseCallback: (event, handler, awaitedResult) => {
|
eventName: 'video-tap', data: videoUrl, promiseCallback: (event, handler, awaitedResult) => {
|
||||||
@@ -48,11 +210,32 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
video {
|
.video-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.diy-video>>>.uni-video-container {
|
/* 基础样式 */
|
||||||
background-color: transparent;
|
.diy-video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* H5端样式 */
|
||||||
|
/* #ifdef H5 */
|
||||||
|
.h5-video {
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
/* #endif */
|
||||||
|
|
||||||
|
/* 微信小程序端样式 */
|
||||||
|
/* #ifdef MP-WEIXIN */
|
||||||
|
.mp-video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
/* #endif */
|
||||||
</style>
|
</style>
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
v-if="fixBtnShow && hasValidKefuConfig && isWeappOfficialKefu"
|
v-if="fixBtnShow && hasValidKefuConfig && isWeappOfficialKefu"
|
||||||
class="btn-item common-bg"
|
class="btn-item common-bg"
|
||||||
open-type="contact"
|
open-type="contact"
|
||||||
:style="{ backgroundImage: currentKefuImg ? `url(${currentKefuImg})` : '', backgroundSize: '100% 100%' }"
|
|
||||||
>
|
>
|
||||||
<text class="icox icox-kefu" v-if="!kefuimg"></text>
|
<text class="icox icox-kefu" v-if="!kefuimg"></text>
|
||||||
</button>
|
</button>
|
||||||
@@ -28,8 +27,13 @@
|
|||||||
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && !showWeappSystemKefu"
|
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && !showWeappSystemKefu"
|
||||||
class="btn-item common-bg"
|
class="btn-item common-bg"
|
||||||
@click="handleUnifiedKefuClick"
|
@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="isAIKefu && !currentKefuImg">🤖</text>
|
||||||
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
|
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
|
||||||
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
|
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
|
||||||
@@ -40,8 +44,13 @@
|
|||||||
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && showWeappSystemKefu"
|
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && showWeappSystemKefu"
|
||||||
class="btn-item common-bg"
|
class="btn-item common-bg"
|
||||||
@click="handleUnifiedKefuClick"
|
@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="isAIKefu && !currentKefuImg">🤖</text>
|
||||||
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
|
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
|
||||||
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
|
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
|
||||||
@@ -52,7 +61,6 @@
|
|||||||
v-if="fixBtnShow && hasValidKefuConfig && showWeappSystemKefu"
|
v-if="fixBtnShow && hasValidKefuConfig && showWeappSystemKefu"
|
||||||
class="btn-item common-bg"
|
class="btn-item common-bg"
|
||||||
open-type="contact"
|
open-type="contact"
|
||||||
:style="{ backgroundImage: currentKefuImg ? `url(${currentKefuImg})` : '', backgroundSize: '100% 100%' }"
|
|
||||||
>
|
>
|
||||||
<text class="icox icox-kefu" v-if="!kefuimg"></text>
|
<text class="icox icox-kefu" v-if="!kefuimg"></text>
|
||||||
</button>
|
</button>
|
||||||
@@ -304,6 +312,13 @@ export default {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kefu-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
.ai-icon {
|
.ai-icon {
|
||||||
font-size: 40rpx;
|
font-size: 40rpx;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -53,8 +53,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
// 检查配置,决定是否显示隐私协议
|
||||||
|
const shopInfo = uni.getStorageSync('shopInfo');
|
||||||
|
if (shopInfo?.ischina == 1 && shopInfo?.index_login_auth == 1) {
|
||||||
this.listenPrivacySettingChange()
|
this.listenPrivacySettingChange()
|
||||||
this.curPageShow()
|
this.curPageShow()
|
||||||
|
} else {
|
||||||
|
// 如果不需要显示隐私协议,直接触发同意事件
|
||||||
|
this.$emit('agree');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
try {
|
try {
|
||||||
@@ -152,28 +159,62 @@ export default {
|
|||||||
},
|
},
|
||||||
// 主动查询用户隐私政策授权状态
|
// 主动查询用户隐私政策授权状态
|
||||||
proactive() {
|
proactive() {
|
||||||
|
// 检查配置,决定是否显示隐私协议
|
||||||
|
const shopInfo = uni.getStorageSync('shopInfo');
|
||||||
|
if (!(shopInfo?.ischina == 1 && shopInfo?.index_login_auth == 1)) {
|
||||||
|
// 如果不需要显示隐私协议,直接触发同意事件
|
||||||
|
this.$emit('agree');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
if (wx.getPrivacySetting) {
|
if (wx.getPrivacySetting) {
|
||||||
wx.getPrivacySetting({
|
wx.getPrivacySetting({
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
// console.log(res)
|
// console.log(res)
|
||||||
if (res.needAuthorization) {
|
if (res.needAuthorization) {
|
||||||
|
// 检查本地存储中是否已经同意过隐私政策
|
||||||
|
const privacyAgreed = uni.getStorageSync('privacyAgreed');
|
||||||
|
if (!privacyAgreed) {
|
||||||
this.popUp()
|
this.popUp()
|
||||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
this.closeOtherPagePopUp(this.disPopUp)
|
this.closeOtherPagePopUp(this.disPopUp)
|
||||||
} else {
|
} else {
|
||||||
this.$emit('agree')
|
this.$emit('agree')
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.$emit('agree')
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.$emit('agree')
|
// 检查本地存储中是否已经同意过隐私政策
|
||||||
|
const privacyAgreed = uni.getStorageSync('privacyAgreed');
|
||||||
|
if (privacyAgreed) {
|
||||||
|
this.$emit('agree');
|
||||||
|
} else {
|
||||||
|
this.popUp()
|
||||||
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
|
this.closeOtherPagePopUp(this.disPopUp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef QUICKAPP-WEBVIEW
|
// #ifdef QUICKAPP-WEBVIEW
|
||||||
this.getPrivacySettingByQuickApp();
|
this.getPrivacySettingByQuickApp();
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5 || WEB
|
||||||
|
// 检查本地存储中是否已经同意过隐私政策
|
||||||
|
const privacyAgreed = uni.getStorageSync('privacyAgreed');
|
||||||
|
if (privacyAgreed) {
|
||||||
|
this.$emit('agree');
|
||||||
|
} else {
|
||||||
|
this.popUp()
|
||||||
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
|
this.closeOtherPagePopUp(this.disPopUp)
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
},
|
},
|
||||||
//初始化监听程序
|
//初始化监听程序
|
||||||
curPageShow() {
|
curPageShow() {
|
||||||
@@ -261,6 +302,8 @@ export default {
|
|||||||
this.privacyResolves.clear()
|
this.privacyResolves.clear()
|
||||||
//关闭弹窗
|
//关闭弹窗
|
||||||
this.disPopUp()
|
this.disPopUp()
|
||||||
|
// 保存用户授权状态到本地存储,无论同意还是不同意,都不再重复弹出
|
||||||
|
uni.setStorageSync('privacyAgreed', true);
|
||||||
//退出小程序
|
//退出小程序
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
content: '未同意隐私协议,无法使用相关功能',
|
content: '未同意隐私协议,无法使用相关功能',
|
||||||
@@ -281,10 +324,8 @@ export default {
|
|||||||
//关闭弹窗
|
//关闭弹窗
|
||||||
this.disPopUp()
|
this.disPopUp()
|
||||||
|
|
||||||
// #ifdef QUICKAPP-WEBVIEW || H5 || WEB
|
// 保存用户授权状态到本地存储,用于所有环境
|
||||||
// 保存用户授权状态到本地存储,用于快应用Webview组件中的H5方式处理
|
|
||||||
uni.setStorageSync('privacyAgreed', true);
|
uni.setStorageSync('privacyAgreed', true);
|
||||||
// #endif
|
|
||||||
this.$emit('agree')
|
this.$emit('agree')
|
||||||
},
|
},
|
||||||
//打开弹窗
|
//打开弹窗
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ export const lang = {
|
|||||||
title: '商品分类',
|
title: '商品分类',
|
||||||
search:'商品搜索',
|
search:'商品搜索',
|
||||||
seeMore : '查看更多',
|
seeMore : '查看更多',
|
||||||
Make: "询底价"
|
Make: "立即咨询"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export const lang = {
|
|||||||
//title为每个页面的标题
|
//title为每个页面的标题
|
||||||
title: '',
|
title: '',
|
||||||
select: '选择',
|
select: '选择',
|
||||||
details:'资源详情',
|
details:'商品详情',
|
||||||
params: '参数',
|
params: '参数',
|
||||||
service: '商品服务',
|
service: '商品服务',
|
||||||
allGoods: '全部商品',
|
allGoods: '全部商品',
|
||||||
@@ -11,7 +11,7 @@ export const lang = {
|
|||||||
home:'首页',
|
home:'首页',
|
||||||
cart:'购物车',
|
cart:'购物车',
|
||||||
leave:'立即留言',
|
leave:'立即留言',
|
||||||
make:'联系我们',
|
make:'立即咨询',
|
||||||
|
|
||||||
send:'配送',
|
send:'配送',
|
||||||
express:'快递发货',
|
express:'快递发货',
|
||||||
@@ -22,7 +22,7 @@ export const lang = {
|
|||||||
status:'该商品已下架',
|
status:'该商品已下架',
|
||||||
sellout:'库存不足',
|
sellout:'库存不足',
|
||||||
max:'已达最大限购数量',
|
max:'已达最大限购数量',
|
||||||
makebtn:'价格电议',
|
makebtn:'立即咨询',
|
||||||
sales:'销量',
|
sales:'销量',
|
||||||
stock:'库存',
|
stock:'库存',
|
||||||
kefu:'客服',
|
kefu:'客服',
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ export const lang = {
|
|||||||
Price:'价格',
|
Price:'价格',
|
||||||
Filter:'筛选',
|
Filter:'筛选',
|
||||||
Search:'请输入您要搜索的商品',
|
Search:'请输入您要搜索的商品',
|
||||||
Make:'立即询底价',
|
Make:'立即咨询',
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,11 +71,14 @@
|
|||||||
"scope.userLocation" : {
|
"scope.userLocation" : {
|
||||||
"desc" : "为了更好地为您提供服务"
|
"desc" : "为了更好地为您提供服务"
|
||||||
},
|
},
|
||||||
"scope.writePhotosAlbum" : {
|
|
||||||
"desc" : "为了更好地为您提供服务"
|
|
||||||
},
|
|
||||||
"scope.userClipboard" : {
|
"scope.userClipboard" : {
|
||||||
"desc" : "为了方便您复制信息"
|
"desc" : "为了方便您复制信息"
|
||||||
|
},
|
||||||
|
"scope.camera" : {
|
||||||
|
"desc" : "用于拍照上传头像"
|
||||||
|
},
|
||||||
|
"scope.writePhotosAlbum" : {
|
||||||
|
"desc" : "用于从相册选择和保存头像"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"requiredPrivateInfos" : [ "chooseLocation", "getLocation", "chooseAddress" ],
|
"requiredPrivateInfos" : [ "chooseLocation", "getLocation", "chooseAddress" ],
|
||||||
@@ -104,7 +107,7 @@
|
|||||||
},
|
},
|
||||||
"router" : {
|
"router" : {
|
||||||
"mode" : "history",
|
"mode" : "history",
|
||||||
"base" : "/hwappx/common/"
|
"base" : "/hwappx/test/"
|
||||||
},
|
},
|
||||||
"title" : "",
|
"title" : "",
|
||||||
"devServer" : {
|
"devServer" : {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
{
|
{
|
||||||
"path": "detail",
|
"path": "detail",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "资源详情"
|
"navigationBarTitleText": "商品详情"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ export default {
|
|||||||
return {
|
return {
|
||||||
minScrollTop: 100, // 设置回到顶端按钮显示要求,最小页面滚动距离
|
minScrollTop: 100, // 设置回到顶端按钮显示要求,最小页面滚动距离
|
||||||
wechatQrcode: '', // 公众号二维码
|
wechatQrcode: '', // 公众号二维码
|
||||||
diyRoute: '/pages/index/index'
|
diyRoute: '/pages/index/index',
|
||||||
|
shopInfo: null // 店铺信息
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onLoad(option) {
|
onLoad(option) {
|
||||||
@@ -15,10 +16,15 @@ export default {
|
|||||||
// #endif
|
// #endif
|
||||||
this.name = option.name || 'DIY_VIEW_INDEX'; // 根据店铺运营模式,打开平台(shop)或者连锁门店(stroe)页面,接口会自行判断
|
this.name = option.name || 'DIY_VIEW_INDEX'; // 根据店铺运营模式,打开平台(shop)或者连锁门店(stroe)页面,接口会自行判断
|
||||||
|
|
||||||
|
// 获取店铺信息
|
||||||
|
this.shopInfo = uni.getStorageSync('shopInfo');
|
||||||
},
|
},
|
||||||
onShow() {
|
onShow() {
|
||||||
// 获取关注公众号二维码
|
// 获取关注公众号二维码
|
||||||
this.getFollowQrcode();
|
this.getFollowQrcode();
|
||||||
|
|
||||||
|
// 重新获取店铺信息
|
||||||
|
this.shopInfo = uni.getStorageSync('shopInfo');
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// 监听图片、文字、商品列表等组件的点击事件
|
// 监听图片、文字、商品列表等组件的点击事件
|
||||||
@@ -59,6 +65,11 @@ export default {
|
|||||||
officialAccountsClose() {
|
officialAccountsClose() {
|
||||||
this.$refs.officialAccountsPopup.close();
|
this.$refs.officialAccountsPopup.close();
|
||||||
},
|
},
|
||||||
|
// 判断是否需要登录授权
|
||||||
|
_needLoginAuth() {
|
||||||
|
// 检查 index_login_auth 都开启时才需要登录授权
|
||||||
|
return this.shopInfo?.index_login_auth == 1;
|
||||||
|
},
|
||||||
// 统一处理点击及触摸事件
|
// 统一处理点击及触摸事件
|
||||||
_handleDiyGroupInteractionEvent(payload) {
|
_handleDiyGroupInteractionEvent(payload) {
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
@@ -66,6 +77,11 @@ export default {
|
|||||||
return true;
|
return true;
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
|
// 如果不需要登录授权,直接返回 true 继续传播
|
||||||
|
if (!this._needLoginAuth()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// 如果已登录,直接返回 true 继续传播
|
// 如果已登录,直接返回 true 继续传播
|
||||||
if (this.storeToken) {
|
if (this.storeToken) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
<view>
|
<view>
|
||||||
<view scroll-y="true" class="goods-detail" :class="isIphoneX ? 'active' : ''">
|
<view scroll-y="true" class="goods-detail" :class="isIphoneX ? 'active' : ''">
|
||||||
<view class="goods-container">
|
<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"
|
<pengpai-fadein-out v-if="goodsSkuDetail.barrage_show && goodsSkuDetail.barrageData" ref="pengpai"
|
||||||
:duration="1600" :wait="1900" :top="200" :left="0" :radius="60" :loop="true"
|
:duration="1600" :wait="1900" :top="200" :left="0" :radius="60" :loop="true"
|
||||||
@@ -17,7 +22,7 @@
|
|||||||
:item-id="'goods_id_' + index">
|
:item-id="'goods_id_' + index">
|
||||||
<view class="item" @click="previewMedia(index)">
|
<view class="item" @click="previewMedia(index)">
|
||||||
<image :src="$util.img(item, { size: 'big' })" @error="swiperImageError(index)"
|
<image :src="$util.img(item, { size: 'big' })" @error="swiperImageError(index)"
|
||||||
mode="aspectFit" />
|
mode="aspectFit" :show-menu-by-longpress="true" />
|
||||||
</view>
|
</view>
|
||||||
</swiper-item>
|
</swiper-item>
|
||||||
</swiper>
|
</swiper>
|
||||||
@@ -458,6 +463,8 @@
|
|||||||
// 商品详情视图
|
// 商品详情视图
|
||||||
import scroll from '@/common/js/scroll-view.js';
|
import scroll from '@/common/js/scroll-view.js';
|
||||||
import detail from './detail.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';
|
import pengpaiFadeinOut from '../pengpai-fadein-out/pengpai-fadein-out.vue';
|
||||||
|
|
||||||
@@ -474,6 +481,15 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
pengpaiFadeinOut
|
pengpaiFadeinOut
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
aiAvatarUrl() {
|
||||||
|
return this.$util.img(IMAGE_CONFIG.aiAvatar);
|
||||||
|
},
|
||||||
|
showAIChatBtn() {
|
||||||
|
const customerService = createCustomerService(this);
|
||||||
|
return customerService.hasValidKefuConfig() && customerService.isAIKefu();
|
||||||
|
}
|
||||||
|
},
|
||||||
mixins: [scroll, detail]
|
mixins: [scroll, detail]
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -481,4 +497,36 @@ export default {
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '@/common/css/goods_detail.scss';
|
@import '@/common/css/goods_detail.scss';
|
||||||
</style>
|
</style>
|
||||||
<style scoped></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>
|
||||||
@@ -26,7 +26,8 @@
|
|||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
diyData: null
|
diyData: null,
|
||||||
|
mpShareData: null //小程序分享数据
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
@@ -38,6 +39,25 @@ export default {
|
|||||||
onShow() {
|
onShow() {
|
||||||
this.$util.hideHomeButton();
|
this.$util.hideHomeButton();
|
||||||
if (this.$refs.category) this.$refs.category[0].pageShow();
|
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() {
|
onUnload() {
|
||||||
if (!this.storeToken && this.$refs.login) this.$refs.login.cancelCompleteInfo();
|
if (!this.storeToken && this.$refs.login) this.$refs.login.cancelCompleteInfo();
|
||||||
@@ -69,6 +89,24 @@ export default {
|
|||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
uni.hideTabBar();
|
uni.hideTabBar();
|
||||||
this.getDiyInfo();
|
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>
|
</script>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
</view> -->
|
</view> -->
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
|
|
||||||
<goods-detail-view :goodsSkuDetail="goodsSkuDetail" ref="goodsDetailView">
|
<goods-detail-view :goodsSkuDetail="goodsSkuDetail" ref="goodsDetailView" @aiChatClick="sendToAIChat(goodsSkuDetail)">
|
||||||
<!-- 价格区域 -->
|
<!-- 价格区域 -->
|
||||||
<template v-slot:price>
|
<template v-slot:price>
|
||||||
<!-- 限时折扣 -->
|
<!-- 限时折扣 -->
|
||||||
@@ -77,7 +77,6 @@
|
|||||||
</block>
|
</block>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<template>
|
<template>
|
||||||
<text class="price price-font" style="font-weight: 600;">{{$lang('makebtn')}}</text>
|
|
||||||
</template>
|
</template>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
@@ -464,6 +463,7 @@
|
|||||||
|
|
||||||
<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 == 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="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 == 2" class="goods-action-button active3" disabled-text="" backgroundColor="#54c952" @click="buyNow" text="商品留言" :disabled="false" /> -->
|
||||||
|
|
||||||
</block>
|
</block>
|
||||||
@@ -515,6 +515,19 @@
|
|||||||
|
|
||||||
<!-- 隐私弹窗 -->
|
<!-- 隐私弹窗 -->
|
||||||
<privacy-popup ref="privacyPopup"></privacy-popup>
|
<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>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -531,9 +544,61 @@ export default {
|
|||||||
goodsDetailView,
|
goodsDetailView,
|
||||||
nsGoodsPromotion
|
nsGoodsPromotion
|
||||||
},
|
},
|
||||||
mixins: [base, detail, scroll]
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '@/common/css/goods_detail.scss';
|
@import '@/common/css/goods_detail.scss';
|
||||||
@import './public/css/detail.scss';
|
@import './public/css/detail.scss';
|
||||||
@@ -545,6 +610,57 @@ export default {
|
|||||||
z-index: 999;
|
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>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
::v-deep .action-icon-wrap .iconfont.icon-shouye1 {
|
::v-deep .action-icon-wrap .iconfont.icon-shouye1 {
|
||||||
@@ -640,4 +756,5 @@ export default {
|
|||||||
height: 72rpx;
|
height: 72rpx;
|
||||||
line-height: 72rpx;
|
line-height: 72rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
<view class="goods-item margin-bottom" v-for="(item, index) in goodsList" :key="index"
|
<view class="goods-item margin-bottom" v-for="(item, index) in goodsList" :key="index"
|
||||||
@click="toDetail(item)">
|
@click="toDetail(item)">
|
||||||
<view class="goods-img">
|
<view class="goods-img">
|
||||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"></image>
|
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" :show-menu-by-longpress="true"></image>
|
||||||
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}
|
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}
|
||||||
</view>
|
</view>
|
||||||
<view class="sell-out" v-if="item.goods_stock <= 0">
|
<view class="sell-out" v-if="item.goods_stock <= 0">
|
||||||
@@ -75,68 +75,23 @@
|
|||||||
<text class="unit price-style small">.{{
|
<text class="unit price-style small">.{{
|
||||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||||
</view>
|
</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>
|
||||||
<view class="pro-info" v-if="item.isinformation == 0">
|
<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="block-wrap">
|
||||||
<view class="sale color-tip" v-if="item.sale_show">已售{{ item.sale_num }}{{ item.unit
|
<view class="sale" v-if="item.sale_show">已售{{ item.sale_num }}{{ item.unit
|
||||||
? item.unit : '件' }}</view>
|
? item.unit : '件' }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="cart-action-wrap" v-if="config.control && item.is_virtual == 0">
|
<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)">
|
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
|
||||||
|
购买
|
||||||
<view class="click-event"></view>
|
<view class="click-event"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!--加号图标 -->
|
|
||||||
<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>
|
||||||
|
|
||||||
<!-- 按钮 -->
|
|
||||||
<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 class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
|
||||||
|
<view class="ai-chat-btn" @click.stop="sendToAIChat(item)">
|
||||||
|
立即咨询
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -147,7 +102,7 @@
|
|||||||
@click="toDetail(item)"
|
@click="toDetail(item)"
|
||||||
:style="{ left: listPosition[index] ? listPosition[index].left : '', top: listPosition[index] ? listPosition[index].top : '' }">
|
:style="{ left: listPosition[index] ? listPosition[index].left : '', top: listPosition[index] ? listPosition[index].top : '' }">
|
||||||
<view class="goods-img">
|
<view class="goods-img">
|
||||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"></image>
|
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" :show-menu-by-longpress="true"></image>
|
||||||
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}
|
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}
|
||||||
</view>
|
</view>
|
||||||
<view class="sell-out" v-if="item.stock <= 0">
|
<view class="sell-out" v-if="item.stock <= 0">
|
||||||
@@ -168,66 +123,23 @@
|
|||||||
<text class="unit price-style small">.{{
|
<text class="unit price-style small">.{{
|
||||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||||
</view>
|
</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>
|
||||||
<view class="pro-info" v-if="item.isinformation == 0">
|
<view class="pro-info" v-if="item.isinformation == 0">
|
||||||
<view class="block-wrap">
|
<view class="block-wrap">
|
||||||
<view class="sale color-tip" v-if="item.sale_show">已售{{ item.sale_num }}{{ item.unit
|
<view class="sale" v-if="item.sale_show">已售{{ item.sale_num }}{{ item.unit
|
||||||
? item.unit : '件' }}</view>
|
? item.unit : '件' }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="cart-action-wrap" v-if="config.control && item.is_virtual == 0">
|
<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)">
|
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
|
||||||
|
购买
|
||||||
<view class="click-event"></view>
|
<view class="click-event"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!--加号图标 -->
|
|
||||||
<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>
|
||||||
|
|
||||||
<!-- 按钮 -->
|
|
||||||
<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 class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
|
||||||
|
<view class="ai-chat-btn" @click.stop="sendToAIChat(item)">
|
||||||
|
立即咨询
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -311,7 +223,14 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
mixins: [list]
|
mixins: [list],
|
||||||
|
methods: {
|
||||||
|
sendToAIChat(item) {
|
||||||
|
this.$util.redirectTo('/pages_goods/detail', {
|
||||||
|
goods_id: item.goods_id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -327,4 +246,24 @@ export default {
|
|||||||
::v-deep .sku-layer .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
::v-deep .sku-layer .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
||||||
max-height: unset !important;
|
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>
|
</style>
|
||||||
@@ -295,7 +295,8 @@
|
|||||||
|
|
||||||
.lineheight-clear {
|
.lineheight-clear {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 商品列表单列样式
|
// 商品列表单列样式
|
||||||
@@ -386,46 +387,30 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
margin-top: 16rpx;
|
margin-top: 0;
|
||||||
|
|
||||||
background-color: var(--main-color);
|
|
||||||
border-radius: 35rpx;
|
|
||||||
padding: 12rpx 24rpx;
|
|
||||||
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.unit {
|
.unit {
|
||||||
margin-right: 6rpx;
|
margin-right: 6rpx;
|
||||||
color: var(--price-color);
|
color: #ff4444;
|
||||||
}
|
}
|
||||||
.price {
|
.price {
|
||||||
color: var(--price-color);
|
color: #ff4444;
|
||||||
color: #fff !important;
|
font-size: 32rpx !important;
|
||||||
font-size: 27rpx !important;
|
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pro-info {
|
.pro-info {
|
||||||
|
margin-top: 10rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: auto;
|
justify-content: space-between;
|
||||||
align-items: center;
|
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 {
|
.block-wrap {
|
||||||
flex: 1;
|
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
margin-right: 20rpx;
|
|
||||||
.sale {
|
.sale {
|
||||||
font-size: $font-size-tag !important;
|
font-size: $font-size-tag !important;
|
||||||
|
color: #999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,6 +528,9 @@
|
|||||||
|
|
||||||
.lineheight-clear {
|
.lineheight-clear {
|
||||||
margin-top: 16rpx;
|
margin-top: 16rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.discount-price {
|
.discount-price {
|
||||||
@@ -552,24 +540,26 @@
|
|||||||
|
|
||||||
.unit {
|
.unit {
|
||||||
margin-right: 6rpx;
|
margin-right: 6rpx;
|
||||||
color: var(--price-color);
|
color: #ff4444;
|
||||||
}
|
}
|
||||||
.price {
|
.price {
|
||||||
color: var(--price-color);
|
color: #ff4444;
|
||||||
|
font-size: 32rpx !important;
|
||||||
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pro-info {
|
.pro-info {
|
||||||
|
margin-top: 10rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: auto;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.block-wrap {
|
.block-wrap {
|
||||||
flex: 1;
|
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
margin-right: 20rpx;
|
|
||||||
.sale {
|
.sale {
|
||||||
font-size: $font-size-tag !important;
|
font-size: $font-size-tag !important;
|
||||||
|
color: #999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -644,9 +634,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.buy-btn {
|
.buy-btn {
|
||||||
background-color: $base-color;
|
background-color: #ff4444;
|
||||||
color: var(--btn-text-color);
|
color: #fff;
|
||||||
border-radius: 50rpx;
|
border-radius: 30rpx;
|
||||||
font-size: $font-size-tag;
|
font-size: $font-size-tag;
|
||||||
padding: 12rpx 30rpx;
|
padding: 12rpx 30rpx;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="discount-price" v-else>
|
<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>
|
}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="discount-price" v-else>
|
<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>
|
}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="member-price-tag"
|
<view class="member-price-tag"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container" :style="wrapperPageStyle">
|
<view class="container" :style="wrapperPageStyle">
|
||||||
<!--自定义导航头部 -->
|
<!--自定义导航头部 - 只在微信小程序显示,H5端隐藏 -->
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
<view class="custom-navbar" ref="pageHeader" v-if="showCustomNavbar">
|
<view class="custom-navbar" ref="pageHeader" v-if="showCustomNavbar">
|
||||||
<view class="header-left">
|
<view class="header-left">
|
||||||
<button class="back-btn" v-show="showBackButton" @click="goBack">
|
<button class="back-btn" v-show="showBackButton" @click="goBack">
|
||||||
<text class="iconfont icon-back"></text>
|
<text class="iconfont icon-back"></text>
|
||||||
</button>
|
</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 v-show="!showMenuButton" class="placeholder"></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
|
||||||
<!-- 聊天内容区域 -->
|
<!-- 聊天内容区域 -->
|
||||||
<view class="chat-content"
|
<view class="chat-content"
|
||||||
@@ -87,6 +79,7 @@ import { mapGetters, mapMutations } from 'vuex'
|
|||||||
import navigationHelper from '@/common/js/navigation';
|
import navigationHelper from '@/common/js/navigation';
|
||||||
import { EventSafety } from '@/common/js/event-safety';
|
import { EventSafety } from '@/common/js/event-safety';
|
||||||
import aiChatMessage from './ai-chat-message.vue';
|
import aiChatMessage from './ai-chat-message.vue';
|
||||||
|
import IMAGE_CONFIG from '@/common/js/image-config.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -101,10 +94,6 @@ export default {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
content: '您好!我是AI智能客服,很高兴为您服务!\n\n我可以帮您:\n• 解答产品相关问题\n• 处理订单问题\n• 提供技术支持\n• 推荐相关商品\n\n请告诉我您需要什么帮助?',
|
content: '您好!我是AI智能客服,很高兴为您服务!\n\n我可以帮您:\n• 解答产品相关问题\n• 处理订单问题\n• 提供技术支持\n• 推荐相关商品\n\n请告诉我您需要什么帮助?',
|
||||||
timestamp: Date.now() - 60000,
|
timestamp: Date.now() - 60000,
|
||||||
actions: [
|
|
||||||
{ type: 'like', icon: 'icon-dianzan1', text: '喜欢', count: 0 },
|
|
||||||
{ type: 'dislike', icon: 'icon-dianzan1', text: '不喜欢', count: 0 }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -121,6 +110,9 @@ export default {
|
|||||||
// --- 聊天内容区域 ---
|
// --- 聊天内容区域 ---
|
||||||
scrollViewHeight: '0px',
|
scrollViewHeight: '0px',
|
||||||
|
|
||||||
|
// --- 键盘高度 ---
|
||||||
|
keyboardHeight: 0,
|
||||||
|
|
||||||
// 事件处理器引用(用于清理)
|
// 事件处理器引用(用于清理)
|
||||||
safeEventHandlers: new Map()
|
safeEventHandlers: new Map()
|
||||||
}
|
}
|
||||||
@@ -133,10 +125,10 @@ export default {
|
|||||||
|
|
||||||
/// ---- 关于AI客服的配置,支持远程配置 ----
|
/// ---- 关于AI客服的配置,支持远程配置 ----
|
||||||
userAvatar() {
|
userAvatar() {
|
||||||
return this.globalAIKefuConfig?.userAvatar || '/static/images/user-avatar.png'
|
return this.globalAIKefuConfig?.userAvatar || this.$util.img(IMAGE_CONFIG.userAvatar)
|
||||||
},
|
},
|
||||||
aiAvatar() {
|
aiAvatar() {
|
||||||
return this.globalAIKefuConfig?.aiAvatar || '/static/images/ai-avatar.png'
|
return this.globalAIKefuConfig?.aiAvatar || this.$util.img(IMAGE_CONFIG.aiAvatar)
|
||||||
},
|
},
|
||||||
/// ---- others ----
|
/// ---- others ----
|
||||||
containerHeight() {
|
containerHeight() {
|
||||||
@@ -154,10 +146,21 @@ export default {
|
|||||||
return ``
|
return ``
|
||||||
},
|
},
|
||||||
wrapperChatContentStyle() {
|
wrapperChatContentStyle() {
|
||||||
|
// #ifdef H5
|
||||||
|
// H5端没有自定义导航栏,不需要paddingTop
|
||||||
|
return {
|
||||||
|
height: this.containerHeight,
|
||||||
|
paddingTop: '0'
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 微信小程序端根据导航栏显示情况决定
|
||||||
return {
|
return {
|
||||||
height: this.containerHeight,
|
height: this.containerHeight,
|
||||||
paddingTop: this.showCustomNavbar ? '0' : (this.navBarHeight + 'px')
|
paddingTop: this.showCustomNavbar ? '0' : (this.navBarHeight + 'px')
|
||||||
}
|
}
|
||||||
|
// #endif
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -178,11 +181,23 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
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() {
|
onHide() {
|
||||||
// 页面隐藏时可暂停音频等
|
// 取消监听键盘高度变化
|
||||||
|
uni.offKeyboardHeightChange();
|
||||||
|
this.keyboardHeight = 0;
|
||||||
|
if (this.$refs.chat && this.$refs.chat.updateKeyboardHeight) {
|
||||||
|
this.$refs.chat.updateKeyboardHeight(0);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnload() {
|
onUnload() {
|
||||||
@@ -257,6 +272,20 @@ export default {
|
|||||||
// ========== 初始化页面 ==========
|
// ========== 初始化页面 ==========
|
||||||
initChat() {
|
initChat() {
|
||||||
console.log('AI聊天页面初始化');
|
console.log('AI聊天页面初始化');
|
||||||
|
|
||||||
|
// 检查是否有从商品分类传递过来的商品信息
|
||||||
|
const selectedProduct = uni.getStorageSync('selectedProduct')
|
||||||
|
if (selectedProduct) {
|
||||||
|
console.log('检测到选择的商品:', selectedProduct)
|
||||||
|
|
||||||
|
// 清除存储的商品信息,避免重复发送
|
||||||
|
uni.removeStorageSync('selectedProduct')
|
||||||
|
|
||||||
|
// 延迟发送商品信息,确保聊天组件已经初始化
|
||||||
|
setTimeout(() => {
|
||||||
|
this.sendProductToAI(selectedProduct)
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async initNavigation() {
|
async initNavigation() {
|
||||||
@@ -483,6 +512,20 @@ export default {
|
|||||||
console.log('视频暂停:', 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) {
|
onLinkOpen(message) {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '打开链接',
|
title: '打开链接',
|
||||||
@@ -571,8 +614,8 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 20rpx 30rpx;
|
padding: 20rpx 30rpx;
|
||||||
background-color: #c4e0ff; /* 浅蓝色 */
|
background-color: white; /* 白色 */
|
||||||
border-bottom: 2rpx solid #eeeeee;
|
border-bottom: 1rpx solid #e5e5e5;
|
||||||
|
|
||||||
.header-left {
|
.header-left {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|||||||
@@ -248,7 +248,8 @@ export default {
|
|||||||
latitude: 0,
|
latitude: 0,
|
||||||
longitude: 0,
|
longitude: 0,
|
||||||
},
|
},
|
||||||
personnel_bg: ''
|
personnel_bg: '',
|
||||||
|
mpShareData: null //小程序分享数据
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onLoad(option) {
|
onLoad(option) {
|
||||||
@@ -303,6 +304,24 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
onShow() {
|
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();
|
this.$util.hideHomeButton();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -449,9 +468,28 @@ export default {
|
|||||||
longitude: parseFloat(this.shop.longitude),
|
longitude: parseFloat(this.shop.longitude),
|
||||||
name: this.$lang('oneClickNavigation'),
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -134,7 +134,8 @@ export default {
|
|||||||
levelId: 0,
|
levelId: 0,
|
||||||
levelInfo: {
|
levelInfo: {
|
||||||
bg_color: '#333'
|
bg_color: '#333'
|
||||||
}
|
},
|
||||||
|
mpShareData: null //小程序分享数据
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
@@ -161,7 +162,26 @@ export default {
|
|||||||
});
|
});
|
||||||
this.levelInfo = levelInfo;
|
this.levelInfo = levelInfo;
|
||||||
},
|
},
|
||||||
onShow() { },
|
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
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
headimgError() {
|
headimgError() {
|
||||||
this.memberInfo.headimg = this.$util.getDefaultImage().head;
|
this.memberInfo.headimg = this.$util.getDefaultImage().head;
|
||||||
@@ -185,6 +205,24 @@ export default {
|
|||||||
}
|
}
|
||||||
this.$util.redirectTo(this.$util.MEMBER_PAGE_URL);
|
this.$util.redirectTo(this.$util.MEMBER_PAGE_URL);
|
||||||
return true;
|
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>
|
</script>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
@click.stop="toDetail(item)">
|
@click.stop="toDetail(item)">
|
||||||
<view class="pic">
|
<view class="pic">
|
||||||
<image :src="$util.img(item.goods_image.split(',')[0], { size: 'mid' })" mode="aspectFill"
|
<image :src="$util.img(item.goods_image.split(',')[0], { size: 'mid' })" mode="aspectFill"
|
||||||
@error="goodsImageError(index)"></image>
|
@error="goodsImageError(index)" :show-menu-by-longpress="true"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="goods_info">
|
<view class="goods_info">
|
||||||
<view class="goods_name font-size-base">{{ item.sku_name }}</view>
|
<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>
|
<text class="iconfont" :class="$util.inArray(item.id, idArr) != -1 ? 'icon-yuan_checked color-base-text' : 'icon-yuan_checkbox'"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="goods-img" @click="toDetail(item)">
|
<view class="goods-img" @click="toDetail(item)">
|
||||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"></image>
|
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" :show-menu-by-longpress="true"></image>
|
||||||
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}</view>
|
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="info-wrap" @click="toDetail(item)">
|
<view class="info-wrap" @click="toDetail(item)">
|
||||||
|
|||||||
28
readme.md
28
readme.md
@@ -37,7 +37,7 @@
|
|||||||
*已有依赖可跳过此步骤*
|
*已有依赖可跳过此步骤*
|
||||||
|
|
||||||
##### 4.1.1.2 【发布构建】
|
##### 4.1.1.2 【发布构建】
|
||||||
1. 使用 HBuilderX 打开项目
|
1. 使用 HBuilderX 打开项目(不用改manifest.json的web,只有H5要改 )
|
||||||
2. 选择菜单栏「发行」 → 「小程序-微信」→ 「发行」,等待构建完成
|
2. 选择菜单栏「发行」 → 「小程序-微信」→ 「发行」,等待构建完成
|
||||||
⚠️ **注意**:底部控制台弹出"请在微信小程序开发者工具中点击上传"后再执行下一步
|
⚠️ **注意**:底部控制台弹出"请在微信小程序开发者工具中点击上传"后再执行下一步
|
||||||
3. 打开资源管理器→项目根目录,右键选择「在终端中打开」,执行命令:
|
3. 打开资源管理器→项目根目录,右键选择「在终端中打开」,执行命令:
|
||||||
@@ -67,7 +67,9 @@
|
|||||||
- 确认 `site.js` 中的 `uniacid` 为客户编号
|
- 确认 `site.js` 中的 `uniacid` 为客户编号
|
||||||
- 用微信开发者工具导入解压后的代码目录
|
- 用微信开发者工具导入解压后的代码目录
|
||||||
- 编译验证无误后,上传发布
|
- 编译验证无误后,上传发布
|
||||||
|
##### 4.1.1.5 【本地设置】
|
||||||
|

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