Compare commits
49 Commits
test/hw-se
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 695d27c5cb | |||
| cd82ba2f27 | |||
| 698d419525 | |||
| a252d42b62 | |||
| 69466f410c | |||
| 39245dfcac | |||
| 80b18cee0f | |||
| b2688dc8a1 | |||
| d5ca77e2d8 | |||
| e789ab2ca3 | |||
| 5e83771193 | |||
| 0d18041037 | |||
| 875cf0b428 | |||
| e16402c9c1 | |||
| dee2887026 | |||
| 6033cc2fa0 | |||
| c1fbabe07b | |||
| 221ef7bb6d | |||
| 55c1e80ed7 | |||
| 79202c4542 | |||
| 3bff91e421 | |||
| 979384c2d6 | |||
| 504e5322fb | |||
| 25fc0a59a8 | |||
| 34e27d0621 | |||
| 913fc90176 | |||
| c1bac6d9e8 | |||
| 31382a2388 | |||
| d009077183 | |||
| 133eb29a71 | |||
| a2616c9757 | |||
| fb47da8095 | |||
| 37958cbe39 | |||
| 9f53f147ee | |||
| 4e480d35df | |||
| bc6d243362 | |||
| a285706112 | |||
| 9c04c031ab | |||
| 0ce929f8a1 | |||
| 61ab9f11bd | |||
| 2747cb49ae | |||
| 9c8ebf8d74 | |||
| e19fef785f | |||
| 92ae114c5c | |||
| b25c366a5b | |||
| b23c21d05b | |||
| 65a24f25bb | |||
| 2a0935b581 | |||
| b31197a8b4 |
@@ -42,18 +42,25 @@ const localDevConfig = ({
|
||||
uniacid: 2,
|
||||
domain: 'http://localhost:8050/',
|
||||
},
|
||||
'100': {
|
||||
uniacid: 100,
|
||||
domain: 'https://5g.aigc-quickapp.com/',
|
||||
},
|
||||
'1': {
|
||||
uniacid: 1,
|
||||
domain: 'https://5g.aigc-quickapp.com/',
|
||||
},
|
||||
'115': {
|
||||
uniacid: 115,
|
||||
domain: 'https://5g.aigc-quickapp.com/',
|
||||
},
|
||||
'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/',
|
||||
},
|
||||
'47': { // 修改弹窗
|
||||
uniacid: 47,
|
||||
domain: 'https://5g.aigc-quickapp.com/',
|
||||
},
|
||||
'2075': { //
|
||||
uniacid: 2075,
|
||||
domain: 'https://xcx.aigc-quickapp.com/',
|
||||
},
|
||||
})['1']; // 选择要使用的环境配置
|
||||
|
||||
export default localDevConfig;
|
||||
1
App.vue
1
App.vue
@@ -266,7 +266,6 @@
|
||||
imgUrl: this.$util.img(share_data.imgUrl)
|
||||
},
|
||||
res => {
|
||||
console.log(res);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,15 +40,6 @@ export default {
|
||||
*/
|
||||
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: {
|
||||
@@ -66,23 +57,18 @@ export default {
|
||||
}
|
||||
};
|
||||
|
||||
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('[AI Service] Dify API请求失败,详细错误:', error);
|
||||
console.error('[AI Service] AiChat API请求失败,详细错误:', error);
|
||||
console.error('[AI Service] 错误堆栈:', error.stack);
|
||||
// 打印更多信息帮助调试
|
||||
console.error('[AI Service] store 状态:', {
|
||||
@@ -102,8 +88,17 @@ export default {
|
||||
return new Promise((resolve, reject) => {
|
||||
const match = Config.baseUrl.match(/^(?:https?:\/\/|wss?:\/\/)([^\/\s:]+)/);
|
||||
const domain = match ? match[1] : '';
|
||||
|
||||
// 修复 5:Domain 提取失败时提前拒绝
|
||||
if (!domain) {
|
||||
const errorMsg = 'WebSocket 域名配置无效';
|
||||
console.error(errorMsg);
|
||||
reject(new Error(errorMsg));
|
||||
if (onComplete) onComplete({ error: errorMsg });
|
||||
return;
|
||||
}
|
||||
|
||||
const wssUrl = `wss://${domain}/ws/aikefu`;
|
||||
console.log(`wssUrl = ${wssUrl}`);
|
||||
const socketTask = wx.connectSocket({
|
||||
url: wssUrl,
|
||||
header: {}
|
||||
@@ -112,9 +107,31 @@ export default {
|
||||
let content = '';
|
||||
let conversationId = '';
|
||||
let isAuthenticated = false;
|
||||
let closedNormally = false;
|
||||
let settled = false;
|
||||
|
||||
// 安全地拒绝(防止重复调用)
|
||||
function safeReject(error) {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(timeout);
|
||||
reject(error);
|
||||
}
|
||||
|
||||
// 安全地完成
|
||||
function safeResolve(result) {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(timeout);
|
||||
resolve(result);
|
||||
}
|
||||
|
||||
// 安全地触发 onComplete
|
||||
function safeComplete(result) {
|
||||
if (onComplete) onComplete(result);
|
||||
}
|
||||
|
||||
socketTask.onOpen(() => {
|
||||
console.log('WebSocket 连接成功,开始认证...');
|
||||
socketTask.send({
|
||||
data: JSON.stringify({
|
||||
action: 'auth',
|
||||
@@ -128,10 +145,8 @@ export default {
|
||||
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({
|
||||
@@ -146,8 +161,8 @@ export default {
|
||||
} else if (data.type === 'auth_failed') {
|
||||
const errorMsg = '认证失败,请重新登录';
|
||||
console.error(errorMsg, data);
|
||||
reject(new Error(errorMsg));
|
||||
if (onComplete) onComplete({ error: errorMsg });
|
||||
safeComplete({ error: errorMsg });
|
||||
safeReject(new Error(errorMsg));
|
||||
socketTask.close();
|
||||
}
|
||||
|
||||
@@ -164,16 +179,14 @@ export default {
|
||||
if (conversationId) {
|
||||
this.setConversationId(conversationId);
|
||||
}
|
||||
if (onComplete) {
|
||||
onComplete({ content, conversation_id: conversationId });
|
||||
}
|
||||
resolve({ content, conversation_id: conversationId });
|
||||
closedNormally = true;
|
||||
safeComplete({ content, conversation_id: conversationId });
|
||||
safeResolve({ content, conversation_id: conversationId });
|
||||
socketTask.close();
|
||||
}
|
||||
|
||||
// 可选:处理 done
|
||||
else if (data.type === 'done') {
|
||||
console.log('对话完成:', data);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
@@ -184,22 +197,19 @@ export default {
|
||||
socketTask.onError((err) => {
|
||||
const errorMsg = 'WebSocket 连接失败';
|
||||
console.error(errorMsg, err);
|
||||
reject(new Error(errorMsg));
|
||||
if (onComplete) onComplete({ error: errorMsg });
|
||||
safeComplete({ error: errorMsg });
|
||||
safeReject(new Error(errorMsg));
|
||||
});
|
||||
|
||||
socketTask.onClose(() => {
|
||||
console.log('WebSocket 连接已关闭');
|
||||
if (!closedNormally && !settled) {
|
||||
const errorMsg = 'WebSocket 连接意外关闭';
|
||||
console.error(errorMsg);
|
||||
safeComplete({ error: errorMsg });
|
||||
safeReject(new Error(errorMsg));
|
||||
}
|
||||
});
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
if (!isAuthenticated || content === '') {
|
||||
console.warn('WebSocket 超时,强制关闭');
|
||||
socketTask.close();
|
||||
reject(new Error('AI服务响应超时'));
|
||||
if (onComplete) onComplete({ error: 'AI服务响应超时' });
|
||||
}
|
||||
}, 10000);
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
@@ -388,7 +398,6 @@ export default {
|
||||
async: false,
|
||||
data: {}
|
||||
});
|
||||
console.log('/api/kefu/health response', response);
|
||||
const available = [response?.data?.status, response?.data?.components?.ai_service_config?.status]
|
||||
.filter(item => item === 'healthy').length > 0;
|
||||
return {
|
||||
|
||||
@@ -98,54 +98,6 @@ 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: '华为帐号登录失败'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,22 +18,15 @@ try {
|
||||
|
||||
// 线上版本部署域名, 注意: 必须以 '/' 结尾,适用于小程序/H5/快应用等场景
|
||||
// 默认域名, 自定义发行时可以修改
|
||||
let defaultDomain = '';
|
||||
let defaultDomain = '';
|
||||
|
||||
// #ifdef H5_XCX_5G_QUICKAPP_COM
|
||||
defaultDomain = 'https://xcx20.5g-quickapp.com';
|
||||
// #endif
|
||||
|
||||
// #ifdef H5_XCX_AIGC_QUICKAPP_COM
|
||||
defaultDomain = 'https://xcx.aigc-quickapp.com/';
|
||||
// #endif
|
||||
|
||||
// #ifdef H5_5G_AIGC_QUICKAPP_COM
|
||||
defaultDomain = 'https://5g.aigc-quickapp.com/';
|
||||
// #endif
|
||||
|
||||
// #ifdef H5_TEST_AIGC_QUICKAPP_COM
|
||||
defaultDomain = 'https://test.aigc-quickapp.com/';
|
||||
// #ifdef H5
|
||||
// 域名由 vue.config.js 中 DefinePlugin 注入的 process.env.UNI_APP_DOMAIN 传入
|
||||
try {
|
||||
if (process.env.UNI_APP_DOMAIN) {
|
||||
defaultDomain = process.env.UNI_APP_DOMAIN;
|
||||
}
|
||||
} catch (e) {}
|
||||
// #endif
|
||||
|
||||
// 确保域名以 '/' 结尾
|
||||
@@ -59,7 +52,6 @@ if (process?.env?.NODE_ENV === 'development') {
|
||||
// 如果本地配置文件不存在或加载失败,保持使用默认配置
|
||||
// 只在开发模式下输出提示信息
|
||||
// #ifdef WEB
|
||||
console.log('本地配置文件 .local.config.js 不存在,使用默认配置');
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
@@ -120,16 +112,5 @@ export default {
|
||||
pingInterval: 1500,
|
||||
|
||||
// 版本号
|
||||
version: '1.0',
|
||||
|
||||
// 华为快应用 HwService 配置
|
||||
// 需在 AppGallery Connect 创建应用后获取
|
||||
huawei: {
|
||||
// AGC 应用配置(替换为实际的 AGC 配置值)
|
||||
appId: '', // 应用ID
|
||||
clientId: '', // 客户端ID
|
||||
clientSecret: '', // 客户端密钥(注意安全,建议由后端下发)
|
||||
// 华为帐号登录回调地址
|
||||
redirectUri: window.location.origin + '/hwappx/common/',
|
||||
}
|
||||
version: '1.0'
|
||||
};
|
||||
@@ -32,7 +32,6 @@ export class CustomerService {
|
||||
|
||||
// 优先级:外部传入 > vuex store > 空对象
|
||||
const servicerConfig = this.externalConfig || this.vm.$store.state.servicerConfig || {};
|
||||
console.log(`【实时客服配置】`, servicerConfig);
|
||||
|
||||
let platformConfig = null;
|
||||
|
||||
@@ -120,9 +119,9 @@ export class CustomerService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到 AI 客服页面(Dify)
|
||||
* 跳转到 AI智能客服页面(AiChat)
|
||||
*/
|
||||
openDifyService() {
|
||||
openAiChatService() {
|
||||
try {
|
||||
// 清除未读数(如果存在)
|
||||
if (typeof this.vm.setAiUnreadCount === 'function') {
|
||||
@@ -136,7 +135,7 @@ export class CustomerService {
|
||||
uni.navigateTo({
|
||||
url: aiChatUrl,
|
||||
fail: (err) => {
|
||||
console.error('跳转 AI 客服失败:', err);
|
||||
console.error('跳转 AI智能客服失败:', err);
|
||||
// H5 兜底
|
||||
// #ifdef H5
|
||||
window.location.href = aiChatUrl;
|
||||
@@ -145,7 +144,7 @@ export class CustomerService {
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('跳转 AI 客服异常:', e);
|
||||
console.error('跳转 AI智能客服异常:', e);
|
||||
uni.showToast({ title: '打开客服失败', icon: 'none' });
|
||||
}
|
||||
}
|
||||
@@ -167,8 +166,6 @@ export class CustomerService {
|
||||
}
|
||||
|
||||
const config = this.getPlatformConfig();
|
||||
console.log('【当前客服配置】', config);
|
||||
console.log('【客服类型】', config.type);
|
||||
|
||||
const { niushop = {}, sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
|
||||
|
||||
@@ -180,31 +177,24 @@ export class CustomerService {
|
||||
// 核心路由:根据 type 决定行为
|
||||
switch (config.type) {
|
||||
case 'aikefu':
|
||||
console.log('【跳转 AI 客服】目标路径: /pages_tool/ai-chat/index');
|
||||
this.openDifyService();
|
||||
this.openAiChatService();
|
||||
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:
|
||||
@@ -225,7 +215,7 @@ export class CustomerService {
|
||||
wx.navigateToMiniProgram({
|
||||
appId: 'wxeb490c6f9b154ef9',
|
||||
path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(wxworkConfig.contact_url)}`,
|
||||
success: () => console.log('跳转企业微信成功'),
|
||||
success: () => {},
|
||||
fail: (err) => {
|
||||
console.error('跳转企业微信失败:', err);
|
||||
this.fallbackToOriginalService();
|
||||
@@ -262,24 +252,20 @@ export class CustomerService {
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -295,7 +281,6 @@ export class CustomerService {
|
||||
|
||||
// 处理第三方链接客服
|
||||
if (config.third_url) {
|
||||
console.log('【跳转第三方链接】', config.third_url);
|
||||
// #ifdef H5
|
||||
window.location.href = config.third_url;
|
||||
// #endif
|
||||
@@ -326,7 +311,7 @@ export class CustomerService {
|
||||
// 此方法仅用于自定义跳转(如 useOfficial: false)
|
||||
if (config.useOfficial !== false) {
|
||||
// 不做任何事,应由 <button open-type="contact"> 触发
|
||||
console.log('使用微信官方客服,请确保按钮为 <button open-type="contact">');
|
||||
|
||||
return;
|
||||
}
|
||||
this.handleCustomWeappService(config, options);
|
||||
@@ -379,12 +364,12 @@ export class CustomerService {
|
||||
|
||||
openAliappService(config) {
|
||||
if (config.type === 'aikefu') {
|
||||
this.openDifyService();
|
||||
this.openAiChatService();
|
||||
} else if (config.type === 'third') {
|
||||
this.openThirdService(config);
|
||||
} else {
|
||||
// 支付宝原生客服由 button open-type="contact" 触发,此处不处理
|
||||
console.log('使用支付宝官方客服');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,7 +565,7 @@ export class CustomerService {
|
||||
uni.navigateTo({
|
||||
url: aiChatUrl,
|
||||
fail: (err) => {
|
||||
console.error('跳转 AI 客服失败:', err);
|
||||
console.error('跳转 AI智能客服失败:', err);
|
||||
// #ifdef H5
|
||||
window.location.href = aiChatUrl;
|
||||
// #endif
|
||||
@@ -588,7 +573,7 @@ export class CustomerService {
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('跳转 AI 客服异常:', e);
|
||||
console.error('跳转 AI智能客服异常:', e);
|
||||
uni.showToast({ title: '打开客服失败', icon: 'none' });
|
||||
}
|
||||
}
|
||||
@@ -644,8 +629,6 @@ export class CustomerService {
|
||||
handleUnifiedKefuClick(options = {}) {
|
||||
const validation = this.validateConfig();
|
||||
|
||||
console.log('【客服配置验证】', validation);
|
||||
|
||||
if (!validation.isValid) {
|
||||
console.error('客服配置无效:', validation.errors);
|
||||
uni.showToast({ title: '客服暂不可用', icon: 'none' });
|
||||
@@ -657,12 +640,10 @@ export class CustomerService {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -200,7 +200,6 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
play(){
|
||||
console.log(123)
|
||||
},
|
||||
async init() {
|
||||
|
||||
|
||||
@@ -61,9 +61,6 @@ 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'
|
||||
}
|
||||
|
||||
@@ -164,7 +164,6 @@ export default {
|
||||
|
||||
// 商品详情
|
||||
if (this.goodsSkuDetail.goods_content) this.goodsSkuDetail.goods_content = (this.goodsSkuDetail.goods_content);
|
||||
console.log(this.goodsSkuDetail.goods_content)
|
||||
// if (this.goodsSkuDetail.goods_content) this.goodsSkuDetail.goods_content = htmlParser(this.goodsSkuDetail.goods_content);
|
||||
|
||||
//商品服务
|
||||
|
||||
@@ -316,11 +316,9 @@ function replaceVideo(html){
|
||||
|
||||
let rep = `<video style="width:100% !important;display:block;max-width: ${screenWidth} !important;"`;
|
||||
var html = html.replace(/\\/g, '').replace(/<video/g, rep);
|
||||
console.log(html)
|
||||
html = html.replace(/<video [^>]*src=['"]([^'"]+)[^>]*>/gi, (match, capture) => {
|
||||
return rep + ' src="' + util.img(capture) + '"/>';
|
||||
});
|
||||
// console.log(html)
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,11 +37,6 @@ 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) {
|
||||
|
||||
@@ -1,361 +0,0 @@
|
||||
/**
|
||||
* 华为快应用 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 }
|
||||
@@ -30,12 +30,12 @@ function processRoutePath(route) {
|
||||
}
|
||||
}
|
||||
// ---- 处理页面目录映射 <end>----
|
||||
|
||||
|
||||
// 去掉pages目录,只保留子目录
|
||||
if (routeParts[0] === 'pages') {
|
||||
routeParts = routeParts.slice(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return routeParts.join("/");
|
||||
}
|
||||
|
||||
@@ -54,11 +54,14 @@ function loadLangPackSync(lang, path) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前本地语言
|
||||
* @returns
|
||||
*/
|
||||
function getCurrentLocale() {
|
||||
return uni.getStorageSync('lang') || "zh-cn";
|
||||
return uni.getStorageSync('lang') || "zh-cn";
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
langList: langConfig.langList,
|
||||
|
||||
@@ -77,18 +80,18 @@ export default {
|
||||
if (!_page) return;
|
||||
|
||||
const locale = getCurrentLocale(); // 获得当前本地语言
|
||||
|
||||
|
||||
let value = ''; // 存放解析后的语言值
|
||||
let langPath = ''; // 存放当前页面语言包路径
|
||||
|
||||
try {
|
||||
//公共语言包(同步加载)
|
||||
var lang = loadLangPackSync(locale, 'common');
|
||||
|
||||
|
||||
//当前页面语言包(同步加载)
|
||||
let route = _page.route;
|
||||
langPath = processRoutePath(route);
|
||||
|
||||
|
||||
// 加载当前页面语言包
|
||||
let currentPageLang = loadLangPackSync(locale, langPath);
|
||||
|
||||
@@ -113,7 +116,7 @@ export default {
|
||||
} else {
|
||||
value = mergedLang[field] !== undefined ? mergedLang[field] : field;
|
||||
}
|
||||
|
||||
|
||||
} catch (e) {
|
||||
console.error('解析语言包失败:', e, { langPath, field, locale });
|
||||
value = field;
|
||||
@@ -129,7 +132,7 @@ export default {
|
||||
|
||||
// 多语言调试,注释后可以关闭控制台输出
|
||||
if (field == value) {
|
||||
console.warn(`警告: 字段 ${field} 在语言包 ${langPath} 中未找到对应值,使用默认值 ${field} 当前语言: ${locale}`);
|
||||
console.warn(`警告: 字段 ${field} 在语言包 ${langPath} 中未找到对应值,使用默认值 ${field} 当前语言: ${locale}`);
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -165,7 +168,7 @@ export default {
|
||||
if (!_page) return;
|
||||
|
||||
const locale = getCurrentLocale();
|
||||
|
||||
|
||||
this.title(this.lang("title"));
|
||||
|
||||
//设置tabbar的文字语言
|
||||
@@ -215,4 +218,4 @@ export default {
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,16 +89,13 @@ export default {
|
||||
let platform = util.getDeviceInfo().platform;
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
console.log('运行Android上')
|
||||
openMapByAndroid(arr[1], arr[0], name)
|
||||
break;
|
||||
case 'ios':
|
||||
console.log('运行iOS上')
|
||||
openMapByIos(arr[1], arr[0], name)
|
||||
break;
|
||||
default:
|
||||
openMapByDefault(arr[1], arr[0], name)
|
||||
console.log('运行在开发者工具上')
|
||||
break;
|
||||
}
|
||||
// #endif
|
||||
|
||||
@@ -43,7 +43,6 @@ export class NavigationHelper {
|
||||
|
||||
query.select('.uni-page-head').boundingClientRect((rect) => {
|
||||
if (rect && rect.height > 0) {
|
||||
console.log('直接查询导航栏高度成功:', rect.height)
|
||||
resolve(rect.height)
|
||||
} else {
|
||||
console.warn('未找到 uni-page-head 元素或高度为0')
|
||||
@@ -64,7 +63,6 @@ export class NavigationHelper {
|
||||
|
||||
const height = menuButtonInfo.bottom +
|
||||
(menuButtonInfo.top - systemInfo.statusBarHeight)
|
||||
console.log('微信小程序导航栏高度:', height)
|
||||
resolve(height)
|
||||
} catch (error) {
|
||||
console.error('微信小程序高度计算失败:', error)
|
||||
@@ -174,7 +172,6 @@ export class NavigationHelper {
|
||||
|
||||
// 处理服务请求
|
||||
async handleServiceRequest(event, component) {
|
||||
console.log('处理导航相关服务请求:', event.type)
|
||||
|
||||
// 安全检查事件目标
|
||||
if (this.shouldProcessNavigationRequest(event)) {
|
||||
@@ -278,7 +275,6 @@ export class NavigationHelper {
|
||||
// 清理缓存
|
||||
clearCache() {
|
||||
this.navigationCache.clear()
|
||||
console.log('导航缓存已清理')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,53 +21,35 @@ export default {
|
||||
methods: {
|
||||
// 判断是否已连接
|
||||
checkOpenSocket() {
|
||||
console.log('判断是否已连接');
|
||||
// alert('判断是否已连接')
|
||||
let self = this;
|
||||
uni.sendSocketMessage({
|
||||
data: 'ping',
|
||||
success: (res) => {
|
||||
console.log('连接成功,检查');
|
||||
// alert('连接成功,检查')
|
||||
// self.getChatList();
|
||||
},
|
||||
fail: (err) => { // 未连接打开websocket连接
|
||||
console.log('连接失败');
|
||||
// alert('连接失败')
|
||||
self.openConnection();
|
||||
}
|
||||
});
|
||||
},
|
||||
openConnection() { // 打开连接
|
||||
console.log('打开连接');
|
||||
// alert('打开连接')
|
||||
// uni.closeSocket(); // 确保已经关闭后再重新打开
|
||||
uni.connectSocket({
|
||||
url: config.webSocket,
|
||||
method: 'POST',
|
||||
success(res) {
|
||||
console.log('连接成功 connectSocket=', res);
|
||||
// alert('连接成功 connectSocket=', res);
|
||||
},
|
||||
fail(err) {
|
||||
console.log('连接失败 connectSocket=', err);
|
||||
}
|
||||
});
|
||||
// uni.onSocketOpen((res) => {
|
||||
// console.log('连接成功', res);
|
||||
// });
|
||||
this.onSocketMessage(); // 打开成功监听服务器返回的消息
|
||||
},
|
||||
// 打开成功监听服务器返回的消息
|
||||
onSocketMessage() { // 消息
|
||||
console.log("开始监听");
|
||||
|
||||
let that = this;
|
||||
this.pingInterval = config.pingInterval;
|
||||
this.timeoutObj = null;
|
||||
uni.onSocketMessage((res) => { //type:init,connect,close,string,order,goods
|
||||
let msg = JSON.parse(res.data);
|
||||
console.log("监听该服务器消息", res);
|
||||
if (msg.type == 'close') {
|
||||
clearInterval(that.timeoutObj);
|
||||
that.timeoutObj = null;
|
||||
@@ -141,22 +123,18 @@ export default {
|
||||
},
|
||||
// 检测心跳reset
|
||||
reset() {
|
||||
console.log("检测心跳");
|
||||
clearInterval(this.timeoutObj);
|
||||
this.start(); // 启动心跳
|
||||
},
|
||||
// 启动心跳 start
|
||||
start() {
|
||||
console.log("启动心跳");
|
||||
let self = this;
|
||||
this.timeoutObj = setInterval(function () {
|
||||
uni.sendSocketMessage({
|
||||
data: 'ping',
|
||||
success: (res) => {
|
||||
console.log('连接中....');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('连接失败重新连接....');
|
||||
self.openConnection();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -34,7 +34,6 @@ export const makePhoneCall = (mobile) => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: `${mobile}`,
|
||||
success(e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
@@ -132,9 +132,7 @@ export default {
|
||||
let url = to;
|
||||
|
||||
// 替换url中的前缀
|
||||
console.log('页面跳转 redirectTo', to, param, mode);
|
||||
url = adaptSubpackageUrl(url);
|
||||
console.log('adaptSubpackageUrl', url);
|
||||
|
||||
if (param != undefined) {
|
||||
Object.keys(param).forEach(function (key) {
|
||||
@@ -178,7 +176,6 @@ export default {
|
||||
break;
|
||||
default:
|
||||
// 保留当前页面,跳转到应用内的某个页面,新增页面到栈顶,长度+1(上限10层)
|
||||
console.log('保留当前页面,navigateTo跳转到应用内的某个页面', url);
|
||||
uni.navigateTo({
|
||||
url
|
||||
});
|
||||
@@ -623,12 +620,8 @@ export default {
|
||||
appId: 'wxeb490c6f9b154ef9', // 是企业微信官方小程序的AppID(固定值, 由腾讯企业微信团队维护, 不需要修改,用于展示"添加企业微信联系人"的官方页面)
|
||||
path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(wxworkConfig?.contact_url)}`,
|
||||
success: () => {
|
||||
console.log('跳转企业微信成功');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('跳转企业微信失败:', err);
|
||||
// 降级处理:使用原有客服方式
|
||||
fallbackFunc && fallbackFunc();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -909,12 +902,11 @@ export default {
|
||||
},
|
||||
success: res => {
|
||||
if (res.code >= 0) {
|
||||
uni.removeStorage({
|
||||
key: 'source_member',
|
||||
success: res => {
|
||||
console.log('删除成功', res)
|
||||
}
|
||||
})
|
||||
uni.removeStorage({
|
||||
key: 'source_member',
|
||||
success: res => {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -938,27 +930,23 @@ export default {
|
||||
success: res => {
|
||||
if (res.data.length) {
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: res.data,
|
||||
success: (res) => {
|
||||
console.log("res", res)
|
||||
},
|
||||
fail: (res) => {
|
||||
console.log('fail', res)
|
||||
}
|
||||
})
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: res.data,
|
||||
success: (res) => {
|
||||
},
|
||||
fail: (res) => {
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-ALIPAY
|
||||
my.requestSubscribeMessage({
|
||||
entityIds: res.data,
|
||||
success: res => {
|
||||
console.log("res", res)
|
||||
},
|
||||
fail: res => {
|
||||
console.log('fail', res)
|
||||
}
|
||||
});
|
||||
my.requestSubscribeMessage({
|
||||
entityIds: res.data,
|
||||
success: res => {
|
||||
},
|
||||
fail: res => {
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
@@ -1213,7 +1201,6 @@ export default {
|
||||
let _isQuickApp = false;
|
||||
try {
|
||||
const ua = navigator?.userAgent?.toLowerCase();
|
||||
// console.log('ua = ', ua);
|
||||
_isQuickApp = ua.indexOf('quickapp') !== -1;
|
||||
|
||||
if (!_isQuickApp) {
|
||||
|
||||
@@ -55,7 +55,6 @@ let Weixin = function () {
|
||||
typeof callback == 'function' && callback(res);
|
||||
},
|
||||
fail: (res) => {
|
||||
console.log('获取收货地址 fail',res);
|
||||
alert(JSON.stringify(res))
|
||||
}
|
||||
});
|
||||
|
||||
@@ -98,9 +98,7 @@ export default {
|
||||
uni.setStorageSync('audio_list', audio_list)
|
||||
}
|
||||
}
|
||||
console.log(uni.getStorageSync('audio_list'))
|
||||
var n = this;
|
||||
// console.log(a)
|
||||
a.onPlay(function () {
|
||||
var e = setInterval(function () {
|
||||
var i = a.currentTime / a.duration * 100 + "%", s = Math.floor(Math.ceil(a.currentTime) / 60), o = (Math.ceil(a.currentTime) % 60 / 100).toFixed(2).slice(-2), r = Math.ceil(a.currentTime);
|
||||
@@ -113,13 +111,12 @@ export default {
|
||||
|
||||
var s = n.$util.img(n.value.audioUrl), o = n.audios[n.value.id].seconds || 0, r = 0, u = 1;
|
||||
0 == u && a.onEnded(function (e) {
|
||||
c[t].status = !1, n.status = !1, c[t].seconds = 0, console.log(c), n.audios = c;
|
||||
c[t].status = !1, n.status = !1, c[t].seconds = 0, n.audios = c;
|
||||
});
|
||||
var c = n.audios;
|
||||
c[t] || (c[t] = {}), a.paused && 0 == o ? (a.src = s, a.play(), 1 == u && (a.loop = !0),
|
||||
c[t].status = !0, n.status = !0, n.pauseOther(t)) : a.paused && o > 0 ? (a.play(), 0 == r ? a.seek(o) : a.seek(0),
|
||||
c[t].status = !0, n.status = !0, n.pauseOther(t)) : (a.pause(), c[t].status = !1, n.status = !1), n.audios = c;
|
||||
console.log(n.audios)
|
||||
},
|
||||
pauseOther: function (e) {
|
||||
var t = this;
|
||||
@@ -190,18 +187,14 @@ export default {
|
||||
var u = s + ":" + o, c = that.audios;
|
||||
c[t].audiowidth = i, c[t].Time = e, c[t].audiotime = u, c[t].seconds = r
|
||||
that.audios = c
|
||||
// console.log(c)
|
||||
console.log(that.audios[that.value.id].audiotime)
|
||||
that.audiotime = that.audios[that.value.id].audiotime
|
||||
that.audiowidth = that.audios[that.value.id].audiowidth
|
||||
console.log(i)
|
||||
that.lyg = i
|
||||
}, 1e3);
|
||||
});
|
||||
|
||||
|
||||
this.audioContext.onEnded(() => {
|
||||
console.log('播放结束');
|
||||
this.status = 0
|
||||
});
|
||||
}
|
||||
|
||||
@@ -256,7 +256,6 @@ export default {
|
||||
try {
|
||||
// 发送视频被点击播放事件
|
||||
this.$emit('channel-video-click-play', item);
|
||||
console.log('播放视频:', item);
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 在微信小程序环境中调用视频播放接口
|
||||
@@ -289,7 +288,6 @@ export default {
|
||||
*/
|
||||
imgError(index) {
|
||||
// 图片加载失败的处理逻辑
|
||||
console.log('图片加载失败:', index);
|
||||
// 为失败的图片设置默认图片
|
||||
const item = this.value.list[index];
|
||||
if (item) {
|
||||
@@ -306,7 +304,6 @@ export default {
|
||||
addMouseDragScroll() {
|
||||
// 只在Web环境中添加
|
||||
// #ifndef MP
|
||||
console.log('addMouseDragScroll called');
|
||||
|
||||
let isDragging = false;
|
||||
let startX = 0;
|
||||
@@ -329,11 +326,9 @@ export default {
|
||||
const handleMouseDown = (e) => {
|
||||
// 检查是否点击在组件内
|
||||
if (this.$el.contains(e.target)) {
|
||||
console.log('mousedown event in component:', e);
|
||||
// 查找可滚动元素
|
||||
currentScrollElement = findScrollableParent(e.target);
|
||||
if (currentScrollElement) {
|
||||
console.log('Found scrollable element:', currentScrollElement);
|
||||
isDragging = true;
|
||||
startX = e.pageX;
|
||||
startScrollLeft = currentScrollElement.scrollLeft;
|
||||
@@ -345,17 +340,14 @@ export default {
|
||||
// 鼠标移动事件
|
||||
const handleMouseMove = (e) => {
|
||||
if (!isDragging || !currentScrollElement) return;
|
||||
console.log('mousemove event:', e);
|
||||
e.preventDefault();
|
||||
const dx = e.pageX - startX;
|
||||
currentScrollElement.scrollLeft = startScrollLeft - dx;
|
||||
console.log('scrollLeft:', currentScrollElement.scrollLeft);
|
||||
};
|
||||
|
||||
// 鼠标释放事件
|
||||
const handleMouseUp = () => {
|
||||
if (isDragging && currentScrollElement) {
|
||||
console.log('mouseup event');
|
||||
currentScrollElement.style.cursor = 'grab';
|
||||
}
|
||||
isDragging = false;
|
||||
@@ -368,21 +360,17 @@ export default {
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
document.addEventListener('mouseleave', handleMouseUp);
|
||||
|
||||
console.log('Global mouse event listeners added');
|
||||
|
||||
// 组件销毁时移除事件监听器
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
document.removeEventListener('mousedown', handleMouseDown);
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
document.removeEventListener('mouseleave', handleMouseUp);
|
||||
console.log('Global mouse event listeners removed');
|
||||
});
|
||||
|
||||
// 为所有.channel-nav元素添加必要的样式
|
||||
setTimeout(() => {
|
||||
const channelNavs = document.querySelectorAll('.channel-nav');
|
||||
console.log('Found channel-nav elements:', channelNavs.length);
|
||||
channelNavs.forEach(element => {
|
||||
element.style.overflowX = 'auto';
|
||||
element.style.overflowY = 'hidden';
|
||||
@@ -391,11 +379,9 @@ export default {
|
||||
element.style.maxWidth = '100%';
|
||||
element.style.cursor = 'grab';
|
||||
element.style.userSelect = 'none'; // 防止文本选择
|
||||
console.log('Added styles to channel-nav:', element);
|
||||
});
|
||||
}, 100); // 延迟执行,确保DOM已完全渲染
|
||||
|
||||
console.log('Mouse drag scroll setup completed');
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,6 @@ export default {
|
||||
const enableEmbedMode = Boolean(this.value?.embedMode)
|
||||
&& Boolean(this.value?.feedToken)
|
||||
&& wechatChannelUtil.isEmbedModeSupported();
|
||||
console.log('enableEmbedMode', enableEmbedMode);
|
||||
return enableEmbedMode;
|
||||
// #endif
|
||||
return false
|
||||
|
||||
@@ -91,7 +91,6 @@ export default {
|
||||
// 页面跳转
|
||||
redirectTo(item) {
|
||||
if (!item.wap_url || this.$util.getCurrRoute() != this.$util.MEMBER_PAGE_URL || this.storeToken) {
|
||||
console.log(item);
|
||||
this.$util.diyRedirectTo(item);
|
||||
} else {
|
||||
this.$refs.login.open(item.wap_url);
|
||||
|
||||
@@ -337,7 +337,7 @@ export default {
|
||||
width: calc(50% - 10rpx);
|
||||
|
||||
&:nth-child(2n + 2) {
|
||||
width: calc(50% - 11rpx);
|
||||
width: calc(50% - 10rpx);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1018,10 +1018,6 @@ export default {
|
||||
width: calc(33.3333333% - 14rpx);
|
||||
box-sizing: border-box;
|
||||
|
||||
&:nth-child(3n + 3) {
|
||||
width: calc(33.33% - 15rpx);
|
||||
}
|
||||
|
||||
&:nth-of-type(1),
|
||||
&:nth-of-type(2),
|
||||
&:nth-of-type(3) {
|
||||
|
||||
@@ -164,7 +164,6 @@ export default {
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.log(link)
|
||||
this.$util.diyRedirectTo(link);
|
||||
},
|
||||
swiperChange(e) {
|
||||
|
||||
@@ -358,11 +358,6 @@ export default {
|
||||
});
|
||||
} else data = this.setPagestyle;
|
||||
|
||||
console.log(`diy-group ['diyDataArray'] = `, {
|
||||
data: data,
|
||||
diyData: this.diyData,
|
||||
diyGlobalData: this.diyGlobalData,
|
||||
})
|
||||
return data;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -109,7 +109,6 @@ export default {
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('get live status', err);
|
||||
});
|
||||
|
||||
// 往后间隔1分钟或更慢的频率去轮询获取直播状态
|
||||
@@ -127,8 +126,7 @@ export default {
|
||||
clearInterval(timer);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('get live status', err);
|
||||
.catch(err => {
|
||||
});
|
||||
}, 60000);
|
||||
// #endif
|
||||
|
||||
@@ -119,7 +119,6 @@ export default {
|
||||
obj += 'border-bottom-left-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
||||
obj += 'border-bottom-right-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
||||
}
|
||||
console.log(obj)
|
||||
return obj;
|
||||
},
|
||||
// 子项样式
|
||||
@@ -140,14 +139,12 @@ export default {
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
console.log(121)
|
||||
var data = {
|
||||
num: this.value.count
|
||||
};
|
||||
console.log(this.value)
|
||||
if (this.value.sources == 'diy') {
|
||||
data.num = 0;
|
||||
data.merch_id_arr = this.value.merchIds.toString();
|
||||
|
||||
@@ -75,8 +75,6 @@ export default {
|
||||
// 允许从相机和相册扫码,h5端不起作用
|
||||
uni.scanCode({
|
||||
success: function (res) {
|
||||
console.log('条码类型:' + res.scanType);
|
||||
console.log('条码内容:' + res.result);
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<template v-for="(item, index) in list">
|
||||
<template v-if="index > 0">
|
||||
<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' }">
|
||||
:style="{ marginBottom: index < list.length - 1 ? value.imageGap * 2 + 'rpx' : 0, width: item.imgWidth, height: item.imgHeight + 'px' }">
|
||||
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
|
||||
:style="item.pageItemStyle" show-menu-by-longpress />
|
||||
</view>
|
||||
@@ -235,8 +235,6 @@ export default {
|
||||
*/
|
||||
calcRowOneLeftOfTwoRight() {
|
||||
let rightHeight = 0; // 右侧两图平分高度
|
||||
let divide = 'left'; // 划分规则,left:左,right:右
|
||||
if (this.list[1].imgWidth === this.list[2].imgWidth) divide = 'right';
|
||||
uni.getSystemInfo({
|
||||
success: res => {
|
||||
this.list.forEach((item, index) => {
|
||||
@@ -246,12 +244,14 @@ export default {
|
||||
item.imgWidth = item.imgWidth / 2;
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
rightHeight = (item.imgHeight - uni.upx2px(this.value.imageGap * 2)) / 2;
|
||||
item.imgWidth += 'px';
|
||||
} else {
|
||||
item.imgWidth = this.list[0].imgWidth;
|
||||
item.imgHeight = rightHeight;
|
||||
}
|
||||
});
|
||||
// 统一追加 px 单位
|
||||
this.list.forEach((item) => {
|
||||
item.imgWidth += 'px';
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -311,7 +311,6 @@ export default {
|
||||
|
||||
this.list.forEach((item, index) => {
|
||||
item.imgWidth += 'px';
|
||||
item.imgHeight;
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -482,7 +481,7 @@ export default {
|
||||
// 一左两右
|
||||
.rubik-cube .template-left,
|
||||
.rubik-cube .template-right {
|
||||
// width: 50%;
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
@@ -221,10 +221,7 @@ export default {
|
||||
|
||||
// 组件创建钩子
|
||||
created() {
|
||||
console.log(`diy-tab-create`, {
|
||||
value: this.mergedValue,
|
||||
tabs: this.mergedValue.tabs
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
// 组件挂载钩子
|
||||
|
||||
@@ -114,7 +114,6 @@ export default {
|
||||
style += 'box-shadow:' + (this.value.ornament.type == 'shadow' ? '0 0 10rpx ' + this.value.ornament.color : '') + ';';
|
||||
style += 'border:' + (this.value.ornament.type == 'stroke' ? '2rpx solid ' + this.value.ornament.color : '') + ';';
|
||||
|
||||
console.log(this.value);
|
||||
|
||||
return style;
|
||||
},
|
||||
@@ -174,7 +173,6 @@ export default {
|
||||
// 图片加载错误处理
|
||||
imgError(index) {
|
||||
// 图片加载失败的处理逻辑
|
||||
console.log('图片加载失败:', index);
|
||||
},
|
||||
|
||||
// 弹窗视频加载完成事件
|
||||
@@ -202,7 +200,6 @@ export default {
|
||||
|
||||
if (width && height) {
|
||||
this.popupVideoRatio = width >= height ? 'landscape' : 'portrait';
|
||||
console.log(`弹窗视频宽高比检测: ${width}x${height}, 比例: ${this.popupVideoRatio}`);
|
||||
} else {
|
||||
// 获取失败,默认竖屏
|
||||
this.popupVideoRatio = 'portrait';
|
||||
@@ -233,10 +230,6 @@ export default {
|
||||
width: calc(33.3333333% - 14rpx);
|
||||
box-sizing: border-box;
|
||||
|
||||
&:nth-child(3n + 3) {
|
||||
width: calc(33.33% - 15rpx);
|
||||
}
|
||||
|
||||
&:nth-of-type(1),
|
||||
&:nth-of-type(2),
|
||||
&:nth-of-type(3) {
|
||||
|
||||
@@ -127,8 +127,6 @@ export default {
|
||||
const videoUrl = this.videoUrl;
|
||||
if (!videoUrl) return;
|
||||
|
||||
console.log('H5端预加载视频获取比例:', videoUrl);
|
||||
|
||||
const video = document.createElement('video');
|
||||
video.preload = 'metadata';
|
||||
video.src = videoUrl;
|
||||
@@ -138,11 +136,9 @@ export default {
|
||||
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 ? '横屏' : '竖屏');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -156,8 +152,6 @@ export default {
|
||||
|
||||
// 视频加载完成事件(组件内video标签)
|
||||
onVideoLoaded(event) {
|
||||
console.log('视频组件 loadedmetadata 事件触发');
|
||||
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
|
||||
@@ -165,7 +159,6 @@ export default {
|
||||
if (event && event.target) {
|
||||
width = event.target.videoWidth || 0;
|
||||
height = event.target.videoHeight || 0;
|
||||
console.log('H5端组件视频宽高:', width, height);
|
||||
}
|
||||
// #endif
|
||||
|
||||
@@ -173,24 +166,20 @@ export default {
|
||||
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('视频暂停');
|
||||
},
|
||||
|
||||
// 视频错误事件
|
||||
|
||||
@@ -149,9 +149,8 @@ export const wechatChannelUtil = {
|
||||
wx.openChannelsActivity({
|
||||
feedId: item.feedId,
|
||||
finderUserName: item.finderUserName,
|
||||
success: (res) => {
|
||||
console.log('打开视频号成功', res);
|
||||
resolve(res);
|
||||
success: (res) => {
|
||||
resolve(res);
|
||||
},
|
||||
fail: (err) => {
|
||||
this.handleError(err, item);
|
||||
|
||||
@@ -2,7 +2,6 @@ export default {
|
||||
methods: {
|
||||
// 异步触发事件
|
||||
async __$emitEvent(payload = {eventName: '__unnamedEvent', data: {}, promiseCallback: null}) {
|
||||
// console.log('__$emitEvent', payload)
|
||||
await this.$eventBus.emit(payload.eventName, payload.data, payload.promiseCallback)
|
||||
},
|
||||
|
||||
|
||||
@@ -186,7 +186,6 @@ export default {
|
||||
uni.getStorage({
|
||||
key: 'shopInfo',
|
||||
success: (e) => {
|
||||
console.log('【调试】当前 shopInfo:', e.data);
|
||||
this.shopInfo = e.data;
|
||||
},
|
||||
fail: () => {
|
||||
|
||||
@@ -53,40 +53,29 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 检查配置,决定是否显示隐私协议
|
||||
const shopInfo = uni.getStorageSync('shopInfo');
|
||||
if (shopInfo?.ischina == 1 && shopInfo?.index_login_auth == 1) {
|
||||
this.listenPrivacySettingChange()
|
||||
this.curPageShow()
|
||||
} else {
|
||||
// 如果不需要显示隐私协议,直接触发同意事件
|
||||
this.$emit('agree');
|
||||
}
|
||||
this.listenPrivacySettingChange()
|
||||
this.curPageShow()
|
||||
this.proactive()
|
||||
},
|
||||
created() {
|
||||
try {
|
||||
// #ifdef MP-WEIXIN
|
||||
//查询微信侧记录的用户是否有待同意的隐私政策信息
|
||||
wx.getPrivacySetting({
|
||||
success: (res) => {
|
||||
// console.log('隐私政策信息', res);
|
||||
// console.log(res.privacyContractName);
|
||||
this.privacyContractName = res.privacyContractName
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef QUICKAPP-WEBVIEW || H5
|
||||
if (this.$util.isQuickApp()) {
|
||||
if (typeof has != 'undefined' && has?.getPrivacySetting) {
|
||||
has.getPrivacySetting({
|
||||
success: (res) => {
|
||||
if (res.privacyContractName) {
|
||||
this.privacyContractName = res.privacyContractName
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
if (this.$util.isQuickApp() && typeof has != 'undefined' && has?.getPrivacySetting) {
|
||||
has.getPrivacySetting({
|
||||
success: (res) => {
|
||||
if (res.privacyContractName) {
|
||||
this.privacyContractName = res.privacyContractName
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
} catch (e) {
|
||||
@@ -94,39 +83,50 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 统一弹窗 + 关闭其他页面的弹窗
|
||||
*/
|
||||
showPrivacyPopup() {
|
||||
this.popUp()
|
||||
this.closeOtherPagePopUp(this.disPopUp)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据是否需要授权决定弹窗或 emit agree
|
||||
*/
|
||||
checkAndShow(needAuthorization) {
|
||||
if (needAuthorization) {
|
||||
const privacyAgreed = uni.getStorageSync('privacyAgreed');
|
||||
if (privacyAgreed) {
|
||||
this.$emit('agree')
|
||||
} else {
|
||||
this.showPrivacyPopup()
|
||||
}
|
||||
} else {
|
||||
this.$emit('agree')
|
||||
}
|
||||
},
|
||||
|
||||
getPrivacySettingByQuickApp() {
|
||||
// #ifdef QUICKAPP-WEBVIEW || H5 || WEB
|
||||
if (this.$util.isQuickApp()) {
|
||||
if (typeof has != 'undefined' && has?.getPrivacySetting) {
|
||||
has.getPrivacySetting({
|
||||
success: (res) => {
|
||||
// console.log(res)
|
||||
if (res.needAuthorization) {
|
||||
this.popUp()
|
||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||
this.closeOtherPagePopUp(this.disPopUp)
|
||||
} else {
|
||||
this.$emit('agree')
|
||||
}
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// 属于嵌入到快应用Webview组件中的情况,按照H5的方式来处理,判断是否已经授权
|
||||
if (uni.getStorageSync('privacyAgreed')) {
|
||||
this.$emit('agree')
|
||||
} else {
|
||||
this.popUp()
|
||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||
this.closeOtherPagePopUp(this.disPopUp)
|
||||
}
|
||||
}
|
||||
if (!this.$util.isQuickApp()) return;
|
||||
if (typeof has != 'undefined' && has?.getPrivacySetting) {
|
||||
has.getPrivacySetting({
|
||||
success: (res) => {
|
||||
this.checkAndShow(res.needAuthorization)
|
||||
},
|
||||
})
|
||||
} else {
|
||||
this.checkAndShow(!uni.getStorageSync('privacyAgreed'))
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听平台侧的隐私授权事件
|
||||
*/
|
||||
listenPrivacySettingChange() {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 监听微信侧隐私政策授权变化事件
|
||||
if (wx.onNeedPrivacyAuthorization) {
|
||||
wx.onNeedPrivacyAuthorization((resolve) => {
|
||||
if (typeof this.privacyAuthorization === 'function') {
|
||||
@@ -137,66 +137,28 @@ export default {
|
||||
// #endif
|
||||
|
||||
// #ifdef QUICKAPP-WEBVIEW || H5 || WEB
|
||||
if (this.$util.isQuickApp()) {
|
||||
// 监听快速应用侧隐私政策授权变化事件
|
||||
if (typeof has != 'undefined' && has?.onPrivacySettingChange) {
|
||||
has.onPrivacySettingChange((res) => {
|
||||
// console.log(res)
|
||||
if (res.needAuthorization) {
|
||||
this.popUp()
|
||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||
this.closeOtherPagePopUp(this.disPopUp)
|
||||
} else {
|
||||
this.$emit('agree')
|
||||
}
|
||||
})
|
||||
}
|
||||
if (this.$util.isQuickApp() && typeof has != 'undefined' && has?.onPrivacySettingChange) {
|
||||
has.onPrivacySettingChange((res) => {
|
||||
this.checkAndShow(res.needAuthorization)
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 主动查询用户隐私政策授权状态,针对快速应用
|
||||
this.getPrivacySettingByQuickApp();
|
||||
},
|
||||
// 主动查询用户隐私政策授权状态
|
||||
proactive() {
|
||||
// 检查配置,决定是否显示隐私协议
|
||||
const shopInfo = uni.getStorageSync('shopInfo');
|
||||
if (!(shopInfo?.ischina == 1 && shopInfo?.index_login_auth == 1)) {
|
||||
// 如果不需要显示隐私协议,直接触发同意事件
|
||||
this.$emit('agree');
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动查询隐私授权状态并弹窗
|
||||
*/
|
||||
proactive() {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (wx.getPrivacySetting) {
|
||||
wx.getPrivacySetting({
|
||||
success: (res) => {
|
||||
// console.log(res)
|
||||
if (res.needAuthorization) {
|
||||
// 检查本地存储中是否已经同意过隐私政策
|
||||
const privacyAgreed = uni.getStorageSync('privacyAgreed');
|
||||
if (!privacyAgreed) {
|
||||
this.popUp()
|
||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||
this.closeOtherPagePopUp(this.disPopUp)
|
||||
} else {
|
||||
this.$emit('agree')
|
||||
}
|
||||
} else {
|
||||
this.$emit('agree')
|
||||
}
|
||||
this.checkAndShow(res.needAuthorization)
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// 检查本地存储中是否已经同意过隐私政策
|
||||
const privacyAgreed = uni.getStorageSync('privacyAgreed');
|
||||
if (privacyAgreed) {
|
||||
this.$emit('agree');
|
||||
} else {
|
||||
this.popUp()
|
||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||
this.closeOtherPagePopUp(this.disPopUp)
|
||||
}
|
||||
// 低版本基础库直接检查本地存储
|
||||
this.checkAndShow(!uni.getStorageSync('privacyAgreed'))
|
||||
}
|
||||
// #endif
|
||||
|
||||
@@ -205,29 +167,24 @@ export default {
|
||||
// #endif
|
||||
|
||||
// #ifdef H5 || WEB
|
||||
// 检查本地存储中是否已经同意过隐私政策
|
||||
const privacyAgreed = uni.getStorageSync('privacyAgreed');
|
||||
if (privacyAgreed) {
|
||||
this.$emit('agree');
|
||||
} else {
|
||||
this.popUp()
|
||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||
this.closeOtherPagePopUp(this.disPopUp)
|
||||
}
|
||||
this.checkAndShow(!uni.getStorageSync('privacyAgreed'))
|
||||
// #endif
|
||||
},
|
||||
//初始化监听程序
|
||||
|
||||
/**
|
||||
* 初始化监听程序(页面级)
|
||||
*/
|
||||
curPageShow() {
|
||||
closeOtherPagePopUpHooks.add(this.disPopUp)
|
||||
this.privacyAuthorization = resolve => {
|
||||
this.privacyResolves.add(resolve)
|
||||
//打开弹窗
|
||||
this.popUp()
|
||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||
this.closeOtherPagePopUp(this.disPopUp)
|
||||
this.showPrivacyPopup()
|
||||
}
|
||||
},
|
||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||
|
||||
/**
|
||||
* 弹窗时关掉其他页面的隐私弹窗,避免重叠
|
||||
*/
|
||||
closeOtherPagePopUp(closePopUp) {
|
||||
closeOtherPagePopUpHooks.forEach(hook => {
|
||||
if (closePopUp !== hook) {
|
||||
@@ -235,16 +192,12 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
//打开隐私协议
|
||||
|
||||
openPrivacyContract() {
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.openPrivacyContract({
|
||||
success(res) {
|
||||
// console.log('打开隐私协议', res);
|
||||
},
|
||||
fail(err) {
|
||||
// console.error('打开隐私协议失败', err)
|
||||
}
|
||||
success(res) {},
|
||||
fail(err) {}
|
||||
});
|
||||
// #endif
|
||||
|
||||
@@ -252,59 +205,44 @@ export default {
|
||||
if (this.$util.isQuickApp()) {
|
||||
if (typeof has != 'undefined' && has?.openPrivacySetting) {
|
||||
has.openPrivacySetting({
|
||||
success: (res) => {
|
||||
// console.log('打开隐私协议', res);
|
||||
},
|
||||
fail: (err) => {
|
||||
// console.error('打开隐私协议失败', err)
|
||||
}
|
||||
success: (res) => {},
|
||||
fail: (err) => {}
|
||||
});
|
||||
} else {
|
||||
// 属于嵌入到快应用Webview组件中的情况,按照H5的方式来处理
|
||||
this.$util.redirectTo(privacyContractPage);
|
||||
}
|
||||
} else {
|
||||
// H5 环境下的处理逻辑
|
||||
this.$util.redirectTo(privacyContractPage);
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
//打开用户服务协议
|
||||
|
||||
openPrivacyService() {
|
||||
// #ifdef QUICKAPP-WEBVIEW || H5 || WEB
|
||||
if (this.$util.isQuickApp()) {
|
||||
if (typeof has != 'undefined' && has?.openPrivacySetting) {
|
||||
has.openPrivacySetting({
|
||||
success: (res) => {
|
||||
// console.log('打开用户服务协议', res);
|
||||
},
|
||||
fail: (err) => {
|
||||
// console.error('打开用户服务协议失败', err)
|
||||
}
|
||||
success: (res) => {},
|
||||
fail: (err) => {}
|
||||
});
|
||||
} else {
|
||||
// 属于嵌入到快应用Webview组件中的情况,按照H5的方式来处理
|
||||
this.$util.redirectTo(privacyServicePage);
|
||||
}
|
||||
} else {
|
||||
// H5 环境下的处理逻辑
|
||||
this.$util.redirectTo(privacyServicePage);
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
// 不同意
|
||||
|
||||
handleDisagree() {
|
||||
this.privacyResolves.forEach(resolve => {
|
||||
resolve({
|
||||
event: 'disagree',
|
||||
})
|
||||
resolve({ event: 'disagree' })
|
||||
})
|
||||
this.privacyResolves.clear()
|
||||
//关闭弹窗
|
||||
this.disPopUp()
|
||||
// 保存用户授权状态到本地存储,无论同意还是不同意,都不再重复弹出
|
||||
|
||||
uni.setStorageSync('privacyAgreed', true);
|
||||
//退出小程序
|
||||
|
||||
uni.showModal({
|
||||
content: '未同意隐私协议,无法使用相关功能',
|
||||
success: () => {
|
||||
@@ -312,7 +250,7 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
// 同意并继续
|
||||
|
||||
handleAgree() {
|
||||
this.privacyResolves.forEach(resolve => {
|
||||
resolve({
|
||||
@@ -321,28 +259,23 @@ export default {
|
||||
})
|
||||
})
|
||||
this.privacyResolves.clear()
|
||||
//关闭弹窗
|
||||
this.disPopUp()
|
||||
|
||||
// 保存用户授权状态到本地存储,用于所有环境
|
||||
uni.setStorageSync('privacyAgreed', true);
|
||||
uni.setStorageSync('privacyAgreed', true);
|
||||
this.$emit('agree')
|
||||
},
|
||||
//打开弹窗
|
||||
|
||||
popUp() {
|
||||
this.showPop = true;
|
||||
},
|
||||
//关闭弹窗
|
||||
|
||||
disPopUp() {
|
||||
this.showPop = false;
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 清理事件监听器和集合
|
||||
this.privacyResolves.clear()
|
||||
closeOtherPagePopUpHooks.delete(this.disPopUp)
|
||||
// 注意:这里需要根据实际情况清理微信和快速应用的事件监听器
|
||||
// 由于微信的 wx.onNeedPrivacyAuthorization 没有对应的 off 方法,这里可能需要其他方式处理
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -89,7 +89,6 @@ export default {
|
||||
}
|
||||
},
|
||||
endVal() {
|
||||
console.log(123)
|
||||
if (this.autoplay) {
|
||||
this.start()
|
||||
}
|
||||
|
||||
@@ -164,7 +164,6 @@
|
||||
appId: 'wxeb490c6f9b154ef9', // 企业微信小程序AppID
|
||||
path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(contactUrl)}`,
|
||||
success: () => {
|
||||
console.log('跳转企业微信成功');
|
||||
this.$util.showToast({
|
||||
title: '跳转成功',
|
||||
icon: 'success'
|
||||
@@ -189,7 +188,6 @@
|
||||
this.wxWorkSDK.addContact({
|
||||
userId: contactId
|
||||
}, (res) => {
|
||||
console.log('打开用户资料成功:', res);
|
||||
}, (err) => {
|
||||
console.error('打开用户资料失败:', err);
|
||||
this.showError('打开用户资料失败');
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
# 华为快应用 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. **登录降级**:如果华为帐号服务不可用,建议提供短信验证码或其他登录方式作为降级方案
|
||||
@@ -107,7 +107,7 @@
|
||||
},
|
||||
"router" : {
|
||||
"mode" : "history",
|
||||
"base" : "/hwappx/test/"
|
||||
"base" : "/hwappx/common/"
|
||||
},
|
||||
"title" : "",
|
||||
"devServer" : {
|
||||
|
||||
25
package.json
25
package.json
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"scripts": {
|
||||
"mp-weixin": "node scripts/mp-weixin.patch.js",
|
||||
"mp-weixin:patch": "node scripts/mp-weixin.patch.js --no-zip",
|
||||
"mp-weixin:dev": "node scripts/mp-weixin.patch.js --mode development",
|
||||
"mp-weixin:dev:patch": "node scripts/mp-weixin.patch.js --mode development --no-zip"
|
||||
"mp-weixin": "node scripts/mp-weixin.patch.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dart-sass": "^1.25.0",
|
||||
@@ -28,44 +25,40 @@
|
||||
"h5-xcx20.5g-quickapp.com": {
|
||||
"title": "H5-xcx20.5g-quickapp.com",
|
||||
"env": {
|
||||
"UNI_PLATFORM": "h5"
|
||||
"UNI_PLATFORM": "h5",
|
||||
"UNI_APP_DOMAIN": "https://xcx20.5g-quickapp.com/"
|
||||
},
|
||||
"define": {
|
||||
"H5_XCX_5G_QUICKAPP_COM": true,
|
||||
"H5_PRODUCTION": true,
|
||||
"PRODUCTION": true
|
||||
}
|
||||
},
|
||||
"h5-xcx.aigc-quickapp.com": {
|
||||
"title": "H5-xcx.aigc-quickapp.com",
|
||||
"env": {
|
||||
"UNI_PLATFORM": "h5"
|
||||
"UNI_PLATFORM": "h5",
|
||||
"UNI_APP_DOMAIN": "https://xcx.aigc-quickapp.com/"
|
||||
},
|
||||
"define": {
|
||||
"H5_XCX_AIGC_QUICKAPP_COM": true,
|
||||
"H5_PRODUCTION": true,
|
||||
"PRODUCTION": true
|
||||
}
|
||||
},
|
||||
"h5-5g.aigc-quickapp.com": {
|
||||
"title": "H5-5g.aigc-quickapp.com",
|
||||
"env": {
|
||||
"UNI_PLATFORM": "h5"
|
||||
"UNI_PLATFORM": "h5",
|
||||
"UNI_APP_DOMAIN": "https://5g.aigc-quickapp.com/"
|
||||
},
|
||||
"define": {
|
||||
"H5_5G_AIGC_QUICKAPP_COM": true,
|
||||
"H5_PRODUCTION": true,
|
||||
"PRODUCTION": true
|
||||
}
|
||||
},
|
||||
"h5-test.aigc-quickapp.com": {
|
||||
"title": "H5-test.aigc-quickapp.com",
|
||||
"env": {
|
||||
"UNI_PLATFORM": "h5"
|
||||
"UNI_PLATFORM": "h5",
|
||||
"UNI_APP_DOMAIN": "https://test.aigc-quickapp.com/"
|
||||
},
|
||||
"define": {
|
||||
"H5_TEST_AIGC_QUICKAPP_COM": true,
|
||||
"H5_PRODUCTION": true,
|
||||
"PRODUCTION": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@
|
||||
{
|
||||
"path": "ai-chat/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "AI 客服"
|
||||
"navigationBarTitleText": "AI智能客服"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<image :src="$util.img(siteInfo.logo_square)" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="info-box" :style="{ color: '#ffffff' }">
|
||||
<text class="font-size-base">{{ siteInfo.site_name }}</text>
|
||||
<text class="font-size-base" @tap="onTitleTap">{{ siteInfo.site_name }}</text>
|
||||
<text>{{ followOfficialAccount.welcomeMsg }}</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -141,6 +141,8 @@
|
||||
<privacy-popup ref="privacyPopup"></privacy-popup>
|
||||
<to-top v-if="showTop" @toTop="scrollToTopNative()"></to-top>
|
||||
<ns-login ref="login"></ns-login>
|
||||
<!-- 版本信息触发区(固定定位,不可见,始终可点击) -->
|
||||
<view class="version-trigger" @tap="onTitleTap"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -243,4 +245,14 @@ export default {
|
||||
bottom: 180rpx !important;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.version-trigger {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 50rpx;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
z-index: 999;
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
@@ -87,12 +87,43 @@ export default {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 打开登录弹窗
|
||||
// console.log('==> _handleDiyGroupInteractionEvent, 未登录,打开登录弹窗', { payload })
|
||||
this.$refs.login?.open();
|
||||
|
||||
// 阻止事件继续传播
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* 标题连击 5 次显示构建版本信息(供测试人员使用)
|
||||
*/
|
||||
onTitleTap() {
|
||||
this._titleTapCount = (this._titleTapCount || 0) + 1;
|
||||
if (this._titleTapTimer) {
|
||||
clearTimeout(this._titleTapTimer);
|
||||
}
|
||||
if (this._titleTapCount >= 5) {
|
||||
this._titleTapCount = 0;
|
||||
this._showVersionInfo();
|
||||
return;
|
||||
}
|
||||
// 2 秒内未达到 5 次则重置计数
|
||||
this._titleTapTimer = setTimeout(() => {
|
||||
this._titleTapCount = 0;
|
||||
}, 2000);
|
||||
},
|
||||
_showVersionInfo() {
|
||||
let v = null;
|
||||
try {
|
||||
v = typeof __app_version !== 'undefined' ? __app_version : null;
|
||||
} catch (e) {}
|
||||
if (v && v.commitId) {
|
||||
uni.showModal({
|
||||
title: '构建版本信息',
|
||||
content: `提交 ID: ${v.commitId}\n构建时间: ${v.buildTime}`,
|
||||
showCancel: false
|
||||
});
|
||||
} else {
|
||||
console.log('未获取到构建版本信息');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -413,10 +413,8 @@ export default {
|
||||
}
|
||||
},
|
||||
success: function (res) {
|
||||
console.log('success', res);
|
||||
},
|
||||
fail: function (res) {
|
||||
console.log('fail', res);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -151,8 +151,7 @@ export default {
|
||||
// #endif
|
||||
}, this.wait);
|
||||
|
||||
// console.log('this.top', this.top);
|
||||
// console.log('this.radius', this.radius);
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
this.animationData = uni
|
||||
|
||||
@@ -566,7 +566,6 @@ export default {
|
||||
// 手指触摸事件 用于菜单左滑
|
||||
touchS(e) {
|
||||
this.startX = e.touches[0].clientX;
|
||||
// console.log('开始' + e.touches[0].clientX);
|
||||
},
|
||||
touchE(e, cartIndex) {
|
||||
this.endX = e.changedTouches[0].clientX;
|
||||
|
||||
@@ -72,7 +72,6 @@ export default {
|
||||
url: '/api/lucky/status',
|
||||
data: {},
|
||||
success: res => {
|
||||
console.log(res)
|
||||
this.langstatus = res.status
|
||||
if(this.langstatus == 1){
|
||||
// this.$langConfig.refresh();
|
||||
|
||||
@@ -762,13 +762,10 @@ export default {
|
||||
}
|
||||
this.$forceUpdate();
|
||||
// this.orderCreateData.coupon[merch_id].coupon_id = data.coupon_id
|
||||
console.log(this.selectCouponId)
|
||||
},
|
||||
//确定使用优惠券
|
||||
useCpopon() {
|
||||
console.log(this.merchCoupon.merch_id)
|
||||
this.orderCreateData.coupon[this.merchCoupon.merch_id].coupon_id = this.selectCouponId;
|
||||
console.log(this.orderCreateData.coupon)
|
||||
this.$refs.couponPopup.close();
|
||||
this.calculate();
|
||||
},
|
||||
|
||||
@@ -137,7 +137,6 @@ export default {
|
||||
}
|
||||
},
|
||||
fail: function(res) {
|
||||
console.log('fail', res);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -359,9 +359,8 @@ export default {
|
||||
getFenxiaoDetail() {
|
||||
this.$api.sendRequest({
|
||||
url: '/fenxiao/api/fenxiao/detail',
|
||||
success: res => {
|
||||
console.log(res)
|
||||
if (res.data) {
|
||||
success: res => {
|
||||
if (res.data) {
|
||||
if (res.data.status == -1) {
|
||||
this.$util.showToast({
|
||||
title: '当前分销商已冻结'
|
||||
|
||||
@@ -136,12 +136,10 @@ export default {
|
||||
image_list: this.$util.img(this.goodsList[this.currIndex].goods_image)
|
||||
}
|
||||
},
|
||||
success: function (res) {
|
||||
console.log('success', res);
|
||||
},
|
||||
fail: function (res) {
|
||||
console.log('fail', res);
|
||||
}
|
||||
success: function (res) {
|
||||
},
|
||||
fail: function (res) {
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -312,9 +312,8 @@
|
||||
if (res.code == 0 && res.data.length) {
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: res.data,
|
||||
fail: res => {
|
||||
console.log('fail', res);
|
||||
},
|
||||
fail: res => {
|
||||
},
|
||||
complete: () => {
|
||||
callback();
|
||||
}
|
||||
|
||||
@@ -118,7 +118,6 @@
|
||||
this.categoryId = 'category-' + index;
|
||||
// 阻止切换分类之后滚动事件也立即执行
|
||||
this.scrollLock = true;
|
||||
console.log(index)
|
||||
|
||||
// this.goodsList = []
|
||||
// this.getgoodslist()
|
||||
@@ -174,9 +173,8 @@
|
||||
data: {
|
||||
is_type_show:1,
|
||||
},
|
||||
success: res => {
|
||||
console.log( res.data)
|
||||
this.categoryTree = res.data
|
||||
success: res => {
|
||||
this.categoryTree = res.data
|
||||
},
|
||||
fail: () => {
|
||||
this.$util.showToast({
|
||||
|
||||
@@ -164,11 +164,8 @@ export default {
|
||||
id:this.goodsData.id
|
||||
},
|
||||
success: res => {
|
||||
console.log(res)
|
||||
if(res.result.detail){
|
||||
this.goodsData = res.result.detail
|
||||
console.log(this.goodsData.goods_image)
|
||||
}else{
|
||||
this.$util.showToast({
|
||||
title: '商品不存在',
|
||||
});
|
||||
@@ -737,7 +734,6 @@ export default {
|
||||
})
|
||||
|
||||
// 清空规格的图片
|
||||
console.log(this.goodsData)
|
||||
return false
|
||||
for (var i = 0; i < this.goodsData.goods_sku_data.length; i++) {
|
||||
if (this.goodsData.goods_sku_data[i].sku_images.length == 0) this.goodsData.goods_sku_data[i].sku_image = '';
|
||||
@@ -796,9 +792,6 @@ export default {
|
||||
} else if (this.goodsData.verify_validity_type == 2) {
|
||||
data.virtual_indate = this.virtualTime;
|
||||
}
|
||||
|
||||
// console.log(this.goodsData.goods_spec_format,'format')
|
||||
// if(this.goodsData.goods_spec_format == '[]'){
|
||||
// data.carmichael= data.carmichael
|
||||
// }
|
||||
var save = null;
|
||||
|
||||
@@ -150,9 +150,8 @@ export default {
|
||||
data: {
|
||||
merch_id:this.merch_id
|
||||
},
|
||||
success: res => {
|
||||
console.log(res)
|
||||
this.merch = res.data
|
||||
success: res => {
|
||||
this.merch = res.data
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -54,9 +54,8 @@ export default {
|
||||
this.$api.sendRequest({
|
||||
url: '/api/config/agreement',
|
||||
data: data,
|
||||
success: res => {
|
||||
console.log(res.data.title)
|
||||
uni.setNavigationBarTitle({
|
||||
success: res => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: res.data.title
|
||||
})
|
||||
this.content = res.data.content
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,19 @@
|
||||
<template>
|
||||
<view class="container" :style="wrapperPageStyle">
|
||||
<!--自定义导航头部 - 只在微信小程序显示,H5端隐藏 -->
|
||||
<view class="wrapper">
|
||||
<!--自定义导航头部 - 绝对定位在顶部,不随键盘上移 -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="custom-navbar" ref="pageHeader" v-if="showCustomNavbar">
|
||||
<view class="header-left">
|
||||
<button class="back-btn" v-show="showBackButton" @click="goBack">
|
||||
<text class="iconfont icon-back"></text>
|
||||
</button>
|
||||
<!-- 占位元素,确保布局平衡 -->
|
||||
<view v-show="!showMenuButton" class="placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 聊天内容区域 -->
|
||||
<view class="chat-content"
|
||||
:style="wrapperChatContentStyle"
|
||||
>
|
||||
<!-- AI聊天组件 -->
|
||||
<!-- 聊天内容区域:从导航栏下方到容器底部 -->
|
||||
<view class="chat-content">
|
||||
<ai-chat-message
|
||||
ref="chat"
|
||||
:initial-messages="initialMessages"
|
||||
@@ -131,37 +127,6 @@ export default {
|
||||
return this.globalAIKefuConfig?.aiAvatar || this.$util.img(IMAGE_CONFIG.aiAvatar)
|
||||
},
|
||||
/// ---- others ----
|
||||
containerHeight() {
|
||||
return `calc(100vh - ${this.navBarHeight + this.statusBarHeight}px)`
|
||||
},
|
||||
wrapperPageStyle() {
|
||||
// #ifdef H5
|
||||
return `top: $ {this.navBarHeight + 'px'};`
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
return `top: -1px;` // 微信小程序需要上移1px, 否则与系统导航栏出现1px的空隙
|
||||
// #endif
|
||||
|
||||
return ``
|
||||
},
|
||||
wrapperChatContentStyle() {
|
||||
// #ifdef H5
|
||||
// H5端没有自定义导航栏,不需要paddingTop
|
||||
return {
|
||||
height: this.containerHeight,
|
||||
paddingTop: '0'
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序端根据导航栏显示情况决定
|
||||
return {
|
||||
height: this.containerHeight,
|
||||
paddingTop: this.showCustomNavbar ? '0' : (this.navBarHeight + 'px')
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
|
||||
async onLoad(options) {
|
||||
@@ -183,7 +148,6 @@ export default {
|
||||
onShow() {
|
||||
// 监听键盘高度变化
|
||||
uni.onKeyboardHeightChange((res) => {
|
||||
console.log('键盘高度变化:', res.height);
|
||||
this.keyboardHeight = res.height;
|
||||
if (this.$refs.chat && this.$refs.chat.updateKeyboardHeight) {
|
||||
this.$refs.chat.updateKeyboardHeight(res.height);
|
||||
@@ -233,7 +197,6 @@ export default {
|
||||
|
||||
setupNavigationEvents() {
|
||||
uni.onWindowResize((res) => {
|
||||
console.log('窗口大小变化:', res.size);
|
||||
this.calculateScrollViewHeight();
|
||||
});
|
||||
},
|
||||
@@ -241,8 +204,6 @@ export default {
|
||||
// ========== 事件处理方法 ==========
|
||||
|
||||
async handleServiceRequest(event) {
|
||||
console.log('处理服务请求:', EventSafety.extractEventData(event));
|
||||
|
||||
if (event.matches('.service-component') ||
|
||||
event.detail?.componentType === 'service') {
|
||||
await this.processServiceRequest(event);
|
||||
@@ -250,12 +211,10 @@ export default {
|
||||
},
|
||||
|
||||
async handleNavigationRequest(event) {
|
||||
console.log('处理导航请求:', event.type);
|
||||
this.emitNavigationInfo(event);
|
||||
},
|
||||
|
||||
handleComponentInteraction(event) {
|
||||
console.log('处理组件交互:', event.detail);
|
||||
this.processComponentInteraction(event);
|
||||
},
|
||||
|
||||
@@ -271,16 +230,11 @@ export default {
|
||||
|
||||
// ========== 初始化页面 ==========
|
||||
initChat() {
|
||||
console.log('AI聊天页面初始化');
|
||||
|
||||
// 检查是否有从商品分类传递过来的商品信息
|
||||
const selectedProduct = uni.getStorageSync('selectedProduct')
|
||||
if (selectedProduct) {
|
||||
console.log('检测到选择的商品:', selectedProduct)
|
||||
|
||||
// 清除存储的商品信息,避免重复发送
|
||||
uni.removeStorageSync('selectedProduct')
|
||||
|
||||
// 延迟发送商品信息,确保聊天组件已经初始化
|
||||
setTimeout(() => {
|
||||
this.sendProductToAI(selectedProduct)
|
||||
@@ -329,7 +283,6 @@ export default {
|
||||
this. $off(eventType, handler);
|
||||
});
|
||||
this.safeEventHandlers.clear();
|
||||
console.log('组件清理完成');
|
||||
},
|
||||
|
||||
showError(message) {
|
||||
@@ -443,11 +396,9 @@ export default {
|
||||
},
|
||||
|
||||
onMessageSent(message) {
|
||||
console.log('用户发送消息:', message);
|
||||
},
|
||||
|
||||
onAIResponse(message) {
|
||||
console.log('AI回复消息:', message);
|
||||
},
|
||||
|
||||
generateAIResponse(userMessage) {
|
||||
@@ -489,7 +440,6 @@ export default {
|
||||
},
|
||||
|
||||
onHistoryLoaded(messages) {
|
||||
console.log('历史消息加载完成:', messages.length);
|
||||
},
|
||||
|
||||
onFilePreview(message) {
|
||||
@@ -497,19 +447,15 @@ export default {
|
||||
},
|
||||
|
||||
onAudioPlay(message) {
|
||||
console.log('音频播放:', message);
|
||||
},
|
||||
|
||||
onAudioPause(message) {
|
||||
console.log('音频暂停:', message);
|
||||
},
|
||||
|
||||
onVideoPlay(message) {
|
||||
console.log('视频播放:', message);
|
||||
},
|
||||
|
||||
onVideoPause(message) {
|
||||
console.log('视频暂停:', message);
|
||||
},
|
||||
|
||||
sendProductToAI(product) {
|
||||
@@ -520,8 +466,6 @@ export default {
|
||||
|
||||
const productContent = `我想了解以下商品的详细信息:\n商品名称:${product.goods_name}\n价格:¥${product.price}\n商品ID:${product.goods_id}`
|
||||
|
||||
console.log('商品信息已发送到AI客服:', productContent)
|
||||
|
||||
// 使用sendMessageWithContent方法发送商品信息
|
||||
this.$refs.chat.sendMessageWithContent(productContent)
|
||||
},
|
||||
@@ -548,7 +492,6 @@ export default {
|
||||
},
|
||||
|
||||
onInputChange(value) {
|
||||
console.log('输入内容:', value);
|
||||
},
|
||||
|
||||
// ✅ 新增:权限校验(核心修复)
|
||||
@@ -596,20 +539,22 @@ export default {
|
||||
@import url('/common/css/iconfont.css');
|
||||
|
||||
/* 页面样式 */
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: white; /* 白色 */
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 48px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 自定义导航头部 */
|
||||
/* 自定义导航头部:固定在wrapper顶部,不随键盘上移 */
|
||||
.custom-navbar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -689,12 +634,18 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
/* 聊天内容区域 */
|
||||
/* 聊天内容区域:从导航栏下方到容器底部 */
|
||||
.chat-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
/* #ifdef MP-WEIXIN */
|
||||
top: 0;
|
||||
/* #endif */
|
||||
/* #ifndef MP-WEIXIN */
|
||||
top: 44px;
|
||||
/* #endif */
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
/* 底部tabBar占位样式 */
|
||||
|
||||
@@ -130,12 +130,10 @@ export default {
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
},
|
||||
toDetail(item) {
|
||||
console.log(item.files_url);
|
||||
const url = item.files_url;
|
||||
uni.showLoading({ title: "正在加载" });
|
||||
try {
|
||||
@@ -147,7 +145,6 @@ export default {
|
||||
uni.openDocument({
|
||||
filePath: filePath,
|
||||
success: () => {
|
||||
console.log("打开成功");
|
||||
},
|
||||
fail: () => {
|
||||
},
|
||||
@@ -161,14 +158,12 @@ export default {
|
||||
uni.hideLoading();
|
||||
},
|
||||
complete: (res) => {
|
||||
console.log("下载完成", res);
|
||||
if (res.statusCode === 404) {
|
||||
uni.showToast({ title: "文件不存在", icon: "none" });
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("CatchClause", e);
|
||||
uni.hideLoading();
|
||||
}
|
||||
},
|
||||
@@ -196,7 +191,6 @@ export default {
|
||||
uni.openDocument({
|
||||
filePath: filePath,
|
||||
success: () => {
|
||||
console.log("打开成功");
|
||||
},
|
||||
fail: () => {
|
||||
},
|
||||
@@ -210,14 +204,12 @@ export default {
|
||||
uni.hideLoading();
|
||||
},
|
||||
complete: (res) => {
|
||||
console.log("下载完成", res);
|
||||
if (res.statusCode === 404) {
|
||||
uni.showToast({ title: "文件不存在", icon: "none" });
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("CatchClause", e);
|
||||
uni.hideLoading();
|
||||
}
|
||||
},
|
||||
@@ -249,7 +241,6 @@ export default {
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("CatchClause", e);
|
||||
uni.hideLoading();
|
||||
}
|
||||
},
|
||||
@@ -265,12 +256,10 @@ export default {
|
||||
this.$util.showToast({ title: res.message });
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
},
|
||||
change(e) {
|
||||
console.log(e);
|
||||
this.ismessage = e.show ? 1 : 0;
|
||||
},
|
||||
tapMessage() {
|
||||
@@ -285,7 +274,6 @@ export default {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: phone + "",
|
||||
success: (res) => {
|
||||
console.log(res);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -293,11 +281,9 @@ export default {
|
||||
uni.setClipboardData({
|
||||
data: text,
|
||||
success: () => {
|
||||
console.log("复制成功");
|
||||
uni.showToast({ title: "复制成功", icon: "success", duration: 2000 });
|
||||
},
|
||||
fail: () => {
|
||||
console.log("复制失败");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -110,7 +110,6 @@ export default {
|
||||
})
|
||||
},
|
||||
toDetail(item) {
|
||||
console.log(item.files_url)
|
||||
uni.showLoading({
|
||||
title: '正在加载'
|
||||
})
|
||||
@@ -118,12 +117,10 @@ export default {
|
||||
uni.downloadFile({
|
||||
url: item.files_url,
|
||||
success: res => {
|
||||
console.log(res)
|
||||
if (res.statusCode === 200) {
|
||||
uni.openDocument({
|
||||
filePath: res.tempFilePath,
|
||||
success: () => {
|
||||
console.log('打开成功')
|
||||
},
|
||||
fail: () => {
|
||||
},
|
||||
@@ -137,7 +134,6 @@ export default {
|
||||
uni.hideLoading()
|
||||
},
|
||||
complete: res => {
|
||||
console.log('下载完成', res)
|
||||
if (res.statusCode === 404) {
|
||||
uni.showToast({
|
||||
title: '文件不存在',
|
||||
@@ -147,7 +143,6 @@ export default {
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.log('CatchClause', e)
|
||||
uni.hideLoading()
|
||||
}
|
||||
},
|
||||
|
||||
@@ -324,9 +324,8 @@ export default {
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: res => {
|
||||
console.log('fail', res);
|
||||
}
|
||||
fail: res => {
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
|
||||
@@ -265,9 +265,8 @@
|
||||
if (res.code == 0 && res.data.length) {
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: res.data,
|
||||
fail: res => {
|
||||
console.log('fail', res);
|
||||
},
|
||||
fail: res => {
|
||||
},
|
||||
complete: () => {
|
||||
callback();
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@ export default {
|
||||
let msg = res.message;
|
||||
if (res.code == 0 && res.data) {
|
||||
newArr = res.data.list;
|
||||
} else {
|
||||
console.log(`后台服务器返回的信息:${msg}`);
|
||||
}
|
||||
mescroll.endSuccess(newArr.length);
|
||||
//设置列表数据
|
||||
|
||||
@@ -2,7 +2,6 @@ export default {
|
||||
methods: {
|
||||
// 跳转至登录页
|
||||
toLogin(url) {
|
||||
console.log('跳转至登录页', {url, currentRoute: this.$util.getCurrentRoute()});
|
||||
this.$refs.login.open(url ?? this.$util.getCurrentRoute());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +264,6 @@ export default {
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: res.data,
|
||||
fail: (res) => {
|
||||
console.log('fail', res)
|
||||
},
|
||||
complete: ()=> {
|
||||
callback();
|
||||
|
||||
@@ -266,7 +266,6 @@ export default {
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: res.data,
|
||||
fail: (res) => {
|
||||
console.log('fail', res)
|
||||
},
|
||||
complete: ()=> {
|
||||
callback();
|
||||
|
||||
@@ -75,7 +75,6 @@ export default {
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("CatchClause", error);
|
||||
uni.hideLoading();
|
||||
}
|
||||
},
|
||||
@@ -103,7 +102,6 @@ export default {
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err);
|
||||
uni.hideLoading();
|
||||
this.$util.showToast({ title: '下载失败' });
|
||||
}
|
||||
@@ -114,7 +112,6 @@ export default {
|
||||
url: '/api/seal/info',
|
||||
data: { keyword: this.keyword },
|
||||
success: (res) => {
|
||||
console.log(res);
|
||||
this.list = res.value;
|
||||
this.info = res;
|
||||
this.title = res.name;
|
||||
|
||||
@@ -64,7 +64,6 @@ export default {
|
||||
uni.openDocument({
|
||||
filePath: res.tempFilePath,
|
||||
success: (res) => {
|
||||
console.log('打开文档成功');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ export default {
|
||||
this.$api.sendRequest({
|
||||
url: '/api/config/defaultvr',
|
||||
success: res => {
|
||||
console.log(res)
|
||||
if (res.code == 0) {
|
||||
uni.setNavigationBarTitle({
|
||||
title:res.data.title
|
||||
|
||||
119
readme.md
119
readme.md
@@ -40,22 +40,20 @@
|
||||
1. 使用 HBuilderX 打开项目(不用改manifest.json的web,只有H5要改 )
|
||||
2. 选择菜单栏「发行」 → 「小程序-微信」→ 「发行」,等待构建完成
|
||||
⚠️ **注意**:底部控制台弹出"请在微信小程序开发者工具中点击上传"后再执行下一步
|
||||
3. 打开资源管理器→项目根目录,右键选择「在终端中打开」,执行命令:
|
||||
```bash
|
||||
npm run mp-weixin
|
||||
```
|
||||
3. 构建完成后,**自动执行**补丁脚本(拷贝 site.js / config 文件 + 生成 ZIP 包),无需手动操作
|
||||
4. 找到项目根目录 `/unpackage/dist/build` 下生成的 mp-weixin 压缩包
|
||||
💡 **示例**:`mp-weixin-2026-01-23-1769152056146.zip`
|
||||
💡 **示例**:`mp-weixin-2026-05-11-3bff91e.zip`
|
||||
*压缩包文件名末尾为 Git 提交 ID*
|
||||
*同时该目录下会生成未压缩的 `mp-weixin` 目录*
|
||||
|
||||
##### 4.1.1.3 【验证与重命名】
|
||||
1. 打开未压缩的 `mp-weixin` 目录,找到 `site.js` 文件,将文件内的 `uniacid` 值改为当前客户编号(如:2812)并保存
|
||||
2. 打开微信开发者工具,导入「mp-weixin」目录,点击「编译」,在开发者工具控制台验证有无报错,以及能否返回对应客户的业务数据
|
||||
3. 确保无误后将生成的 mp-weixin 压缩包重命名
|
||||
- **定制化版**格式:`定制化-客户编号-域名-mp-weixin-当前日期-生成编号.zip`
|
||||
- **定制化版**格式:`定制化-客户编号-域名-mp-weixin-当前日期-Git提交ID.zip`
|
||||
- **通用版**无需重命名
|
||||
|
||||
💡 **示例**:`custom-2812-xcx.aigc-quickapp.com-mp-weixin-2026-01-22-1769152056146.zip`
|
||||
💡 **示例**:`custom-2812-xcx.aigc-quickapp.com-mp-weixin-2026-05-11-3bff91e.zip`
|
||||
|
||||
🚫 **禁止**:压缩包命名禁止包含 `/ \ : * ? " < > |` 等特殊字符
|
||||
|
||||
@@ -63,10 +61,10 @@
|
||||
1. 将重命名后的文件发送给技术人员
|
||||
*📌通用版直接将生成的 mp-weixin 压缩包发送给技术人员*
|
||||
2. **技术人员操作流程**:
|
||||
- 解压压缩包
|
||||
- 确认 `site.js` 中的 `uniacid` 为客户编号
|
||||
- 用微信开发者工具导入解压后的代码目录
|
||||
- 编译验证无误后,上传发布
|
||||
- 解压压缩包
|
||||
- 确认 `site.js` 中的 `uniacid` 为客户编号
|
||||
- 用微信开发者工具导入解压后的代码目录
|
||||
- 编译验证无误后,上传发布
|
||||
##### 4.1.1.5 【本地设置】
|
||||

|
||||
#####
|
||||
@@ -85,66 +83,93 @@
|
||||
📌 **测试版**保留原有 `/hwappx/test/`
|
||||
💡 **个性化定制示例**:`/hwappx/2811/`,其中 2811 为定制化版客户编号
|
||||
|
||||
3. 选择菜单栏「发行」 → 「自定义发行」 →(「H5-xcx.aigc-quickapp.com」查看.local.config.js中站点的网址前缀,站点的网址前缀是什么就选自定义发行中相同的格式)
|
||||
例如:网址是5g.aigc-quickapp.com,自定义发行就选H5-5g.aigc-quickapp.com
|
||||
网址是dev.aigc-quickapp.com,自定义发行就选H5-xcx.aigc-quickapp.com
|
||||
3. 选择菜单栏「发行」 → 「自定义发行」 → 选择对应的站点脚本
|
||||
💡 例如:网址是 `5g.aigc-quickapp.com` 选 `H5-5g.aigc-quickapp.com`
|
||||
4. 修改以下配置:
|
||||
1. **网站标题**:快应用
|
||||
2. **网站域名**:当前客户域名(示例:xcx.aigc-quickapp.com或者5g.aigc-quickapp.com)
|
||||
2. **网站域名**:当前客户域名
|
||||
3. 确认后点击「发行」等待构建完成
|
||||
|
||||
⚠️ **注意**:底部控制台弹出“项目 lucky_shop 导出Web成功,路径为:D:\项目文件\项目根目录\unpackage\dist\build\web”后再执行下一步
|
||||
💡 **示例**:`项目 lucky_shop 导出Web成功,路径为:D:\0.项目源码\lucky_shop\unpackage\dist\build\web`
|
||||
5. 按控制台提示的路径找到 `web` 目录,将该目录下所有文件手动打包成一个 `.zip` 压缩包
|
||||
*仅打包文件,不包含外层 web 目录*
|
||||
⚠️ **注意**:构建完成后自动输出到 `unpackage/dist/build/h5/`,格式为 `{域名}-{base路径}.zip`
|
||||
💡 **示例**:`xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip`
|
||||
|
||||
##### 4.2.1.2 【重命名】
|
||||
1. 按版本类型规范重命名压缩包:
|
||||
- **通用版**:`hwappx-common-域名-时间.zip`
|
||||
💡 **示例**:`hwappx-common-xcx.aigc-quickapp.com-2026-01-24.zip`
|
||||
|
||||
- **定制化版**:`客户名称-定制化---hwappx-客户编号-域名-时间.zip`
|
||||
💡 **示例**:`POCT检测分析平台-定制化---hwappx-2811-xcx.aigc-quickapp.com-2026-01-24.zip`
|
||||
|
||||
🚫 **禁止**:压缩包命名禁止包含 `/ \ : * ? " < > |` 等特殊字符
|
||||
|
||||
##### 4.2.1.3 【交付与最终发布】🔍
|
||||
|
||||
1. 将重命名后的压缩包发送给运维人员
|
||||
##### 4.2.1.2 【交付与最终发布】🔍
|
||||
1. 将生成的 `.zip` 文件发送给运维人员
|
||||
2. **运维人员操作流程**:
|
||||
1. 解压压缩包
|
||||
2. 打开快应用开发者工具,导入解压后的代码目录
|
||||
3. 验证代码无报错后,执行上传发布操作
|
||||
##### 4.2.1.3 【测试流程】
|
||||
|
||||
##### 4.2.1.3 【测试流程】
|
||||
H5:打开自己的浏览器输入网址(https://5g.aigc-quickapp.com/hwappx/common/?uniacid=75)改站点
|
||||
如果是test就把hwappx/common/改为hwappx/test/----------改站点
|
||||
|
||||
---
|
||||
|
||||
## 5. 📝 构建脚本说明
|
||||
## 5. 📝 构建与版本信息
|
||||
|
||||
### 5.1 可用的 npm 脚本命令
|
||||
### 5.1 自动注入机制
|
||||
|
||||
| 命令 | 说明 |
|
||||
生产构建时,`vue.config.js` 中的 `VersionInjectPlugin` 会自动执行以下操作:
|
||||
|
||||
| 操作 | 适用平台 | 说明 |
|
||||
|------|---------|------|
|
||||
| `__app_version` 全局变量注入 | 所有平台 | 构建产物的 `vendor.js`(小程序)或 `index.html`(H5)中自动内联版本数据 |
|
||||
| 构建版本信息查看 | 所有平台 | 首页右下角连击 5 次弹出模态框显示 `commitId` 和 `buildTime` |
|
||||
|
||||
版本数据包含:
|
||||
- **commitId**:当前构建的 Git 短提交 ID
|
||||
- **buildTime**:构建时间
|
||||
|
||||
> **开发模式**:HMR 热更新时由 webpack emit 钩子自动处理,不影响开发体验。
|
||||
|
||||
### 5.2 自动 ZIP 打包
|
||||
|
||||
**H5 自定义发行**构建完成后自动执行:
|
||||
|
||||
| 步骤 | 说明 |
|
||||
|------|------|
|
||||
| `npm run mp-weixin` | 默认构建(production模式,包含ZIP文件) |
|
||||
| `npm run mp-weixin:patch` | 只打补丁(production模式,不创建ZIP文件) |
|
||||
| `npm run mp-weixin:dev` | 开发模式构建(development模式,包含ZIP文件) |
|
||||
| `npm run mp-weixin:dev:patch` | 只打补丁(development模式,不创建ZIP文件) |
|
||||
| 重命名输出目录 | `{域名}-{base路径用.连接}`(如 `xcx.aigc-quickapp.com-hwappx.common`) |
|
||||
| 创建 ZIP 文件 | 文件名格式:`{目录名}-{Git提交ID}-{上海时区时间戳}.zip` |
|
||||
| 清理 | ZIP 创建完成后自动删除源目录 |
|
||||
|
||||
### 5.2 构建脚本功能
|
||||
💡 **示例 ZIP**:`xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip`
|
||||
|
||||
- 复制 `project.config.json` 和 `project.private.config.json` 文件到构建目录
|
||||
- 复制 `site.js` 文件到构建目录
|
||||
- 在 `vendor.js` 文件开头追加 `import site from "../site.js";` 语句
|
||||
- 支持创建构建结果的 ZIP 压缩包
|
||||
- 自动打开 ZIP 文件所在目录
|
||||
**微信小程序**生产构建时自动执行:
|
||||
- 拷贝 `site.js` / `project.config.json` / `project.private.config.json` 到构建目录
|
||||
- 创建 ZIP 包(文件名包含 Git 提交 ID)
|
||||
|
||||
### 5.3 环境配置
|
||||
|
||||
域名配置统一通过 `package.json` 中各脚本的 `env.UNI_APP_DOMAIN` 传入:
|
||||
|
||||
```json
|
||||
"h5-xcx.aigc-quickapp.com": {
|
||||
"title": "...",
|
||||
"env": {
|
||||
"UNI_PLATFORM": "h5",
|
||||
"UNI_APP_DOMAIN": "https://xcx.aigc-quickapp.com/"
|
||||
},
|
||||
"define": { "PRODUCTION": true }
|
||||
}
|
||||
```
|
||||
|
||||
`config.js` 和 `vue.config.js` 均通过 `process.env.UNI_APP_DOMAIN` 读取,**新增/删除脚本无需改动代码**。
|
||||
|
||||
---
|
||||
|
||||
## 6. 🔄 版本历史
|
||||
|
||||
### 6.1 v1.3
|
||||
### 6.1 v1.5.1
|
||||
|
||||
- `VersionInjectPlugin`:webpack emit 钩子自动注入 `__app_version` 到所有平台产物
|
||||
- 首页右下角连击 5 次显示构建版本信息(commitId + buildTime)
|
||||
- `mp-weixin.patch.js` 精简为仅文件拷贝和 ZIP 打包
|
||||
- 修复 `openWxWorkServiceChat` 方法中 fail 回调语法错误
|
||||
- ZIP 文件名末尾使用 Git 提交 ID 替换时间戳
|
||||
- 构建脚本幂等性修复,支持多次运行不产生重复注入
|
||||
|
||||
### 6.2 v1.3
|
||||
- 修复微信小程序构建脚本,支持复制 `project.config.json` 和 `project.private.config.json` 文件
|
||||
- 增强构建脚本功能,添加命令行参数支持
|
||||
- 在 `package.json` 中添加相关 npm 脚本命令
|
||||
|
||||
@@ -18,9 +18,18 @@ async function createZipWithSystemCommand(sourceDir, outputDir) {
|
||||
throw new Error(`源目录不存在: ${sourceDir}`);
|
||||
}
|
||||
|
||||
// 生成文件名
|
||||
// 获取当前 Git 提交 ID(短 hash)
|
||||
let commitId = 'unknown';
|
||||
try {
|
||||
const { stdout } = await execAsync('git rev-parse --short HEAD', { cwd: path.join(__dirname, '..') });
|
||||
commitId = stdout.trim();
|
||||
} catch (e) {
|
||||
console.warn('无法获取 Git 提交 ID,使用 unknown 代替');
|
||||
}
|
||||
|
||||
// 生成文件名:mp-weixin-<日期>-<提交ID>.zip
|
||||
const dateString = new Date().toISOString().split('T')[0];
|
||||
const zipFileName = `mp-weixin-${dateString}-${new Date().getTime()}.zip`;
|
||||
const zipFileName = `mp-weixin-${dateString}-${commitId}.zip`;
|
||||
const zipFilePath = path.join(outputDir, zipFileName);
|
||||
|
||||
let command;
|
||||
|
||||
@@ -2,21 +2,16 @@
|
||||
/**
|
||||
* fix-wechat-miniapp.js
|
||||
*
|
||||
* 该脚本用于修复微信小程序的一些问题:
|
||||
* 1. 将项目根目录下的 site.js 文件拷贝到 unpackage/dist/build/mp-weixin 目录下,如果文件存在则覆盖
|
||||
* 2. 将 `const site = require("../site.js");` 这段代码追加到
|
||||
* unpackage/dist/build/mp-weixin/common/vendor.js 文件内容开头部分,
|
||||
* 如果这个文件开头已经有了这行代码,则不追加
|
||||
* 该脚本用于微信小程序的构建后处理(仅在生产构建时由 vue.config.js 触发):
|
||||
* 1. 将 project.config.json 及 project.private.config.json 拷贝到构建产物目录
|
||||
* 2. 将 site.js 拷贝到构建产物目录
|
||||
* 3. 创建 ZIP 压缩包
|
||||
*
|
||||
* 注意:version.js 生成和 vendor.js 注入已由 webpack 插件 (VersionInjectPlugin) 在 emit 钩子中自动处理。
|
||||
*
|
||||
* 使用:
|
||||
* node fix-wechat-miniapp.js # 打补丁并创建 ZIP 文件(默认 mode=production)
|
||||
* node fix-wechat-miniapp.js --no-zip # 只打补丁,不创建 ZIP 文件
|
||||
* node fix-wechat-miniapp.js --mode development # 使用 development 模式打补丁
|
||||
* node fix-wechat-miniapp.js --mode production # 使用 production 模式打补丁(默认)
|
||||
*
|
||||
* 注意:
|
||||
* - 在 Windows 上路径使用反斜杠也是可以的;脚本使用 path.join 来兼容不同平台。
|
||||
* - 运行前请确认在项目根目录运行(site.js 在当前工作目录下)。
|
||||
* node fix-wechat-miniapp.js # 拷贝文件并创建 ZIP(默认 mode=production)
|
||||
* node fix-wechat-miniapp.js --no-zip # 只拷贝文件,不创建 ZIP
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
@@ -26,37 +21,19 @@ const path = require('path');
|
||||
const { openFileDirectory } = require('./open-directory');
|
||||
const { createZipWithSystemCommand } = require('./create-zip');
|
||||
|
||||
async function commonPatch(mode = 'production') {
|
||||
async function copyAssets(mode = 'production') {
|
||||
try {
|
||||
|
||||
// 根据当前脚本所在目录(scripts),定位到项目根目录
|
||||
const cwd = path.join(__dirname, '..');
|
||||
|
||||
const srcSitePath = path.join(cwd, 'site.js');
|
||||
const destDir = path.join(cwd, 'unpackage', 'dist', mode === 'production' ? 'build' : 'dev', 'mp-weixin');
|
||||
const destSitePath = path.join(destDir, 'site.js');
|
||||
const vendorPath = path.join(destDir, 'common', 'vendor.js');
|
||||
|
||||
// 1) 检查源文件是否存在
|
||||
const srcExists = await exists(srcSitePath);
|
||||
if (!srcExists) {
|
||||
console.error(`源文件不存在: ${srcSitePath}`);
|
||||
process.exitCode = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保目标目录存在
|
||||
await ensureDir(destDir);
|
||||
|
||||
// 复制 project.config.json 及 project.private.config.json 文件到 destDir 下面
|
||||
// 复制 project.config.json 和 project.private.config.json
|
||||
const configFiles = ['project.config.json', 'project.private.config.json'];
|
||||
for (const fileName of configFiles) {
|
||||
const srcPath = path.join(cwd, fileName);
|
||||
const destPath = path.join(destDir, fileName);
|
||||
|
||||
// 检查源文件是否存在
|
||||
const fileExists = await exists(srcPath);
|
||||
if (fileExists) {
|
||||
if (await exists(srcPath)) {
|
||||
await fsp.copyFile(srcPath, destPath);
|
||||
console.log(`已拷贝: ${srcPath} -> ${destPath}`);
|
||||
} else {
|
||||
@@ -64,47 +41,15 @@ async function commonPatch(mode = 'production') {
|
||||
}
|
||||
}
|
||||
|
||||
// 复制 site.js 到目标目录(覆盖)
|
||||
await fsp.copyFile(srcSitePath, destSitePath);
|
||||
console.log(`已拷贝: ${srcSitePath} -> ${destSitePath}`);
|
||||
|
||||
// 2) 处理 vendor.js:在开头追加 require 语句(如果还没有的话)
|
||||
const vendorExists = await exists(vendorPath);
|
||||
if (!vendorExists) {
|
||||
console.warn(`目标 vendor.js 不存在,跳过追加 require。路径: ${vendorPath}`);
|
||||
return;
|
||||
// 复制 site.js
|
||||
const srcSitePath = path.join(cwd, 'site.js');
|
||||
if (await exists(srcSitePath)) {
|
||||
await fsp.copyFile(srcSitePath, path.join(destDir, 'site.js'));
|
||||
console.log(`已拷贝: ${srcSitePath} -> ${destDir}`);
|
||||
} else {
|
||||
console.error(`源文件不存在: ${srcSitePath}`);
|
||||
process.exitCode = 2;
|
||||
}
|
||||
|
||||
let vendorContent = await fsp.readFile(vendorPath, 'utf8');
|
||||
|
||||
const requireLine = 'const site = require("../site");';
|
||||
// 检查文件开头(例如前 5 行或前 400 字符)是否已经包含该 require 语句
|
||||
const inspectPrefix = vendorContent.slice(0, 400);
|
||||
const requireRegex = new RegExp(
|
||||
// 匹配可能存在的引号或分号差异
|
||||
'^\\s*const\\s+site\\s*=\\s*require\\s*\\([\'"]\\.\\./site[\'"]\\);?',
|
||||
'm'
|
||||
);
|
||||
|
||||
if (requireRegex.test(inspectPrefix)) {
|
||||
console.log('vendor.js 开头已包含 require("../site") 语句,跳过追加。');
|
||||
return;
|
||||
}
|
||||
|
||||
// 把 requireLine + newline 插入文件最前面。
|
||||
// 保留原始换行风格(若可能)
|
||||
const eol = vendorContent.includes('\r\n') ? '\r\n' : '\n';
|
||||
let prefix = '';
|
||||
|
||||
// 保留可能的 BOM
|
||||
if (vendorContent.charCodeAt(0) === 0xfeff) {
|
||||
prefix = '\uFEFF';
|
||||
vendorContent = vendorContent.slice(1);
|
||||
}
|
||||
|
||||
const newContent = prefix + requireLine + eol + vendorContent;
|
||||
await fsp.writeFile(vendorPath, newContent, 'utf8');
|
||||
console.log(`已在 vendor.js 开头追加: ${requireLine}`);
|
||||
} catch (err) {
|
||||
console.error('发生错误:', err);
|
||||
process.exitCode = 1;
|
||||
@@ -112,33 +57,27 @@ async function commonPatch(mode = 'production') {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// 解析命令行参数
|
||||
const argv = process.argv.slice(2);
|
||||
const options = {
|
||||
noZip: argv.includes('--no-zip'),
|
||||
mode: 'production' // 默认值
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
// 解析 --mode 参数
|
||||
const modeIndex = argv.indexOf('--mode');
|
||||
if (modeIndex !== -1 && modeIndex + 1 < argv.length) {
|
||||
options.mode = argv[modeIndex + 1];
|
||||
}
|
||||
|
||||
// 1) 打补丁
|
||||
await commonPatch(options.mode);
|
||||
// await commonPatch('development');
|
||||
// 1) 拷贝文件
|
||||
await copyAssets(options.mode);
|
||||
|
||||
// 2) 创建 ZIP 文件(如果未指定 --no-zip)
|
||||
if (!options.noZip) {
|
||||
// 2) 创建 ZIP(仅 production 模式,且未指定 --no-zip)
|
||||
if (!options.noZip && options.mode === 'production') {
|
||||
const cwd = path.join(__dirname, '..');
|
||||
const buildDir = options.mode === 'production' ? 'build' : 'dev';
|
||||
const sourceDir = path.join(cwd, 'unpackage', 'dist', buildDir, 'mp-weixin');
|
||||
const destDir = path.join(cwd, 'unpackage', 'dist', buildDir);
|
||||
const sourceDir = path.join(cwd, 'unpackage', 'dist', 'build', 'mp-weixin');
|
||||
const destDir = path.join(cwd, 'unpackage', 'dist', 'build');
|
||||
const zipFilePath = await createZipWithSystemCommand(sourceDir, destDir);
|
||||
console.log(`ZIP 文件路径: ${zipFilePath}`);
|
||||
|
||||
// 3) 自动打开zip所在的目录
|
||||
await openFileDirectory(zipFilePath);
|
||||
} else {
|
||||
console.log('跳过创建 ZIP 文件和打开目录');
|
||||
@@ -158,7 +97,6 @@ async function ensureDir(dirPath) {
|
||||
try {
|
||||
await fsp.mkdir(dirPath, { recursive: true });
|
||||
} catch (e) {
|
||||
// ignore if exists
|
||||
if (e.code !== 'EEXIST') throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +55,13 @@ async function openDirectoryWindows(directoryPath) {
|
||||
|
||||
console.log(`📁 在文件资源管理器中打开: ${normalizedPath}`);
|
||||
|
||||
// 方法1: 使用 explorer 命令
|
||||
// 方法1: 使用 explorer 命令(spawn + detached,不等待 GUI 进程)
|
||||
try {
|
||||
const command = `explorer "${normalizedPath}"`;
|
||||
await execAsync(command);
|
||||
const child = spawn('explorer', [normalizedPath], {
|
||||
stdio: 'ignore',
|
||||
detached: true
|
||||
});
|
||||
child.unref();
|
||||
console.log('✅ 已启动文件资源管理器');
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
||||
@@ -89,8 +89,6 @@ const store = new Vuex.Store({
|
||||
status: false,
|
||||
path: ''
|
||||
},
|
||||
// 华为快应用帐号信息
|
||||
huaweiAccountInfo: null,
|
||||
},
|
||||
mutations: {
|
||||
// 设置是否可以领取新人礼
|
||||
@@ -248,16 +246,7 @@ const store = new Vuex.Store({
|
||||
// 设置主题颜色
|
||||
setThemeColor(state, value) {
|
||||
state.themeColor = value;
|
||||
},
|
||||
// 华为快应用帐号信息
|
||||
setHuaweiAccountInfo(state, value) {
|
||||
state.huaweiAccountInfo = value;
|
||||
if (value) {
|
||||
uni.setStorageSync('huaweiAccountInfo', value);
|
||||
} else {
|
||||
uni.removeStorageSync('huaweiAccountInfo');
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
// AI智能助手配置
|
||||
@@ -314,8 +303,6 @@ const store = new Vuex.Store({
|
||||
this.commit('setGlobalStoreInfo', null);
|
||||
}
|
||||
this.commit('setInitStatus', true)
|
||||
// 初始化华为快应用 HwService(H5 壳模式下的快应用环境)
|
||||
this.dispatch('initHwService');
|
||||
resolve(data);
|
||||
}
|
||||
}
|
||||
@@ -429,7 +416,6 @@ const store = new Vuex.Store({
|
||||
},
|
||||
// 生成主题颜色CSS变量
|
||||
themeColorSet() {
|
||||
// console.log('样式颜色设置...');
|
||||
let theme = this.state.themeStyle;
|
||||
if (!theme?.main_color || !theme?.aux_color) return;
|
||||
try {
|
||||
@@ -453,31 +439,6 @@ const store = new Vuex.Store({
|
||||
} catch (e) {
|
||||
console.error('设置主题颜色失败', e);
|
||||
}
|
||||
// console.log('themeColor => ', this.state.themeColor);
|
||||
},
|
||||
// 初始化华为快应用 HwService
|
||||
initHwService() {
|
||||
// #ifdef H5_XCX_5G_QUICKAPP_COM || H5_XCX_AIGC_QUICKAPP_COM || H5_5G_AIGC_QUICKAPP_COM || H5_TEST_AIGC_QUICKAPP_COM
|
||||
try {
|
||||
const HwService = require('@/common/js/hw-jssdk.js').default;
|
||||
if (HwService.isQuickAppEnv()) {
|
||||
HwService.init().then(() => {
|
||||
console.log('[Store] HwService 初始化成功');
|
||||
// 检查是否有缓存的华为帐号信息
|
||||
const cachedInfo = uni.getStorageSync('huaweiAccountInfo');
|
||||
if (cachedInfo) {
|
||||
this.commit('setHuaweiAccountInfo', cachedInfo);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('[Store] HwService 初始化失败:', err);
|
||||
});
|
||||
} else {
|
||||
console.log('[Store] 非快应用环境,跳过 HwService 初始化');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('[Store] HwService 未配置,跳过初始化');
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
260
vue.config.js
260
vue.config.js
@@ -1,27 +1,65 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { execSync } = require('child_process');
|
||||
const { zionUniMpLoadPackagePlugin } = require('zion-uniapp-mp-load-package/webpack');
|
||||
|
||||
console.log('[vue.config] 已加载');
|
||||
|
||||
// 确保 UNI_APP_DOMAIN 有值(从 package.json 脚本配置中读取)
|
||||
if (!process.env.UNI_APP_DOMAIN && process.env.UNI_SCRIPT) {
|
||||
try {
|
||||
const pkg = require('./package.json');
|
||||
const scriptCfg = pkg['uni-app']?.scripts?.[process.env.UNI_SCRIPT];
|
||||
if (scriptCfg?.env?.UNI_APP_DOMAIN) {
|
||||
process.env.UNI_APP_DOMAIN = scriptCfg.env.UNI_APP_DOMAIN;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 输出 H5 域名信息到控制台
|
||||
if (process.env.UNI_PLATFORM === 'h5') {
|
||||
console.log(`\n[H5 发行] 脚本: ${process.env.UNI_SCRIPT || '(默认)'}`);
|
||||
console.log(`[H5 发行] 域名: ${process.env.UNI_APP_DOMAIN || '(未配置)'}\n`);
|
||||
}
|
||||
|
||||
// 打印出process.env
|
||||
// for (let key in process.env) {
|
||||
// console.log(key, process.env[key]);
|
||||
// }
|
||||
|
||||
|
||||
// 定义UNI_OUTPUT_DIR环境变量
|
||||
// 定义UNI_OUTPUT_DIR环境变量(H5 自定义发行脚本使用)
|
||||
try {
|
||||
if (!process.env.NODE_ENV) {
|
||||
throw new Error('NODE_ENV 环境变量未设置');
|
||||
}
|
||||
|
||||
const buildDir = process.env.NODE_ENV === 'production' ? 'build' : 'dev';
|
||||
|
||||
const platform = process.env.UNI_PLATFORM || '';
|
||||
const UNI_SCRIPT = process.env.UNI_SCRIPT ?? '';
|
||||
|
||||
if ([
|
||||
// 参照 package.json 中定义的 "uni-app" --> "scripts"
|
||||
'h5-xcx20.5g-quickapp.com', 'h5-xcx.aigc-quickapp.com', 'h5-5g.aigc-quickapp.com', 'h5-test.aigc-quickapp.com'
|
||||
].includes(UNI_SCRIPT)) {
|
||||
process.env.UNI_OUTPUT_DIR = path.join(__dirname, `unpackage/dist/${buildDir}/h5/${UNI_SCRIPT}`);
|
||||
// 所有 H5 自定义脚本统一处理,无需在 vue.config.js 中硬编码脚本名
|
||||
if (platform === 'h5' && UNI_SCRIPT) {
|
||||
// 从 manifest.json 读取 h5.router.base,拼接到输出路径
|
||||
let baseSuffix = '';
|
||||
try {
|
||||
const manifestPath = path.join(__dirname, 'manifest.json');
|
||||
baseSuffix = _readManifestBase(manifestPath);
|
||||
} catch (e) {}
|
||||
// 从脚本 env 中获取域名(优先使用 UNI_APP_DOMAIN,否则从 UNI_SCRIPT 提取)
|
||||
let domain = '';
|
||||
if (process.env.UNI_APP_DOMAIN) {
|
||||
try {
|
||||
domain = new URL(process.env.UNI_APP_DOMAIN).hostname;
|
||||
} catch (e) {
|
||||
domain = process.env.UNI_APP_DOMAIN.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
|
||||
}
|
||||
} else {
|
||||
domain = UNI_SCRIPT.replace(/^h5-/, '');
|
||||
}
|
||||
// 格式: {域名}-{base用.连接}
|
||||
const outputDirName = baseSuffix ? `${domain}-${baseSuffix}` : domain;
|
||||
process.env.UNI_OUTPUT_DIR = path.join(__dirname, `unpackage/dist/${buildDir}/h5/${outputDirName}`);
|
||||
console.log('UNI_OUTPUT_DIR = ', process.env.UNI_OUTPUT_DIR);
|
||||
}
|
||||
|
||||
@@ -29,6 +67,211 @@ try {
|
||||
console.log('NODE_ENV 环境变量未设置,默认使用开发模式');
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 manifest.json 中的 h5.router.base,去掉注释后解析
|
||||
* 返回 base 路径的 . 连接形式(如 /hwappx/common/ → hwappx.common)
|
||||
*/
|
||||
function _readManifestBase(manifestPath) {
|
||||
try {
|
||||
const raw = fs.readFileSync(manifestPath, 'utf8');
|
||||
// 去掉 /* */ 和 // 注释(HBuilder 生成的 JSON 允许注释)
|
||||
const clean = raw.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*$/gm, '');
|
||||
const manifest = JSON.parse(clean);
|
||||
if (manifest?.h5?.router?.base) {
|
||||
return manifest.h5.router.base.replace(/^\/+|\/+$/g, '').replace(/\//g, '.');
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[manifest] 读取 h5.router.base 失败:', e.message);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 webpack 插件:在通用产物(vendor.js)中内联注入版本数据
|
||||
* 利用 emit 钩子在资源写入磁盘前处理,支持 HMR(每次重编译都会自动注入)
|
||||
* 适用于所有平台(mp-weixin / h5 等)
|
||||
*/
|
||||
class VersionInjectPlugin {
|
||||
constructor() {
|
||||
this._cachedCommitId = null;
|
||||
}
|
||||
apply(compiler) {
|
||||
compiler.hooks.emit.tapAsync('VersionInjectPlugin', (compilation, callback) => {
|
||||
const mode = process.env.NODE_ENV === 'production' ? 'production' : 'development';
|
||||
const platform = process.env.UNI_PLATFORM || '';
|
||||
|
||||
// 获取 Git 提交 ID(缓存,避免每次 HMR 都执行)
|
||||
if (!this._cachedCommitId) {
|
||||
try {
|
||||
this._cachedCommitId = execSync('git rev-parse --short HEAD', { cwd: __dirname }).toString().trim();
|
||||
} catch (e) {
|
||||
this._cachedCommitId = 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
const versionData = { commitId: this._cachedCommitId, buildTime: new Date().toISOString() };
|
||||
const inlineVersion = JSON.stringify(versionData);
|
||||
|
||||
// 尝试注入到 vendor.js(mp-weixin / mp-* 等平台)
|
||||
const vendorKey = 'common/vendor.js';
|
||||
const vendorAsset = compilation.assets[vendorKey];
|
||||
if (vendorAsset) {
|
||||
let content = typeof vendorAsset.source === 'function' ? vendorAsset.source() : vendorAsset._value;
|
||||
if (typeof content !== 'string') content = content.toString();
|
||||
|
||||
// 清理旧注入
|
||||
content = content.replace(/^\s*const\s+site\s*=\s*require\s*\(['"]\.\.\/site(\.js)?['"]\);?\s*\r?\n/gm, '');
|
||||
content = content.replace(/^\s*const\s+__app_version\s*=\s*(require\s*\(['"]\.\.\/version(\.json|\.js)?['"]\)|.+);?\s*\r?\n/gm, '');
|
||||
|
||||
// 只注入版本信息(通用),site.js require 仅在 mp-weixin 生产构建时注入
|
||||
const lines = [`const __app_version = ${inlineVersion};`];
|
||||
if (platform === 'mp-weixin' && mode === 'production') {
|
||||
lines.unshift('const site = require("../site.js");');
|
||||
}
|
||||
|
||||
const eol = content.includes('\r\n') ? '\r\n' : '\n';
|
||||
let prefix = '';
|
||||
if (content.charCodeAt(0) === 0xfeff) {
|
||||
prefix = '\uFEFF';
|
||||
content = content.slice(1);
|
||||
}
|
||||
|
||||
content = prefix + lines.join(eol) + eol + content;
|
||||
|
||||
compilation.assets[vendorKey] = {
|
||||
source: () => content,
|
||||
size: () => content.length
|
||||
};
|
||||
|
||||
console.log(`[VersionInject] ✅ vendor.js 已注入 (platform=${platform}, mode=${mode}, commit=${this._cachedCommitId})`);
|
||||
} else {
|
||||
// H5 等平台可能没有 common/vendor.js,改为尝试注入到 index.html
|
||||
// #ifdef H5
|
||||
const htmlKey = 'index.html';
|
||||
const htmlAsset = compilation.assets[htmlKey];
|
||||
if (htmlAsset) {
|
||||
let html = typeof htmlAsset.source === 'function' ? htmlAsset.source() : htmlAsset._value;
|
||||
if (typeof html !== 'string') html = html.toString();
|
||||
// 在 </head> 前注入 script 标签
|
||||
const scriptTag = `<script>var __app_version = ${inlineVersion};</script>`;
|
||||
if (!html.includes('__app_version')) {
|
||||
html = html.replace('</head>', `${scriptTag}\n</head>`);
|
||||
compilation.assets[htmlKey] = {
|
||||
source: () => html,
|
||||
size: () => html.length
|
||||
};
|
||||
console.log(`[VersionInject] ✅ index.html 已注入 (platform=${platform}, commit=${this._cachedCommitId})`);
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
|
||||
// 生产构建后处理:mp-weixin 拷贝 site.js + ZIP;H5 自动打包 ZIP
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
compiler.hooks.afterEmit.tapAsync('ProductionZipPack', (compilation, callback) => {
|
||||
const platform = process.env.UNI_PLATFORM || '';
|
||||
const UNI_SCRIPT = process.env.UNI_SCRIPT ?? '';
|
||||
|
||||
if (platform === 'mp-weixin') {
|
||||
console.log('\n🚀 mp-weixin 生产构建完成,运行补丁脚本(site.js 拷贝 + ZIP 打包)...');
|
||||
try {
|
||||
execSync(`"${process.execPath}" scripts/mp-weixin.patch.js --mode production`, { cwd: __dirname, stdio: 'inherit' });
|
||||
} catch (e) {
|
||||
console.error('补丁脚本执行失败:', e.message);
|
||||
}
|
||||
} else if (platform === 'h5' && UNI_SCRIPT) {
|
||||
this._zipH5Build(compilation);
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_computeH5OutputName() {
|
||||
// 计算输出目录名: {域名}-{base用.连接}
|
||||
let baseSuffix = _readManifestBase(path.join(__dirname, 'manifest.json'));
|
||||
let domain = '';
|
||||
if (process.env.UNI_APP_DOMAIN) {
|
||||
try {
|
||||
domain = new URL(process.env.UNI_APP_DOMAIN).hostname;
|
||||
} catch (e) {
|
||||
domain = process.env.UNI_APP_DOMAIN.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
|
||||
}
|
||||
} else {
|
||||
domain = (process.env.UNI_SCRIPT || '').replace(/^h5-/, '');
|
||||
}
|
||||
const result = baseSuffix ? `${domain}-${baseSuffix}` : domain;
|
||||
console.log('[H5Zip] 输出目录名:', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
_zipH5Build(compilation) {
|
||||
const outputDirName = this._computeH5OutputName();
|
||||
const buildDir = process.env.NODE_ENV === 'production' ? 'build' : 'dev';
|
||||
const h5Dir = path.join(__dirname, 'unpackage', 'dist', buildDir, 'h5');
|
||||
|
||||
// 实际构建输出目录(由 webpack 决定)
|
||||
const actualOutput = compilation.compiler.outputPath;
|
||||
const targetDir = path.join(h5Dir, outputDirName);
|
||||
|
||||
// 如果路径一致,跳过重命名
|
||||
if (actualOutput !== targetDir) {
|
||||
try {
|
||||
if (fs.existsSync(targetDir)) {
|
||||
fs.rmSync(targetDir, { recursive: true });
|
||||
}
|
||||
fs.renameSync(actualOutput, targetDir);
|
||||
console.log(`[H5Zip] 输出目录已重命名为: ${outputDirName}`);
|
||||
} catch (e) {
|
||||
console.error('[H5Zip] 目录重命名失败:', e.message);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
console.log(`[H5Zip] 输出目录名正确: ${outputDirName}`);
|
||||
}
|
||||
|
||||
// 标准时区时间戳格式: 20260511_1058
|
||||
const fmt = new Intl.DateTimeFormat('en-CA', {
|
||||
timeZone: 'Asia/Shanghai',
|
||||
year: 'numeric', month: '2-digit', day: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit', hour12: false
|
||||
});
|
||||
const parts = fmt.formatToParts(new Date());
|
||||
const get = (t) => parts.find(p => p.type === t).value;
|
||||
const ts = `${get('year')}${get('month')}${get('day')}_${get('hour')}${get('minute')}`;
|
||||
|
||||
const zipFileName = `${outputDirName}-${this._cachedCommitId}-${ts}.zip`;
|
||||
const zipFilePath = path.join(h5Dir, zipFileName);
|
||||
|
||||
console.log(`\n📦 H5 ZIP: ${zipFileName}...`);
|
||||
|
||||
let command;
|
||||
const sourcePath = targetDir.replace(/'/g, "''");
|
||||
const destPath = zipFilePath.replace(/'/g, "''");
|
||||
if (process.platform === 'win32') {
|
||||
command = `powershell -Command "Compress-Archive -Path '${sourcePath}\\*' -DestinationPath '${destPath}' -Force"`;
|
||||
} else {
|
||||
const sourceBase = path.dirname(targetDir);
|
||||
const sourceName = path.basename(targetDir);
|
||||
command = `cd '${sourceBase}' && zip -r '${zipFilePath}' '${sourceName}' -x '*/node_modules/*' '*.git/*' '*.DS_Store'`;
|
||||
}
|
||||
|
||||
try {
|
||||
execSync(command, { stdio: 'inherit' });
|
||||
console.log(`✅ H5 ZIP: ${zipFileName}`);
|
||||
// 删除已打包的源目录
|
||||
// fs.rmSync(targetDir, { recursive: true, force: true });
|
||||
// console.log(`[H5Zip] 已删除源目录: ${outputDirName}`);
|
||||
} catch (e) {
|
||||
console.error('H5 ZIP 打包失败:', e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出配置
|
||||
module.exports = {
|
||||
productionSourceMap: false,
|
||||
@@ -118,5 +361,8 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 所有模式下添加版本注入插件(适用于所有平台)
|
||||
config.plugins.push(new VersionInjectPlugin());
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user