Compare commits

..

137 Commits

Author SHA1 Message Date
1cfd2db49e feat: 集成华为快应用帐号登录 2026-05-08 15:49:29 +08:00
2342a14f2e fix(mp-weixin): 将 import 语句改为 require 2026-05-07 17:36:43 +08:00
36b0c22e47 fix: 修复二维码长按识别属性 2026-05-07 16:38:53 +08:00
4a52b9c229 fix(util): 去除URL中的双斜杠 2026-05-07 16:25:37 +08:00
9ec90a906f perf(diy-tab): optimize tab rendering with v-if 2026-05-07 16:14:28 +08:00
e125a5f39c chore: 添加修复长按菜单语法错误的脚本 2026-05-07 15:43:57 +08:00
8c0ec46dd8 style: 统一图片组件属性格式 2026-05-07 15:43:33 +08:00
b65ec35d3c chore: 更新 appid 2026-05-07 15:13:06 +08:00
d113d6125b feat(components): 在图片组件中添加长按显示菜单功能 2026-05-07 15:12:59 +08:00
2f24661cd9 fix(ai-service): 统一用户标识默认值为anonymous 2026-05-06 11:26:10 +08:00
50908a274b refactor(ai-service): 使用getUniacid方法统一获取uniacid 2026-05-06 11:13:08 +08:00
9e68562de5 chore(ai): 增加调试日志输出 2026-05-06 10:53:52 +08:00
8e5561c2a7 refactor(ai-service): 提取域名配置参数以减少重复代码 2026-04-30 16:39:55 +08:00
8d5c543a4f chore:扩大按钮【】 2026-04-28 17:51:54 +08:00
cf411407dd chore:名片可以分享了 2026-04-28 17:35:40 +08:00
86e4be302b chore:商品分类可以分享了 2026-04-28 11:31:32 +08:00
2fd06796e1 chore:加了输入框扩大按钮 2026-04-28 09:59:52 +08:00
60c51e551a chore:修改表情位置 2026-04-28 09:12:15 +08:00
d0d3ac47c6 chore:修改表情位置 2026-04-28 08:59:34 +08:00
af677f6565 chore:资源详情改为商品详情 2026-04-25 16:53:40 +08:00
f889efeb16 chore: 2026-04-25 16:45:41 +08:00
d026bb6180 chore:增加一个后台控制首页弹出的隐私权限 2026-04-25 16:15:17 +08:00
82b856f5d9 chore:表情的布局改为横板 2026-04-25 14:36:31 +08:00
7f33b6948a chore:min-height: 300px; 替换成 min-height: 0px; 2026-04-25 14:26:48 +08:00
3489b89a0b chore:智能客服加了表情 2026-04-25 14:15:03 +08:00
dc4f2769f9 chore:资源详情里的立即咨询可以跳转到智能客服 2026-04-25 11:58:03 +08:00
5cf87e4d56 chore:资源详情的背景设改为绿色 2026-04-25 11:27:32 +08:00
c9ca63cff5 chore:联系我们改为立即质询 2026-04-25 11:09:47 +08:00
a6072f4895 chore: 2026-04-25 10:22:27 +08:00
38947ddc62 chore:立即咨询点击会弹出对话框 2026-04-25 10:09:38 +08:00
cfe83ada38 chore:点击商品分类中的立即咨询不要跳转到智能客服,跳转到资源详情页面 2026-04-25 09:59:45 +08:00
1e9e0ffea8 chore:资源详情智能客服按钮同步智能客服 2026-04-25 09:50:45 +08:00
28acdfb621 chore:红色背景收一些 2026-04-25 09:29:23 +08:00
ad9f1c495b chore:联系我们改为立即质询 2026-04-25 09:22:33 +08:00
7c2a7468e7 chore:调整红色背景 2026-04-25 09:03:13 +08:00
b2051dcf92 chore:立即质询背景改红色 2026-04-25 08:48:06 +08:00
e3c86de84a chore:删除红色的立即质询 2026-04-25 08:42:03 +08:00
6e0eb5d4f3 chore:询底价改为立即咨询 2026-04-25 08:38:15 +08:00
9d186ff094 chore:发送字体按钮调整变大 2026-04-24 16:49:01 +08:00
8611ec4dc3 chore:发送按钮改为微信的发送按钮 2026-04-24 16:38:29 +08:00
89bfd94a07 chore:上传image-config.js 2026-04-24 15:46:42 +08:00
5cf8b7b467 chore:发送按钮调整变大 2026-04-23 15:24:32 +08:00
2cc0162435 chore:输入框不被遮挡了 2026-04-22 11:59:58 +08:00
d8e75a0c9e chore: 2026-04-21 14:15:46 +08:00
8af3c0ccb9 chore:微信小程序真机版本可以运行,微信小程序客服图标显示正常 2026-04-20 17:50:37 +08:00
d8b78168a8 chore:改了头像的地址 2026-04-20 15:44:58 +08:00
7675e8e04d chore: 2026-04-20 09:52:27 +08:00
88d05048a6 chore:长按图片可以识别图中二维码 2026-04-20 09:36:08 +08:00
28a92c0635 chore:ai-service.js 的代码url: 'wss://dev.aigc-quickapp.com/ws/aikefu',被替换 2026-04-18 11:36:18 +08:00
b5a47b8b4e chore:聊天页面的我和时间换位置 2026-04-17 16:03:30 +08:00
4f99e8b5f2 chore:H5的发送按钮变成正圆形 2026-04-17 15:18:31 +08:00
87fc49146c chore:时间居中 2026-04-17 14:19:43 +08:00
826fcfb024 chore:时间和智能助手在同一水平线上 2026-04-17 11:49:33 +08:00
7174d87b79 chore:用户的气泡中商品名称,商品ID,价格都左对齐 2026-04-17 10:13:07 +08:00
4bc747cd9c chore:增加了用户默认头像,自定义头像 2026-04-17 09:48:46 +08:00
e046868665 chore:用户点击自己头像可以自定义头像 2026-04-17 09:21:10 +08:00
19c3ec4f9f chore:小程序输入框向下移动 2026-04-17 08:44:01 +08:00
ffbc15929d chore:智能客服的历史时间回复正常 2026-04-16 14:03:37 +08:00
524280d816 chore:智能助手加了时间 2026-04-16 11:00:50 +08:00
6e736e0733 chore:资源详情页面的智能客服头像变为圆形 2026-04-16 10:02:50 +08:00
4891566793 chore:智能客服改了头像 2026-04-16 09:34:56 +08:00
b02f5fbc21 chore:智能客服换了头像 2026-04-15 17:46:06 +08:00
4f5a300afa chore:H5的智能客服聊天页面上面导航栏自定义的删除 2026-04-15 17:18:22 +08:00
aae6e76797 chore:智能客服聊天不乱窜了 2026-04-15 10:23:43 +08:00
3162d6a9f2 chore:H5的智能客服聊天正常 2026-04-15 09:08:10 +08:00
8010ac45a6 chore:智能客服图标换了 2026-04-14 17:29:17 +08:00
baebba4cef chore:导航栏删除在线客服 2026-04-14 16:30:42 +08:00
1be7155fe8 chore:智能客服的头像换了 2026-04-14 09:42:09 +08:00
611c0b930b chore:删除喜欢不喜欢有帮助没帮助 2026-04-13 17:29:13 +08:00
ec1903519d chore:用户提问和客服回答可以看见了 2026-04-13 09:30:08 +08:00
b1953a020d chore:喜欢不喜欢和👍水平 2026-04-10 15:10:41 +08:00
3ce26322e0 chore: 2026-04-09 16:04:11 +08:00
588ec60948 chore:发送按钮向上调动 2026-04-08 10:10:47 +08:00
962ea124fa chore:退出聊天界面后,在进入聊天页面后没有多余的气泡 2026-04-08 09:14:57 +08:00
0ca0fe3b0d chore:修改了客服样式 2026-04-07 15:21:29 +08:00
cef2b6e02e chore:客服回答不重复 2026-04-07 15:10:26 +08:00
e018f9a8b9 chore:修改H5商品价格样式 2026-03-31 09:38:16 +08:00
def8a6cf6f chore:横竖屏 2026-03-30 14:49:24 +08:00
857f295923 chore:H5和小程序都可以自己分辨横竖屏 2026-03-28 17:38:26 +08:00
50ee885b74 chore:微信小程序实现竖屏是竖屏,横屏是横屏 2026-03-25 14:44:25 +08:00
410f248510 chore:H5视频改比例 2026-03-25 09:39:48 +08:00
ba5fcfaab1 chore:微信小程序的视频改为竖屏播放 2026-03-24 16:43:00 +08:00
5a46bb8d69 chore:智能客服回答不重复 2026-03-19 14:26:32 +08:00
e33ad16d10 chore:商品详情页面右上角有跳转到智能客服 2026-03-17 09:53:24 +08:00
d8ba7ace0f chore:跳转智能客服的更多详情按钮放到商品页面里面 2026-03-16 17:21:33 +08:00
875072bb68 chore:商品可以分享给智能客服进行解答 2026-03-14 10:53:55 +08:00
0912b9b031 chore:智能客服加了商品的图标 2026-03-13 16:41:00 +08:00
7ffe4acc0f chore:隐藏了更多功能,录音图标 2026-03-13 13:57:24 +08:00
4bcf0cbd2d chore:可以正常清空聊天记录 2026-03-12 18:29:11 +08:00
fdf85dfec7 chore:更换头像正常 2026-03-12 18:25:12 +08:00
1d62b37060 chore:头像功能从相册选择功能正常了 2026-03-12 15:09:27 +08:00
4ad93ceace refactor: 将视频标题样式绑定从对象改为数组 2026-02-07 18:11:42 +08:00
294856d9ef Merge branch 'test/diy-tabs' into dev/1.0 2026-02-07 17:53:30 +08:00
ba2c7d6d64 Revert "chore:点击中英文按钮不跳到‘我的’了"
This reverts commit 19a53ef414.
2026-02-07 17:51:10 +08:00
ec9e76fda7 chore:改了中英文模式 2026-02-07 16:53:40 +08:00
5dba8071e4 refactor(diy-tab): 统一样式绑定语法为数组形式
将模板中的样式绑定从对象展开语法改为数组形式,保持代码风格一致
2026-02-07 16:16:43 +08:00
19a53ef414 chore:点击中英文按钮不跳到‘我的’了 2026-02-07 16:10:55 +08:00
e943b11c8f fix(组件): 修复tab组件中子组件moduleIndex冲突问题
为tab组件中的子组件添加tabIndex前缀,避免不同tab页中子组件的moduleIndex冲突
2026-02-07 15:57:49 +08:00
e9db54980e refactor(diy-tab): 使用模板字符串和可选链优化key生成逻辑 2026-02-07 14:59:26 +08:00
3ddcc665b8 refactor(diy-tab): 统一使用生成函数获取tab和panel的key
使用getTabKey和getPanelKey方法统一生成key,提高代码可维护性
2026-02-07 14:11:51 +08:00
a981c02baf chore:智能客服页面右上角改成齿轮图标,更多功能图标改为(...) 2026-02-07 13:59:43 +08:00
1c3f759b35 feat: 为DIY标签组件添加可配置的动画效果 2026-02-07 11:41:59 +08:00
bb58866192 feat: 为标签组件添加悬停动画和对齐选项 2026-02-07 11:34:45 +08:00
069b3c2c07 feat: 为DIY标签页组件添加滚动容器和样式 2026-02-06 17:55:44 +08:00
c6fd13956a chore:H5和微信小程序的第三方客服悬浮按钮应该会显示 👤 2026-02-06 17:49:48 +08:00
a489e24988 chore:改了企业微信客服,智能客服的悬浮按钮 2026-02-06 17:18:58 +08:00
6897fc631b feat: 重构DIY标签页组件,支持多种布局和样式配置 2026-02-06 16:47:32 +08:00
6d23a64aef chore:智能客服的页面全部显示出来 2026-02-06 16:30:33 +08:00
f34533b0c7 chore:进入智能客服的页面加了智能客服的头像 2026-02-06 16:08:50 +08:00
72c74f44f0 chore:删除弹出的框的一个叉 2026-02-06 16:02:11 +08:00
7f3d9c0f10 chore:将更改头像和昵称的按钮放在...的设置中 2026-02-06 15:56:00 +08:00
37f95e7267 chore:改了客服的图标 2026-02-05 18:30:16 +08:00
d3c630e860 chore:后台点击什么,页面显示什么 2026-02-05 16:58:54 +08:00
c5b57cedeb chore:后台点击不启用,H5和微信小程序不显示 2026-02-05 16:34:33 +08:00
bf24746a7c chore;更改了客服和中英文按钮的顺序 2026-02-05 10:14:27 +08:00
5ff2925ba1 chore: 2026-02-05 09:56:37 +08:00
3dc81e7f53 chore:优化了hover 2026-02-05 09:50:36 +08:00
0417c5c5ed chore:优化了hover 2026-02-05 09:27:43 +08:00
db26a85c8b chore:商品,名片,我的都能正常跳转了 2026-02-04 17:09:57 +08:00
54c39259a7 chore:代码合并,整理功能 2026-02-02 18:04:59 +08:00
15361211b1 Merge pull request 'tjh/merge' (#3) from tjh/merge into dev/1.0
Reviewed-on: #3
2026-02-02 09:52:19 +00:00
e301ddc6ec chore:微信小程序标签栏点击可以跳转 2026-01-31 17:04:50 +08:00
a560ad4a5d chore:后台点击什么页面就显示什么 2026-01-31 10:24:12 +08:00
7f7a18803f chore:智能客服正常运行 2026-01-31 10:06:59 +08:00
42563d7184 chore:改了pages-tool/ai-chat/ai-chat-message.vue 2026-01-31 09:47:41 +08:00
14a903f42a chore:改了components/chat-message/chat-message.vue 2026-01-31 09:44:05 +08:00
c9f8614927 chore:改了common/js/lang.js 2026-01-31 09:23:36 +08:00
fe9303bf4c chore:改了store/index.js 2026-01-31 09:20:31 +08:00
e4dfd0ae11 chore:改了project.config.json 2026-01-31 09:17:57 +08:00
7037d0b083 chore:改了pages-tool/ai-chat/index.vue 2026-01-31 09:14:00 +08:00
e8a79bd245 chore:改了golbalconfig.js 2026-01-31 09:11:00 +08:00
047cf765da chore:改了pages.json 2026-01-31 09:07:48 +08:00
3a30b0817d chore:替换了pages/index/index.vue 2026-01-31 09:03:58 +08:00
a464a8731f chore:替换了customer-service.js 2026-01-31 08:59:30 +08:00
7599a80943 chore:替换了ai-service.js 2026-01-31 08:55:57 +08:00
992a01cc76 chore:替了chat-message.vue 2026-01-31 08:52:55 +08:00
dd09c66215 chore:替了hover 2026-01-31 08:48:18 +08:00
167 changed files with 6683 additions and 3086 deletions

View File

@@ -41,7 +41,19 @@ const localDevConfig = ({
'local-2': { // 测试平台
uniacid: 2,
domain: 'http://localhost:8050/',
},
})['2811']; // 选择要使用的环境配置
},
'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']; // 选择要使用的环境配置
export default localDevConfig;

View File

@@ -5,6 +5,17 @@ import store from '@/store/index.js'
let currentConversationId = null;
const CONVERSATION_KEY = 'ai_conversation_id';
/**
* 获取域名配置参数(避免重复代码)
*/
function getDomainParams() {
return {
imgDomain: Config.imgDomain,
h5Domain: Config.h5Domain,
baseUrl: Config.baseUrl
};
}
// 初始化时从本地读取会话ID
try {
const saved = uni.getStorageSync(CONVERSATION_KEY);
@@ -16,12 +27,28 @@ try {
}
export default {
/**
* 获取uniacid
* @returns {number} uniacid
*/
getUniacid() {
return uni.getStorageSync('uniacid') || Config.uniacid; // 从缓存中获取uniacid,或使用默认uniacid
},
/**
* 发送普通消息(blocking 模式)
*/
async sendMessage(message, options = {}) {
try {
// 调试:打印关键状态
console.log('[AI Service] sendMessage 开始执行');
console.log('[AI Service] store.state.memberInfo:', store.state.memberInfo);
console.log('[AI Service] uniacid:', this.getUniacid());
console.log('[AI Service] currentConversationId:', this.getConversationId());
const aiConfig = store.getters.globalAIKefuConfig;
console.log('[AI Service] aiConfig:', aiConfig);
const params = {
url: '/api/kefu/chat',
data: {
@@ -31,28 +58,38 @@ export default {
query: message,
response_mode: 'blocking', // 强制 blocking
user: store.state.memberInfo?.id || 'anonymous',
conversation_id: this.getConversationId() || ''
conversation_id: this.getConversationId() || '',
...getDomainParams()
},
header: {
'Content-Type': 'application/json'
}
};
console.log('Sending blocking request to:', params.url);
console.log('Request data:', params.data);
console.log('[AI Service] 发送请求到:', params.url);
console.log('[AI Service] 请求参数:', JSON.stringify(params.data, null, 2));
const response = await http.sendRequest({
...params,
async: false
});
console.log('[AI Service] 收到响应:', response);
const result = this.handleResponse(response);
if (result.conversationId) {
this.setConversationId(result.conversationId);
}
return result;
} catch (error) {
console.error('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服务暂时不可用,请稍后重试');
}
},
@@ -60,55 +97,112 @@ export default {
/**
* 流式消息入口(自动适配平台)
*/
async sendStreamMessage(message, onChunk, onComplete) {
// #ifdef MP-WEIXIN
// 微信小程序:降级为普通请求 + 前端打字模拟
try {
const result = await this.sendMessage(message);
const content = result.content || '';
const conversationId = result.conversationId || '';
// 保存会话ID(确保连续对话)
if (conversationId) {
this.setConversationId(conversationId);
}
// 模拟打字效果
let index = 0;
const chunkSize = 2; // 每次显示2个字符
return new Promise((resolve) => {
const timer = setInterval(() => {
if (index < content.length) {
const chunk = content.substring(index, index + chunkSize);
index += chunkSize;
if (onChunk) onChunk(chunk);
} else {
clearInterval(timer);
if (onComplete) {
onComplete({
content: content,
conversation_id: conversationId
});
}
resolve({ content, conversation_id: conversationId });
}
}, 80); // 打字速度:80ms/次
});
} catch (error) {
console.error('小程序流式消息降级失败:', error);
if (onComplete) {
onComplete({ error: error.message || '发送失败' });
}
throw error;
}
async sendStreamMessage(message, onChunk, onComplete) {
// #ifdef MP-WEIXIN
return new Promise((resolve, reject) => {
const match = Config.baseUrl.match(/^(?:https?:\/\/|wss?:\/\/)([^\/\s:]+)/);
const domain = match ? match[1] : '';
const wssUrl = `wss://${domain}/ws/aikefu`;
console.log(`wssUrl = ${wssUrl}`);
const socketTask = wx.connectSocket({
url: wssUrl,
header: {}
});
let content = '';
let conversationId = '';
let isAuthenticated = false;
socketTask.onOpen(() => {
console.log('WebSocket 连接成功,开始认证...');
socketTask.send({
data: JSON.stringify({
action: 'auth',
uniacid: this.getUniacid(),
token: store.state.token || 'test_token',
user_id: store.state.memberInfo?.id || 'anonymous'
})
});
});
socketTask.onMessage((res) => {
try {
const data = JSON.parse(res.data);
console.log('收到 WebSocket 消息:', data);
if (data.type === 'auth_success') {
console.log('认证成功,发送聊天消息...');
isAuthenticated = true;
socketTask.send({
data: JSON.stringify({
action: 'chat',
uniacid: this.getUniacid(),
query: message,
user_id: store.state.memberInfo?.id || 'anonymous',
conversation_id: this.getConversationId() || '',
...getDomainParams()
})
});
} else if (data.type === 'auth_failed') {
const errorMsg = '认证失败,请重新登录';
console.error(errorMsg, data);
reject(new Error(errorMsg));
if (onComplete) onComplete({ error: errorMsg });
socketTask.close();
}
// 处理流式消息块
else if (data.event === 'message') {
const text = data.answer || data.content || data.text || '';
content += text;
if (onChunk) onChunk(text);
}
// 处理流结束
else if (data.event === 'message_end') {
conversationId = data.conversation_id || '';
if (conversationId) {
this.setConversationId(conversationId);
}
if (onComplete) {
onComplete({ content, conversation_id: conversationId });
}
resolve({ content, conversation_id: conversationId });
socketTask.close();
}
// 可选:处理 done
else if (data.type === 'done') {
console.log('对话完成:', data);
}
} catch (e) {
console.error('WebSocket 消息解析失败:', e, '原始数据:', res.data);
}
});
socketTask.onError((err) => {
const errorMsg = 'WebSocket 连接失败';
console.error(errorMsg, err);
reject(new Error(errorMsg));
if (onComplete) onComplete({ error: errorMsg });
});
socketTask.onClose(() => {
console.log('WebSocket 连接已关闭');
});
const timeout = setTimeout(() => {
if (!isAuthenticated || content === '') {
console.warn('WebSocket 超时,强制关闭');
socketTask.close();
reject(new Error('AI服务响应超时'));
if (onComplete) onComplete({ error: 'AI服务响应超时' });
}
}, 10000);
});
// #endif
// #ifdef H5
// H5:使用真实流式(EventSource / Fetch)
return this.sendHttpStream(message, onChunk, onComplete);
// #endif
},
},
/**
* HTTP 流式请求(仅 H5 使用)
*/
@@ -123,72 +217,85 @@ export default {
'Accept': 'text/event-stream',
},
body: JSON.stringify({
uniacid: store.state.uniacid || '1',
uniacid: this.getUniacid(),
token: store.state.token || 'test_token',
inputs: {},
query: message,
response_mode: 'streaming',
user_id: store.state.memberInfo?.id || 'anonymous',
conversation_id: this.getConversationId() || ''
conversation_id: this.getConversationId() || '',
...getDomainParams()
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
if (!response.ok || !response.body) {
throw new Error('无效响应');
}
if (!response.body) {
throw new Error('响应体不可用');
}
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
let content = '';
let conversationId = '';
function processBuffer(buf, callback) {
const lines = buf.split('\n');
buf = lines.pop() || '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// 按行分割,保留不完整的最后一行
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // 未完成的行留到下次
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('data:')) {
const jsonStr = trimmed.slice(5).trim();
if (jsonStr) {
try {
try {
const jsonStr = trimmed.slice(5).trim();
if (jsonStr && jsonStr !== '[DONE]') {
const data = JSON.parse(jsonStr);
if (data.event === 'message') {
const text = data.answer || data.text || '';
content += text;
callback(text);
if (onChunk) onChunk(text);
}
if (data.conversation_id) {
conversationId = data.conversation_id;
}
if (data.event === 'message_end') {
// 可选:提前完成
// 可提前结束
}
} catch (e) {
console.warn('解析流数据失败:', e);
}
} catch (e) {
console.warn('解析失败:', e, line);
}
}
}
return buf;
}
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
buffer = processBuffer(buffer, (chunk) => {
if (onChunk) onChunk(chunk);
});
// 处理最后残留的 buffer(如果有)
if (buffer.trim().startsWith('data:')) {
try {
const jsonStr = buffer.trim().slice(5);
if (jsonStr) {
const data = JSON.parse(jsonStr);
if (data.event === 'message') {
const text = data.answer || '';
content += text;
if (onChunk) onChunk(text);
}
if (data.conversation_id) {
conversationId = data.conversation_id;
}
}
} catch (e) {
console.warn('最后 buffer 解析失败:', e);
}
}
if (onComplete) {
onComplete({
content,
conversation_id: conversationId
});
onComplete({ content, conversation_id: conversationId });
}
return { content, conversation_id: conversationId };
} catch (error) {
@@ -196,11 +303,6 @@ export default {
throw error;
}
// #endif
// #ifdef MP-WEIXIN
// 理论上不会执行到这里,但防止 fallback
return this.sendStreamMessage(message, onChunk, onComplete);
// #endif
},
/**
@@ -256,17 +358,19 @@ export default {
* 生成新会话(如需)
*/
async generateConversationId() {
const params = {
url: '/api/kefu/createConversation',
data: {
uniacid: store.state.uniacid,
user_id: store.state.memberInfo?.id || 'anonymous',
member_id: store.state.memberInfo?.id || '',
},
header: {
'Content-Type': 'application/json'
}
};
const params = {
url: '/api/kefu/createConversation',
data: {
uniacid: this.getUniacid(),
token: store.state.token || 'test_token',
user_id: store.state.memberInfo?.id || 'anonymous',
member_id: store.state.memberInfo?.id || 'anonymous',
...getDomainParams()
},
header: {
'Content-Type': 'application/json'
}
};
const response = await http.sendRequest({
...params,
async: false
@@ -284,6 +388,7 @@ export default {
async: false,
data: {}
});
console.log('/api/kefu/health response', response);
const available = [response?.data?.status, response?.data?.components?.ai_service_config?.status]
.filter(item => item === 'healthy').length > 0;
return {
@@ -305,7 +410,10 @@ export default {
try {
const response = await http.sendRequest({
url: '/api/kefu/clear-conversation',
data: { conversation_id: conversationId },
data: {
conversation_id: conversationId,
...getDomainParams()
},
async: false
});
return response.success;
@@ -324,13 +432,14 @@ export default {
throw new Error('会话ID(conversation_id)是必填参数');
}
const requestData = {
uniacid: params.uniacid || store.state.uniacid || '1',
uniacid: params.uniacid || this.getUniacid(),
conversation_id: params.conversation_id,
user_id: params.user_id || store.state.memberInfo?.id || 'anonymous',
limit: params.limit || 20,
offset: params.offset || 0,
member_id: params.member_id || store.state.memberInfo?.id || '',
token: params.token || ''
member_id: params.member_id || store.state.memberInfo?.id || 'anonymous',
token: params.token || '',
...getDomainParams()
};
const response = await http.sendRequest({
url: '/api/kefu/getHistory',
@@ -369,13 +478,17 @@ export default {
if (window.currentEventSource) {
window.currentEventSource.close();
}
const params = new URLSearchParams({
uniacid: store.state.uniacid || '1',
user_id: store.state.memberInfo?.id || '123456',
const paramsObj = {
uniacid: this.getUniacid(),
user_id: store.state.memberInfo?.id || 'anonymous',
member_id: store.state.memberInfo?.id || 'anonymous',
token: store.state.token || 'test_token',
query: message,
conversation_id: conversationId || '',
stream: 'true'
});
stream: 'true',
...getDomainParams()
};
const params = new URLSearchParams(paramsObj);
const url = `${Config.baseUrl}/api/kefu/chat?${params.toString()}`;
try {
const eventSource = new EventSource(url);

View File

@@ -98,6 +98,54 @@ export default {
});
}
// #endif
// #ifdef H5_XCX_5G_QUICKAPP_COM || H5_XCX_AIGC_QUICKAPP_COM || H5_5G_AIGC_QUICKAPP_COM || H5_TEST_AIGC_QUICKAPP_COM
this._huaweiSignIn(callback);
// #endif
},
/**
* 华为快应用帐号登录
*/
async _huaweiSignIn(callback) {
try {
const HwService = require('@/common/js/hw-jssdk.js').default;
// 确保 HwService 已初始化
if (!HwService.isQuickAppEnv()) {
console.warn('[Auth] 非快应用环境,跳过华为帐号登录');
return;
}
const userInfo = await HwService.Account.signIn();
if (userInfo && userInfo.openId) {
// 将华为 openId 传给后端换取系统 token
this.$api.sendRequest({
url: '/wechat/api/quickapp/huawei_auth',
data: {
openid: userInfo.openId,
unionid: userInfo.unionId,
nickname: userInfo.displayName,
avatar: userInfo.avatarUrl,
gender: userInfo.gender
},
success: res => {
if (res.code >= 0) {
if (res.data.openid) this.authInfo.hw_openid = res.data.openid;
if (res.data.unionid) this.authInfo.hw_unionid = res.data.unionid;
typeof callback == 'function' && callback(this.authInfo);
} else {
this.$util.showToast({
title: res.message ? res.message : '华为帐号登录失败'
});
}
}
});
}
} catch (error) {
console.error('[Auth] 华为帐号登录失败:', error);
this.$util.showToast({
title: '华为帐号登录失败'
});
}
}
}
}

View File

@@ -120,5 +120,16 @@ export default {
pingInterval: 1500,
// 版本号
version: '1.0'
version: '1.0',
// 华为快应用 HwService 配置
// 需在 AppGallery Connect 创建应用后获取
huawei: {
// AGC 应用配置(替换为实际的 AGC 配置值)
appId: '', // 应用ID
clientId: '', // 客户端ID
clientSecret: '', // 客户端密钥(注意安全,建议由后端下发)
// 华为帐号登录回调地址
redirectUri: window.location.origin + '/hwappx/common/',
}
};

View File

@@ -2,68 +2,15 @@
* 客服统一处理服务
* 整合各种客服方式,提供统一的调用接口
*/
class CustomerService {
constructor(vueInstance, externalConfig = {}) {
if (!vueInstance.$lang) {
throw new Error('CustomerService 必须在 Vue 实例中初始化');
}
import IMAGE_CONFIG from './image-config.js';
export class CustomerService {
constructor(vueInstance, externalConfig = null) {
this.vm = vueInstance;
this.externalConfig = externalConfig; // 外部传入的最新配置(优先级最高)
this.latestPlatformConfig = null;
}
getSupoortKeFuList() {
if (!this.vm) return [];
const vm = this.vm;
return [
{
id: 'weixin-official',
name: vm.$lang('customer.weChatKefu'),
isOfficial: true,
type: 'weapp'
},
{
id: 'custom-kefu',
name: vm.$lang('customer.systemKefu'),
isOfficial: false
},
{
id: 'qyweixin-kefu',
name: vm.$lang('customer.weChatWorkKefu'),
isOfficial: false
},
]
}
/**
* 打开客服选择弹窗
*/
openCustomerSelectPopupDialog() {
const kefu_list = this.getSupoortKeFuList();
const kefuNames = kefu_list.map(item => item.name);
uni.showActionSheet({
itemList: kefuNames,
success: (res) => {
const kefu = kefu_list[res.tapIndex];
this.externalConfig = kefu ?? this.externalConfig ?? {};
if (kefu.isOfficial) {
uni.openCustomerServiceConversation({
sessionFrom: 'weapp',
showMessageCard: true
});
} else if (kefu.id === 'custom-kefu') {
this.handleCustomerClick();
} else if (kefu.id === 'qyweixin-kefu') {
this.handleQyWeixinKefuClick();
}
}
});
}
/**
* 强制刷新配置(支持传入外部配置)
* @param {Object} externalConfig 外部最新配置
@@ -83,28 +30,29 @@ class CustomerService {
return this.latestPlatformConfig;
}
// 优先级:外部传入的最新配置 > vuex配置 > 空对象
// 优先级:外部传入 > vuex store > 空对象
const servicerConfig = this.externalConfig || this.vm.$store.state.servicerConfig || {};
console.log(`【实时客服配置】`, servicerConfig);
let platformConfig = null;
// #ifdef H5
platformConfig = servicerConfig.h5 ? (typeof servicerConfig.h5 === 'object' ? servicerConfig.h5 : null) : null;
platformConfig = servicerConfig.h5 && typeof servicerConfig.h5 === 'object' ? servicerConfig.h5 : null;
// #endif
// #ifdef MP-WEIXIN
platformConfig = servicerConfig.weapp ? (typeof servicerConfig.weapp === 'object' ? servicerConfig.weapp : null) : null;
platformConfig = servicerConfig.weapp && typeof servicerConfig.weapp === 'object' ? servicerConfig.weapp : null;
// #endif
// #ifdef MP-ALIPAY
platformConfig = servicerConfig.aliapp ? (typeof servicerConfig.aliapp === 'object' ? servicerConfig.aliapp : null) : null;
platformConfig = servicerConfig.aliapp && typeof servicerConfig.aliapp === 'object' ? servicerConfig.aliapp : null;
// #endif
// #ifdef PC
platformConfig = servicerConfig.pc ? (typeof servicerConfig.pc === 'object' ? servicerConfig.pc : null) : null;
platformConfig = servicerConfig.pc && typeof servicerConfig.pc === 'object' ? servicerConfig.pc : null;
// #endif
// 处理空数组情况(你的配置中pc/aliapp是空数组,转为null)
// 防止空数组被当作有效配置
if (Array.isArray(platformConfig)) {
platformConfig = null;
}
@@ -130,6 +78,15 @@ class CustomerService {
return config && typeof config === 'object' && config.type;
}
/**
* 检查是否有有效的客服配置(用于判断是否显示客服按钮)
* @returns {Boolean}
*/
hasValidKefuConfig() {
const config = this.getPlatformConfig();
return config && typeof config === 'object' && config.type && config.type !== 'none';
}
/**
* 验证客服配置完整性
* @returns {Object} 验证结果
@@ -144,32 +101,18 @@ class CustomerService {
warnings: []
};
if (!config) {
result.isValid = false;
result.errors.push('客服配置不存在');
return result;
}
if (config.type === 'aikefu') {
return result;
}
if (!config.type) {
if (!config || !config.type) {
result.isValid = false;
result.errors.push('客服类型未配置');
return result;
}
if (config.type === 'wxwork') {
if (!wxworkConfig) {
result.isValid = false;
result.errors.push('企业微信配置不存在');
} else {
if (!wxworkConfig.enable) {
result.warnings.push('企业微信功能未启用');
}
if (!wxworkConfig.contact_url) {
result.warnings.push('企业微信活码链接未配置,将使用原有客服方式');
}
if (!wxworkConfig || !wxworkConfig.enable) {
result.warnings.push('企业微信未启用');
}
if (!wxworkConfig.contact_url) {
result.warnings.push('企业微信活码链接未配置');
}
}
@@ -177,16 +120,39 @@ class CustomerService {
}
/**
* 跳转到AI客服页面
* 跳转到 AI 客服页面(Dify)
*/
openAIKeFuService() {
const vm = this.vm;
vm.$util.redirectTo(vm.$util.AI_CHAT_PAGE_URL);
openDifyService() {
try {
// 清除未读数(如果存在)
if (typeof this.vm.setAiUnreadCount === 'function') {
this.vm.setAiUnreadCount(0);
}
// ✅ 修正路径:必须与 pages.json 中注册的路径一致
const aiChatUrl = '/pages_tool/ai-chat/index';
// ✅ 使用 navigateTo 保留返回栈(体验更好)
uni.navigateTo({
url: aiChatUrl,
fail: (err) => {
console.error('跳转 AI 客服失败:', err);
// H5 兜底
// #ifdef H5
window.location.href = aiChatUrl;
// #endif
uni.showToast({ title: '打开客服失败', icon: 'none' });
}
});
} catch (e) {
console.error('跳转 AI 客服异常:', e);
uni.showToast({ title: '打开客服失败', icon: 'none' });
}
}
/**
* 处理客服点击事件
* @param {Object} options 选项参数
* 处理客服点击事件(统一入口)
* @param {Object} options 选项参数(用于消息卡片等)
*/
handleCustomerClick(options = {}) {
const validation = this.validateConfig();
@@ -201,51 +167,61 @@ class CustomerService {
}
const config = this.getPlatformConfig();
const { niushop = {}, sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options || {};
console.log('【当前客服配置】', config);
console.log('【客服类型】', config.type);
const { niushop = {}, sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
if (config.type === 'none') {
this.showNoServicePopup();
return;
}
// 核心分支:根据最新的type处理
// 核心路由:根据 type 决定行为
switch (config.type) {
case 'aikefu':
this.openAIKeFuService();
console.log('【跳转 AI 客服】目标路径: /pages_tool/ai-chat/index');
this.openDifyService();
break;
case 'wxwork':
console.log('【跳转企业微信客服】');
this.openWxworkService(false, config, options);
break;
case 'third':
console.log('【跳转第三方客服】');
this.openThirdService(config);
break;
case 'miniprogram':
console.log('【跳转第三方小程序客服】');
this.openThirdService(config);
break;
case 'niushop':
console.log('【跳转牛商客服】');
this.openNiushopService(niushop);
break;
case 'weapp':
console.log('【跳转微信官方客服】');
this.openWeappService(config, options);
break;
case 'aliapp':
console.log('【跳转支付宝客服】');
this.openAliappService(config);
break;
default:
console.error('【未知客服类型】', config.type);
this.makePhoneCall();
}
}
/**
* 打开企业微信客服
* @param {boolean} useOriginalService 是否使用原有客服方式
* @param {Object} servicerConfig 客服配置
* @param {Object} options 选项参数
*/
// ================== 各类型客服实现 ==================
openWxworkService(useOriginalService = false, servicerConfig = null, options = {}) {
const config = servicerConfig || this.getPlatformConfig();
const wxworkConfig = this.getWxworkConfig();
const { sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options || {};
const { sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
// #ifdef MP-WEIXIN
if (wxworkConfig?.enable && wxworkConfig?.contact_url && !useOriginalService) {
if (!useOriginalService && wxworkConfig?.enable && wxworkConfig?.contact_url) {
wx.navigateToMiniProgram({
appId: 'wxeb490c6f9b154ef9',
path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(wxworkConfig.contact_url)}`,
@@ -256,9 +232,16 @@ class CustomerService {
}
});
} else {
// 检查是否有企业微信配置
if (!config.wxwork_url && !config.corpid) {
console.error('企业微信配置不完整,缺少 wxwork_url 或 corpid');
uni.showToast({ title: '企业微信配置不完整', icon: 'none' });
this.fallbackToPhoneCall();
return;
}
wx.openCustomerServiceChat({
extInfo: { url: config.wxwork_url },
corpId: config.corpid,
extInfo: { url: config.wxwork_url || '' },
corpId: config.corpid || '',
showMessageCard: true,
sendMessageTitle,
sendMessagePath,
@@ -268,227 +251,181 @@ class CustomerService {
// #endif
// #ifdef H5
if (wxworkConfig?.enable && wxworkConfig?.contact_url) {
if (!useOriginalService && wxworkConfig?.enable && wxworkConfig?.contact_url) {
window.location.href = wxworkConfig.contact_url;
} else if (config.wxwork_url) {
location.href = config.wxwork_url;
window.location.href = config.wxwork_url;
} else {
this.fallbackToPhoneCall();
}
// #endif
}
/**
* 打开第三方客服
* @param {Object} config 客服配置
*/
openThirdService(config) {
console.log('【第三方客服配置】', config);
console.log('【配置字段】', Object.keys(config));
// 支持多种可能的字段名
const miniAppId = config.mini_app_id || config.miniAppId || config.appid || config.appId || config.app_id;
const miniAppPath = config.mini_app_path || config.miniAppPath || config.path || config.page_path || '';
console.log('【解析后的小程序配置】AppID:', miniAppId, 'Path:', miniAppPath);
// 优先处理第三方微信小程序客服
if (miniAppId) {
console.log('【跳转第三方小程序】AppID:', miniAppId, 'Path:', miniAppPath);
// #ifdef MP-WEIXIN
wx.navigateToMiniProgram({
appId: miniAppId,
path: miniAppPath,
success: () => {
console.log('【跳转第三方小程序成功】');
},
fail: (err) => {
console.error('【跳转第三方小程序失败】', err);
uni.showToast({ title: '跳转失败,请稍后重试', icon: 'none' });
}
});
// #endif
// #ifdef H5
uni.showToast({ title: '第三方小程序客服仅在微信小程序中可用', icon: 'none' });
// #endif
return;
}
// 处理第三方链接客服
if (config.third_url) {
console.log('【跳转第三方链接】', config.third_url);
// #ifdef H5
window.location.href = config.third_url;
// #endif
// #ifdef MP-WEIXIN
uni.setClipboardData({
data: config.third_url,
success: () => {
uni.showToast({ title: '链接已复制,请在浏览器打开', icon: 'none' });
}
});
// #endif
} else {
console.error('【第三方客服配置不完整】缺少 mini_app_id 或 third_url');
this.fallbackToPhoneCall();
}
}
/**
* 打开牛商客服
* @param {Object} niushop 牛商参数
*/
openNiushopService(niushop) {
if (Object.keys(niushop).length > 0) {
if (Object.keys(niushop).length > 0 && this.vm.$util?.redirectTo) {
this.vm.$util.redirectTo('/pages_tool/chat/room', niushop);
} else {
this.makePhoneCall();
}
}
/**
* 打开微信小程序客服
* @param {Object} config 客服配置
* @param {Object} options 选项参数
*/
openWeappService(config, options = {}) {
if (!this.shouldUseCustomService(config)) {
console.log('使用官方微信小程序客服');
// 如果 useOfficial 为 true 或 undefined,则使用原生系统客服(由 button open-type="contact" 触发)
// 此方法仅用于自定义跳转(如 useOfficial: false)
if (config.useOfficial !== false) {
// 不做任何事,应由 <button open-type="contact"> 触发
console.log('使用微信官方客服,请确保按钮为 <button open-type="contact">');
return;
}
console.log('使用自定义微信小程序客服');
this.handleCustomWeappService(config, options);
}
/**
* 处理自定义微信小程序客服
* @param {Object} config 客服配置
* @param {Object} options 选项参数
*/
handleCustomWeappService(config, options = {}) {
const { sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options || {};
if (config.customServiceUrl) {
let url = config.customServiceUrl;
const params = [];
const { sendMessageTitle, sendMessagePath, sendMessageImg } = options;
if (sendMessageTitle) params.push(`title=${encodeURIComponent(sendMessageTitle)}`);
if (sendMessagePath) params.push(`path=${encodeURIComponent(sendMessagePath)}`);
if (sendMessageImg) params.push(`img=${encodeURIComponent(sendMessageImg)}`);
if (params.length > 0) {
url += (url.includes('?') ? '&' : '?') + params.join('&');
}
uni.navigateTo({
url: url,
fail: (err) => {
console.error('跳转自定义客服页面失败:', err);
this.tryThirdPartyService(config, options);
}
});
uni.navigateTo({ url });
return;
}
this.tryThirdPartyService(config, options);
}
/**
* 尝试使用第三方客服
* @param {Object} config 客服配置
* @param {Object} options 选项参数
*/
tryThirdPartyService(config, options = {}) {
if (config.thirdPartyServiceUrl) {
// #ifdef H5
window.open(config.thirdPartyServiceUrl, '_blank');
// #endif
// 支持第三方微信小程序客服
if (config.thirdPartyMiniAppId || config.mini_app_id) {
// #ifdef MP-WEIXIN
if (config.thirdPartyMiniAppId) {
wx.navigateToMiniProgram({
appId: config.thirdPartyMiniAppId,
path: config.thirdPartyMiniAppPath || '',
fail: (err) => {
console.error('跳转第三方小程序失败:', err);
this.fallbackToPhoneCall();
}
});
} else {
uni.setClipboardData({
data: config.thirdPartyServiceUrl,
success: () => {
uni.showModal({
title: '客服链接已复制',
content: '客服链接已复制到剪贴板,请在浏览器中粘贴访问',
showCancel: false
});
}
});
}
wx.navigateToMiniProgram({
appId: config.thirdPartyMiniAppId || config.mini_app_id,
path: config.thirdPartyMiniAppPath || config.mini_app_path || ''
});
// #endif
return;
}
// 支持第三方链接客服
if (config.thirdPartyServiceUrl || config.third_url) {
const serviceUrl = config.thirdPartyServiceUrl || config.third_url;
// #ifdef H5
window.open(serviceUrl, '_blank');
// #endif
// #ifdef MP-WEIXIN
uni.setClipboardData({ data: serviceUrl });
uni.showToast({ title: '客服链接已复制', icon: 'none' });
// #endif
return;
}
this.fallbackToPhoneCall();
}
/**
* 降级到电话客服
*/
fallbackToPhoneCall() {
uni.showModal({
title: '联系客服',
content: '在线客服暂时不可用,是否拨打电话联系客服?',
success: (res) => {
if (res.confirm) {
this.makePhoneCall();
}
}
});
}
/**
* 打开支付宝小程序客服
* @param {Object} config 客服配置
*/
openAliappService(config) {
console.log('支付宝小程序客服', config);
switch (config.type) {
case 'aikefu':
this.openAIKeFuService();
break;
case 'third':
this.openThirdService(config);
break;
default:
console.log('使用支付宝官方客服');
break;
if (config.type === 'aikefu') {
this.openDifyService();
} else if (config.type === 'third') {
this.openThirdService(config);
} else {
// 支付宝原生客服由 button open-type="contact" 触发,此处不处理
console.log('使用支付宝官方客服');
}
}
/**
* 拨打电话
*/
makePhoneCall(mobileNumber) {
if (mobileNumber) {
return uni.makePhoneCall({
phoneNumber: mobileNumber
});
}
// ================== 辅助方法 ==================
// 从缓存中获取电话信息
uni.getStorage({
key: 'shopInfo',
success: (res) => {
const shopInfo = res.data;
const mobile = shopInfo?.mobile ?? '';
if (mobile) {
uni.makePhoneCall({
phoneNumber: mobile
});
makePhoneCall() {
this.vm.$api.sendRequest({
url: '/api/site/shopcontact',
success: res => {
if (res.code === 0 && res.data?.mobile) {
uni.makePhoneCall({ phoneNumber: res.data.mobile });
} else {
uni.showToast({
title: '暂无客服电话',
icon: 'none'
});
uni.showToast({ title: '暂无客服电话', icon: 'none' });
}
},
fail: () => {
uni.showToast({ title: '获取客服电话失败', icon: 'none' });
}
});
}
/**
* 显示无客服弹窗
*/
showNoServicePopup() {
const siteInfo = this.vm.$store.state.siteInfo || {};
const message = siteInfo?.site_tel
? `请联系客服,客服电话是${siteInfo.site_tel}`
const message = siteInfo?.site_tel
? `请联系客服,客服电话是 ${siteInfo.site_tel}`
: '抱歉,商家暂无客服,请线下联系';
uni.showModal({
title: '联系客服',
content: message,
showCancel: false
});
uni.showModal({ title: '联系客服', content: message, showCancel: false });
}
/**
* 显示配置错误弹窗
* @param {Array} errors 错误列表
*/
showConfigErrorPopup(errors) {
const message = errors.join('\n');
uni.showModal({
title: '配置错误',
content: `客服配置有误:\n${message}`,
title: '客服配置错误',
content: `配置有误:\n${errors.join('\n')}`,
showCancel: false
});
}
/**
* 降级处理:使用原有客服方式
*/
fallbackToOriginalService() {
uni.showModal({
title: '提示',
content: '无法直接添加企业微信客服,是否使用其他方式联系客服?',
content: '无法直接添加企业微信,是否使用其他方式联系客服?',
success: (res) => {
if (res.confirm) {
this.openWxworkService(true);
@@ -497,9 +434,19 @@ class CustomerService {
});
}
fallbackToPhoneCall() {
uni.showModal({
title: '提示',
content: '在线客服不可用,是否拨打电话联系客服?',
success: (res) => {
if (res.confirm) this.makePhoneCall();
}
});
}
/**
* 获取客服按钮配置
* @returns {Object} 按钮配置
* 获取按钮配置(用于 template 中 v-if / open-type 判断)
* @returns {Object}
*/
getButtonConfig() {
const config = this.getPlatformConfig();
@@ -507,11 +454,10 @@ class CustomerService {
let openType = '';
// #ifdef MP-WEIXIN
if (config.type === 'weapp') {
openType = config.useOfficial !== false ? 'contact' : '';
if (config.type === 'weapp' && config.useOfficial !== false) {
openType = 'contact';
}
// #endif
// #ifdef MP-ALIPAY
if (config.type === 'aliapp') openType = 'contact';
// #endif
@@ -520,26 +466,239 @@ class CustomerService {
}
/**
* 判断是否应该使用自定义客服处理
* @param {Object} config 客服配置
* @returns {boolean} 是否使用自定义客服
* 获取客服按钮图标
* @returns {String} 图标URL
*/
shouldUseCustomService(config) {
// #ifdef MP-WEIXIN
if (config?.type === 'weapp') {
return config.useOfficial === false;
getKefuButtonIcon() {
const shopInfo = this.vm.shopInfo || {};
const config = this.getPlatformConfig();
// 优先使用指定的智能客服头像
if (config?.type === 'aikefu' || config?.type === 'wxwork' || config?.type === 'qyweixin') {
return this.vm.$util.img(IMAGE_CONFIG.aiAvatar);
}
return shopInfo.kefuimg || this.vm.$util?.getDefaultImage()?.kefu || '';
}
/**
* 判断是否为微信官方客服
* @returns {Boolean}
*/
isWeappOfficialKefu() {
const config = this.getPlatformConfig();
return config?.type === 'weapp';
}
/**
* 判断是否为智能客服
* @returns {Boolean}
*/
isAIKefu() {
const config = this.getPlatformConfig();
return config?.type === 'aikefu';
}
/**
* 判断是否为企业微信客服
* @returns {Boolean}
*/
isWxworkKefu() {
const config = this.getPlatformConfig();
return config?.type === 'wxwork' || config?.type === 'qyweixin';
}
/**
* 判断是否为第三方客服
* @returns {Boolean}
*/
isThirdKefu() {
const config = this.getPlatformConfig();
return config?.type === 'third';
}
/**
* 判断是否需要同时显示小程序系统客服
* @returns {Boolean}
*/
shouldShowExtraWeappSystemKefu() {
const config = this.getPlatformConfig();
return (config?.show_system_service === true || config?.show_system_service === '1') && config?.type !== 'weapp';
}
/**
* 计算容器高度
* @param {Boolean} isLanguageSwitchEnabled 是否启用语言切换
* @param {Boolean} showWeappSystemKefu 是否显示小程序系统客服
* @param {Boolean} fixBtnShow 是否显示按钮
* @returns {String} 高度值(px)
*/
getContainerHeight(isLanguageSwitchEnabled, showWeappSystemKefu, fixBtnShow) {
if (!fixBtnShow) return '160px';
let buttonCount = 1;
if (isLanguageSwitchEnabled) buttonCount++;
if (showWeappSystemKefu) buttonCount++;
const totalRpx = 94 * buttonCount - 14;
const pxValue = Math.round(totalRpx * 0.5);
return `${pxValue}px`;
}
/**
* 获取未读消息数量
* @returns {Number}
*/
getUnreadCount() {
return this.vm.$store?.state?.aiUnreadCount || 0;
}
/**
* 拨打电话联系客服
* @param {String} phoneNumber 电话号码
*/
makePhoneCallByNumber(phoneNumber) {
if (phoneNumber) {
uni.makePhoneCall({ phoneNumber });
} else {
this.makePhoneCall();
}
}
/**
* 打开 AI 智能助手
*/
openAIChat() {
try {
if (typeof this.vm.setAiUnreadCount === 'function') {
this.vm.setAiUnreadCount(0);
}
const aiChatUrl = '/pages_tool/ai-chat/index';
uni.navigateTo({
url: aiChatUrl,
fail: (err) => {
console.error('跳转 AI 客服失败:', err);
// #ifdef H5
window.location.href = aiChatUrl;
// #endif
uni.showToast({ title: '打开客服失败', icon: 'none' });
}
});
} catch (e) {
console.error('跳转 AI 客服异常:', e);
uni.showToast({ title: '打开客服失败', icon: 'none' });
}
}
/**
* 打开客服选择对话框(预留方法)
*/
openCustomerSelectPopupDialog() {
uni.showToast({ title: '客服选择功能开发中', icon: 'none' });
}
/**
* 打开客服选择弹出层(ActionSheet)
* @param {Array} kefuList 客服列表
*/
openKefuSelectPopup(kefuList) {
const kefuNames = kefuList.map(item => item.name);
uni.showActionSheet({
itemList: kefuNames,
success: (res) => {
const selectedKefu = kefuList[res.tapIndex];
const cs = createCustomerService(this.vm, selectedKefu);
if (selectedKefu.isOfficial) {
uni.openCustomerServiceConversation({
sessionFrom: 'weapp',
showMessageCard: true
});
} else if (selectedKefu.id === 'qyweixin-kefu') {
if (uni.getSystemInfoSync().platform === 'wechat') {
uni.navigateTo({
url: '/pages_tool/qyweixin-kefu/index'
});
} else {
const qyweixinUrl = this.vm.shopInfo?.qyweixinUrl;
if (qyweixinUrl) {
window.location.href = qyweixinUrl;
} else {
uni.showToast({ title: '企业微信客服未配置', icon: 'none' });
}
}
} else {
cs.handleCustomerClick();
}
}
});
}
/**
* 核心方法:统一客服入口(带验证和配置检查)
* @param {Object} options 选项参数
*/
handleUnifiedKefuClick(options = {}) {
const validation = this.validateConfig();
console.log('【客服配置验证】', validation);
if (!validation.isValid) {
console.error('客服配置无效:', validation.errors);
uni.showToast({ title: '客服暂不可用', icon: 'none' });
return;
}
if (validation.warnings.length > 0) {
console.warn('客服配置警告:', validation.warnings);
}
const platformConfig = this.getPlatformConfig();
console.log('【当前客服配置】', platformConfig);
// 检查企业微信配置
if (platformConfig.type === 'wxwork') {
const wxworkConfig = this.getWxworkConfig();
console.log('【企业微信配置】', wxworkConfig);
// #ifdef MP-WEIXIN
if (!wxworkConfig?.enable || !wxworkConfig?.contact_url) {
console.warn('企业微信配置不完整,使用原生客服');
uni.showToast({ title: '企业微信配置不完整', icon: 'none' });
}
// #endif
// #ifdef H5
if (!wxworkConfig?.contact_url && !platformConfig.wxwork_url) {
console.error('企业微信链接未配置');
uni.showToast({ title: '企业微信链接未配置', icon: 'none' });
return;
}
// #endif
}
// 直接调用统一处理方法,由 CustomerService 内部根据配置路由
try {
this.handleCustomerClick({
sendMessageTitle: options.sendMessageTitle || '来自悬浮按钮的咨询',
sendMessagePath: options.sendMessagePath || '/pages/index/index'
});
} catch (error) {
console.error('客服处理失败:', error);
uni.showToast({ title: '打开客服失败', icon: 'none' });
}
// #endif
return true;
}
}
/**
* 创建客服服务实例
* @param {Object} vueInstance Vue实例
* @param {Object} externalConfig 外部最新配置
* @returns {CustomerService} 客服服务实例
* @param {Object} vueInstance Vue 实例(通常是 this)
* @param {Object} externalConfig 可选:外部传入的最新配置(如从 DIY 数据中提取)
* @returns {CustomerService}
*/
export function createCustomerService(vueInstance, externalConfig = {}) {
export function createCustomerService(vueInstance, externalConfig = null) {
return new CustomerService(vueInstance, externalConfig);
}

View File

@@ -61,6 +61,9 @@ class EventBus {
return 'xhs'
// #endif
// #ifdef H5_XCX_5G_QUICKAPP_COM || H5_XCX_AIGC_QUICKAPP_COM || H5_5G_AIGC_QUICKAPP_COM || H5_TEST_AIGC_QUICKAPP_COM
return 'quickapp'
// #endif
return 'unknown'
}

View File

@@ -65,6 +65,10 @@ export default {
componentRefresh() {
return this.$store.state.componentRefresh;
},
// AI客服配置
globalAIKefuConfig() {
return this.$store.state.globalAIKefuConfig;
},
// 客服配置
servicerConfig() {
return this.$store.state.servicerConfig;

View File

@@ -37,6 +37,11 @@ const app_type = 'app';
const app_type_name = 'APP';
// #endif
// #ifdef H5_XCX_5G_QUICKAPP_COM || H5_XCX_AIGC_QUICKAPP_COM || H5_5G_AIGC_QUICKAPP_COM || H5_TEST_AIGC_QUICKAPP_COM
const app_type = 'quickapp';
const app_type_name = '快应用';
// #endif
export default {
sendRequest(params) {
if (!Config.baseUrl) {

361
common/js/hw-jssdk.js Normal file
View File

@@ -0,0 +1,361 @@
/**
* 华为快应用 HwService JS SDK 统一封装
* 用于 H5 壳模式下的快应用环境,通过 hms-js-base 桥接层调用 HMS Core 服务
*
* 架构说明:
* - 所有 HMS 服务通过 @hw-hmscore/hms-js-base 的 hmsjsb.invoke() 统一桥接
* - Analytics Kit 有独立封装 @hw-hmscore/analytics-sdk-quickapp(可选安装)
* - Account Kit 无独立 quickapp npm 包,直接通过 hmsjsb.invoke 调用
* - Push Kit 无独立 quickapp npm 包,直接通过 hmsjsb.invoke 调用
*
* 使用方式:
* import HwService from '@/common/js/hw-jssdk.js'
*
* // 初始化
* await HwService.init()
*
* // 华为帐号登录
* const userInfo = await HwService.Account.signIn()
*
* // 分析服务埋点
* HwService.Analytics.onEvent('event_name', { key: 'value' })
*
* @version 1.1.0
*/
// hms-js-base 提供底层 JS Bridge 桥接能力
let hmsjsb = null
let hmsbase = null
// Analytics SDK 独立包(可选安装)
let HiAnalytics = null
// #ifdef H5_XCX_5G_QUICKAPP_COM || H5_XCX_AIGC_QUICKAPP_COM || H5_5G_AIGC_QUICKAPP_COM || H5_TEST_AIGC_QUICKAPP_COM
try {
// 加载基础桥接库(必装)
const hmsBase = require('@hw-hmscore/hms-js-base')
hmsjsb = hmsBase.hmsjsb
hmsbase = hmsBase.hmsbase
// 尝试加载 Analytics SDK(可选)
try {
HiAnalytics = require('@hw-hmscore/analytics-sdk-quickapp')
} catch (e) {
console.log('[HwService] Analytics SDK 未安装,将使用 invoke 桥接方式')
}
} catch (e) {
console.log('[HwService] hms-js-base 未安装或不在快应用环境')
}
// #endif
class HwService {
constructor() {
this._initialized = false
this._config = null
// 子服务实例
this.Account = null
this.Analytics = null
this.Push = null
}
/**
* 判断当前是否运行在快应用引擎环境
*/
isQuickAppEnv() {
// #ifdef H5_XCX_5G_QUICKAPP_COM || H5_XCX_AIGC_QUICKAPP_COM || H5_5G_AIGC_QUICKAPP_COM || H5_TEST_AIGC_QUICKAPP_COM
if (typeof window !== 'undefined') {
return !!(
window.qa ||
window.__HwService__ ||
window.hms ||
hmsjsb || // hms-js-base 加载成功
navigator.userAgent.indexOf('QuickApp') > -1
)
}
// #endif
return false
}
/**
* 初始化 HwService
* @param {Object} config { appId, clientId, clientSecret }
*/
async init(config = {}) {
if (this._initialized) {
console.log('[HwService] 已初始化,跳过')
return
}
this._config = config
console.log('[HwService] 开始初始化...')
try {
// 子服务实例化
this.Account = new AccountService(hmsjsb)
this.Analytics = new AnalyticsService(hmsjsb, HiAnalytics)
this.Push = new PushService(hmsjsb)
this._initialized = true
console.log('[HwService] 初始化完成')
} catch (error) {
console.error('[HwService] 初始化失败:', error)
throw error
}
}
}
// ============================================================
// 底层 invoke 封装
// ============================================================
/**
* 通过 hmsjsb.invoke 调用 HMS Core API 的 Promise 封装
* @param {Object} hmsjsbRef hmsjsb 引用
* @param {string} apiName API 名称
* @param {Object} args 参数
* @returns {Promise<Object>}
*/
function invokeHmsApi(hmsjsbRef, apiName, args = {}) {
return new Promise((resolve, reject) => {
if (!hmsjsbRef || typeof hmsjsbRef.invoke !== 'function') {
reject(new Error('[HwService] hmsjsb.invoke 不可用'))
return
}
hmsjsbRef.invoke({
apiName,
args,
success: (res) => resolve(res),
fail: (err) => reject(err)
})
})
}
// ============================================================
// 华为帐号服务 (Account Kit)
// 通过 hmsjsb.invoke 桥接(无独立 npm 包)
// ============================================================
class AccountService {
constructor(hmsjsbRef) {
this._hmsjsb = hmsjsbRef
}
/**
* 华为帐号登录
* @param {Array} scopes 授权范围
* @returns {Promise<Object>} 用户信息
*/
async signIn(scopes = ['openid', 'profile']) {
console.log('[HwService.Account] signIn...')
// apiName 需根据实际华为文档确认,常见值有:
// 'accountSignIn' | 'com.huawei.hms.api.Account.signIn'
const result = await invokeHmsApi(this._hmsjsb, 'accountSignIn', { scopes })
return this._formatUserInfo(result)
}
/**
* 静默获取华为帐号用户信息
*/
async getUserInfo() {
const result = await invokeHmsApi(this._hmsjsb, 'accountGetUserInfo', {})
return this._formatUserInfo(result)
}
/**
* 退出登录
*/
async signOut() {
try {
await invokeHmsApi(this._hmsjsb, 'accountSignOut', {})
} catch (e) {
console.warn('[HwService.Account] signOut 失败:', e)
}
}
/**
* 统一格式化用户信息
*/
_formatUserInfo(rawInfo) {
if (!rawInfo) return null
return {
openId: rawInfo.openId || rawInfo.openid || '',
unionId: rawInfo.unionId || rawInfo.unionid || '',
displayName: rawInfo.displayName || rawInfo.nickName || rawInfo.nickname || '',
avatarUrl: rawInfo.avatarUrl || rawInfo.avatar || rawInfo.headPictureUrl || '',
gender: rawInfo.gender || 0,
country: rawInfo.country || '',
province: rawInfo.province || '',
city: rawInfo.city || '',
raw: rawInfo
}
}
}
// ============================================================
// 华为分析服务 (Analytics Kit)
// 支持 @hw-hmscore/analytics-sdk-quickapp(优先)或 invoke 桥接
// ============================================================
class AnalyticsService {
constructor(hmsjsbRef, hiAnalyticsSdk) {
this._hmsjsb = hmsjsbRef
this._sdk = hiAnalyticsSdk
}
/**
* 上报事件
* @param {string} eventName 事件名称
* @param {Object} params 事件参数
*/
onEvent(eventName, params = {}) {
// 方案一:Analytics SDK(独立封装包)
if (this._sdk && typeof this._sdk.onEvent === 'function') {
this._sdk.onEvent(eventName, params)
return
}
if (this._sdk && typeof this._sdk.HiAnalytics?.getInstance === 'function') {
try {
const instance = this._sdk.HiAnalytics.getInstance()
instance.onEvent(eventName, params)
return
} catch (e) {
// fallthrough
}
}
// 方案二:invoke 桥接
if (this._hmsjsb) {
this._hmsjsb.invoke({
apiName: 'analyticsOnEvent',
args: { eventName, params },
success: () => {},
fail: (err) => console.warn('[HwService.Analytics] onEvent 失败:', err)
})
return
}
// fallback
console.log('[HwService.Analytics] onEvent (fallback):', eventName, params)
}
/**
* 设置用户属性
* @param {Object} profile
*/
setUserProfile(profile = {}) {
if (this._sdk && typeof this._sdk.setUserProfile === 'function') {
this._sdk.setUserProfile(profile)
return
}
if (this._sdk && typeof this._sdk.HiAnalytics?.getInstance === 'function') {
try {
const instance = this._sdk.HiAnalytics.getInstance()
instance.setUserProfile(profile)
return
} catch (e) {}
}
if (this._hmsjsb) {
this._hmsjsb.invoke({
apiName: 'analyticsSetUserProfile',
args: { profile },
success: () => {},
fail: (err) => console.warn('[HwService.Analytics] setUserProfile 失败:', err)
})
return
}
console.log('[HwService.Analytics] setUserProfile (fallback):', profile)
}
/**
* 设置用户 ID
* @param {string} userId
*/
setUserId(userId) {
if (this._sdk && typeof this._sdk.setUserId === 'function') {
this._sdk.setUserId(userId)
return
}
if (this._sdk && typeof this._sdk.HiAnalytics?.getInstance === 'function') {
try {
const instance = this._sdk.HiAnalytics.getInstance()
instance.setUserId(userId)
return
} catch (e) {}
}
if (this._hmsjsb) {
this._hmsjsb.invoke({
apiName: 'analyticsSetUserId',
args: { userId },
success: () => {},
fail: (err) => console.warn('[HwService.Analytics] setUserId 失败:', err)
})
return
}
console.log('[HwService.Analytics] setUserId (fallback):', userId)
}
/**
* 页面浏览事件
*/
onPageView(pageName, pageClassOverride = '') {
this.onEvent('$PageView', {
pageName,
pageClassOverride: pageClassOverride || pageName
})
}
}
// ============================================================
// 华为推送服务 (Push Kit)
// 通过 hmsjsb.invoke 桥接(无独立 npm 包)
// ============================================================
class PushService {
constructor(hmsjsbRef) {
this._hmsjsb = hmsjsbRef
this._token = null
}
/**
* 获取推送令牌
*/
async getToken() {
if (this._token) return this._token
try {
const result = await invokeHmsApi(this._hmsjsb, 'pushGetToken', {})
this._token = result.token || result
return this._token
} catch (e) {
console.warn('[HwService.Push] getToken 不可用:', e)
return null
}
}
/**
* 监听推送令牌变更
*/
onTokenReceived(callback) {
if (!this._hmsjsb) return
this._hmsjsb.on('pushOnToken', callback)
}
/**
* 监听消息到达
*/
onMessageReceived(callback) {
if (!this._hmsjsb) return
this._hmsjsb.on('pushOnMessageReceived', callback)
}
/**
* 监听通知点击
*/
onNotificationClicked(callback) {
if (!this._hmsjsb) return
this._hmsjsb.on('pushOnNotificationClicked', callback)
}
}
// ============================================================
// 导出单例
// ============================================================
const hwService = new HwService()
export default hwService
export { AccountService, AnalyticsService, PushService }

15
common/js/image-config.js Normal file
View 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;

View File

@@ -30,12 +30,12 @@ function processRoutePath(route) {
}
}
// ---- 处理页面目录映射 <end>----
// 去掉pages目录,只保留子目录
if (routeParts[0] === 'pages') {
routeParts = routeParts.slice(1);
}
}
return routeParts.join("/");
}
@@ -54,14 +54,11 @@ function loadLangPackSync(lang, path) {
}
}
/**
* 获得当前本地语言
* @returns
*/
function getCurrentLocale() {
return uni.getStorageSync('lang') || "zh-cn";
return uni.getStorageSync('lang') || "zh-cn";
}
export default {
langList: langConfig.langList,
@@ -80,18 +77,18 @@ export default {
if (!_page) return;
const locale = getCurrentLocale(); // 获得当前本地语言
let value = ''; // 存放解析后的语言值
let langPath = ''; // 存放当前页面语言包路径
try {
//公共语言包(同步加载)
var lang = loadLangPackSync(locale, 'common');
//当前页面语言包(同步加载)
let route = _page.route;
langPath = processRoutePath(route);
// 加载当前页面语言包
let currentPageLang = loadLangPackSync(locale, langPath);
@@ -116,7 +113,7 @@ export default {
} else {
value = mergedLang[field] !== undefined ? mergedLang[field] : field;
}
} catch (e) {
console.error('解析语言包失败:', e, { langPath, field, locale });
value = field;
@@ -132,7 +129,7 @@ export default {
// 多语言调试,注释后可以关闭控制台输出
if (field == value) {
console.warn(`警告: 字段 ${field} 在语言包 ${langPath} 中未找到对应值,使用默认值 ${field} 当前语言: ${locale}`);
console.warn(`警告: 字段 ${field} 在语言包 ${langPath} 中未找到对应值,使用默认值 ${field} 当前语言: ${locale}`);
}
return value;
@@ -168,7 +165,7 @@ export default {
if (!_page) return;
const locale = getCurrentLocale();
this.title(this.lang("title"));
//设置tabbar的文字语言
@@ -218,4 +215,4 @@ export default {
}
return list;
}
}
}

View File

@@ -213,6 +213,8 @@ export default {
// console.log(img_path)
path = img_path;
}
// 修复:去除 URL 中的双斜杠,避免微信 downloadFile 域名匹配失败
path = path.replace(/([^:]\/)\/+/g, "$1");
}
// path += '?t=' + parseInt(new Date().getTime() / 1000);

View File

@@ -0,0 +1,38 @@
/**
* 微信小程序图片长按识别二维码 mixin
* 自动为页面中的所有 image 组件添加 show-menu-by-longpress 属性
*/
export default {
data() {
return {
// 需要排除的图片类名(不需要长按识别的)
excludeImageClass: ['no-longpress', 'swiper-image', 'banner-image']
}
},
mounted() {
// #ifdef MP-WEIXIN
this.$nextTick(() => {
this.addLongpressToImages()
})
// #endif
},
methods: {
/**
* 为页面中的所有 image 添加长按识别支持
*/
addLongpressToImages() {
// 获取当前页面所有 image 组件
const images = document.querySelectorAll && document.querySelectorAll('image')
if (images) {
images.forEach(img => {
// 检查是否需要排除
const className = img.className || ''
const shouldExclude = this.excludeImageClass.some(cls => className.includes(cls))
if (!shouldExclude) {
img.setAttribute('show-menu-by-longpress', 'true')
}
})
}
}
}
}

View File

@@ -12,7 +12,7 @@
<view :class="['item', value.ornament.type]" :style="itemCss">
<view class="article-img">
<image class="cover-img" :src="$util.img(item.cover_img)" mode="widthFix"
@error="imgError(index)" />
@error="imgError(index)" show-menu-by-longpress/>
</view>
<view class="info-wrap">
<text class="title">{{ item.article_title }}</text>

View File

@@ -17,7 +17,7 @@
<view class="fui-audio style3" :style="{ background: value.background }" v-else>
<!-- <audio src="/static/audio/bgm.mp3" controls loop></audio> -->
<view class="img">
<image :src="$util.img(value.imageUrl)"></image>
<image :src="$util.img(value.imageUrl)" show-menu-by-longpress></image>
</view>
<view class="content">
<view class="name" :style="{ color: value.textcolor }">{{ value.text }}</view>

View File

@@ -10,7 +10,7 @@
:style="{ fontSize: value.titleStyle.fontSize * 2 + 'rpx', color: value.titleStyle.textColor, fontWeight: value.titleStyle.fontWeight ? 'bold' : '' }">
{{ value.titleStyle.leftText }}
</view>
<image v-else class="left-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix"></image>
<image v-else class="left-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix" show-menu-by-longpress></image>
<view class="head-content" v-if="value.titleStyle.style == 'style-1'"
:style="{ color: value.titleStyle.textColor }">低至0元免费拿</view>
<view class="head-right"
@@ -28,7 +28,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl || value.btnStyle.control">
@@ -41,7 +41,7 @@
<view class="bg">
<view class="curr" :style="{ width: progress(item) * 2 + 'rpx' }">
<image class="progress-bar" mode="widthFix"
:src="$util.img('public/uniapp/bargain/progress_bar_01.png')" />
:src="$util.img('public/uniapp/bargain/progress_bar_01.png')" show-menu-by-longpress/>
</view>
</view>
<view class="num" v-if="item.is_bargaining">
@@ -89,7 +89,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" :lazy-load="true" />
@error="imageError(index)" :lazy-load="true" show-menu-by-longpress/>
<image class="bg"
v-if="value.saleStyle.control && value.template == 'horizontal-slide' && value.style != 'style-2'"
:src="$util.img('public/uniapp/bargain/bg.png')" mode="widthFix" />
@@ -134,7 +134,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex)" :lazy-load="true" />
@error="imageError(dataIndex)" :lazy-load="true" show-menu-by-longpress/>
<image class="bg"
v-if="value.saleStyle.control && value.template == 'horizontal-slide' && value.style != 'style-2'"
:src="$util.img('public/uniapp/bargain/bg.png')" mode="widthFix" />

View File

@@ -2,7 +2,7 @@
<view data-component-name="diy-category-item" class="item-wrap" :class="type">
<block v-if="type == 'category' && category.child_list && category.child_list.length">
<view class="category-adv" v-if="category.image_adv" @tap.stop="diyRedirectTo(category.link_url)">
<image :src="$util.img(category.image_adv)" mode="widthFix" />
<image :src="$util.img(category.image_adv)" mode="widthFix" show-menu-by-longpress />
</view>
<block v-if="value.level == 2">
@@ -11,7 +11,7 @@
<view class="category-item" v-for="(one, oneIndex) in category.child_list" :key="oneIndex"
@tap.stop="$util.redirectTo('/pages_goods/list', { category_id: one.category_id })">
<view class="img-box">
<image :src="$util.img(one.image)" mode="widthFix" />
<image :src="$util.img(one.image)" mode="widthFix" show-menu-by-longpress />
</view>
<view class="name">{{ one.category_name }}</view>
</view>
@@ -25,7 +25,7 @@
<view class="category-item" v-for="(two, twoIndex) in one.child_list" :key="twoIndex"
@tap.stop="$util.redirectTo('/pages_goods/list', { category_id: two.category_id })">
<view class="img-box">
<image :src="$util.img(two.image)" mode="widthFix" :lazy-load="true" />
<image :src="$util.img(two.image)" mode="widthFix" :lazy-load="true" show-menu-by-longpress />
</view>
<view class="name">{{ two.category_name }}</view>
</view>
@@ -82,10 +82,10 @@
<block v-if="goodsList.length">
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
<view class="goods-img" @tap.stop="toDetail(item)">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" />
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}
</view>
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" show-menu-by-longpress />
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}
</view>
</view>
<view class="info-wrap">
<view class="name-wrap" @tap.stop="toDetail(item)">
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
@@ -99,17 +99,14 @@
<text class="unit price-style small">.{{
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
</block>
<block v-else>
<text class="unit price-style small">{{ $lang('Make') ? $lang('Make') : '询底价' }}</text>
</block>
</view>
<view class="member-price-tag"
v-if="item.member_price && item.member_price == showPrice(item)">
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" show-menu-by-longpress />
</view>
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
<image :src="$util.img('public/uniapp/index/discount.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress />
</view>
</view>
<view class="footer-wrap">
@@ -120,6 +117,12 @@
<text>{{ showMarketPrice(item) }}</text>
</view>
</view>
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
<view class="ai-chat-btn" @tap.stop="sendToAIChat(item)">
立即咨询
</view>
</view>
<!-- <view class="right-wrap" v-if="value.template == 2 || value.template == 4">
<block v-if="item.is_virtual">
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
@@ -155,7 +158,7 @@
</view>
</block>
<view class="category-empty" v-else>
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress />
</view>
<!-- <view class="end-tips" ref="endTips" v-if="last && (categoryId == -1 || !category.child_list || (category.child_list && categoryId == category.child_list.length - 1))">已经到底了~</view>
<view class="end-tips" ref="endTips" v-else @tap.stop="switchCategory('next')">
@@ -174,34 +177,30 @@
:data-template="value.template">
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
<view class="goods-img" @tap.stop="toDetail(item)">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"
:lazy-load="true" />
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
</view>
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"
:lazy-load="true" show-menu-by-longpress />
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
</view>
<view class="info-wrap">
<view class="name-wrap" @tap.stop="toDetail(item)">
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
</view>
<view class="price-wrap">
<view class="discount-price">
<block v-if="item.isinformation == 0">
<text class="unit price-style small">¥</text>
<text class="price price-style large">{{
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
<text class="unit price-style small">.{{
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
</block>
<block v-else>
<text class="unit price-style small">{{ $lang('Make') ? $lang('Make') : '询底价' }}</text>
</block>
</view>
<block v-if="item.isinformation == 0">
<text class="unit price-style small">¥</text>
<text class="price price-style large">{{
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
<text class="unit price-style small">.{{
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
</block>
</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 :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" show-menu-by-longpress />
</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 :src="$util.img('public/uniapp/index/discount.png')" mode="widthFix" show-menu-by-longpress />
</view>
</view>
<view class="footer-wrap">
@@ -212,6 +211,12 @@
<text>{{ showMarketPrice(item) }}</text>
</view>
</view>
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
<view class="ai-chat-btn" @tap.stop="sendToAIChat(item)">
立即咨询
</view>
</view>
<view class="right-wrap" v-if="value.template == 2">
<block v-if="item.is_virtual">
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
@@ -718,6 +723,15 @@ export default {
}
}
},
/**
* 跳转到商品详情
* @param {Object} item 商品信息
*/
sendToAIChat(item) {
this.$util.redirectTo('/pages_goods/detail', {
goods_id: item.goods_id
});
},
diyRedirectTo(link) {
if (!link) return;
this.$util.diyRedirectTo(JSON.parse(link));
@@ -1384,4 +1398,25 @@ export default {
color: var(--main-color);
}
}
.ai-chat-btn-wrap {
display: flex;
justify-content: flex-end;
margin-top: auto;
padding-top: 10rpx;
}
.ai-chat-btn {
background-color: #ff0000;
color: #fff;
padding: 12rpx 30rpx;
border-radius: 40rpx;
font-size: 26rpx;
font-weight: 500;
line-height: 1.2;
text-align: center;
white-space: nowrap;
display: inline-block;
}
</style>

View File

@@ -43,7 +43,7 @@
<view class="category-item" :id="'category-one-' + index" v-for="(item, index) in templateFourData"
:key="index" :class="{ select: oneCategorySelect == index }" @tap.stop="templateFourOneFn(index)">
<view class="image-warp" :class="[{ 'color-base-border': oneCategorySelect == index }]">
<image :src="$util.img(item.image)" mode="aspectFill" />
<image :src="$util.img(item.image)" mode="aspectFill" show-menu-by-longpress/>
</view>
<view :class="['text', { 'color-base-bg': oneCategorySelect == index }]">{{ item.category_name }}
</view>
@@ -52,7 +52,7 @@
<view class="category-item-all" @tap.stop="$refs.templateFourPopup.open()">
<view class="category-item-all-wrap">
<text class="text">展开</text>
<image class="img" :src="$util.img('/public/uniapp/category/unfold.png')" mode="aspectFill"></image>
<image class="img" :src="$util.img('/public/uniapp/category/unfold.png')" mode="aspectFill" show-menu-by-longpress></image>
</view>
</view>
<uni-popup type="top" ref="templateFourPopup" :top="uniPopupTop">
@@ -61,7 +61,7 @@
<view class="item" :class="{ selected: oneCategorySelect == index }"
@tap.stop="templateFourOneFn(index)" v-for="(item, index) in templateFourData" :key="index">
<view class="image-warp" :class="[{ 'color-base-border': oneCategorySelect == index }]">
<image :src="$util.img(item.image)" mode="aspectFill"></image>
<image :src="$util.img(item.image)" mode="aspectFill" show-menu-by-longpress></image>
</view>
<view :class="['text', { 'color-base-bg': oneCategorySelect == index }]">{{
item.category_name }}</view>
@@ -119,12 +119,12 @@
</view>
</block>
<view class="category-empty" v-else>
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress></image>
<view class="tips">暂时没有分类哦!</view>
</view>
</view>
<view class="category-empty" v-else>
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress></image>
<view class="tips">暂时没有分类哦!</view>
</view>

View File

@@ -14,7 +14,7 @@
</view>
<view class="video-info">
<!-- 视频标题,支持多行显示控制 -->
<view class="video-title" :style="{ '--title-line-clamp': titleLineClamp }">{{ value.videoTitle }}
<view class="video-title" :style="[{ '--title-line-clamp': titleLineClamp }]">{{ value.videoTitle }}
</view>
<!-- 视频统计信息 -->
<view class="video-stats" v-if="showViewCount && value.showViewCount">{{ value.viewCount }}次观看</view>
@@ -24,7 +24,7 @@
<!-- 跳转式视频播放 -->
<view v-else @tap.stop="playVideo" class="video-container">
<view class="video-cover-wrap" :style="[coverStyle]">
<image class="video-cover" :src="$util.img(value.coverUrl)" mode="aspectFill"></image>
<image class="video-cover" :src="$util.img(value.coverUrl)" mode="aspectFill" show-menu-by-longpress></image>
<view class="channel-play-btn" v-if="showPlayBtn" :style="[playBtnStyle]">
<view class="play-icon-css"></view>
</view>
@@ -34,7 +34,7 @@
</view>
<view class="video-info">
<!-- 视频标题,支持多行显示控制 -->
<view class="video-title" :style="{ '--title-line-clamp': titleLineClamp }">{{ value.videoTitle }}
<view class="video-title" :style="[{ '--title-line-clamp': titleLineClamp }]">{{ value.videoTitle }}
</view>
<!-- 视频统计信息,非列表模式下显示 -->
<view class="video-stats" v-if="showViewCount && value.showViewCount && !listMode">{{ value.viewCount }}次观看</view>

View File

@@ -5,7 +5,7 @@
:class="[value.ornament.type]" :style="goodsItemCss">
<view class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :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)" show-menu-by-longpress/>
</view>
<view class="info-wrap"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl || value.priceStyle.lineControl || value.btnStyle.control">

View File

@@ -6,7 +6,7 @@
<view class="button-box" @tap.stop="$util.diyRedirectTo(item.link)"
:style="{ width: value.imageSize + 'px', height: value.imageSize + 'px', fontSize: value.imageSize + 'px' }">
<image v-if="!item.iconType || item.iconType == 'img'" :src="$util.img(item.imageUrl)" mode="aspectFit"
:show-menu-by-longpress="true" />
show-menu-by-longpress />
<diy-icon v-else-if="item.iconType && item.iconType == 'icon'" :icon="item.icon"
:value="item.style ? item.style : null"></diy-icon>
</view>

View File

@@ -9,7 +9,7 @@
<view class="li-item" v-for="(item, index) in list" :key="index">
<image class="brand-pic" :src="$util.img(item.image_url)" mode="aspectFit"
@tap.stop="handlerClick(item)" @error="imgError(index)"
:style="itemCss" />
:style="itemCss" show-menu-by-longpress/>
</view>
</view>
</view>

View File

@@ -8,7 +8,8 @@
<image class="goods-img"
:src="$util.img(item.goods_image, { size: goodsValue.template == 'large-mode' ? 'big' : 'mid' })"
mode="widthFix" @error="imgError(index)"
:style="{ borderRadius: goodsValue.imgAroundRadius * 2 + 'rpx' }" />
:style="{ borderRadius: goodsValue.imgAroundRadius * 2 + 'rpx' }"
show-menu-by-longpress />
<view class="sell-out" v-if="item.stock <= 0">
<text class="iconfont icon-shuqing"></text>
</view>
@@ -48,9 +49,9 @@
<view class="member-price"
v-if="(item.member_price && item.member_price == showPrice(item)) || item.promotion_type == 1">
<image v-if="item.member_price && item.member_price == showPrice(item)"
:src="$util.img('public/uniapp/index/VIP.png')" />
:src="$util.img('public/uniapp/index/VIP.png')" show-menu-by-longpress />
<image v-else-if="item.promotion_type == 1"
:src="$util.img('public/uniapp/index/discount.png')" />
:src="$util.img('public/uniapp/index/discount.png')" show-menu-by-longpress />
</view>
<view v-if="goodsValue.priceStyle.lineControl && showMarketPrice(item)"
class="delete-price price-font"
@@ -117,7 +118,7 @@
<view class="goods-img-wrap">
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix" @error="imgError(index)"
:lazy-load="true" />
:lazy-load="true" show-menu-by-longpress />
<view class="sell-out" v-if="item.stock <= 0">
<text class="iconfont icon-shuqing"></text>
</view>
@@ -156,9 +157,9 @@
<view class="member-price"
v-if="(item.member_price && item.member_price == showPrice(item)) || item.promotion_type == 1">
<image v-if="item.member_price && item.member_price == showPrice(item)"
:src="$util.img('public/uniapp/index/VIP.png')" />
:src="$util.img('public/uniapp/index/VIP.png')" show-menu-by-longpress />
<image v-else-if="item.promotion_type == 1"
:src="$util.img('public/uniapp/index/discount.png')" />
:src="$util.img('public/uniapp/index/discount.png')" show-menu-by-longpress />
</view>
<view v-if="goodsValue.priceStyle.lineControl && showMarketPrice(item)"
class="delete-price price-font"
@@ -184,7 +185,7 @@
<view class="goods-img-wrap">
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
@error="imgError(dataIndex)" :lazy-load="true" />
@error="imgError(dataIndex)" :lazy-load="true" show-menu-by-longpress />
<view class="sell-out" v-if="dataItem.stock <= 0">
<text class="iconfont icon-shuqing"></text>
</view>
@@ -226,9 +227,9 @@
v-if="(dataItem.member_price && dataItem.member_price == showPrice(dataItem)) || dataItem.promotion_type == 1">
<image
v-if="dataItem.member_price && dataItem.member_price == showPrice(dataItem)"
:src="$util.img('public/uniapp/index/VIP.png')" />
:src="$util.img('public/uniapp/index/VIP.png')" show-menu-by-longpress />
<image v-else-if="dataItem.promotion_type == 1"
:src="$util.img('public/uniapp/index/discount.png')" />
:src="$util.img('public/uniapp/index/discount.png')" show-menu-by-longpress />
</view>
<view v-if="goodsValue.priceStyle.lineControl && showMarketPrice(dataItem)"
class="delete-price"

View File

@@ -17,7 +17,7 @@
<div class="goods-img-wrap">
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
@error="imgError(index, dataIndex)" :lazy-load="true" />
@error="imgError(index, dataIndex)" :lazy-load="true" show-menu-by-longpress />
<view class="sell-out" v-if="dataItem.stock <= 0">
<text class="iconfont icon-shuqing"></text>
</view>
@@ -33,7 +33,7 @@
<view class="label-wrap" v-if="goodsValue.labelStyle.support"
:style="{ background: goodsValue.labelStyle.bgColor, color: goodsValue.labelStyle.color }">
<image :src="$util.img('app/component/view/goods_recommend/img/label.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress />
<text>{{ goodsValue.labelStyle.title }}</text>
</view>
<view class="discount-price">

View File

@@ -23,7 +23,7 @@
:src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
mode="aspectFill"
:style="{ maxWidth: value.imageSize * 2 + 'rpx', maxHeight: value.imageSize * 2 + 'rpx', borderRadius: value.aroundRadius * 2 + 'rpx' }"
:show-menu-by-longpress="true" />
show-menu-by-longpress />
<diy-icon v-if="item.iconType == 'icon'" :icon="item.icon"
:value="item.style ? item.style : null"
@@ -69,7 +69,7 @@
:src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
mode="aspectFill"
:style="{ maxWidth: value.imageSize * 2 + 'rpx', maxHeight: value.imageSize * 2 + 'rpx', borderRadius: value.aroundRadius * 2 + 'rpx' }"
:show-menu-by-longpress="true" />
show-menu-by-longpress />
<diy-icon v-if="item.iconType == 'icon'" :icon="item.icon"
:value="item.style ? item.style : null"
:style="{ maxWidth: value.imageSize * 2 + 'rpx', maxHeight: value.imageSize * 2 + 'rpx', width: '100%', height: '100%' }"></diy-icon>

View File

@@ -317,7 +317,12 @@ export default {
this.diyGlobalData.value.forEach((item, index) => {
item.pageStyle = '';
// 给每个组件增加位置属性,用于定位,搜索、分类导航等定位
item.moduleIndex = index + 1;
// 如果是Tab组件内部的子组件,添加tabIndex前缀避免moduleIndex冲突
if (this.diyData.tabIndex !== undefined) {
item.moduleIndex = `${this.diyData.tabIndex}-${index + 1}`;
} else {
item.moduleIndex = index + 1;
}
// 特殊处理搜索框 当显示位置为滚动至顶部固定时,只设置背景颜色
if (item.componentName == 'Search' && item.positionWay == 'fixed') {

View File

@@ -7,7 +7,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)">
@error="imageError(index)" show-menu-by-longpress>
</image>
</view>
<view class="content"
@@ -44,7 +44,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
<image class="bg" v-if="value.saleStyle.control"
:src="$util.img('public/uniapp/groupbuy/bg.png')" mode="widthFix" />
<view class="num" v-if="value.saleStyle.control"
@@ -85,7 +85,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex)" />
@error="imageError(dataIndex)" show-menu-by-longpress/>
<image class="bg" v-if="value.saleStyle.control"
:src="$util.img('public/uniapp/groupbuy/bg.png')" mode="widthFix" />
<view class="num" v-if="value.saleStyle.control"

View File

@@ -2,7 +2,7 @@
<view data-component-name="diy-hot-area" :style="hotAreaWarp" class="hot-area-box">
<view class="simple-graph-wrap">
<image :style="{ height: value.imgHeight }" :src="$util.img(value.imageUrl)" mode="widthFix"
:show-menu-by-longpress="true" />
show-menu-by-longpress />
<!-- 热区功能 -->
<view class="heat-map" v-for="(mapItem, mapIndex) in value.heatMapData" :key="mapIndex" :style="{
width: mapItem.width + '%',

View File

@@ -15,12 +15,12 @@
'width': (item.imgWidth / 2 + 'rpx') + ';',
'height': (item.imgHeight / 2 + 'rpx') + ';'
}" :src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
:show-menu-by-longpress="true" @tap.stop="redirectTo(item.link)"></image>
show-menu-by-longpress @tap.stop="redirectTo(item.link)"></image>
<image v-else :style="{
'width': (item.imgWidth / 2 + 'rpx') + ';',
'height': (item.imgHeight / 2 + 'rpx') + ';'
}" :src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
:show-menu-by-longpress="true" @tap.stop="previewImg(item.imageUrl)"></image>
show-menu-by-longpress @tap.stop="previewImg(item.imageUrl)"></image>
</view>
<!-- 文字部分 -->

View File

@@ -4,7 +4,7 @@
<block v-if="imgAdsValue.list.length == 1">
<view class="simple-graph-wrap" :style="imgAdsSwiper" @tap.stop="handlerClick(imgAdsValue.list[0].link)">
<image :style="{ height: imgAdsValue.list[0].imgHeight }"
:src="$util.img(imgAdsValue.list[0].imageUrl)" mode="widthFix" :show-menu-by-longpress="true" />
:src="$util.img(imgAdsValue.list[0].imageUrl)" mode="widthFix" show-menu-by-longpress />
</view>
</block>
<swiper v-else class="swiper" :style="{ height: swiperHeight }" :class="{
@@ -18,7 +18,7 @@
:key="index" v-if="item.imageUrl" @tap.stop="handlerClick(item.link)">
<view class="item" :style="imgAdsSwiper + 'height: ' + item.imgHeight">
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
:show-menu-by-longpress="true" />
show-menu-by-longpress />
</view>
</swiper-item>
</swiper>

View File

@@ -58,9 +58,9 @@
:key="index" @tap.stop="toCateGoodsList(item.category_id_2, 2)">
<view class="item-box">
<image :src="$util.img(item.image)" v-if="item.image"
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress/>
<image :src="$util.getDefaultImage().goods" v-else
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress/>
<view>{{ item.category_name }}</view>
</view>
</view>
@@ -73,9 +73,9 @@
:key="index" @tap.stop="toCateGoodsList(item.category_id_2, 2)">
<view class="item-box">
<image :src="$util.img(item.image)" v-if="item.image"
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress/>
<image :src="$util.getDefaultImage().goods" v-else
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress/>
<view>{{ item.category_name }}</view>
</view>
</view>
@@ -88,7 +88,7 @@
:key="index" v-if="index >= (page - 1) * 10 && index < page * 10"
@tap.stop="toCateGoodsList(item.category_id_2, 2)">
<view class="item-box">
<image :src="item.image" mode="aspectFill" />
<image :src="item.image" mode="aspectFill" show-menu-by-longpress/>
<view>{{ item.category_name }}</view>
</view>
</view>
@@ -104,19 +104,19 @@
<!-- 分类广告 -->
<image class="category_adv" v-if="cateList[pageIndex].image_adv"
:src="$util.img(cateList[pageIndex].image_adv)" mode="widthFix" />
:src="$util.img(cateList[pageIndex].image_adv)" mode="widthFix" show-menu-by-longpress/>
<view class="goods-list double-column" v-if="goodsList[pageIndex].list.length">
<view class="goods-item" v-for="(item, index) in goodsList[pageIndex].list"
:key="index" @tap.stop="toDetail(item)">
<view class="goods-img">
<image :src="goodsImg(item.goods_image)" mode="widthFix"
@error="imgError(index)" />
@error="imgError(index)" show-menu-by-longpress/>
<view class="color-base-bg goods-tag"
v-if="value.goodsTag == 'default' && goodsTag(item) != ''">{{
goodsTag(item) }}</view>
<view class="goods-tag-img" v-if="value.goodsTag == 'diy'">
<image :src="$util.img(value.tagImg.imageUrl)" />
<image :src="$util.img(value.tagImg.imageUrl)" show-menu-by-longpress/>
</view>
</view>
<view class="info-wrap">
@@ -133,11 +133,11 @@
<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" />
mode="widthFix" show-menu-by-longpress/>
</view>
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
<image :src="$util.img('public/uniapp/index/discount.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress/>
</view>
</view>
<view class="pro-info">

View File

@@ -3,7 +3,7 @@
<view class="fui-list-group merchgroup" v-for="(item, index) in value.list" :key="index">
<view class="fui-list jump" v-if="index == 0">
<view class="fui-list-media">
<image class="round" :src="$util.img(item.imageUrl)" style="border-radius:6rpx"></image>
<image class="round" :src="$util.img(item.imageUrl)" style="border-radius:6rpx" show-menu-by-longpress></image>
</view>
<view class="fui-list-inner">
<view class="title" style="height: 75rpx;">

View File

@@ -1,7 +1,7 @@
<template>
<view data-component-name="diy-listmenu" class="diy-listmenu" :style="style">
<view class="fui-cell-group">
<!-- <image mode="widthFix" style="width: 100%;" :src="$util.img(item.imageUrl)"></image> -->
<!-- <image mode="widthFix" style="width: 100%;" :src="$util.img(item.imageUrl)" show-menu-by-longpress></image> -->
<view v-for="(item, index) in value.list" @tap.stop="redirectTo(item.link)" class="fui-cell"
:class="item.iconType == 'img' ? 'img-cell' : ''">

View File

@@ -25,7 +25,7 @@
<block v-if="value.isShowAnchorInfo">
<image
:src="liveInfo.anchor_img != '' ? $util.img(liveInfo.anchor_img) : $util.getDefaultImage().head"
class="anchor-img" @error="liveInfo.anchor_img = $util.getDefaultImage().head" />
class="anchor-img" @error="liveInfo.anchor_img = $util.getDefaultImage().head" show-menu-by-longpress/>
<text class="anchor-name">主播:{{ liveInfo.anchor_name }}</text>
</block>
<text class="separate" v-if="value.isShowAnchorInfo && value.isShowLiveGood">|</text>

View File

@@ -6,7 +6,7 @@
<view class="info-wrap" :style="infoStyle" v-if="memberInfo">
<view class="headimg" @tap.stop="getWxAuth">
<image :src="memberInfo.headimg ? $util.img(memberInfo.headimg) : $util.getDefaultImage().head"
mode="widthFix" @error="memberInfo.headimg = $util.getDefaultImage().head" />
mode="widthFix" @error="memberInfo.headimg = $util.getDefaultImage().head" show-menu-by-longpress/>
</view>
<view class="info">
<!-- #ifdef MP -->
@@ -66,7 +66,7 @@
<view class="info-wrap" v-else :style="infoStyle" @tap.stop="redirect($util.MEMBER_PAGE_URL)">
<view class="headimg">
<image :src="$util.getDefaultImage().head" mode="widthFix"></image>
<image :src="$util.getDefaultImage().head" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="info">
<view class="nickname"><text class="name">{{ $lang('login') }}</text></view>
@@ -117,7 +117,7 @@
</block>
<block v-if="value.style == 3">
<view class="super-info">
<image :src="$util.img('public/uniapp/member/open_member.png')" class="title" mode="heightFix" />
<image :src="$util.img('public/uniapp/member/open_member.png')" class="title" mode="heightFix" show-menu-by-longpress/>
<view class="desc">开通可享更多权益</view>
</view>
<view class="super-text">
@@ -130,7 +130,7 @@
<view class="member-info-style4" v-show="value.style == 4">
<view class="super-member" v-if="superMember" :style="superMemberStyle">
<view class="super-info">
<image :src="$util.img('app/component/view/member_info/img/style_4_vip_huangguan.png')" class="title" mode="widthFix" />
<image :src="$util.img('app/component/view/member_info/img/style_4_vip_huangguan.png')" class="title" mode="widthFix" show-menu-by-longpress/>
<view class="desc">开通可享更多权益</view>
</view>
<view class="super-text" :class="{ 'more' : memberInfo && memberInfo.member_level_type }">
@@ -203,7 +203,7 @@
<text class="label">头像</text>
<button open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image :src="avatarUrl ? avatarUrl : $util.getDefaultImage().head"
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" />
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
<text class="iconfont icon-right color-tip"></text>
</button>
</view>
@@ -218,7 +218,7 @@
<button open-type="getAuthorize" scope="userInfo" @getAuthorize="aliappGetUserinfo"
:plain="true" class="border-0">
<image :src="avatarUrl ? avatarUrl : $util.getDefaultImage().head"
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" />
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
<text class="iconfont icon-right color-tip"></text>
</button>
</view>

View File

@@ -6,7 +6,7 @@
style="margin-right: 10rpx;">
<view class="icon-block">
<template v-if="value.style == 3">
<image :src="$util.img('public/uniapp/member/order/wait_pay.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/member/order/wait_pay.png')" mode="widthFix" show-menu-by-longpress/>
<view class="icon-shade"
:style="'-webkit-mask-image: url(' + $util.img('public/uniapp/member/order/wait_pay_shade.png') + ')'">
</view>
@@ -25,7 +25,7 @@
style="margin-right: 10rpx;">
<view class="icon-block">
<template v-if="value.style == 3">
<image :src="$util.img('public/uniapp/member/order/wait_send.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/member/order/wait_send.png')" mode="widthFix" show-menu-by-longpress></image>
<view class="icon-shade"
:style="'-webkit-mask-image: url(' + $util.img('public/uniapp/member/order/wait_send_shade.png') + ')'">
</view>
@@ -44,7 +44,7 @@
style="margin-right: 10rpx;">
<view class="icon-block">
<template v-if="value.style == 3">
<image :src="$util.img('public/uniapp/member/order/wait_confirm.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/member/order/wait_confirm.png')" mode="widthFix" show-menu-by-longpress/>
<view class="icon-shade"
:style="'-webkit-mask-image: url(' + $util.img('public/uniapp/member/order/wait_confirm_shade.png') + ')'">
</view>
@@ -63,7 +63,7 @@
style="margin-right: 10rpx;">
<view class="icon-block">
<template v-if="value.style == 3">
<image :src="$util.img('public/uniapp/member/order/wait_use.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/member/order/wait_use.png')" mode="widthFix" show-menu-by-longpress/>
<view class="icon-shade"
:style="'-webkit-mask-image: url(' + $util.img('public/uniapp/member/order/wait_rate_shade.png') + ')'">
</view>
@@ -79,7 +79,7 @@
<view class="item-wrap" @tap.stop="redirect('/pages_tool/order/activist')">
<view class="icon-block">
<template v-if="value.style == 3">
<image :src="$util.img('public/uniapp/member/order/refunding.png')" mode="widthFix" />
<image :src="$util.img('public/uniapp/member/order/refunding.png')" mode="widthFix" show-menu-by-longpress/>
<view class="icon-shade"
:style="'-webkit-mask-image: url(' + $util.img('public/uniapp/member/order/refunding_shade.png') + ')'">
</view>

View File

@@ -7,7 +7,7 @@
:style="itemCss" @tap.stop="handlerClick(item)">
<view class="merch-img">
<image class="cover-img" :src="$util.img(item.merch_image)" mode="widthFix"
@error="imgError(index)" />
@error="imgError(index)" show-menu-by-longpress />
</view>
<view class="info-wrap">
<text class="title">{{ item.merch_name }}</text>
@@ -30,7 +30,7 @@
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
<image
:src="$util.img(item.merch_image) || $util.img('public/uniapp/default_img/goods.png')"
mode="aspectFill" :show-menu-by-longpress="true"
mode="aspectFill" show-menu-by-longpress
style="max-width: 80rpx; max-height: 80rpx; border-radius: 50rpx;" />
</view>
<text class="graphic-text"

View File

@@ -25,10 +25,10 @@
<view class="notes-img-wrap" :class="{ 'notes-img-wrap-list': item.cover_type == 1 }">
<image v-if="item.cover_type == 0" :src="$util.img(item.img)" @error="imageError(i)"
mode="aspectFill" class="notes-item-image" />
mode="aspectFill" class="notes-item-image" show-menu-by-longpress/>
<image v-else v-for="(imgItem, imgIndex) in item.img" :key="imgIndex"
:src="$util.img(imgItem)" @error="imageError(i)" mode="aspectFit"
class="notes-item-image-li" />
class="notes-item-image-li" show-menu-by-longpress/>
</view>
<view class="notes-item-con">

View File

@@ -3,9 +3,9 @@
<view class="fui-picture">
<view v-for="(item, index) in value.list" style="line-height: 0;">
<image mode="widthFix" style="width: 100%;height:auto" :src="$util.img(item.imageUrl)"
v-if="item.link.wap_url" @tap.stop="handlerClick(item)"></image>
v-if="item.link.wap_url" @tap.stop="handlerClick(item)" show-menu-by-longpress></image>
<image mode="widthFix" style="width: 100%;height:auto" :src="$util.img(item.imageUrl)" v-else
@tap.stop="handlerClick(item)"></image>
@tap.stop="handlerClick(item)" show-menu-by-longpress></image>
</view>
<!-- <view wx:if="{{!childitem.linkurl}}" bindtap="previewImg" data-src="{{childitem.imgurl}}" style="padding:{{diyitem.style.paddingtop==0?0:diyitem.style.paddingtop+'rpx'}} {{diyitem.style.paddingleft==0?0:diyitem.style.paddingleft+'rpx'}}" wx:for="{{diyitem.data}}" wx:for-index="childid" wx:for-item="childitem" wx:key="{{childid}}">
<image mode="widthFix" src="{{childitem.imgurl}}" style="{{bannerheight?'height:'+bannerheight+'px':'height:auto'}}"></image>

View File

@@ -7,7 +7,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl || value.priceStyle.lineControl || value.btnStyle.control">
@@ -66,7 +66,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view :class="['content', { 'multi-content': value.nameLineMode == 'multiple' }]"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
@@ -117,7 +117,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex)" />
@error="imageError(dataIndex)" show-menu-by-longpress/>
</view>
<view :class="['content', { 'multi-content': value.nameLineMode == 'multiple' }]"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">

View File

@@ -10,18 +10,18 @@
:style="{ fontSize: value.titleStyle.fontSize * 2 + 'rpx', color: value.titleStyle.textColor, fontWeight: value.titleStyle.fontWeight ? 'bold' : '' }">
{{ value.titleStyle.leftText }}
</view>
<image v-else class="left-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix" />
<image v-else class="left-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix" show-menu-by-longpress/>
<view class="head-content">
<view class="img-warp">
<block v-if="headData && headData.member_list && headData.member_list.length">
<image v-for="(item, index) in headData.member_list" :key="index"
:src="$util.img(item.member_img || 'public/static/img/default_img/square.png')"
mode="aspectFill" @error="headImageError(index)" />
mode="aspectFill" @error="headImageError(index)" show-menu-by-longpress/>
</block>
<block v-else>
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" />
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" />
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" />
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" show-menu-by-longpress/>
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" show-menu-by-longpress/>
<image :src="$util.img('public/static/img/default_img/square.png')" mode="aspectFill" show-menu-by-longpress/>
</block>
</view>
<view :style="{ color: value.titleStyle.textColor }">
@@ -43,7 +43,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl || value.priceStyle.lineControl || value.btnStyle.control">
@@ -105,7 +105,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
<view v-if="value.groupStyle.control && value.style == 'style-1'" class="num" :style="{
color: value.theme == 'diy' ? value.groupStyle.color : '',
background: value.theme == 'diy' ? 'linear-gradient(to right,' + value.groupStyle.bgColorStart + ',' + value.groupStyle.bgColorEnd + ')' : ''
@@ -165,7 +165,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex)" />
@error="imageError(dataIndex)" show-menu-by-longpress/>
<view v-if="value.groupStyle.control && value.style != 'style-2'" class="num" :style="{
color: value.theme == 'diy' ? value.groupStyle.color : '',
background: value.theme == 'diy' ? 'linear-gradient(to right,' + value.groupStyle.bgColorStart + ',' + value.groupStyle.bgColorEnd + ')' : ''

View File

@@ -7,7 +7,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl || value.btnStyle.control">
@@ -45,7 +45,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view :class="['content', { 'multi-content': value.nameLineMode == 'multiple' }]"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
@@ -79,7 +79,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex)" />
@error="imageError(dataIndex)" show-menu-by-longpress/>
</view>
<view :class="['content', { 'multi-content': value.nameLineMode == 'multiple' }]"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">

View File

@@ -10,7 +10,7 @@
<view class="quick-img" v-if="item.imageUrl || item.icon">
<image v-if="item.iconType == 'img'"
:src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
mode="heightFix" :show-menu-by-longpress="true"></image>
mode="heightFix" show-menu-by-longpress ></image>
<diy-icon v-if="item.iconType == 'icon'" :icon="item.icon"
:value="item.style ? item.style : null" :style="{ fontSize: '60rpx' }"></diy-icon>
</view>

View File

@@ -11,7 +11,7 @@
<view :class="['item', value.mode]" @tap.stop="handlerClick(value.list[0].link, value.list[0].imageUrl)"
:style="{ marginRight: value.imageGap * 2 + 'rpx', width: list[0].imgWidth, height: list[0].imgHeight + 'px' }">
<image :src="$util.img(value.list[0].imageUrl)" :mode="list[0].imageMode || 'scaleToFill'"
:style="list[0].pageItemStyle" :show-menu-by-longpress="true" />
:style="list[0].pageItemStyle" show-menu-by-longpress />
</view>
</view>
@@ -21,7 +21,7 @@
<view :key="index" :class="['item', value.mode]" @tap.stop="handlerClick(item.link, item.imageUrl)"
:style="{ marginBottom: value.imageGap * 2 + 'rpx', width: item.imgWidth, height: item.imgHeight + 'px' }">
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
:style="item.pageItemStyle" show-menu-by-longpress />
</view>
</template>
</template>
@@ -35,7 +35,7 @@
:style="{ marginRight: value.imageGap * 2 + 'rpx', width: list[0].imgWidth, height: list[0].imgHeight + 'px' }"
@tap.stop="handlerClick(value.list[0].link, value.list[0].imageUrl)">
<image :src="$util.img(value.list[0].imageUrl)" :mode="list[0].imageMode || 'scaleToFill'"
:style="list[0].pageItemStyle" :show-menu-by-longpress="true" />
:style="list[0].pageItemStyle" show-menu-by-longpress />
</view>
</view>
@@ -44,7 +44,7 @@
:style="{ marginBottom: value.imageGap * 2 + 'rpx', width: list[1].imgWidth, height: list[1].imgHeight + 'px' }"
@tap.stop="handlerClick(value.list[1].link, value.list[1].imageUrl)">
<image :src="$util.img(value.list[1].imageUrl)" :mode="list[1].imageMode || 'scaleToFill'"
:style="list[1].pageItemStyle" :show-menu-by-longpress="true" />
:style="list[1].pageItemStyle" show-menu-by-longpress />
</view>
<view class="template-bottom">
<template v-for="(item, index) in list">
@@ -55,7 +55,7 @@
height: item.imgHeight + 'px'
}">
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
:style="item.pageItemStyle" show-menu-by-longpress />
</view>
</template>
</template>
@@ -68,7 +68,7 @@
@tap.stop="handlerClick(item.link, item.imageUrl)"
:style="{ marginRight: value.imageGap * 2 + 'rpx', marginBottom: value.imageGap * 2 + 'rpx', width: item.widthStyle, height: item.imgHeight + 'px' }">
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
:style="item.pageItemStyle" show-menu-by-longpress />
</view>
</template>
</view>

View File

@@ -5,7 +5,7 @@
@tap.stop="handlerSearchClick">
<block v-if="[1, 2].includes(value.searchStyle)">
<view class="img" v-if="value.searchStyle == 2 && value.iconType == 'img'">
<image :src="$util.img(value.imageUrl)" mode="heightFix" />
<image :src="$util.img(value.imageUrl)" mode="heightFix" show-menu-by-longpress/>
</view>
<diy-icon class="icon" v-if="value.searchStyle == 2 && value.iconType == 'icon'" :icon="value.icon"
:value="value.style ? value.style : 'null'"
@@ -32,7 +32,7 @@
</view>
<view class="img" v-if="value.iconType == 'img'"
@tap.stop="handlerRedirectToClick(value.searchLink)">
<image :src="$util.img(value.imageUrl)" mode="heightFix" />
<image :src="$util.img(value.imageUrl)" mode="heightFix" show-menu-by-longpress/>
</view>
<diy-icon class="icon" v-if="value.iconType == 'icon'" :icon="value.icon"
:value="value.style ? value.style : 'null'"

View File

@@ -11,7 +11,7 @@
:style="{ fontSize: value.titleStyle.fontSize * 2 + 'rpx', color: value.titleStyle.textColor, fontWeight: value.titleStyle.fontWeight ? 'bold' : '' }">
{{ value.titleStyle.leftText }}
</view>
<image v-else class="title-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix" />
<image v-else class="title-img" :src="$util.img(value.titleStyle.leftImg)" mode="heightFix" show-menu-by-longpress/>
<text v-if="value.titleStyle.style == 'style-2' && isFuture"
:style="{ color: value.titleStyle.textColor }" class="seckill-style-name">距离开始</text>
<view class="time" :style="{ background: value.titleStyle.timeBgColor }">
@@ -40,7 +40,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content">
<view class="goods-name" v-if="value.goodsNameStyle.control"
@@ -57,7 +57,7 @@
}">
<image v-if="value.style == 'style-2'" class="progress-bar"
:src="$util.img('public/uniapp/seckill/progress_bar_01.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress/>
</view>
</view>
<view class="num"
@@ -110,7 +110,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content" v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
<view class="goods-name" v-if="value.goodsNameStyle.control"
@@ -155,7 +155,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(index)" />
@error="imageError(index)" show-menu-by-longpress/>
</view>
<view class="content" v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
<view class="goods-name" v-if="value.goodsNameStyle.control"
@@ -166,7 +166,7 @@
<view class="discount-price" v-if="value.priceStyle.mainControl">
<image v-if="value.style == 'style-2'" class="tag"
:src="$util.img('addon/seckill/component/view/seckill/img/style_4_tag.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress/>
<text class="unit price-style small"
:style="{ color: value.theme == 'diy' ? value.priceStyle.mainColor : '' }">¥</text>
<text class="price price-style large"
@@ -194,7 +194,7 @@
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
@error="imageError(dataIndex, pageIndex)" />
@error="imageError(dataIndex, pageIndex)" show-menu-by-longpress/>
</view>
<view class="content"
v-if="value.goodsNameStyle.control || value.priceStyle.mainControl">
@@ -206,7 +206,7 @@
<view class="discount-price" v-if="value.priceStyle.mainControl">
<image v-if="value.style == 'style-2'" class="tag"
:src="$util.img('addon/seckill/component/view/seckill/img/style_4_tag.png')"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress/>
<text class="unit price-style small"
:style="{ color: value.theme == 'diy' ? value.priceStyle.mainColor : '' }">¥</text>
<text class="price price-style large"

View File

@@ -22,8 +22,8 @@
</view>
<view class="store-image" @tap.stop="selectStore()">
<image :src="$util.img(globalStoreInfo.store_image)"
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill"></image>
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill"></image>
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill" show-menu-by-longpress></image>
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill" show-menu-by-longpress></image>
</view>
</view>
</block>
@@ -33,8 +33,8 @@
<view class="store-info">
<view class="store-image" @tap.stop="selectStore()">
<image :src="$util.img(globalStoreInfo.store_image)"
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill"></image>
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill"></image>
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill" show-menu-by-longpress></image>
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill" show-menu-by-longpress></image>
</view>
<view class="info-box" :style="{ color: value.textColor }">
<block v-if="globalStoreInfo && globalStoreInfo.store_id">

File diff suppressed because it is too large Load Diff

View File

@@ -89,14 +89,14 @@
<view class="style9-box">
<view class="style9-center">
<view class="left-img">
<image :src="$util.img('public/uniapp/diy/style9-1.png')" />
<image :src="$util.img('public/uniapp/diy/style9-1.png')" show-menu-by-longpress/>
</view>
<text class="style9-title"
:style="{ fontSize: value.fontSize * 2 + 'rpx', color: value.textColor, fontWeight: value.fontWeight }">
{{ value.text }}
</text>
<view class="right-img">
<image :src="$util.img('public/uniapp/diy/style9-2.png')" />
<image :src="$util.img('public/uniapp/diy/style9-2.png')" show-menu-by-longpress/>
</view>
<view class="style9-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
@tap.stop="handlerClick(value.more.link)">
@@ -114,7 +114,7 @@
<view class="style10-box">
<view class="style10-center">
<view class="left-img">
<image :src="$util.img('public/uniapp/diy/style10-1.png')"></image>
<image :src="$util.img('public/uniapp/diy/style10-1.png')" show-menu-by-longpress></image>
</view>
<text class="style10-title" v-if="$util.img('public/uniapp/diy/style10-3.png')" :style="{
backgroundImage: 'url(' + $util.img('public/uniapp/diy/style10-3.png') + ')',
@@ -128,7 +128,7 @@
{{ value.text }}
</text>
<view class="right-img">
<image :src="$util.img('public/uniapp/diy/style10-2.png')"></image>
<image :src="$util.img('public/uniapp/diy/style10-2.png')" show-menu-by-longpress></image>
</view>
<view class="style10-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
@tap.stop="handlerClick(value.more.link)">
@@ -164,10 +164,10 @@
</view>
<image class="center-img" :src="$util.img('public/uniapp/diy/style11-1.png')"
mode="widthFix"></image>
mode="widthFix" show-menu-by-longpress></image>
<image class="right-img" :src="$util.img('public/uniapp/diy/style11-2.png')"
mode="widthFix"></image>
mode="widthFix" show-menu-by-longpress></image>
</view>
</view>
</view>
@@ -190,7 +190,7 @@
</view>
</view>
<view class="style13" v-else-if="value.style == 'style-13'">
<image class="left-img" :src="$util.img('public/uniapp/diy/style13-1.png')" mode="widthFix"></image>
<image class="left-img" :src="$util.img('public/uniapp/diy/style13-1.png')" mode="widthFix" show-menu-by-longpress></image>
<view class="style13-title" :style="{
fontSize: value.fontSize * 2 + 'rpx',
color: value.textColor,
@@ -198,7 +198,7 @@
}">
{{ value.text }}
</view>
<image class="right-img" :src="$util.img('public/uniapp/diy/style13-1.png')" mode="widthFix"></image>
<image class="right-img" :src="$util.img('public/uniapp/diy/style13-1.png')" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="style14" v-else-if="value.style == 'style-14'">
<view class="title-wrap">

View File

@@ -6,10 +6,10 @@
<view v-for="(item, index) in value.list" :key="index" class="goods-item" @tap="showVideo(item)">
<view class="goods-img-wrap">
<image class="goods-img" style="border-radius:10rpx 10rpx 0 0;" :src="$util.img(item.imageUrl)"
mode="widthFix" @error="imgError(index)"></image>
mode="widthFix" @error="imgError(index)" show-menu-by-longpress ></image>
<view style="position:absolute;top:10rpx;right:10rpx;">
<image style="width:30rpx;" :src="$util.img('addon/personnel/shop/view/enterprise/play.png')"
mode="widthFix"></image>
mode="widthFix" show-menu-by-longpress ></image>
</view>
</view>
<view class="info-wrap">
@@ -34,10 +34,10 @@
'max-height': '200rpx;',
'border-radius': '8rpx;'
}" :src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')" mode="widthFix"
:show-menu-by-longpress="true"></image>
show-menu-by-longpress ></image>
<view style="position:absolute;top:10rpx;right:10rpx;">
<image style="width:30rpx;"
:src="$util.img('addon/personnel/shop/view/enterprise/play.png')" mode="widthFix">
:src="$util.img('addon/personnel/shop/view/enterprise/play.png')" mode="widthFix" show-menu-by-longpress >
</image>
</view>
</view>
@@ -53,9 +53,19 @@
<!-- 视频播放弹窗 -->
<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;">
<video class="adaptive-video" :autoPauseIfNavigate="true" :autoPauseIfOpenNative="true"
:autoplay="false" :enableAutoRotation="true" id="myVideo" :src="video_url" :controls="true"></video>
<view class="video-container" :style="popupVideoStyle">
<video
ref="popupVideoRef"
class="adaptive-video"
:autoPauseIfNavigate="true"
:autoPauseIfOpenNative="true"
:autoplay="false"
:enableAutoRotation="true"
id="myVideo"
:src="video_url"
:controls="true"
@loadedmetadata="onPopupVideoLoaded"
></video>
</view>
</uni-popup>
</view>
@@ -126,12 +136,38 @@ export default {
isIndicatorDots() {
return this.value.carousel.type != 'hide' &&
1 != Math.ceil(this.value.list.length / (this.value.pageCount * this.value.rowCount));
},
// 弹窗视频容器样式
popupVideoStyle() {
let style = 'position:fixed;width:100%;left:0;';
if (this.popupVideoRatio) {
// 根据视频比例设置位置
if (this.popupVideoRatio === 'portrait') {
// 竖屏视频:居中显示,使用9/16比例
style += 'top:50%;transform:translateY(-50%);';
style += 'aspect-ratio: 9 / 16;';
} else {
// 横屏视频:靠近顶部显示,使用16/9比例
style += 'top:20%;';
style += 'aspect-ratio: 16 / 9;';
}
} else {
// 默认:居中显示,使用竖屏比例
style += 'top:50%;transform:translateY(-50%);';
style += 'aspect-ratio: 9 / 16;';
}
return style;
}
},
methods: {
// 显示视频播放弹窗
showVideo(item) {
this.video_url = item.videoUrl;
// 重置视频比例为null,等待新的检测结果
this.popupVideoRatio = null;
this.$refs.videoPopup.open();
},
@@ -139,6 +175,39 @@ export default {
imgError(index) {
// 图片加载失败的处理逻辑
console.log('图片加载失败:', index);
},
// 弹窗视频加载完成事件
onPopupVideoLoaded(event) {
this.$nextTick(() => {
let width = 0;
let height = 0;
// H5 端:获取真实视频宽高
// #ifdef H5
const video = this.$refs.popupVideoRef;
if (video) {
width = video.videoWidth || 0;
height = video.videoHeight || 0;
}
// #endif
// 微信小程序端:通过事件对象获取视频宽高
// #ifdef MP-WEIXIN
if (event && event.detail) {
width = event.detail.width || 0;
height = event.detail.height || 0;
}
// #endif
if (width && height) {
this.popupVideoRatio = width >= height ? 'landscape' : 'portrait';
console.log(`弹窗视频宽高比检测: ${width}x${height}, 比例: ${this.popupVideoRatio}`);
} else {
// 获取失败,默认竖屏
this.popupVideoRatio = 'portrait';
}
});
}
}
}

View File

@@ -1,7 +1,36 @@
<template>
<video data-component-name="diy-video" class="diy-video" :src="$util.img(value.videoUrl)"
:poster="$util.img(value.imageUrl)" :style="videoWarpCss" objectFit="cover"
@tap="handlerClick(value.videoUrl)"></video>
<view class="video-container" :style="containerStyle">
<!-- H5端使用原生video标签 -->
<!-- #ifdef H5 -->
<video
ref="videoRef"
data-component-name="diy-video"
class="diy-video h5-video"
:src="videoUrl"
:poster="posterUrl"
:style="videoWarpCss"
controls
preload="metadata"
@loadedmetadata="onVideoLoaded"
@click="handlerClick(videoUrl)"
></video>
<!-- #endif -->
<!-- 微信小程序端使用uni-app的video组件 -->
<!-- #ifdef MP-WEIXIN -->
<video
ref="videoRef"
data-component-name="diy-video"
class="diy-video mp-video"
:src="videoUrl"
:poster="posterUrl"
:style="videoWarpCss"
controls
@loadedmetadata="onVideoLoaded"
@error="onVideoError"
></video>
<!-- #endif -->
</view>
</template>
<script>
@@ -15,27 +44,160 @@ export default {
}
},
data() {
return {};
return {
videoRatio: null, // 视频比例:'landscape'(横屏)或 'portrait'(竖屏)
videoUrl: '',
posterUrl: ''
};
},
created() {
this.initVideoData();
},
created() { },
mixins: [DiyMinx],
watch: {
// 组件刷新监听
componentRefresh: function (nval) { }
value: {
immediate: true,
handler(newVal) {
if (newVal) {
this.initVideoData();
}
}
}
},
computed: {
videoWarpCss: function () {
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-right-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
obj += 'border-bottom-left-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
obj += 'border-bottom-right-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
}
return obj;
},
// 动态计算容器比例
containerStyle() {
let style = '';
if (this.videoRatio === 'landscape') {
// 横屏视频:16/9比例
style += 'aspect-ratio: 16 / 9;';
} else if (this.videoRatio === 'portrait') {
// 竖屏视频:9/16比例
style += 'aspect-ratio: 9 / 16;';
} else {
// 未加载完成 → 默认使用竖屏比例
style += 'aspect-ratio: 9 / 16;';
}
return style;
}
},
methods: {
// 初始化视频数据
initVideoData() {
this.videoUrl = this.getVideoUrl();
this.posterUrl = this.getPosterUrl();
// H5端:预加载视频获取宽高
// #ifdef H5
this.preloadVideoForRatio();
// #endif
},
// 获取视频URL
getVideoUrl() {
if (!this.value || !this.value.videoUrl) return '';
const videoUrl = this.value.videoUrl;
// 如果是完整的http/https链接,直接使用
if (videoUrl.indexOf('http://') === 0 || videoUrl.indexOf('https://') === 0) {
return videoUrl;
}
// 否则使用$util.img()处理
return this.$util.img(videoUrl);
},
// 获取封面图URL
getPosterUrl() {
if (!this.value || !this.value.imageUrl) return '';
return this.$util.img(this.value.imageUrl);
},
// H5端:预加载视频获取宽高比例
preloadVideoForRatio() {
const videoUrl = this.videoUrl;
if (!videoUrl) return;
console.log('H5端预加载视频获取比例:', videoUrl);
const video = document.createElement('video');
video.preload = 'metadata';
video.src = videoUrl;
video.muted = true;
video.playsInline = true;
video.addEventListener('loadedmetadata', () => {
const width = video.videoWidth;
const height = video.videoHeight;
console.log('H5端预加载获取视频宽高:', width, height);
if (width && height) {
this.videoRatio = width >= height ? 'landscape' : 'portrait';
console.log('H5端视频比例判定:', this.videoRatio, width >= height ? '横屏' : '竖屏');
}
});
video.addEventListener('error', (e) => {
console.error('H5端预加载视频失败:', e);
});
// 触发加载
video.load();
},
// 视频加载完成事件(组件内video标签)
onVideoLoaded(event) {
console.log('视频组件 loadedmetadata 事件触发');
let width = 0;
let height = 0;
// #ifdef H5
if (event && event.target) {
width = event.target.videoWidth || 0;
height = event.target.videoHeight || 0;
console.log('H5端组件视频宽高:', width, height);
}
// #endif
// #ifdef MP-WEIXIN
if (event && event.detail) {
width = event.detail.width || 0;
height = event.detail.height || 0;
console.log('微信小程序端视频宽高:', width, height);
}
// #endif
if (width && height) {
this.videoRatio = width >= height ? 'landscape' : 'portrait';
console.log('视频比例最终判定:', this.videoRatio);
}
},
// 视频播放事件
onVideoPlay() {
console.log('视频开始播放');
},
// 视频暂停事件
onVideoPause() {
console.log('视频暂停');
},
// 视频错误事件
onVideoError(event) {
console.error('视频加载失败:', event);
},
async handlerClick(videoUrl) {
await this.__$emitEvent({
eventName: 'video-tap', data: videoUrl, promiseCallback: (event, handler, awaitedResult) => {
@@ -48,11 +210,32 @@ export default {
</script>
<style scoped>
video {
.video-container {
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;
}
</style>
/* H5端样式 */
/* #ifdef H5 */
.h5-video {
object-fit: cover;
}
/* #endif */
/* 微信小程序端样式 */
/* #ifdef MP-WEIXIN */
.mp-video {
width: 100%;
height: 100%;
}
/* #endif */
</style>

View File

@@ -13,14 +13,14 @@
<view class="message-item " :class="message.isItMe ? 'right' : 'left'">
<block v-if="message.isItMe">
<view class="head_img">
<image class="img" :src="myHeadImg" v-if="myHeadImg" @error="myHeadImgError" mode="aspectFit"/>
<image class="img" :src="defaultHead" mode="aspectFit" v-else/>
<image class="img" :src="myHeadImg" v-if="myHeadImg" @error="myHeadImgError" mode="aspectFit" show-menu-by-longpress/>
<image class="img" :src="defaultHead" mode="aspectFit" v-else show-menu-by-longpress/>
</view>
</block>
<block v-else>
<view class="head_img">
<image class="img" :src="avatar" mode="aspectFit" v-if="avatar"></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else></image>
<image class="img" :src="avatar" mode="aspectFit" v-if="avatar" show-menu-by-longpress></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else show-menu-by-longpress></image>
</view>
</block>
<view class="chat_text">
@@ -34,20 +34,20 @@
<view class="message-item " :class="message.isItMe ? 'right' : 'left'">
<block v-if="message.isItMe">
<view class="head_img">
<image class="img" :src="myHeadImg" v-if="myHeadImg" mode="aspectFit"></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else></image>
<image class="img" :src="myHeadImg" v-if="myHeadImg" mode="aspectFit" show-menu-by-longpress></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else show-menu-by-longpress></image>
</view>
</block>
<block v-else>
<view class="head_img">
<image class="img" :src="avatar" mode="aspectFit" v-if="avatar"></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else></image>
<image class="img" :src="avatar" mode="aspectFit" v-if="avatar" show-menu-by-longpress></image>
<image class="img" :src="defaultHead" mode="aspectFit" v-else show-menu-by-longpress></image>
</view>
</block>
<view class="chat_img">
<text class="iconfont icon-warn margin-right color-base-text" v-if="message.isItMe && !message.sendStatus"></text>
<view class="content_img" @click="previewMedia($util.img(message.image))" :style="{ backgroundImage: 'url(' + $util.img(message.image) + ')' }">
<!-- <image class="img_img" :src="$util.img(message.image)" mode="aspectFit"></image> -->
<!-- <image class="img_img" :src="$util.img(message.image)" mode="aspectFit" show-menu-by-longpress></image> -->
</view>
<!-- <text class="iconfont icon-warn margin-left" v-if="!message.isItMe && !message.sendStatus"></text> -->
</view>
@@ -64,7 +64,7 @@
</view>
<uni-popup ref="imgPopup" type="center">
<view class="imagePop">
<image :src="$util.img(currImg)" mode="aspectFit"></image>
<image :src="$util.img(currImg)" mode="aspectFit" show-menu-by-longpress></image>
</view>
</uni-popup>
</view>
@@ -179,6 +179,7 @@
.chat-message {
width: 100%;
height: 100%;
background: white; /* 白色 */
.message {
padding: 13rpx 20rpx;
@@ -270,7 +271,7 @@
flex-direction: row-reverse;
.content {
background-color: #4cd964;
background-color: #c4e0ff; /* 浅蓝色 */
margin-right: 28rpx;
word-break: break-all;
line-height: 36rpx;

View File

@@ -1,43 +1,79 @@
<template>
<!-- 悬浮按钮 -->
<view v-if="pageCount == 1 || need" class="fixed-box" :style="[customContainerStyle, {
height: fixBtnShow ? '400rpx' : '320rpx',
backgroundImage: bgUrl ? `url(${bgUrl})` : '',
backgroundSize: 'cover'
}]">
<view v-if="pageCount === 1 || need" class="fixed-box"
:style="[customContainerStyle, {
height: containerHeight,
backgroundImage: bgUrl ? `url(${bgUrl})` : '',
backgroundSize: 'cover'
}]">
<!-- 中英文切换按钮 -->
<view v-if="isLanguageSwitchEnabled && fixBtnShow" class="btn-item common-bg" @click="toggleLanguage">
<view
v-if="isLanguageSwitchEnabled && fixBtnShow"
class="btn-item common-bg"
@click="toggleLanguage"
>
<text>{{ currentLangDisplayName }}</text>
</view>
<!-- 微信官方客服按钮-->
<button
v-if="fixBtnShow && hasValidKefuConfig && isWeappOfficialKefu"
class="btn-item common-bg"
open-type="contact"
>
<text class="icox icox-kefu" v-if="!kefuimg"></text>
</button>
<!-- AI 智能助手 -->
<view v-if="fixBtnShow && enableAIChat" class="btn-item common-bg" @click="openAIChat"
:style="{ backgroundImage: aiAgentimg ? `url(${aiAgentimg})` : '', backgroundSize: '100% 100%' }">
<text class="ai-icon" v-if="!aiAgentimg">🤖</text>
<!-- 其他类型客服按钮(智能客服、企业微信等)-->
<view
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && !showWeappSystemKefu"
class="btn-item common-bg"
@click="handleUnifiedKefuClick"
>
<image
v-if="currentKefuImg"
:src="currentKefuImg"
mode="aspectFill"
class="kefu-img"
show-menu-by-longpress></image>
<text class="ai-icon" v-if="isAIKefu && !currentKefuImg">🤖</text>
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
</view>
<!-- 微信小程序客服按钮 -->
<!-- #ifdef MP-WEIXIN -->
<button class="btn-item common-bg" hoverClass="none" openType="contact" sessionFrom="weapp" showMessageCard="true"
:style="[{ backgroundImage: kefuimg ? `url(${kefuimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]">
<text class="icox icox-kefu" v-if="!kefuimg"></text>
<!-- 智能客服按钮(当同时显示小程序系统客服时)-->
<view
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && showWeappSystemKefu"
class="btn-item common-bg"
@click="handleUnifiedKefuClick"
>
<image
v-if="currentKefuImg"
:src="currentKefuImg"
mode="aspectFill"
class="kefu-img"
show-menu-by-longpress></image>
<text class="ai-icon" v-if="isAIKefu && !currentKefuImg">🤖</text>
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
</view>
<!-- 小程序系统客服按钮 -->
<button
v-if="fixBtnShow && hasValidKefuConfig && showWeappSystemKefu"
class="btn-item common-bg"
open-type="contact"
>
<text class="icox icox-kefu" v-if="!kefuimg"></text>
</button>
<!-- #endif -->
<!-- 普通客服(仅当未启用 AI 时显示) -->
<!-- #ifdef H5 -->
<template v-if="fixBtnShow">
<button class="btn-item common-bg" hoverClass="none" @click="openCustomerSelectPopup"
:style="[{ backgroundImage: kefuimg ? `url(${kefuimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]">
<text class="icox icox-kefu" v-if="!kefuimg"></text>
</button>
</template>
<!-- #endif -->
<!-- 电话按钮(始终显示) -->
<view v-if="fixBtnShow" class="btn-item common-bg" @click="call()"
:style="[{ backgroundImage: phoneimg ? `url(${phoneimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]">
<!-- 电话按钮 -->
<view
v-if="fixBtnShow"
class="btn-item common-bg"
@click="call"
:style="[{ backgroundImage: phoneimg ? `url(${phoneimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]"
>
<text class="iconfont icon-dianhua" v-if="!phoneimg"></text>
</view>
@@ -56,16 +92,13 @@ export default {
return {
pageCount: 0,
fixBtnShow: true,
shopInfo: null,
currentLangIndex: 0,
langIndexMap: {},
customerService: null,
customerService: null,
};
},
computed: {
// 安全读取 shopInfo 中的字段,避免 undefined 报错
bgUrl() {
return this.shopInfo?.bgUrl || '';
},
@@ -84,9 +117,6 @@ export default {
isLanguageSwitchEnabled() {
return !!this.shopInfo?.ischina;
},
enableAIChat() {
return !!this.shopInfo?.enableAIChat;
},
currentLangDisplayName() {
const lang = this.langIndexMap[this.currentLangIndex];
return lang === 'zh-cn' ? 'EN' : 'CN';
@@ -96,23 +126,77 @@ export default {
},
customButtonStyle() {
return this.shopInfo?.floatingButton?.button || {};
},
unreadCount() {
const customerService = createCustomerService(this);
return customerService.getUnreadCount();
},
currentKefuImg() {
const customerService = createCustomerService(this);
return customerService.getKefuButtonIcon();
},
isWeappOfficialKefu() {
const customerService = createCustomerService(this);
return customerService.isWeappOfficialKefu();
},
isAIKefu() {
const customerService = createCustomerService(this);
return customerService.isAIKefu();
},
isWxworkKefu() {
const customerService = createCustomerService(this);
return customerService.isWxworkKefu();
},
isThirdKefu() {
const customerService = createCustomerService(this);
return customerService.isThirdKefu();
},
showWeappSystemKefu() {
const customerService = createCustomerService(this);
return customerService.shouldShowExtraWeappSystemKefu();
},
hasValidKefuConfig() {
const customerService = createCustomerService(this);
return customerService.hasValidKefuConfig();
},
//根据显示的按钮数量动态计算容器高度
containerHeight() {
if (!this.fixBtnShow) return '320rpx';
let buttonCount = 1;
if (this.isLanguageSwitchEnabled) buttonCount++;
if (this.showWeappSystemKefu) buttonCount++;
buttonCount++;
const totalRpx = 94 * buttonCount - 14;
const pxValue = Math.round(totalRpx * 0.5);
return ` $ {pxValue}px`;
}
},
watch: {
shopInfo: {
handler(newVal) {
},
immediate: true
}
},
created() {
this.customerService = createCustomerService(this);
this.initLanguage();
this.pageCount = getCurrentPages().length;
uni.getStorage({
key: 'shopInfo',
success: (e) => {
console.log('【调试】当前 shopInfo:', e.data);
this.shopInfo = e.data;
},
fail: () => {
console.warn('未获取到 shopInfo,使用默认设置');
}
});
// 初始化 customerService 实例
this.customerService = createCustomerService(this);
},
methods: {
/**
* 初始化多语言配置
*/
initLanguage() {
this.langList = this.$langConfig.list();
this.langIndexMap = {};
@@ -132,36 +216,30 @@ export default {
}
},
/**
* 电话联系客服
*/
call() {
this.customerService.makePhoneCall(this.tel);
this.customerService.makePhoneCallByNumber(this.tel);
},
/**
* 切换中英文语言,并刷新当前页面(保留所有参数)
*/
toggleLanguage() {
this.currentLangIndex = this.currentLangIndex === 0 ? 1 : 0;
const targetLang = this.langIndexMap[this.currentLangIndex];
// 调用语言切换逻辑(设置 storage + 清空缓存)
this.$langConfig.change(targetLang);
uni.setStorageSync("lang", targetLang);
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const currentRoute = currentPage.route;
uni.reLaunch({
url: '/' + currentRoute
});
},
/**
* 打开 AI 智能助手
*/
openAIChat() {
this.$util.redirectTo(this.$util.AI_CHAT_PAGE_URL);
},
/**
* 打开客服选择对话框
*/
openCustomerSelectPopup() {
this.customerService.openCustomerSelectPopupDialog();
handleUnifiedKefuClick() {
this.customerService.handleUnifiedKefuClick({
sendMessageTitle: '来自悬浮按钮的咨询',
sendMessagePath: '/pages/index/index'
});
}
}
}
@@ -196,17 +274,15 @@ export default {
margin: 14rpx 0;
transition: 0.1s;
color: var(--hover-nav-text-color);
border-radius: 40rpx;
border-radius: 50%;
width: 80rpx;
height: 80rpx;
padding: 0;
overflow: hidden;
}
/* 定义共同的背景颜色 */
.common-bg {
background-color: var(--hover-nav-bg-color);
/* 使用变量以保持一致性 */
background-color: var(--hover-nav-bg-color);
}
.btn-item text {
@@ -236,7 +312,12 @@ export default {
font-weight: bold;
}
.kefu-img {
width: 100%;
height: 100%;
display: block;
border-radius: 50%;
}
.ai-icon {
font-size: 40rpx;

View File

@@ -8,7 +8,7 @@
:src="mOption.src"
mode="widthFix"
@click="toTopClick"
/>
show-menu-by-longpress/>
</template>
<script>

View File

@@ -3,12 +3,12 @@
<swiper :indicator-dots="advList.length > 1" indicator-active-color="#ffffff" :autoplay="true" :interval="3000" :duration="1000" v-if="advList.length > 1" @change="changeSwiper" :current="currentIndex" :style="{ height: swiperHeight + 'px' }" class="item-wrap">
<swiper-item v-for="(item, index) in advList" :key="index" @click="jumppage(item.adv_url)">
<view class="image-box">
<image :src="$util.img(item.adv_image)" mode="widthFix" :id="'content-wrap' + index"/>
<image :src="$util.img(item.adv_image)" mode="widthFix" :id="'content-wrap' + index" show-menu-by-longpress/>
</view>
</swiper-item>
</swiper>
<view v-else class="container-box item-wrap">
<image :src="$util.img(advList[0]['adv_image'])" mode="widthFix" lazy-load="true" @load="imageLoad" @click="jumppage(advList[0].adv_url)"/>
<image :src="$util.img(advList[0]['adv_image'])" mode="widthFix" lazy-load="true" @load="imageLoad" @click="jumppage(advList[0].adv_url)" show-menu-by-longpress/>
</view>
</view>
</template>

View File

@@ -1,7 +1,7 @@
<template>
<view class="message">
<view class="goods-item" v-if="goodsInfo.goods_name || goodsInfo.en_goods_name">
<image :src="$util.img(goodsInfo.sku_image)" mode="aspectFill"></image>
<image :src="$util.img(goodsInfo.sku_image)" mode="aspectFill" show-menu-by-longpress></image>
<view class="goods-info">
<view class="goods-name">{{ goodsInfo.sku_name ? goodsInfo.sku_name : (isEnEnv ? goodsInfo.en_goods_name : goodsInfo.goods_name) }}</view>
<view class="goods-bottom">
@@ -14,7 +14,7 @@
</view>
</view>
<view class="goods-item" v-else-if="goodsDetail">
<image :src="$util.img(goodsDetail.sku_image)" mode="aspectFill"></image>
<image :src="$util.img(goodsDetail.sku_image)" mode="aspectFill" show-menu-by-longpress></image>
<view class="goods-info">
<view class="goods-name">{{ goodsDetail.sku_name ? goodsDetail.sku_name : (isEnEnv ? goodsDetail.en_goods_name : goodsDetail.goods_name) }}</view>
<view class="goods-bottom">

View File

@@ -1,7 +1,7 @@
<template>
<view class="message">
<view class="goods-item" v-if="orderdetails">
<image :src="$util.img(orderdetails.order_goods ? orderdetails.order_goods[0].sku_image : '')" mode="aspectFill"></image>
<image :src="$util.img(orderdetails.order_goods ? orderdetails.order_goods[0].sku_image : '')" mode="aspectFill" show-menu-by-longpress></image>
<view class="goods-info">
<view class="goods-name">{{ orderdetails.order_goods ? orderdetails.order_goods[0].sku_name : '' }}</view>
<view class="font-size-goods-tag">订单状态:{{ orderdetails.order_status_name }}</view>
@@ -16,7 +16,7 @@
</view>
</view>
<view class="goods-item" v-else-if="orderInfo">
<image :src="$util.img(orderInfo.order_goods ? orderInfo.order_goods[0].sku_image : '')" mode="aspectFill"></image>
<image :src="$util.img(orderInfo.order_goods ? orderInfo.order_goods[0].sku_image : '')" mode="aspectFill" show-menu-by-longpress></image>
<view class="goods-info">
<view class="goods-name">{{ orderInfo.order_goods ? orderInfo.order_goods[0].sku_name : '' }}</view>
<view class="font-size-goods-tag">订单状态:{{ orderInfo.order_status_name }}</view>

View File

@@ -1,7 +1,7 @@
<template>
<view class="goods">
<view class="goods-msg">
<image :src="$util.img(goodsInfo.sku_image)" mode="aspectFill"></image>
<image :src="$util.img(goodsInfo.sku_image)" mode="aspectFill" show-menu-by-longpress></image>
<view class="goods-item">
<view class="title">{{ isEnEnv ? goodsInfo.en_goods_name : goodsInfo.goods_name }}</view>
<view class="goods-sku">

View File

@@ -10,7 +10,7 @@
</view>
<view v-if="copyright && copyright.gov_record" class="gov-wrap" @click="toHref(copyright.gov_url)" target="_blank">
<image :src="$util.img('public/uniapp/common/gov_record.png')" alt="公安备案" />
<image :src="$util.img('public/uniapp/common/gov_record.png')" alt="公安备案" show-menu-by-longpress/>
<text>{{ copyright.gov_record }}</text>
</view>
</view> -->

View File

@@ -1,6 +1,6 @@
<template>
<view class="empty" :class="{ fixed: fixed }">
<view class="empty_img"><image :src="$util.img('public/uniapp/common/common-empty.png')" mode="aspectFit"></image></view>
<view class="empty_img"><image :src="$util.img('public/uniapp/common/common-empty.png')" mode="aspectFit" show-menu-by-longpress></image></view>
<view class="color-tip margin-top title" :style="{ color: textColor + ' !important' }">{{ text }}</view>
<view class="color-tip margin-bottom font-size-sub">{{ subText }}</view>
<button type="primary" size="mini" class="button mini" @click="goIndex()" v-if="isIndex">{{ emptyBtn.text }}</button>

View File

@@ -89,7 +89,7 @@
</view>
<view class="box img-boxs">
<view class="img-box" v-for="(v, k) in item.img_lists" :key="k" @click="uploadImg(index)">
<image :src="$util.img(v)" mode="aspectFill"></image>
<image :src="$util.img(v)" mode="aspectFill" show-menu-by-longpress></image>
<text class="iconfont icon-guanbi" @click.stop="delImg(k, index)"></text>
</view>
<view class="img-box" @click="addImg(index)"><text class="iconfont icon-add1"></text></view>

View File

@@ -6,7 +6,7 @@
<view class="sku-info" :style="{ height: systemInfo.windowHeight * 2 + 'rpx' }">
<view class="header">
<view class="img-wrap" @click="previewMedia()">
<image :src="$util.img(goodsDetail.sku_image, { size: 'mid' })" @error="imageError()" />
<image :src="$util.img(goodsDetail.sku_image, { size: 'mid' })" @error="imageError()" show-menu-by-longpress/>
</view>
<view class="main">
<view class="goodname">{{ goodsDetail.sku_name ? goodsDetail.sku_name : (isEnEnv ?
@@ -33,7 +33,7 @@
@click="change(item_value.sku_id, item_value.spec_id)">
<image v-if="item_value.image"
:src="$util.img(item_value.image, { size: 'small' })"
@error="valueImageError(index, index_value)" />
@error="valueImageError(index, index_value)" show-menu-by-longpress/>
<text>{{ item_value.spec_value_name }}</text>
</view>
</view>

View File

@@ -5,14 +5,14 @@
<view class="header">
<block v-if="type == 'point' && goodsDetail.type && goodsDetail.type != 1">
<view class="img-wrap" v-if="goodsDetail.type == 2">
<image :src="goodsDetail.image ? $util.img(goodsDetail.image) : $util.img('public/uniapp/point/coupon.png')" mode="aspectFit" />
<image :src="goodsDetail.image ? $util.img(goodsDetail.image) : $util.img('public/uniapp/point/coupon.png')" mode="aspectFit" show-menu-by-longpress/>
</view>
<view class="img-wrap" v-if="goodsDetail.type == 3">
<image :src="goodsDetail.image ? $util.img(goodsDetail.image) : $util.img('public/uniapp/point/hongbao.png')" mode="aspectFit" />
<image :src="goodsDetail.image ? $util.img(goodsDetail.image) : $util.img('public/uniapp/point/hongbao.png')" mode="aspectFit" show-menu-by-longpress/>
</view>
</block>
<view v-else class="img-wrap" @click="previewMedia()">
<image :src="$util.img(goodsDetail.sku_image, { size: 'mid' })" @error="imageError()" mode="aspectFit" />
<image :src="$util.img(goodsDetail.sku_image, { size: 'mid' })" @error="imageError()" mode="aspectFit" show-menu-by-longpress/>
</view>
<view class="main">
@@ -151,7 +151,7 @@
selected: item_value['selected'] || skuId == item_value.sku_id,
disabled: item_value['disabled'] || (!item_value['selected'] && disabled)
}" class="items" @click="change(item_value.sku_id, item_value.spec_id)">
<image v-if="item_value.image" :src="$util.img(item_value.image, { size: 'small' })" @error="valueImageError(index, index_value)" />
<image v-if="item_value.image" :src="$util.img(item_value.image, { size: 'small' })" @error="valueImageError(index, index_value)" show-menu-by-longpress/>
<text>{{ item_value.spec_value_name }}</text>
</view>
</view>

View File

@@ -0,0 +1,80 @@
<template>
<image
:src="src"
:mode="mode"
:lazy-load="lazyLoad"
:fade-show="fadeShow"
:webp="webp"
:show-menu-by-longpress="enableLongpress"
:class="imageClass"
:style="imageStyle"
@load="onLoad"
@error="onError"
@tap="onClick"
>
<slot></slot>
</image>
</template>
<script>
export default {
name: 'NsImage',
props: {
// 图片路径
src: {
type: String,
required: true
},
// 图片裁剪模式
mode: {
type: String,
default: 'aspectFill'
},
// 是否开启长按识别二维码(默认开启)
enableLongpress: {
type: Boolean,
default: true
},
// 是否懒加载
lazyLoad: {
type: Boolean,
default: false
},
// 图片显示动画
fadeShow: {
type: Boolean,
default: true
},
// 支持webp
webp: {
type: Boolean,
default: false
},
// 自定义类名
imageClass: {
type: [String, Array, Object],
default: ''
},
// 自定义样式
imageStyle: {
type: [String, Object],
default: ''
}
},
methods: {
onLoad(e) {
this.$emit('load', e)
},
onError(e) {
this.$emit('error', e)
},
onClick(e) {
this.$emit('tap', e)
}
}
}
</script>
<style lang="scss" scoped>
/* 可添加全局图片样式 */
</style>

View File

@@ -18,7 +18,7 @@
<view class="item-wrap" v-if="isOpenCaptcha">
<text class="label">验证码</text>
<input type="number" placeholder="请输入验证码" v-model="formData.vercode" maxlength="4" />
<image :src="captcha.img" class="captcha" @click="getCaptcha"></image>
<image :src="captcha.img" class="captcha" @click="getCaptcha" show-menu-by-longpress></image>
</view>
<view class="item-wrap">
<text class="label">动态码</text>
@@ -79,7 +79,7 @@
<text class="label">头像</text>
<button open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image :src="avatarUrl ? avatarUrl : $util.getDefaultImage().head"
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" />
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
<text class="iconfont icon-right color-tip"></text>
</button>
</view>
@@ -96,7 +96,7 @@
<button open-type="getAuthorize" scope="userInfo" @getAuthorize="aliappGetUserinfo"
:plain="true" class="border-0">
<image :src="avatarUrl ? avatarUrl : $util.getDefaultImage().head"
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" />
@error="avatarUrl = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
<text class="iconfont icon-right color-tip"></text>
</button>
</view>

View File

@@ -16,7 +16,7 @@
<view v-if="data.navStyle == 2" class="content-wrap" @click="toLink(data.moreLink.wap_url)">
<view class="title-wrap" :style="{ color: data.textNavColor }">
<view>
<image :src="$util.img(data.topNavImg)" mode="heightFix"></image>
<image :src="$util.img(data.topNavImg)" mode="heightFix" show-menu-by-longpress></image>
</view>
<view :style="{ color: data.textNavColor }">{{ data.title }}</view>
</view>
@@ -24,7 +24,7 @@
<view v-if="data.navStyle == 3" class="content-wrap">
<view class="title-wrap" @click="toLink(data.moreLink.wap_url)">
<image :src="$util.img(data.topNavImg)" mode="aspectFit"></image>
<image :src="$util.img(data.topNavImg)" mode="aspectFit" show-menu-by-longpress></image>
</view>
<view class="search" @click="$util.redirectTo('/pages_tool/goods/search')" :style="{ height: menuButtonInfo.height - 2 + 'px', lineHeight: menuButtonInfo.height - 2 + 'px' }">
<text class="iconfont icon-sousuo3"></text>

View File

@@ -53,8 +53,15 @@ export default {
}
},
mounted() {
this.listenPrivacySettingChange()
this.curPageShow()
// 检查配置,决定是否显示隐私协议
const shopInfo = uni.getStorageSync('shopInfo');
if (shopInfo?.ischina == 1 && shopInfo?.index_login_auth == 1) {
this.listenPrivacySettingChange()
this.curPageShow()
} else {
// 如果不需要显示隐私协议,直接触发同意事件
this.$emit('agree');
}
},
created() {
try {
@@ -152,28 +159,62 @@ export default {
},
// 主动查询用户隐私政策授权状态
proactive() {
// 检查配置,决定是否显示隐私协议
const shopInfo = uni.getStorageSync('shopInfo');
if (!(shopInfo?.ischina == 1 && shopInfo?.index_login_auth == 1)) {
// 如果不需要显示隐私协议,直接触发同意事件
this.$emit('agree');
return;
}
// #ifdef MP-WEIXIN
if (wx.getPrivacySetting) {
wx.getPrivacySetting({
success: (res) => {
// console.log(res)
if (res.needAuthorization) {
this.popUp()
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
this.closeOtherPagePopUp(this.disPopUp)
// 检查本地存储中是否已经同意过隐私政策
const privacyAgreed = uni.getStorageSync('privacyAgreed');
if (!privacyAgreed) {
this.popUp()
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
this.closeOtherPagePopUp(this.disPopUp)
} else {
this.$emit('agree')
}
} else {
this.$emit('agree')
}
},
});
} else {
this.$emit('agree')
// 检查本地存储中是否已经同意过隐私政策
const privacyAgreed = uni.getStorageSync('privacyAgreed');
if (privacyAgreed) {
this.$emit('agree');
} else {
this.popUp()
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
this.closeOtherPagePopUp(this.disPopUp)
}
}
// #endif
// #ifdef QUICKAPP-WEBVIEW
this.getPrivacySettingByQuickApp();
// #endif
// #ifdef H5 || WEB
// 检查本地存储中是否已经同意过隐私政策
const privacyAgreed = uni.getStorageSync('privacyAgreed');
if (privacyAgreed) {
this.$emit('agree');
} else {
this.popUp()
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
this.closeOtherPagePopUp(this.disPopUp)
}
// #endif
},
//初始化监听程序
curPageShow() {
@@ -261,6 +302,8 @@ export default {
this.privacyResolves.clear()
//关闭弹窗
this.disPopUp()
// 保存用户授权状态到本地存储,无论同意还是不同意,都不再重复弹出
uni.setStorageSync('privacyAgreed', true);
//退出小程序
uni.showModal({
content: '未同意隐私协议,无法使用相关功能',
@@ -281,10 +324,8 @@ export default {
//关闭弹窗
this.disPopUp()
// #ifdef QUICKAPP-WEBVIEW || H5 || WEB
// 保存用户授权状态到本地存储,用于快应用Webview组件中的H5方式处理
// 保存用户授权状态到本地存储,用于所有环境
uni.setStorageSync('privacyAgreed', true);
// #endif
this.$emit('agree')
},
//打开弹窗

View File

@@ -6,7 +6,7 @@
:src="$util.img('public/uniapp/common/mescroll-totop.png')"
mode="widthFix"
@click="toTopClick"
/>
show-menu-by-longpress/>
</template>
<script>

View File

@@ -0,0 +1,158 @@
# 华为快应用 HwService JS SDK 集成指南
## 概述
本文档介绍如何将该 uni-app 项目以 H5 壳模式部署到华为快应用平台,并集成华为 HwService JS SDK(帐号服务 + 分析服务)。
## 项目架构
```
H5 壳模式快应用架构:
┌─────────────────────────────────────┐
│ 华为快应用引擎 (QuickApp Runtime) │
│ ┌─────────────────────────────────┐ │
│ │ WebView (H5 页面) │ │
│ │ ┌───────────────────────────┐ │ │
│ │ │ uni-app 应用 │ │ │
│ │ │ ├─ common/js/hw-jssdk.js │ │ │
│ │ │ ├─ common/js/auth.js │ │ │
│ │ │ └─ store/index.js │ │ │
│ │ └───────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
│ │ HwService Bridge │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ HMS Core (华为移动服务) │ │
│ │ ├─ Account Kit (帐号服务) │ │
│ │ ├─ Analytics Kit (分析服务) │ │
│ │ └─ Push Kit (推送服务,可选) │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────┘
```
## 部署前准备
### 1. 华为 AppGallery Connect 配置
1. 登录 [AppGallery Connect](https://developer.huawei.com/consumer/cn/service/josp/agc/index.html)
2. 创建应用(选择「快应用」类型)
3. 开启以下服务:
- **华为帐号服务 (Account Kit)**
- **华为分析服务 (Analytics Kit)**(可选)
- **华为推送服务 (Push Kit)**(可选)
4. 下载 `agconnect-services.json` 配置文件
5. 将配置文件放入项目根目录
### 2. 安装 npm 依赖
```bash
# 基础桥接库(必装,提供 hmsjsb.invoke 桥接能力)
npm install --save @hw-hmscore/hms-js-base
# 华为分析服务封装包(可选,提供更完善的 API 封装)
npm install --save @hw-hmscore/analytics-sdk-quickapp
```
**注意:**
- **Account Kit(帐号服务)** 和 **Push Kit(推送服务)** 没有独立的 quickapp npm 包,统一通过 `@hw-hmscore/hms-js-base` 的 `hmsjsb.invoke()` 桥接调用
- `@hw-hmscore/analytics-sdk-quickapp` 版本 `6.10.0-301`(ISC 许可证),**可选安装**——不装则走 invoke 桥接
- 相关 npm 包由华为官方发布(`hmscore@huawei.com`)
### 3. 配置 HUAWEI 条件编译
本项目的 `package.json` 已为各快应用域名定义自定义发行脚本:
| 脚本名 | 条件编译标记 | 用途 |
|--------|------------|------|
| `h5-xcx20.5g-quickapp.com` | `H5_XCX_5G_QUICKAPP_COM` | 5G 快应用 |
| `h5-xcx.aigc-quickapp.com` | `H5_XCX_AIGC_QUICKAPP_COM` | AIGC 快应用 |
| `h5-5g.aigc-quickapp.com` | `H5_5G_AIGC_QUICKAPP_COM` | 5G AIGC 快应用 |
| `h5-test.aigc-quickapp.com` | `H5_TEST_AIGC_QUICKAPP_COM` | 测试快应用 |
当使用 HBuilderX → 发行 → 自定义发行 时,对应的 `#ifdef` 条件编译块会被激活。
### 4. 配置华为参数
编辑 `common/js/config.js` 中的 `huawei` 配置项:
```javascript
huawei: {
appId: '你的AGC应用ID',
clientId: '你的客户端ID',
clientSecret: '你的客户端密钥(建议由后端下发)',
redirectUri: window.location.origin + '/hwappx/common/',
}
```
**注意**:`clientSecret` 敏感信息建议由后端接口动态下发,避免在前端硬编码。
## 后端配合
### 新增接口
华为帐号登录需要后端增加以下接口:
| 接口路径 | 方法 | 说明 |
|---------|------|------|
| `/wechat/api/quickapp/huawei_auth` | POST | 华为 openId 换 token |
**请求参数**:
| 字段 | 类型 | 说明 |
|------|------|------|
| `openid` | string | 华为帐号 openId |
| `unionid` | string | 华为帐号 unionId(可选) |
| `nickname` | string | 用户昵称 |
| `avatar` | string | 头像 URL |
| `gender` | int | 性别(0:未知 1:男 2:女) |
**返回格式**(需与现有登录接口保持一致):
```json
{
"code": 0,
"data": {
"openid": "华为openId",
"unionid": "华为unionId"
}
}
```
## 已修改的文件清单
| 文件 | 变更类型 | 说明 |
|------|---------|------|
| `common/js/hw-jssdk.js` | **新增** | HwService 统一封装(Account/Analytics/Push) |
| `common/js/auth.js` | 修改 | 新增 `_huaweiSignIn` 方法,快应用环境下使用华为帐号登录 |
| `common/js/http.js` | 修改 | 新增 `H5_XCX_*_QUICKAPP_COM` 条件编译,请求类型为 `quickapp` |
| `common/js/event-bus.js` | 修改 | 新增 `quickapp` 平台检测分支 |
| `common/js/config.js` | 修改 | 新增 `huawei` 配置项 |
| `store/index.js` | 修改 | 新增 `huaweiAccountInfo` 状态/`initHwService` action |
## 发布流程
1. 使用 HBuilderX 打开项目
2. 修改 `manifest.json` → Web 配置 → 基础路径(`/hwappx/common/`)
3. 选择「发行」→「自定义发行」→ 选择对应的快应用脚本
4. 构建完成后,将 `unpackage/dist/build/web/` 目录打包为 zip
5. 使用华为快应用 IDE 导入并发布
## 验证方式
### 检查 HwService 是否可用
```javascript
const HwService = require('@/common/js/hw-jssdk.js').default;
console.log('是否快应用环境:', HwService.isQuickAppEnv());
```
### 验证华为帐号登录
在华为手机上打开快应用,点击登录,应弹出华为帐号授权窗口。
### 验证分析服务
集成后可在 AppGallery Connect 控制台查看埋点数据。
## 注意事项
1. **H5 壳模式限制**:HwService 依赖华为快应用引擎注入的 Bridge,在普通浏览器中不可用
2. **真机测试**:必须在华为手机上测试,模拟器可能无法完整模拟 Bridge
3. **条件编译生效**:必须通过 HBuilderX 的自定义发行脚本构建,`#ifdef` 标记才会生效
4. **clientSecret 安全**:建议由后端接口动态下发,不要前端硬编码
5. **登录降级**:如果华为帐号服务不可用,建议提供短信验证码或其他登录方式作为降级方案

View File

@@ -3,5 +3,5 @@ export const lang = {
title: '商品分类',
search:'商品搜索',
seeMore : '查看更多',
Make: "询底价"
Make: "立即咨询"
}

View File

@@ -2,7 +2,7 @@ export const lang = {
//title为每个页面的标题
title: '',
select: '选择',
details:'资源详情',
details:'商品详情',
params: '参数',
service: '商品服务',
allGoods: '全部商品',
@@ -11,7 +11,7 @@ export const lang = {
home:'首页',
cart:'购物车',
leave:'立即留言',
make:'联系我们',
make:'立即咨询',
send:'配送',
express:'快递发货',
@@ -22,7 +22,7 @@ export const lang = {
status:'该商品已下架',
sellout:'库存不足',
max:'已达最大限购数量',
makebtn:'价格电议',
makebtn:'立即咨询',
sales:'销量',
stock:'库存',
kefu:'客服',

View File

@@ -6,6 +6,6 @@ export const lang = {
Price:'价格',
Filter:'筛选',
Search:'请输入您要搜索的商品',
Make:'立即询底价',
Make:'立即咨询',
}

View File

@@ -71,11 +71,14 @@
"scope.userLocation" : {
"desc" : "为了更好地为您提供服务"
},
"scope.writePhotosAlbum" : {
"desc" : "为了更好地为您提供服务"
},
"scope.userClipboard" : {
"desc" : "为了方便您复制信息"
},
"scope.camera" : {
"desc" : "用于拍照上传头像"
},
"scope.writePhotosAlbum" : {
"desc" : "用于从相册选择和保存头像"
}
},
"requiredPrivateInfos" : [ "chooseLocation", "getLocation", "chooseAddress" ],
@@ -104,7 +107,7 @@
},
"router" : {
"mode" : "history",
"base" : "/hwappx/2811/"
"base" : "/hwappx/test/"
},
"title" : "",
"devServer" : {

View File

@@ -43,7 +43,7 @@
{
"path": "detail",
"style": {
"navigationBarTitleText": "资源详情"
"navigationBarTitleText": "商品详情"
}
},
{

View File

@@ -54,8 +54,8 @@
indicator-color="rgba(255,255,255,0.6)" :indicator-dots="true" interval="3000">
<swiper-item v-for="(item, index) in adv.list" :key="index">
<image class="slide-image" @click="$util.diyRedirectTo(item.link)" height="100%"
:src="$util.img(item.imageUrl)" width="100%"></image>
<!-- <image bindtap="adverclose" class="slide-image" height="100%" src="{{item.imgurl}}" width="100%" wx:if="{{item.click==1}}"></image> -->
:src="$util.img(item.imageUrl)" width="100%" show-menu-by-longpress></image>
<!-- <image bindtap="adverclose" class="slide-image" height="100%" src="{{item.imgurl}}" width="100%" wx:if="{{item.click==1}}" show-menu-by-longpress></image> -->
</swiper-item>
</swiper>
<view bindtap="adverclose" class="small-bot-close" @click="closePopupWindow">
@@ -66,7 +66,7 @@
<swiper class="swiper" style="width:100%;height: 1200rpx;" :autoplay="true" interval="3000" circular="true" :indicator-dots="true" indicator-color="#000" indicator-active-color="red">
<swiper-item class="swiper-item" v-for="(item, index) in adv.list" :key="index" v-if="item.imageUrl" @click="$util.diyRedirectTo(item.link)">
<view class="item">
<image :src="$util.img(item.imageUrl)" mode="aspectFit" :show-menu-by-longpress="true"/>
<image :src="$util.img(item.imageUrl)" mode="aspectFit" show-menu-by-longpress></image>
</view>
</swiper-item>
</swiper>
@@ -85,7 +85,7 @@
<view @touchmove.prevent class="official-accounts-inner" v-if="wechatQrcode">
<uni-popup ref="officialAccountsPopup" type="center">
<view class="official-accounts-wrap">
<image class="content" :src="$util.img(wechatQrcode)" mode="aspectFit"></image>
<image class="content" :src="$util.img(wechatQrcode)" mode="aspectFit" show-menu-by-longpress></image>
<text class="desc">关注了解更多</text>
<text class="close iconfont icon-round-close" @click="officialAccountsClose"></text>
</view>
@@ -96,7 +96,7 @@
<uni-popup ref="collectPopupWindow" type="top" class="wap-floating wap-floating-collect">
<view v-if="showTip" class="collectPopupWindow"
:style="{ marginTop: (collectTop + statusBarHeight) * 2 + 'rpx' }">
<image :src="$util.img('public/uniapp/index/collect2.png')" mode="aspectFit" />
<image :src="$util.img('public/uniapp/index/collect2.png')" mode="aspectFit" show-menu-by-longpress/>
<text @click="closeCollectPopupWindow">我知道了</text>
</view>
</uni-popup>

View File

@@ -3,7 +3,8 @@ export default {
return {
minScrollTop: 100, // 设置回到顶端按钮显示要求,最小页面滚动距离
wechatQrcode: '', // 公众号二维码
diyRoute: '/pages/index/index'
diyRoute: '/pages/index/index',
shopInfo: null // 店铺信息
};
},
onLoad(option) {
@@ -15,10 +16,15 @@ export default {
// #endif
this.name = option.name || 'DIY_VIEW_INDEX'; // 根据店铺运营模式,打开平台(shop)或者连锁门店(stroe)页面,接口会自行判断
// 获取店铺信息
this.shopInfo = uni.getStorageSync('shopInfo');
},
onShow() {
// 获取关注公众号二维码
this.getFollowQrcode();
// 重新获取店铺信息
this.shopInfo = uni.getStorageSync('shopInfo');
},
mounted() {
// 监听图片、文字、商品列表等组件的点击事件
@@ -59,6 +65,11 @@ export default {
officialAccountsClose() {
this.$refs.officialAccountsPopup.close();
},
// 判断是否需要登录授权
_needLoginAuth() {
// 检查 index_login_auth 都开启时才需要登录授权
return this.shopInfo?.index_login_auth == 1;
},
// 统一处理点击及触摸事件
_handleDiyGroupInteractionEvent(payload) {
// #ifdef H5
@@ -66,6 +77,11 @@ export default {
return true;
// #endif
// 如果不需要登录授权,直接返回 true 继续传播
if (!this._needLoginAuth()) {
return true;
}
// 如果已登录,直接返回 true 继续传播
if (this.storeToken) {
return true;

View File

@@ -2,6 +2,11 @@
<view>
<view scroll-y="true" class="goods-detail" :class="isIphoneX ? 'active' : ''">
<view class="goods-container">
<!-- AI客服悬浮按钮 -->
<view class="ai-chat-float-btn" @tap.stop="$emit('aiChatClick')" v-if="showAIChatBtn">
<image :src="aiAvatarUrl" mode="aspectFill" class="ai-avatar-icon" />
</view>
<!-- 弹幕 -->
<pengpai-fadein-out v-if="goodsSkuDetail.barrage_show && goodsSkuDetail.barrageData" ref="pengpai"
:duration="1600" :wait="1900" :top="200" :left="0" :radius="60" :loop="true"
@@ -17,7 +22,7 @@
:item-id="'goods_id_' + index">
<view class="item" @click="previewMedia(index)">
<image :src="$util.img(item, { size: 'big' })" @error="swiperImageError(index)"
mode="aspectFit" />
mode="aspectFit" show-menu-by-longpress />
</view>
</swiper-item>
</swiper>
@@ -130,7 +135,7 @@
<view class="list-wrap" style="display: flex;">
<view class="name-wrap">
<image :src="$util.img(goodsSkuDetail.merchinfo.merch_image)" mode="widthFix"
style="width: 100rpx;height: 100rpx;border-radius: 50rpx;"></image>
style="width: 100rpx;height: 100rpx;border-radius: 50rpx;" show-menu-by-longpress ></image>
</view>
<view class="other-wrap">
<view class="address" style="margin-left: 30rpx;">
@@ -214,8 +219,8 @@
@click="selectStore(item)">
<view class="item-box">
<view class="item-image">
<image :src="$util.img(item.store_image)" v-if="item.store_image" />
<image :src="$util.getDefaultImage().store" v-else />
<image :src="$util.img(item.store_image)" v-if="item.store_image" show-menu-by-longpress />
<image :src="$util.getDefaultImage().store" v-else show-menu-by-longpress />
</view>
<view class="item-info">
<view class="item-title">
@@ -243,7 +248,7 @@
<view class="detail-community" v-if="goodsSkuDetail.qr_data && goodsSkuDetail.qr_data.qr_state == 1">
<view class="community-box">
<image :src="$util.img('public/uniapp/goods/detail_erweiImage.png')" mode="aspectFill"></image>
<image :src="$util.img('public/uniapp/goods/detail_erweiImage.png')" mode="aspectFill" show-menu-by-longpress ></image>
<view class="community-content">
<view class="community-title">{{ goodsSkuDetail.qr_data.qr_name }}</view>
<view class="community-txt">{{ goodsSkuDetail.qr_data.community_describe }}</view>
@@ -259,7 +264,7 @@
<view>添加社群</view>
</view>
<view class="community-model-content-draw" v-if="goodsSkuDetail.qr_data && goodsSkuDetail.qr_data.qr_img">
<image :src="goodsSkuDetail.qr_data.qr_img != '' && goodsSkuDetail.qr_data.qr_state == 1 ? $util.img(goodsSkuDetail.qr_data.qr_img) : $util.img('public/uniapp/goods/detail_erweiImage.png') " mode="aspectFill" show-menu-by-longpress="true"/>
<image :src="goodsSkuDetail.qr_data.qr_img != '' && goodsSkuDetail.qr_data.qr_state == 1 ? $util.img(goodsSkuDetail.qr_data.qr_img) : $util.img('public/uniapp/goods/detail_erweiImage.png') " mode="aspectFill" show-menu-by-longpress/>
</view>
<view class="community-model-content-text">长按识别二维码,添加社群</view>
</view>
@@ -296,10 +301,10 @@
<view class="evaluator-face">
<image v-if="item.member_headimg" :src="$util.img(item.member_headimg)"
@error="item.member_headimg = $util.getDefaultImage().head"
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress />
<image v-else :src="$util.getDefaultImage().head"
@error="item.member_headimg = $util.getDefaultImage().head"
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress />
</view>
<view class="evaluator-name-wrap">
<text class="evaluator-name using-hidden"
@@ -320,7 +325,7 @@
<view class="evaluate-img" v-if="item.images">
<view class="img-box" v-for="(img, img_index) in item.images" :key="img_index"
@click="previewEvaluate(index, img_index, 'images')">
<image :src="$util.img(img)" mode="aspectFill" />
<image :src="$util.img(img)" mode="aspectFill" show-menu-by-longpress />
</view>
</view>
</scroll-view>
@@ -385,7 +390,7 @@
<template v-if="poster != '-1'">
<view class="poster-wrap">
<view class="image-wrap">
<image :src="$util.img(poster)" :show-menu-by-longpress="true" mode="widthFix" />
<image :src="$util.img(poster)" show-menu-by-longpress mode="widthFix" />
<view class="close iconfont icon-close" @click="closePosterPopup()"></view>
</view>
<!-- #ifdef MP || APP-PLUS -->
@@ -458,6 +463,8 @@
// 商品详情视图
import scroll from '@/common/js/scroll-view.js';
import detail from './detail.js';
import IMAGE_CONFIG from '@/common/js/image-config.js';
import { createCustomerService } from '@/common/js/customer-service.js';
import pengpaiFadeinOut from '../pengpai-fadein-out/pengpai-fadein-out.vue';
@@ -474,6 +481,15 @@ export default {
components: {
pengpaiFadeinOut
},
computed: {
aiAvatarUrl() {
return this.$util.img(IMAGE_CONFIG.aiAvatar);
},
showAIChatBtn() {
const customerService = createCustomerService(this);
return customerService.hasValidKefuConfig() && customerService.isAIKefu();
}
},
mixins: [scroll, detail]
};
</script>
@@ -481,4 +497,36 @@ export default {
<style lang="scss">
@import '@/common/css/goods_detail.scss';
</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>

View File

@@ -6,38 +6,38 @@
<text class="promotion-mark ">限时折扣</text>
<text class="title">当前商品正在参加{{ item.promotion_name }}</text>
<text class="iconfont icon-right"></text>
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" /></view> -->
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" show-menu-by-longpress/></view> -->
</view>
<view v-else-if="item.promotion_type == 'groupbuy'" class="item" @click="redirectTo('/pages_promotion/groupbuy/detail', { groupbuy_id: item.groupbuy_id })">
<!-- <view v-else-if="item.promotion_type == 'groupbuy'" class="item" @click="redirectTo('/pages_promotion/groupbuy/detail', { id: item.groupbuy_id })"> -->
<text class="promotion-mark ">团购</text>
<text class="title">当前商品正在参加{{ item.promotion_name }}</text>
<text class="iconfont icon-right"></text>
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" /></view> -->
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" show-menu-by-longpress/></view> -->
</view>
<view v-else-if="item.promotion_type == 'pintuan'" class="item" @click="redirectTo('/pages_promotion/pintuan/detail', { pintuan_id: item.pintuan_id })">
<text class="promotion-mark ">拼团</text>
<text class="title">当前商品正在参加{{ item.promotion_name }}</text>
<text class="iconfont icon-right"></text>
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" /></view> -->
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" show-menu-by-longpress/></view> -->
</view>
<view v-else-if="item.promotion_type == 'seckill'" class="item" @click="redirectTo('/pages_promotion/seckill/detail', { seckill_id: item.id })">
<text class="promotion-mark ">秒杀</text>
<text class="title">当前商品正在参加{{ item.promotion_name }}</text>
<text class="iconfont icon-right"></text>
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" /></view> -->
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" show-menu-by-longpress/></view> -->
</view>
<view v-else-if="item.promotion_type == 'topic'" class="item" @click="redirectTo('/pages_promotion/topics/goods_detail', { id: item.id })">
<text class="promotion-mark ">专题活动</text>
<text class="title">当前商品正在参加{{ item.promotion_name }}</text>
<text class="iconfont icon-right"></text>
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" /></view> -->
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" show-menu-by-longpress/></view> -->
</view>
<view v-else-if="item.promotion_type == 'bargain'" class="item" @click="redirectTo('/pages_promotion/bargain/detail', { b_id: item.bargain_id })">
<text class="promotion-mark ">砍价</text>
<text class="title">当前商品正在参加{{ item.promotion_name }}</text>
<text class="iconfont icon-right"></text>
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" /></view> -->
<!-- <view class="img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" show-menu-by-longpress/></view> -->
</view>
<view v-else-if="item.promotion_type == 'pinfan'" class="item" @click="redirectTo('/pages_promotion/pinfan/detail', { pinfan_id: item.pintuan_id })">
<text class="promotion-mark ">拼团返利</text>

View File

@@ -4,7 +4,7 @@
<view class="round bg-gradual-orange flex justify-start shadow" style="padding: 3px;">
<view class="cu-avatar cu-a-sm round" :style="{ backgroundImage: 'url(' + $util.img(penpaiData.img) + ')' }">
<!-- #ifdef APP-NVUE -->
<!-- <image :src="penpaiData.img" class="avatarimg"></image> -->
<!-- <image :src="penpaiData.img" class="avatarimg" show-menu-by-longpress></image> -->
<!-- #endif -->
</view>
<view class="padding-lr-sm flex align-center">

View File

@@ -34,7 +34,7 @@
@click="singleElection(siteIndex, cartIndex)"></view>
<view @click="toGoodsDetail(item)" class="goods-img">
<image :src="$util.img(item.sku_image, { size: 'mid' })"
@error="imageError(siteIndex, cartIndex)" mode="aspectFill" />
@error="imageError(siteIndex, cartIndex)" mode="aspectFill" show-menu-by-longpress/>
</view>
<view class="goods-info">
<view>
@@ -66,7 +66,7 @@
<text class="unit price-style small">.{{
parseFloat(item.member_price).toFixed(2).split('.')[1]
}}</text>
<image :src="$util.img('public/uniapp/index/VIP.png')" />
<image :src="$util.img('public/uniapp/index/VIP.png')" show-menu-by-longpress/>
</view>
</view>
</block>
@@ -81,7 +81,7 @@
parseFloat(item.discount_price).toFixed(2).split('.')[1]
}}</text>
<image
:src="$util.img('public/uniapp/index/discount.png')" />
:src="$util.img('public/uniapp/index/discount.png')" show-menu-by-longpress/>
</view>
</view>
</block>
@@ -96,7 +96,7 @@
<text class="unit price-style small">.{{
parseFloat(item.member_price).toFixed(2).split('.')[1]
}}</text>
<image :src="$util.img('public/uniapp/index/VIP.png')" />
<image :src="$util.img('public/uniapp/index/VIP.png')" show-menu-by-longpress/>
</view>
</view>
</block>
@@ -150,7 +150,7 @@
<view class="iconfont icon-yuan_checked color-tip"></view>
<view class="goods-img">
<image :src="$util.img(goodsItem.sku_image, { size: 'mid' })"
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress/>
</view>
<view class="goods-info">
<view class="goods-name">{{ goodsItem.sku_name }}</view>
@@ -176,7 +176,7 @@
<text class="unit price-style small">.{{
parseFloat(goodsItem.member_price).toFixed(2).split('.')[1]
}}</text>
<image :src="$util.img('public/uniapp/index/VIP.png')" />
<image :src="$util.img('public/uniapp/index/VIP.png')" show-menu-by-longpress/>
</template>
<template v-else>
<text class="unit price-style small">{{
@@ -256,7 +256,7 @@
:style="{ backgroundColor: discount.coupon_info.receive_type != 'wait' ? '#F2F2F2' : 'var(--main-color-shallow)' }">
<view class="info-wrap">
<image class="coupon-line" mode="heightFix"
:src="$util.img('public/uniapp/coupon/coupon_line.png')" />
:src="$util.img('public/uniapp/coupon/coupon_line.png')" show-menu-by-longpress/>
<view class="coupon-money">
<template v-if="discount.coupon_info.type == 'reward'">
<text class="unit">{{ $lang('common.currencySymbol') }}</text>

View File

@@ -26,7 +26,8 @@
export default {
data() {
return {
diyData: null
diyData: null,
mpShareData: null //小程序分享数据
};
},
onLoad() {
@@ -38,6 +39,25 @@ export default {
onShow() {
this.$util.hideHomeButton();
if (this.$refs.category) this.$refs.category[0].pageShow();
//小程序分享
// #ifdef MP-WEIXIN
this.$util.getMpShare('/pages_goods/category').then(res => {
this.mpShareData = res;
}).catch(err => {
// 如果后台没配置,用默认值
this.mpShareData = {
appMessage: {
title: '发现超多好物,快来看看',
path: '/pages_goods/category'
},
timeLine: {
title: '发现超多好物,快来看看',
query: '/pages_goods/category'
}
};
});
// #endif
},
onUnload() {
if (!this.storeToken && this.$refs.login) this.$refs.login.cancelCompleteInfo();
@@ -69,6 +89,24 @@ export default {
onPullDownRefresh() {
uni.hideTabBar();
this.getDiyInfo();
},
onShareAppMessage() {
return this.mpShareData?.appMessage || {
title: '发现超多好物,快来看看',
path: '/pages_goods/category'
};
},
onShareTimeline() {
return this.mpShareData?.timeLine || {
title: '发现超多好物,快来看看',
query: '/pages_goods/category'
};
},
onAddToFavorites() {
return {
title: '发现超多好物,快来看看',
path: '/pages_goods/category'
};
}
};
</script>

View File

@@ -6,7 +6,7 @@
</view> -->
<!-- #endif -->
<goods-detail-view :goodsSkuDetail="goodsSkuDetail" ref="goodsDetailView">
<goods-detail-view :goodsSkuDetail="goodsSkuDetail" ref="goodsDetailView" @aiChatClick="sendToAIChat(goodsSkuDetail)">
<!-- 价格区域 -->
<template v-slot:price>
<!-- 限时折扣 -->
@@ -49,7 +49,7 @@
<text class="price price-font">{{ parseFloat(goodsSkuDetail.member_price).toFixed(2).split('.')[0] }}</text>
<text class="price-symbol price-font">.{{ parseFloat(goodsSkuDetail.member_price).toFixed(2).split('.')[1] }}</text>
<view class="member-vip-wrap">
<image :src="$util.img('public/uniapp/goods/member_vip.png')" mode="aspectFit"/>
<image :src="$util.img('public/uniapp/goods/member_vip.png')" mode="aspectFit" show-menu-by-longpress/>
</view>
<view class="member-price-wrap" v-if="!goodsSkuDetail.market_price_show">
<text class="unit price-font">原价</text>
@@ -77,7 +77,6 @@
</block>
<block v-else>
<template>
<text class="price price-font" style="font-weight: 600;">{{$lang('makebtn')}}</text>
</template>
</block>
</view>
@@ -141,7 +140,7 @@
v-for="(item, index) in goodsSkuDetail.card_info.relation_goods"
@click="$util.redirectTo('/pages_goods/detail', { goods_id: item.goods_id })"
>
<image :src="$util.img(item.sku_image)" mode="aspectFill"/>
<image :src="$util.img(item.sku_image)" mode="aspectFill" show-menu-by-longpress/>
<view class="content">
<view class="name multi-hidden">{{ item.sku_name }}</view>
<view class="price">¥{{ item.price }}</view>
@@ -222,13 +221,13 @@
<text>{{ bundling[0].bl_name }}</text>
</view>
<text class="iconfont icon-right"></text>
<!-- <view class="more-img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" /></view> -->
<!-- <view class="more-img-wrap"><image :src="$util.img('public/uniapp/goods/detail_more.png')" mode="aspectFit" show-menu-by-longpress/></view> -->
</view>
<scroll-view class="combo-goods-wrap color-tip" scroll-x="true">
<view class="goods-wrap">
<view class="goods-item" @click="toGoodsDetail(skuId)" v-if="bundlingType == true">
<view class="combo-img">
<image :src="$util.img(goodsSkuDetail.sku_image, { size: 'mid' })" @error="imageError()" />
<image :src="$util.img(goodsSkuDetail.sku_image, { size: 'mid' })" @error="imageError()" show-menu-by-longpress/>
<view class="price-wrap">
<text class="unit price-font">{{ $lang('common.currencySymbol') }}</text>
<text class="price price-font">{{ goodsSkuDetail.price }}</text>
@@ -240,7 +239,7 @@
<template v-if="index < 3">
<view class="goods-item" @click="toGoodsDetail(item.sku_id)">
<view class="combo-img">
<image :src="$util.img(item.sku_image, { size: 'mid' })" @error="bundlingImageError(0, index)" />
<image :src="$util.img(item.sku_image, { size: 'mid' })" @error="bundlingImageError(0, index)" show-menu-by-longpress/>
<view class="price-wrap">
<text class="unit price-font">{{ $lang('common.currencySymbol') }}</text>
<text class="price price-font">{{ item.price }}</text>
@@ -273,7 +272,7 @@
<view class="goods-wrap">
<view class="goods-item" @click="toGoodsDetail(skuId)">
<view class="combo-img">
<image :src="$util.img(goodsSkuDetail.sku_image, { size: 'mid' })" @error="imageError()" />
<image :src="$util.img(goodsSkuDetail.sku_image, { size: 'mid' })" @error="imageError()" show-menu-by-longpress/>
<view class="price-wrap">
<text class="unit price-font">{{ $lang('common.currencySymbol') }}</text>
<text class="price price-font">{{ goodsSkuDetail.price }}</text>
@@ -285,7 +284,7 @@
<template v-if="goods_index < 3">
<view class="goods-item" @click="toGoodsDetail(goods.sku_id)">
<view class="combo-img">
<image :src="$util.img(goods.sku_image, { size: 'mid' })" @error="bundlingImageError(index, goods_index)" />
<image :src="$util.img(goods.sku_image, { size: 'mid' })" @error="bundlingImageError(index, goods_index)" show-menu-by-longpress/>
<view class="price-wrap">
<text class="unit price-font">{{ $lang('common.currencySymbol') }}</text>
<text class="price price-font">{{ goods.price }}</text>
@@ -324,7 +323,7 @@
<view class="coupon-item ns-gradient-diy-goods-list" v-for="(item, index) in couponList" :key="index" v-if="hackReset">
<view class="coupon-info" :style="{ backgroundColor: item.useState == 2 ? '#F2F2F2' : 'var(--main-color-shallow)' }">
<view class="info-wrap" :class="{ disabled: item.useState == 2 }">
<image class="coupon-line" mode="heightFix" :src="$util.img('public/uniapp/coupon/coupon_line.png')"></image>
<image class="coupon-line" mode="heightFix" :src="$util.img('public/uniapp/coupon/coupon_line.png')" show-menu-by-longpress></image>
<view class="coupon-money">
<template v-if="item.type == 'reward'">
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
@@ -461,11 +460,12 @@
<ns-goods-action-icon :text="$lang('follow')" :icon="whetherCollection == 1 ? 'icon-likefill color-base-text' : 'icon-guanzhu'" @click="editCollection" />
<!-- #endif -->
<block v-if="goodsSkuDetail.isinformation >= 1">
<ns-goods-action-button v-if="goodsSkuDetail.isinformation == 1" class="goods-action-button active3" disabled-text="" backgroundColor="#54c952" @click="phoneClick(goodsSkuDetail.goods_mobile)" :text="$lang('make')" :disabled="false" />
<ns-goods-action-button v-if="goodsSkuDetail.isinformation == 2" class="goods-action-button active3" disabled-text="" backgroundColor="#54c952" @click="showinformation" :text="$lang('leave')" :disabled="false" />
<!-- <ns-goods-action-button v-if="goodsSkuDetail.isinformation == 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="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" /> -->
</block>
<block v-else>
<block v-if="goodsSkuDetail.stock == 0 && !goodsSkuDetail.sku_spec_format">
@@ -515,6 +515,19 @@
<!-- 隐私弹窗 -->
<privacy-popup ref="privacyPopup"></privacy-popup>
<!-- 联系电话弹窗 -->
<uni-popup ref="contactPopup" type="center">
<view class="contact-popup">
<view class="contact-content">
<text class="contact-text">可在手机端拨打 021-51815181</text>
</view>
<view class="contact-buttons">
<button class="contact-btn cancel-btn" @click="closeContactPopup">取消</button>
<button class="contact-btn copy-btn" @click="copyPhoneNumber">复制到剪切板</button>
</view>
</view>
</uni-popup>
</view>
</template>
@@ -531,9 +544,61 @@ export default {
goodsDetailView,
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>
<style lang="scss">
@import '@/common/css/goods_detail.scss';
@import './public/css/detail.scss';
@@ -545,6 +610,57 @@ export default {
z-index: 999;
}
.contact-popup {
width: 600rpx;
background: #ffffff;
border-radius: 16rpx;
overflow: hidden;
}
.contact-content {
padding: 60rpx 40rpx;
text-align: center;
}
.contact-text {
font-size: 32rpx;
color: #333333;
line-height: 1.5;
}
.contact-buttons {
display: flex;
border-top: 1px solid #e5e5e5;
}
.contact-btn {
flex: 1;
height: 90rpx;
line-height: 90rpx;
text-align: center;
font-size: 32rpx;
border: none;
background: transparent;
margin: 0;
padding: 0;
border-radius: 0;
}
.contact-btn::after {
border: none;
}
.cancel-btn {
color: #333333;
background: #ffffff;
border-right: 1px solid #e5e5e5;
}
.copy-btn {
color: #ffffff;
background: #54c952;
}
</style>
<style scoped>
::v-deep .action-icon-wrap .iconfont.icon-shouye1 {
@@ -640,4 +756,5 @@ export default {
height: 72rpx;
line-height: 72rpx;
}
</style>

View File

@@ -51,7 +51,7 @@
<view class="goods-item margin-bottom" v-for="(item, index) in goodsList" :key="index"
@click="toDetail(item)">
<view class="goods-img">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"></image>
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" show-menu-by-longpress ></image>
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}
</view>
<view class="sell-out" v-if="item.goods_stock <= 0">
@@ -75,68 +75,23 @@
<text class="unit price-style small">.{{
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
</view>
<view class="discount-price" v-else>
<text class="price price-style large">{{ langstatus ? $lang('Make') : '询底价' }}</text>
</view>
<view class="member-price-tag"
v-if="item.member_price && item.member_price == showPrice(item)">
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix"></image>
</view>
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
<image :src="$util.img('public/uniapp/index/discount.png')" mode="widthFix"></image>
</view>
</view>
<view class="pro-info" v-if="item.isinformation == 0">
<view class="delete-price color-tip price-font" v-if="showMarketPrice(item)">
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
<text>{{ showMarketPrice(item) }}</text>
</view>
<view class="block-wrap">
<view class="sale 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>
</view>
<view class="cart-action-wrap" v-if="config.control && item.is_virtual == 0">
<!-- 购物车图标 -->
<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"
<view class="cart buy-btn click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
购买
<view class="click-event"></view>
</view>
<!--加号图标 -->
<view v-else-if="config.style == 'icon-add'" :style="{
color: config.theme == 'diy' ? config.textColor : '',
borderColor: config.theme == 'diy' ? config.textColor : ''
}" class="cart plus-sign-btn iconfont icon-add1 click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
<view class="click-event"></view>
</view>
<!-- 按钮 -->
<view v-else-if="config.style == 'button'" :style="{
backgroundColor: config.theme == 'diy' ? config.bgColor : '',
color: config.theme == 'diy' ? config.textColor : '',
fontWeight: config.theme == 'diy' ? (config.fontWeight ? 'bold' : 'normal') : '',
padding: config.theme == 'diy' ? '12rpx ' + config.padding * 2 + 'rpx' : ''
}" class="cart buy-btn click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
{{ config.text }}
<view class="click-event"></view>
</view>
<!--自定义图标 -->
<view v-else-if="config.style == 'icon-diy'" :style="{
color: config.theme == 'diy' ? config.textColor : ''
}" class="icon-diy click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
<view class="click-event"></view>
<diy-icon :icon="config.iconDiy.icon"
:value="config.iconDiy.style ? config.iconDiy.style : null"></diy-icon>
</view>
</view>
</view>
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
<view class="ai-chat-btn" @click.stop="sendToAIChat(item)">
立即咨询
</view>
</view>
</view>
@@ -147,7 +102,7 @@
@click="toDetail(item)"
:style="{ left: listPosition[index] ? listPosition[index].left : '', top: listPosition[index] ? listPosition[index].top : '' }">
<view class="goods-img">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"></image>
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" show-menu-by-longpress ></image>
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}
</view>
<view class="sell-out" v-if="item.stock <= 0">
@@ -168,66 +123,23 @@
<text class="unit price-style small">.{{
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
</view>
<view class="discount-price" v-else>
<text class="price price-style large">{{ langstatus ? $lang('Make') : '询底价' }}</text>
</view>
<view class="member-price-tag"
v-if="item.member_price && item.member_price == showPrice(item)">
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix"></image>
</view>
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
<image :src="$util.img('public/uniapp/index/discount.png')" mode="widthFix"></image>
</view>
<view class="delete-price color-tip price-font" v-if="showMarketPrice(item)">
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
<text>{{ showMarketPrice(item) }}</text>
</view>
</view>
<view class="pro-info" v-if="item.isinformation == 0">
<view class="block-wrap">
<view class="sale 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>
</view>
<view class="cart-action-wrap" v-if="config.control && item.is_virtual == 0">
<!-- 购物车图标 -->
<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"
<view class="cart buy-btn click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
购买
<view class="click-event"></view>
</view>
<!--加号图标 -->
<view v-else-if="config.style == 'icon-add'" :style="{
color: config.theme == 'diy' ? config.textColor : '',
borderColor: config.theme == 'diy' ? config.textColor : ''
}" class="cart plus-sign-btn iconfont icon-add1 click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
<view class="click-event"></view>
</view>
<!-- 按钮 -->
<view v-else-if="config.style == 'button'" :style="{
backgroundColor: config.theme == 'diy' ? config.bgColor : '',
color: config.theme == 'diy' ? config.textColor : '',
fontWeight: config.theme == 'diy' ? (config.fontWeight ? 'bold' : 'normal') : '',
padding: config.theme == 'diy' ? '12rpx ' + config.padding * 2 + 'rpx' : ''
}" class="cart buy-btn click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
{{ config.text }}
<view class="click-event"></view>
</view>
<!--自定义图标 -->
<view v-else-if="config.style == 'icon-diy'" :style="{
color: config.theme == 'diy' ? config.textColor : ''
}" class="icon-diy click-wrap" :id="'goods-' + item.id"
@click.stop="$refs.goodsSkuIndex.addCart(config.cartEvent, item, $event)">
<view class="click-event"></view>
<diy-icon :icon="config.iconDiy.icon"
:value="config.iconDiy.style ? config.iconDiy.style : null"></diy-icon>
</view>
</view>
</view>
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
<view class="ai-chat-btn" @click.stop="sendToAIChat(item)">
立即咨询
</view>
</view>
</view>
@@ -311,7 +223,14 @@ export default {
data() {
return {};
},
mixins: [list]
mixins: [list],
methods: {
sendToAIChat(item) {
this.$util.redirectTo('/pages_goods/detail', {
goods_id: item.goods_id
});
}
}
};
</script>
@@ -327,4 +246,24 @@ export default {
::v-deep .sku-layer .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
max-height: unset !important;
}
.ai-chat-btn-wrap {
display: flex;
justify-content: flex-end;
margin-top: auto;
padding-top: 10rpx;
}
.ai-chat-btn {
background-color: #ff0000;
color: #fff;
padding: 12rpx 30rpx;
border-radius: 40rpx;
font-size: 26rpx;
font-weight: 500;
line-height: 1.2;
text-align: center;
white-space: nowrap;
display: inline-block;
}
</style>

View File

@@ -295,7 +295,8 @@
.lineheight-clear {
display: flex;
justify-content: flex-end;
justify-content: space-between;
align-items: center;
}
// 商品列表单列样式
@@ -386,46 +387,30 @@
display: inline-block;
font-weight: bold;
line-height: 1;
margin-top: 16rpx;
background-color: var(--main-color);
border-radius: 35rpx;
padding: 12rpx 24rpx;
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
margin-top: 0;
.unit {
margin-right: 6rpx;
color: var(--price-color);
color: #ff4444;
}
.price {
color: var(--price-color);
color: #fff !important;
font-size: 27rpx !important;
color: #ff4444;
font-size: 32rpx !important;
font-weight: bold !important;
}
}
.pro-info {
margin-top: 10rpx;
display: flex;
margin-top: auto;
justify-content: space-between;
align-items: center;
position: relative;
.delete-price {
text-decoration: line-through;
font-size: $font-size-tag !important;
flex: 1;
.unit {
margin-right: 0;
}
}
.block-wrap {
flex: 1;
line-height: 1;
margin-right: 20rpx;
.sale {
font-size: $font-size-tag !important;
color: #999;
}
}
}
@@ -543,6 +528,9 @@
.lineheight-clear {
margin-top: 16rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.discount-price {
@@ -552,24 +540,26 @@
.unit {
margin-right: 6rpx;
color: var(--price-color);
color: #ff4444;
}
.price {
color: var(--price-color);
color: #ff4444;
font-size: 32rpx !important;
font-weight: bold !important;
}
}
.pro-info {
margin-top: 10rpx;
display: flex;
margin-top: auto;
justify-content: space-between;
align-items: center;
.block-wrap {
flex: 1;
line-height: 1;
margin-right: 20rpx;
.sale {
font-size: $font-size-tag !important;
color: #999;
}
}
}
@@ -644,9 +634,9 @@
}
.buy-btn {
background-color: $base-color;
color: var(--btn-text-color);
border-radius: 50rpx;
background-color: #ff4444;
color: #fff;
border-radius: 30rpx;
font-size: $font-size-tag;
padding: 12rpx 30rpx;
line-height: 1;

View File

@@ -59,7 +59,7 @@
<view class="iconfont icon-right"></view>
</view>
</view>
<image class="address-line" :src="$util.img('public/uniapp/order/address-line.png')"></image>
<image class="address-line" :src="$util.img('public/uniapp/order/address-line.png')" show-menu-by-longpress></image>
</view>
<view class="address-box" :class="{ 'not-delivery-type': goodsData.delivery.express_type.length <= 1 }" v-if="orderCreateData.delivery.delivery_type == 'local'">
@@ -112,7 +112,7 @@
</view>
</view>
<image class="address-line" :src="$util.img('public/uniapp/order/address-line.png')"></image>
<image class="address-line" :src="$util.img('public/uniapp/order/address-line.png')" show-menu-by-longpress></image>
</view>
<!-- 门店信息 -->
@@ -154,7 +154,7 @@
</view>
</block>
<view v-else class="empty">当前无自提门店,请选择其它配送方式</view>
<image class="address-line" :src="$util.img('public/uniapp/order/address-line.png')"></image>
<image class="address-line" :src="$util.img('public/uniapp/order/address-line.png')" show-menu-by-longpress></image>
</view>
</template>
@@ -170,7 +170,7 @@
<view class="goods-item" v-for="(goodsItem, goodsIndex) in goodsSpecFormat(calculateGoodsData.goods_list)" :key="goodsIndex">
<view class="goods-wrap">
<view class="goods-img" @click="$util.redirectTo('/pages_goods/detail', { goods_id: goodsItem.goods_id })">
<image :src="$util.img(goodsItem.sku_image, { size: 'mid' })" @error="imageError(goodsIndex)" mode="aspectFill"/>
<image :src="$util.img(goodsItem.sku_image, { size: 'mid' })" @error="imageError(goodsIndex)" mode="aspectFill" show-menu-by-longpress/>
</view>
<view class="goods-info">
<view class="top-wrap">
@@ -521,7 +521,7 @@
<view class="coupon-item" v-for="(couponItem, couponIndex) in merchCoupon.data" :key="couponIndex" @click="selectCoupon(couponItem,merchCoupon.merch_id)">
<view class="coupon-info" :style="{ backgroundColor: 'var(--main-color-shallow)' }">
<view class="info-wrap">
<image class="coupon-line" mode="heightFix" :src="$util.img('public/uniapp/coupon/coupon_line.png')"/>
<image class="coupon-line" mode="heightFix" :src="$util.img('public/uniapp/coupon/coupon_line.png')" show-menu-by-longpress/>
<view class="coupon-money">
<template v-if="couponItem.type == 'divideticket'">
<text class="unit">{{ $lang('common.currencySymbol') }}</text>

View File

@@ -111,9 +111,9 @@
<view class="code">
<image :src="$util.img(orderData.pickup_barcode)"
@click="previewMedia($util.img(orderData.pickup_barcode))" class="barcode"
mode="widthFix" />
mode="widthFix" show-menu-by-longpress/>
<image :src="$util.img(orderData.pickup)" @click="previewMedia($util.img(orderData.pickup))"
class="qrcode" mode="widthFix" />
class="qrcode" mode="widthFix" show-menu-by-longpress/>
</view>
</view>
</view>
@@ -126,7 +126,7 @@
<view class="goods-wrap">
<view class="goods-img" @click="goDetail(goodsItem)">
<image :src="$util.img(goodsItem.sku_image, { size: 'mid' })"
@error="imageError(goodsIndex)" mode="aspectFill" />
@error="imageError(goodsIndex)" mode="aspectFill" show-menu-by-longpress/>
</view>
<view class="goods-info" @click="goDetail(goodsItem)">
<view class="goods-name">{{ goodsItem.sku_name }}</view>
@@ -174,7 +174,7 @@
<view class="box img-box" v-if="item.controller == 'Img'">
<view class="img" v-for="(img, imgIndex) in item.img_lists" :key="imgIndex">
<image :src="$util.img(img)" mode="widthFix"
@click="previewMedia($util.img(img))" />
@click="previewMedia($util.img(img))" show-menu-by-longpress/>
</view>
</view>
<view class="box" v-else>
@@ -314,7 +314,7 @@
<text class="tit">{{ item.value.title }}:</text>
<view class="box img-box" v-if="item.controller == 'Img'">
<view class="img" v-for="(img, imgIndex) in item.img_lists" :key="imgIndex">
<image :src="$util.img(img)" mode="widthFix" @click="previewMedia($util.img(img))"></image>
<image :src="$util.img(img)" mode="widthFix" @click="previewMedia($util.img(img))" show-menu-by-longpress></image>
</view>
</view>
<view class="box" v-else>
@@ -345,8 +345,8 @@
<block v-if="orderData.virtual_goods && orderData.goods_class == 2 && orderData.virtual_goods.is_veirfy == 0">
<view class="verify-code-wrap">
<view class="code">
<image :src="$util.img(orderData.virtualgoods_barcode)" class="barcode" mode="widthFix"></image>
<image :src="$util.img(orderData.virtualgoods)" class="qrcode" mode="widthFix"></image>
<image :src="$util.img(orderData.virtualgoods_barcode)" class="barcode" mode="widthFix" show-menu-by-longpress></image>
<image :src="$util.img(orderData.virtualgoods)" class="qrcode" mode="widthFix" show-menu-by-longpress></image>
<text>请将条形码或二维码出示给核销员</text>
</view>
<view class="hr"></view>
@@ -426,8 +426,8 @@
<block v-if="orderData.virtual_goods && orderData.goods_class == 4">
<view class="verify-code-wrap" v-if="orderData.virtual_goods.is_veirfy == 0">
<view class="code">
<image :src="$util.img(orderData.virtualgoods_barcode)" class="barcode" mode="widthFix"></image>
<image :src="$util.img(orderData.virtualgoods)" class="qrcode" mode="widthFix"></image>
<image :src="$util.img(orderData.virtualgoods_barcode)" class="barcode" mode="widthFix" show-menu-by-longpress></image>
<image :src="$util.img(orderData.virtualgoods)" class="qrcode" mode="widthFix" show-menu-by-longpress></image>
<text>请将条形码或二维码出示给核销员</text>
</view>
<view class="hr"></view>

View File

@@ -24,7 +24,7 @@
<view class="goods-item">
<view class="goods-wrap">
<view @click="goDetail(orderData.exchange_goods_id)" class="goods-img">
<image :src="exchangeImage(orderData)" @error="imageError()" mode="aspectFill"></image>
<image :src="exchangeImage(orderData)" @error="imageError()" mode="aspectFill" show-menu-by-longpress></image>
</view>
<view class="goods-info" @click="goDetail(orderData.exchange_goods_id)">
<view class="goods-name">{{ orderData.exchange_name }}</view>

View File

@@ -43,7 +43,7 @@
<view class="goods-img">
<image :src="$util.img(goodsItem.sku_image, { size: 'mid' })"
@error="imageError(orderIndex, goodsIndex)" mode="aspectFill"
:lazy-load="true" />
:lazy-load="true" show-menu-by-longpress/>
</view>
<view class="goods-info">
<view class="pro-info">
@@ -85,12 +85,12 @@
:key="goodsIndex">
<image :src="$util.img(goodsItem.sku_image, { size: 'mid' })"
@error="imageError(orderIndex, goodsIndex)" mode="aspectFill"
:lazy-load="true" />
:lazy-load="true" show-menu-by-longpress/>
</view>
</view>
</scroll-view>
<view class="shade">
<image :src="$util.img('public/uniapp/order/order-shade.png')"></image>
<image :src="$util.img('public/uniapp/order/order-shade.png')" show-menu-by-longpress></image>
</view>
</view>
</block>
@@ -108,7 +108,7 @@
</view>
<view class="order-action" v-if="orderItem.action.length > 0">
<view class="order-time" v-if="orderItem.order_status == 0" id="action-date">
<image :src="$util.img('public/uniapp/order/time.png')"></image>
<image :src="$util.img('public/uniapp/order/time.png')" show-menu-by-longpress></image>
剩余时间:
<uni-count-down :day="orderItem.discountTimeMachine.d"
:hour="orderItem.discountTimeMachine.h"

View File

@@ -11,11 +11,11 @@
<view v-if="status === ''">
<block v-if="basicsConfig.is_apply == 1">
<view class="image" :class="fenXiaoAgreement.agreement.img ? '' : 'color-base-bg'">
<image v-if="fenXiaoAgreement.agreement.img" class="apply-adv" :src="$util.img(fenXiaoAgreement.agreement.img)" mode="widthFix" style="width: 100%;"/>
<image v-if="fenXiaoAgreement.agreement.img" class="apply-adv" :src="$util.img(fenXiaoAgreement.agreement.img)" mode="widthFix" style="width: 100%;" show-menu-by-longpress/>
<view class="bg ns-gradient-otherpages-fenxiao-apply-apply-bg" v-else>
<view class="bg-title"><text>请填写申请信息</text></view>
<view class="bg-img">
<image :src="$util.img('public/uniapp/fenxiao/apply/edit.png')" mode="scaleToFill"></image>
<image :src="$util.img('public/uniapp/fenxiao/apply/edit.png')" mode="scaleToFill" show-menu-by-longpress></image>
</view>
</view>
</view>
@@ -65,7 +65,7 @@
</view>
<view class="goods-list" v-for="(item, index) in goodsList" :key="index" @click="toDetail(item)">
<view class="goods-img">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"/>
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" show-menu-by-longpress/>
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}</view>
</view>
<view class="goods-content">
@@ -88,7 +88,7 @@
</block>
<view class="empty" v-else-if="basicsConfig.is_apply == 0" style="margin-top:40rpx;">
<block v-if="fenxiaoConfig.fenxiao_condition == 2 || fenxiaoConfig.fenxiao_condition == 3">
<image :src="$util.img('public/uniapp/fenxiao/index/no-fenxiao.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/no-fenxiao.png')" mode="widthFix" show-menu-by-longpress></image>
<text v-if="fenxiaoConfig.fenxiao_condition == 2">申请成为{{ fenxiaoWords.fenxiao_name }},需要您的消费次数需要达到{{ fenxiaoConfig.consume_count }}次</text>
<text v-if="fenxiaoConfig.fenxiao_condition == 3">
申请成为{{ fenxiaoWords.fenxiao_name }},需要您的消费金额需要达到{{ fenxiaoConfig.consume_money | moneyFormat }}元
@@ -102,7 +102,7 @@
</view>
<view class="goods-list" v-for="(item, index) in goodsList" :key="index" @click="toDetail(item)">
<view class="goods-img">
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"/>
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" show-menu-by-longpress/>
<view class="color-base-bg goods-tag" v-if="goodsTag(item) != ''">{{ goodsTag(item) }}</view>
</view>
<view class="goods-content">
@@ -122,12 +122,12 @@
</view>
</view>
<view class="empty" v-else>
<image :src="$util.img('public/uniapp/fenxiao/index/no-fenxiao.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/no-fenxiao.png')" mode="widthFix" show-menu-by-longpress></image>
<text>您还不是分销商</text>
</view>
</view>
<view class="empty" v-else>
<image :src="$util.img('public/uniapp/fenxiao/index/no-fenxiao.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/no-fenxiao.png')" mode="widthFix" show-menu-by-longpress></image>
<text v-if="status == 1">您已提交{{ fenxiaoWords.fenxiao_name }}申请,等待平台审核</text>
<block v-else-if="status == -1">
<text>您提交的{{ fenxiaoWords.fenxiao_name }}申请,已被拒绝,请再接再厉</text>

View File

@@ -4,8 +4,8 @@
<block slot="list">
<view class="balances" v-if="accountList.length" v-for="item in accountList" :key="item.id">
<image v-if="item.type == 'order'" :src="$util.img('public/uniapp/fenxiao/bill/jiesuan.png')"
mode="widthFix"></image>
<image v-else :src="$util.img('public/uniapp/fenxiao/bill/withdraw.png')" mode="widthFix"></image>
mode="widthFix" show-menu-by-longpress></image>
<image v-else :src="$util.img('public/uniapp/fenxiao/bill/withdraw.png')" mode="widthFix" show-menu-by-longpress></image>
<view class="balances-info">
<text>{{ item.type_name }}</text>
<text>账单编号: {{ item.account_no }}</text>

View File

@@ -6,8 +6,8 @@
@click="toFenxiaoOrder(item)">
<view class="li-box" :class="{ active: index + 1 == teamList.length }">
<image v-if="item.headimg" :src="$util.img(item.headimg)" @error="imageError(index)"
mode="aspectFill"></image>
<image v-else :src="$util.getDefaultImage().head"></image>
mode="aspectFill" show-menu-by-longpress></image>
<image v-else :src="$util.getDefaultImage().head" show-menu-by-longpress></image>
<view class="member-info">
<view class="member-name">
<view class="left">

View File

@@ -23,7 +23,7 @@
@click="navToDetailPage(item)">
<view class="image-wrap">
<image :src="$util.img(item.sku_image, { size: 'mid' })" @error="imageError(index)"
mode="aspectFill" />
mode="aspectFill" show-menu-by-longpress />
</view>
<view class="goods-content">
<view class="goods-name">
@@ -109,7 +109,7 @@
<template v-if="poster != '-1'">
<view>
<view class="image-wrap">
<image :src="$util.img(poster)" :show-menu-by-longpress="true" />
<image :src="$util.img(poster)" show-menu-by-longpress />
</view>
<!-- #ifdef MP || APP-PLUS -->
<view class="save" @click="saveGoodsPoster()">保存图片</view>

View File

@@ -8,7 +8,7 @@
<view class="member">
<view class="member-pic">
<image :src="info.headimg ? $util.img(info.headimg) : $util.getDefaultImage().head"
@error="info.headimg = $util.getDefaultImage().head" mode="aspectFill" />
@error="info.headimg = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
<!-- <text class="level-name ">{{ info.level_name }}</text> -->
</view>
<view class="member-info">
@@ -79,14 +79,14 @@
<view class="fenxiao-index-allmoney" style="padding: 0;border-radius:24rpx;margin-top: 0;"
@click="toPoster()">
<image :src="$util.img('public/uniapp/fenxiao/index/commissionFission-qrcode1.png')" mode="widthFix"
style="width: 100%;border-radius:24rpx" />
style="width: 100%;border-radius:24rpx" show-menu-by-longpress/>
</view>
<!-- 功能列表 -->
<view class="fenxiao-menu-list">
<view class="menu-item" @click="$util.redirectTo('/pages_promotion/fenxiao/team')">
<view class="icon-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/down-line.png')" mode="widthFix">
<image :src="$util.img('public/uniapp/fenxiao/index/down-line.png')" mode="widthFix" show-menu-by-longpress>
</image>
</view>
<view class="info">
@@ -96,7 +96,7 @@
</view>
<view class="menu-item" @click="$util.redirectTo('/pages_promotion/fenxiao/withdraw_list')">
<view class="icon-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/jifenjiangli.png')" mode="widthFix">
<image :src="$util.img('public/uniapp/fenxiao/index/jifenjiangli.png')" mode="widthFix" show-menu-by-longpress>
</image>
</view>
<view class="info">
@@ -107,7 +107,7 @@
</view>
<view class="menu-item" @click="$util.redirectTo('/pages_promotion/fenxiao/order')">
<view class="icon-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/fenxiaodingdan.png')" mode="widthFix">
<image :src="$util.img('public/uniapp/fenxiao/index/fenxiaodingdan.png')" mode="widthFix" show-menu-by-longpress>
</image>
</view>
<view class="info">
@@ -118,7 +118,7 @@
<view class="menu-item" @click="toPoster()">
<view class="icon-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/friend.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/friend.png')" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="info">
<view class="title">我的邀请码</view>
@@ -127,7 +127,7 @@
</view>
<!-- <view class="menu-item" @click="$util.redirectTo('/pages_promotion/fenxiao/promote')">
<view class="icon-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/code.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/code.png')" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="info">
<view class="title">邀请好友</view>
@@ -136,7 +136,7 @@
</view> -->
<!-- <view class="menu-item" @click="$util.redirectTo('/pages_promotion/fenxiao/bill')">
<view class="icon-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/bill.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/bill.png')" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="info">
<view class="title">账单报表</view>
@@ -145,7 +145,7 @@
</view>
<view class="menu-item" @click="$util.redirectTo('/pages_promotion/fenxiao/goods_list', { templateId: templateId })">
<view class="icon-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/market.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/market.png')" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="info">
<view class="title">{{ fenxiaoWords.concept }}商品</view>
@@ -154,7 +154,7 @@
</view> -->
<!-- <view class="menu-item" @click="$util.redirectTo('/pages_promotion/fenxiao/ranking_list', { type: 'profit' })">
<view class="icon-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/commission_rank.png')" mode="widthFix">
<image :src="$util.img('public/uniapp/fenxiao/index/commission_rank.png')" mode="widthFix" show-menu-by-longpress>
</image>
</view>
<view class="info">
@@ -164,7 +164,7 @@
</view> -->
<!-- <view class="menu-item" @click="$util.redirectTo('/pages_promotion/fenxiao/ranking_list', { type: 'invited_num' })">
<view class="icon-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/promoter_rank.png')" mode="widthFix">
<image :src="$util.img('public/uniapp/fenxiao/index/promoter_rank.png')" mode="widthFix" show-menu-by-longpress>
</image>
</view>
<view class="info">
@@ -174,7 +174,7 @@
</view> -->
<!-- <view class="menu-item" @click="$util.redirectTo('/pages_promotion/fenxiao/level')" v-if="levelList.length">
<view class="icon-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/team_reward.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/team_reward.png')" mode="widthFix" show-menu-by-longpress></image>
</view>
<view class="info">
<view class="title">等级说明</view>
@@ -187,7 +187,7 @@
<view class="fenxiao-index-other">
<view @click="$util.redirectTo('/pages_promotion/fenxiao/team')" class="all-money-item">
<view class="img-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/down-line.png')" mode="aspectFill"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/down-line.png')" mode="aspectFill" show-menu-by-longpress></image>
</view>
<view class="all-money-tit-wrap">
<text class="all-money-tit">我的粉丝</text>
@@ -198,7 +198,7 @@
<view class="fenxiao-index-other">
<view @click="$util.redirectTo('/pages_promotion/fenxiao/child_fenxiao')" class="all-money-item">
<view class="img-wrap">
<image :src="$util.img('public/uniapp/fenxiao/index/yejikaohe.png')" mode="aspectFill"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/yejikaohe.png')" mode="aspectFill" show-menu-by-longpress></image>
</view>
<view class="all-money-tit-wrap">
<text class="all-money-tit">{{ fenxiaoWords.fenxiao_name }}</text>
@@ -210,7 +210,7 @@
</block>
<ns-copyright></ns-copyright>
<view class="empty" v-if="!info.fenxiao_id > 0 && showEmpty">
<image :src="$util.img('public/uniapp/fenxiao/index/no-fenxiao.png')" mode="widthFix"></image>
<image :src="$util.img('public/uniapp/fenxiao/index/no-fenxiao.png')" mode="widthFix" show-menu-by-longpress></image>
<text>您还不是{{ fenxiaoWords.fenxiao_name }},请先提交申请</text>
<view @click="$util.redirectTo('/pages_promotion/fenxiao/apply')" class="color-base-bg">立即加入</view>
</view>

View File

@@ -10,7 +10,7 @@
<view class="level-wrap">
<view class="member-info">
<view class="head-img">
<image :src="fenxiaoInfo.headimg ? $util.img(fenxiaoInfo.headimg) : $util.getDefaultImage().head" @error="fenxiaoInfo.headimg = $util.getDefaultImage().head" mode="aspectFill"/>
<image :src="fenxiaoInfo.headimg ? $util.img(fenxiaoInfo.headimg) : $util.getDefaultImage().head" @error="fenxiaoInfo.headimg = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
</view>
<view class="nickname">{{ fenxiaoInfo.nickname }}</view>
<view class="level-name">{{ item.level_name }}</view>

View File

@@ -24,7 +24,7 @@
<view class="goods-wrap">
<view class="goods-img">
<image :src="$util.img(orderItem.sku_image, { size: 'mid' })"
@error="imageError(orderIndex)" mode="aspectFill" :lazy-load="true"></image>
@error="imageError(orderIndex)" mode="aspectFill" :lazy-load="true" show-menu-by-longpress></image>
</view>
<view class="goods-info">
<view class="top-wrap">

View File

@@ -12,7 +12,7 @@
</view>
<view class="detail-body">
<view class="detail-body-box">
<view class="goods-image"><image :src="$util.img(orderData.sku_image, { size: 'mid' })" @error="imageError()" mode="aspectFill"></image></view>
<view class="goods-image"><image :src="$util.img(orderData.sku_image, { size: 'mid' })" @error="imageError()" mode="aspectFill" show-menu-by-longpress></image></view>
<view class="order-info">
<view class="goods-name">{{ orderData.sku_name }}</view>
<view class="goods-sub-section margin-top">

View File

@@ -6,7 +6,7 @@
<view class="earnings-head-wrap">
<view class="earnings-head">
<image class="name" :src="$util.img('public/uniapp/fenxiao/promote/my_earnings.png')"
mode="aspectFit"></image>
mode="aspectFit" show-menu-by-longpress></image>
<image class="money-bg" :src="$util.img('public/uniapp/fenxiao/promote/money.png')"
mode="aspectFill"></image>
<view class="content">
@@ -26,7 +26,7 @@
<view class="earnings-item" v-for="(item, index) in promote.list" :key="index">
<image class="item-img"
:src="item.headimg ? $util.img(item.headimg) : $util.getDefaultImage().head"
@error="item.headimg = $util.getDefaultImage().head" mode="aspectFill"></image>
@error="item.headimg = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress></image>
<view class="item-content">
<view class="item-name multi-hidden">{{ item.nickname }}</view>
</view>
@@ -38,7 +38,7 @@
</view>
<view class="activity-rules">
<image class="rules-name" :src="$util.img('public/uniapp/fenxiao/promote/activity_rules.png')"
mode="aspectFit"></image>
mode="aspectFit" show-menu-by-longpress></image>
<view class="content" v-if="promoteContent.content"><rich-text
:nodes="promoteContent.content"></rich-text></view>
<view class="rules-empty" v-else>暂无活动规则</view>

View File

@@ -4,7 +4,7 @@
<swiper-item v-for="(item, index) in poster" :key="index">
<view class="swiper-item">
<view class="poster-wrap">
<image :src="$util.img(item)" mode="widthFix" :show-menu-by-longpress="true" />
<image :src="$util.img(item)" mode="widthFix" show-menu-by-longpress />
</view>
</view>
</swiper-item>

View File

@@ -8,7 +8,7 @@
<view class="info">
<view class="info-pic">
<image :src="info.headimg ? $util.img(info.headimg) : $util.getDefaultImage().head"
@error="info.headimg = $util.getDefaultImage().head" mode="aspectFill" />
@error="info.headimg = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
</view>
<view class="member-info">
<view class="rank-info-box">
@@ -81,7 +81,7 @@
<view class="content">
<view class="head-img">
<image :src="item.headimg ? $util.img(item.headimg) : $util.getDefaultImage().head"
@error="item.headimg = $util.getDefaultImage().head" mode="aspectFill" />
@error="item.headimg = $util.getDefaultImage().head" mode="aspectFill" show-menu-by-longpress/>
</view>
<view class="nickname">{{ item.nickname }}</view>
</view>

View File

@@ -17,7 +17,7 @@
<view class="goods-wrap">
<view class="goods-img">
<image :src="$util.img(orderItem.sku_image, { size: 'mid' })"
@error="imageError(orderIndex)" mode="aspectFill" :lazy-load="true"></image>
@error="imageError(orderIndex)" mode="aspectFill" :lazy-load="true" show-menu-by-longpress></image>
</view>
<view class="goods-info">
<view class="top-wrap">

Some files were not shown because too many files have changed in this diff Show More