upload project
This commit is contained in:
commit
06961cae04
422 changed files with 110626 additions and 0 deletions
834
subpackages/qr/qr-code/qr-code.js
Normal file
834
subpackages/qr/qr-code/qr-code.js
Normal file
|
|
@ -0,0 +1,834 @@
|
|||
// Get application instance
|
||||
const app = getApp();
|
||||
const apiClient = require('../../../utils/api-client.js'); // Import your API client
|
||||
const friendAPI = require('../../../utils/friend-api.js'); // Import friend API for loading target user info
|
||||
|
||||
// Page configuration
|
||||
Page({
|
||||
// Page data
|
||||
data: {
|
||||
username: '加载中...', // User name (loading state)
|
||||
userId: '加载中...', // User ID (loading state)
|
||||
qrCodeUrl: '', // QR code image URL
|
||||
isDarkMode: false, // Whether in dark mode
|
||||
isLoading: true, // Loading state
|
||||
userInfo: null, // Store complete user info
|
||||
isTestData: false, // Flag to indicate if using test data
|
||||
},
|
||||
|
||||
// Test/fallback user data to prevent crashes
|
||||
getTestUserData: function() {
|
||||
return {
|
||||
user: {
|
||||
id: 'test_123456',
|
||||
customId: 'TEST001',
|
||||
nickname: 'Test User',
|
||||
phone: '13800138000',
|
||||
avatar: 'https://via.placeholder.com/100x100/4CAF50/white?text=T',
|
||||
gender: 1,
|
||||
birthday: '1990-01-01',
|
||||
location: 'Test City',
|
||||
signature: 'This is a test user for development',
|
||||
isActive: true,
|
||||
createdAt: '2024-01-01T00:00:00Z',
|
||||
updatedAt: '2024-01-01T00:00:00Z'
|
||||
},
|
||||
token: 'test_token_123456789',
|
||||
refreshToken: 'test_refresh_token_123456789',
|
||||
expiresAt: Date.now() + (7 * 24 * 60 * 60 * 1000), // 7 days from now
|
||||
permissions: ['basic', 'location', 'social'],
|
||||
settings: {
|
||||
locationPrivacy: 'friends',
|
||||
showPhone: false,
|
||||
allowSearch: true
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// Page load lifecycle function
|
||||
onLoad: function(options) {
|
||||
// 接收传入的 customId 参数(如果是从用户预览页或好友详情页跳转过来的)
|
||||
const targetCustomId = options?.customId || null;
|
||||
|
||||
// Check theme settings first
|
||||
const isDarkMode = wx.getStorageSync('isDarkMode') || false;
|
||||
this.setData({
|
||||
isDarkMode: isDarkMode
|
||||
});
|
||||
|
||||
// Load user information
|
||||
this.loadUserInfo(targetCustomId);
|
||||
},
|
||||
|
||||
// Load user information with guaranteed fallback
|
||||
loadUserInfo: function(targetCustomId) {
|
||||
// Show loading
|
||||
wx.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
// 如果传入了 targetCustomId,说明要显示陌生人或好友的二维码
|
||||
if (targetCustomId) {
|
||||
this.loadTargetUserInfo(targetCustomId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 否则显示当前用户的二维码
|
||||
// Start with test data immediately to ensure something always works
|
||||
const testUserData = this.getTestUserData();
|
||||
|
||||
this.setData({
|
||||
username: testUserData.user.nickname,
|
||||
userId: testUserData.user.customId,
|
||||
userInfo: testUserData,
|
||||
isLoading: false,
|
||||
isTestData: true
|
||||
});
|
||||
|
||||
// Generate QR code immediately with test data
|
||||
this.generateQRCodeWithData(testUserData);
|
||||
|
||||
// Hide loading
|
||||
wx.hideLoading();
|
||||
|
||||
// Show test data notification
|
||||
wx.showToast({
|
||||
title: '使用测试数据',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
// Try to get real data in background (optional)
|
||||
this.tryLoadRealUserData();
|
||||
},
|
||||
|
||||
// 加载目标用户(陌生人或好友)的信息
|
||||
loadTargetUserInfo: async function(targetCustomId) {
|
||||
console.log('📱 开始加载目标用户信息, customId:', targetCustomId);
|
||||
|
||||
if (!targetCustomId) {
|
||||
console.error('❌ targetCustomId 为空');
|
||||
wx.hideLoading();
|
||||
wx.showModal({
|
||||
title: '参数错误',
|
||||
content: '用户ID不存在',
|
||||
showCancel: false,
|
||||
confirmText: '确定',
|
||||
success: () => {
|
||||
wx.navigateBack();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 先尝试获取好友详情(如果是好友)
|
||||
try {
|
||||
console.log('🔍 尝试获取好友详情, customId:', targetCustomId);
|
||||
const detail = await friendAPI.getFriendDetail(targetCustomId);
|
||||
if (detail && detail.code === 0 && detail.data) {
|
||||
console.log('✅ 获取好友详情成功');
|
||||
const userData = detail.data;
|
||||
// 格式化用户信息,匹配二维码生成所需的格式
|
||||
const formattedUserInfo = {
|
||||
user: {
|
||||
customId: userData.customId || targetCustomId,
|
||||
nickname: userData.nickname || userData.remark || '用户',
|
||||
avatar: userData.avatar || '',
|
||||
id: userData.id || userData.customId
|
||||
}
|
||||
};
|
||||
|
||||
this.setData({
|
||||
username: formattedUserInfo.user.nickname,
|
||||
userId: formattedUserInfo.user.customId,
|
||||
userInfo: formattedUserInfo,
|
||||
isLoading: false,
|
||||
isTestData: false
|
||||
});
|
||||
|
||||
// 使用现有逻辑生成二维码
|
||||
this.generateQRCodeWithData(formattedUserInfo);
|
||||
wx.hideLoading();
|
||||
console.log('✅ 好友二维码生成完成');
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
// 不是好友,继续走搜索流程
|
||||
console.log('⚠️ 获取好友详情失败(可能不是好友),尝试搜索用户:', err.message || err);
|
||||
}
|
||||
|
||||
// 如果不是好友,使用搜索接口
|
||||
console.log('🔍 使用搜索接口查找用户, customId:', targetCustomId);
|
||||
const resp = await friendAPI.searchUsers(targetCustomId, 'custom_id', 1, 1);
|
||||
const user = resp?.data?.users?.[0];
|
||||
|
||||
if (!user) {
|
||||
console.error('❌ 未找到用户信息');
|
||||
wx.hideLoading();
|
||||
wx.showModal({
|
||||
title: '加载失败',
|
||||
content: '未找到该用户信息',
|
||||
showCancel: false,
|
||||
confirmText: '确定',
|
||||
success: () => {
|
||||
wx.navigateBack();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ 搜索到用户信息:', user);
|
||||
|
||||
// 格式化用户信息
|
||||
const formattedUserInfo = {
|
||||
user: {
|
||||
customId: user.customId || targetCustomId,
|
||||
nickname: user.nickname || '用户',
|
||||
avatar: user.avatar || '',
|
||||
id: user.id || user.customId
|
||||
}
|
||||
};
|
||||
|
||||
this.setData({
|
||||
username: formattedUserInfo.user.nickname,
|
||||
userId: formattedUserInfo.user.customId,
|
||||
userInfo: formattedUserInfo,
|
||||
isLoading: false,
|
||||
isTestData: false
|
||||
});
|
||||
|
||||
// 使用现有逻辑生成二维码
|
||||
this.generateQRCodeWithData(formattedUserInfo);
|
||||
wx.hideLoading();
|
||||
console.log('✅ 陌生人二维码生成完成');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 加载目标用户信息失败:', error);
|
||||
wx.hideLoading();
|
||||
wx.showModal({
|
||||
title: '加载失败',
|
||||
content: error.message || '无法加载用户信息,请稍后重试',
|
||||
showCancel: false,
|
||||
confirmText: '确定',
|
||||
success: () => {
|
||||
wx.navigateBack();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Try to load real user data in background (won't break if fails)
|
||||
tryLoadRealUserData: async function() {
|
||||
try {
|
||||
|
||||
// Try local storage first
|
||||
let userInfo = wx.getStorageSync('userInfo');
|
||||
|
||||
// Try API if no local data
|
||||
if (!userInfo || !userInfo.user) {
|
||||
|
||||
const response = await apiClient.getUserInfo();
|
||||
|
||||
if (response && response.code === 0 && response.data) {
|
||||
userInfo = response.data;
|
||||
wx.setStorageSync('userInfo', userInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// If we got real data, update the UI
|
||||
if (userInfo && userInfo.user) {
|
||||
|
||||
this.setData({
|
||||
username: userInfo.user.nickname || userInfo.user.customId || '真实用户',
|
||||
userId: userInfo.user.customId || userInfo.user.id || 'REAL001',
|
||||
userInfo: userInfo,
|
||||
isTestData: false
|
||||
});
|
||||
|
||||
// Regenerate QR code with real data
|
||||
this.generateQRCodeWithData(userInfo);
|
||||
|
||||
wx.showToast({
|
||||
title: '已加载真实数据',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
|
||||
// Do nothing - we already have test data working
|
||||
}
|
||||
},
|
||||
|
||||
// Generate QR code with provided user data (guaranteed to work)
|
||||
generateQRCodeWithData: function(userData) {
|
||||
|
||||
if (!userData || !userData.user) {
|
||||
console.error('未提供用于生成二维码的用户数据');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create QR code data object
|
||||
// const qrData = {
|
||||
|
||||
// type: 'user_card',
|
||||
// username: userData.user.nickname || userData.user.customId,
|
||||
// customId: userData.user.customId,
|
||||
// nickname: userData.user.nickname,
|
||||
// avatar: userData.user.avatar,
|
||||
// isTestData: this.data.isTestData,
|
||||
// timestamp: Date.now()
|
||||
// };
|
||||
|
||||
// Convert to JSON string for QR code
|
||||
const qrCodeData = "FINDME:" + userData.user.customId;//JSON.stringify(qrData);
|
||||
|
||||
// Generate QR code URL using online service (guaranteed to work)
|
||||
try {
|
||||
const encodedData = encodeURIComponent(qrCodeData);
|
||||
const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&format=png&data=${encodedData}`;
|
||||
|
||||
this.setData({
|
||||
qrCodeUrl: qrCodeUrl
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('生成二维码失败 URL:', error);
|
||||
|
||||
// Ultimate fallback - use a simple text-based QR code
|
||||
const simpleData = `${userData.user.nickname}-${userData.user.customId}`;
|
||||
const encodedSimpleData = encodeURIComponent(simpleData);
|
||||
const fallbackUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodedSimpleData}`;
|
||||
|
||||
this.setData({
|
||||
qrCodeUrl: fallbackUrl
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
// Navigate back
|
||||
navigateBack: function() {
|
||||
wx.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
},
|
||||
|
||||
// Show menu
|
||||
showMenu: function() {
|
||||
// Different menu options based on whether user is logged in or using test data
|
||||
const menuItems = ['保存图片', '分享二维码', '刷新数据'];
|
||||
|
||||
if (this.data.isTestData) {
|
||||
menuItems.push('前往登录');
|
||||
}
|
||||
|
||||
menuItems.push('Settings');
|
||||
|
||||
wx.showActionSheet({
|
||||
itemList: menuItems,
|
||||
success: (res) => {
|
||||
switch (res.tapIndex) {
|
||||
case 0:
|
||||
this.saveQRCode();
|
||||
break;
|
||||
case 1:
|
||||
this.shareQRCode();
|
||||
break;
|
||||
case 2:
|
||||
this.refreshUserInfo();
|
||||
break;
|
||||
case 3:
|
||||
if (this.data.isTestData) {
|
||||
this.goToLogin();
|
||||
} else {
|
||||
this.goToSettings();
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (!this.data.isTestData) {
|
||||
this.goToSettings();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Navigate to login page
|
||||
goToLogin: function() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/login/login'
|
||||
});
|
||||
},
|
||||
|
||||
// Navigate to settings page
|
||||
goToSettings: function() {
|
||||
wx.navigateTo({
|
||||
url: '/subpackages/settings/settingss/settingss'
|
||||
});
|
||||
},
|
||||
|
||||
// Force test data (for debugging) - only works when not logged in
|
||||
forceTestData: function() {
|
||||
// Check if user is actually logged in
|
||||
const userInfo = wx.getStorageSync('userInfo');
|
||||
if (userInfo && userInfo.user && userInfo.token) {
|
||||
wx.showModal({
|
||||
title: 'Cannot Use Test Data',
|
||||
content: 'You are currently logged in. Please log out first to use test data.',
|
||||
showCancel: false,
|
||||
confirmText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const testUserData = this.getTestUserData();
|
||||
|
||||
this.setData({
|
||||
username: testUserData.user.nickname,
|
||||
userId: testUserData.user.customId,
|
||||
userInfo: testUserData,
|
||||
isTestData: true,
|
||||
qrCodeUrl: '' // Clear current QR code
|
||||
});
|
||||
|
||||
this.generateQRCodeWithData(testUserData);
|
||||
|
||||
wx.showToast({
|
||||
title: 'Test data loaded',
|
||||
icon: 'success'
|
||||
});
|
||||
},
|
||||
|
||||
// Refresh user info
|
||||
refreshUserInfo: function() {
|
||||
|
||||
this.setData({
|
||||
isLoading: true,
|
||||
username: '令人耳目一新……',
|
||||
userId: '请稍等...',
|
||||
qrCodeUrl: ''
|
||||
});
|
||||
|
||||
// Try to reload user info (will use real data if available, test data if not)
|
||||
this.loadUserInfo();
|
||||
},
|
||||
|
||||
// Refresh QR code
|
||||
refreshQRCode: function() {
|
||||
|
||||
if (!this.data.userInfo) {
|
||||
|
||||
this.loadUserInfo();
|
||||
return;
|
||||
}
|
||||
|
||||
wx.showLoading({
|
||||
title: '正在刷新二维码...'
|
||||
});
|
||||
|
||||
// Clear current QR code
|
||||
this.setData({
|
||||
qrCodeUrl: ''
|
||||
});
|
||||
|
||||
// Regenerate with current data
|
||||
setTimeout(() => {
|
||||
this.generateQRCodeWithData(this.data.userInfo);
|
||||
wx.hideLoading();
|
||||
|
||||
wx.showToast({
|
||||
title: '二维码已刷新',
|
||||
icon: 'success'
|
||||
});
|
||||
}, 500);
|
||||
},
|
||||
|
||||
// QR code image load success
|
||||
onQRCodeLoad: function() {
|
||||
|
||||
},
|
||||
|
||||
// QR code image load error
|
||||
onQRCodeError: function(e) {
|
||||
console.error('二维码图片加载失败:', e);
|
||||
|
||||
// Try to regenerate with simpler data
|
||||
if (this.data.userInfo) {
|
||||
|
||||
const userData = this.data.userInfo;
|
||||
const simpleData = `${userData.user.nickname || 'User'}-${userData.user.customId || 'ID'}`;
|
||||
const encodedData = encodeURIComponent(simpleData);
|
||||
const fallbackUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodedData}`;
|
||||
|
||||
this.setData({
|
||||
qrCodeUrl: fallbackUrl
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Save QR code to album
|
||||
saveQRCode: function() {
|
||||
if (!this.data.qrCodeUrl) {
|
||||
wx.showToast({
|
||||
title: '没有二维码可以保存',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Download and save
|
||||
wx.downloadFile({
|
||||
url: this.data.qrCodeUrl,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
wx.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: () => {
|
||||
wx.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success'
|
||||
});
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('保存二维码失败:', error);
|
||||
wx.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('二维码下载失败:', error);
|
||||
wx.showToast({
|
||||
title: '下载失败'+error.errMsg,
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Share QR code
|
||||
shareQRCode: function() {
|
||||
if (!this.data.qrCodeUrl) {
|
||||
wx.showToast({
|
||||
title: '没有可分享的二维码',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
wx.downloadFile({
|
||||
url: this.data.qrCodeUrl,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
wx.showShareImageMenu({
|
||||
path: res.tempFilePath,
|
||||
success: () => {
|
||||
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('分享失败:', error);
|
||||
wx.showToast({
|
||||
title: '分享失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('无法下载共享:', error);
|
||||
wx.showToast({
|
||||
title: '分享失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Enhanced scan QR code with better error handling
|
||||
scanQRCode: function() {
|
||||
wx.navigateTo({
|
||||
url: '/subpackages/qr/qr-scan/qr-scan',
|
||||
})
|
||||
// Check camera permission first
|
||||
// wx.getSetting({
|
||||
// success: (res) => {
|
||||
// if (res.authSetting['scope.camera'] === false) {
|
||||
// wx.showModal({
|
||||
// title: '需要相机权限',
|
||||
// content: '扫描二维码需要使用相机,请允许访问相机',
|
||||
// showCancel: true,
|
||||
// cancelText: '取消',
|
||||
// confirmText: '去设置',
|
||||
// success: (modalRes) => {
|
||||
// if (modalRes.confirm) {
|
||||
// wx.openSetting();
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Proceed with scanning
|
||||
// wx.scanCode({
|
||||
// // onlyFromCamera: true,
|
||||
// scanType: ['qrCode'],
|
||||
// success: (res) => {
|
||||
|
||||
// try {
|
||||
// const scannedData = JSON.parse(res.result);
|
||||
|
||||
// if (scannedData.type === 'user_card') {
|
||||
// this.handleUserCardScan(scannedData);
|
||||
// } else {
|
||||
// this.handleGenericScan(res.result);
|
||||
// }
|
||||
// } catch (error) {
|
||||
|
||||
// this.handleGenericScan(res.result);
|
||||
// }
|
||||
// },
|
||||
// fail: (error) => {
|
||||
// console.error('扫描失败:', error);
|
||||
|
||||
// if (error.errMsg.includes('cancel')) {
|
||||
// // User cancelled scanning
|
||||
|
||||
// } else {
|
||||
// wx.showToast({
|
||||
// title: '扫描失败,请重试',
|
||||
// icon: 'none'
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
},
|
||||
|
||||
// Handle user card QR code scan (Enhanced version)
|
||||
handleUserCardScan: function(userData) {
|
||||
|
||||
const isTestData = userData.isTestData || false;
|
||||
const dataType = isTestData ? ' (Test Data)' : '';
|
||||
|
||||
// Check if trying to scan own QR code
|
||||
if (this.data.userInfo &&
|
||||
(userData.userId === this.data.userInfo.user.customId ||
|
||||
userData.userId === this.data.userInfo.user.id)) {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '不能添加自己为好友',
|
||||
showCancel: false,
|
||||
confirmText: '知道了'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
wx.showModal({
|
||||
title: `扫描到用户${dataType}`,
|
||||
content: `用户名: ${userData.username}\n用户ID: ${userData.userId}${isTestData ? '\n\n注意:这是测试数据' : ''}`,
|
||||
showCancel: true,
|
||||
cancelText: '取消',
|
||||
confirmText: isTestData ? '知道了' : '添加好友',
|
||||
success: (res) => {
|
||||
if (res.confirm && !isTestData) {
|
||||
this.addFriend(userData);
|
||||
} else if (res.confirm && isTestData) {
|
||||
wx.showToast({
|
||||
title: '无法添加测试用户',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Add friend functionality using your existing API
|
||||
addFriend: async function(userData) {
|
||||
|
||||
// Show loading
|
||||
wx.showLoading({
|
||||
title: '添加中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
try {
|
||||
// Check if user is logged in
|
||||
const currentUserInfo = wx.getStorageSync('userInfo');
|
||||
if (!currentUserInfo || !currentUserInfo.token) {
|
||||
wx.hideLoading();
|
||||
wx.showModal({
|
||||
title: '未登录',
|
||||
content: '请先登录后再添加好友',
|
||||
showCancel: true,
|
||||
cancelText: '取消',
|
||||
confirmText: '去登录',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
wx.navigateTo({
|
||||
url: '/pages/login/login'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Use your existing API method
|
||||
const response = await apiClient.addFriend(
|
||||
userData.userId, // targetId
|
||||
'通过扫描二维码添加' // message
|
||||
);
|
||||
|
||||
wx.hideLoading();
|
||||
|
||||
if (response && response.code === 0) {
|
||||
// Success
|
||||
wx.showModal({
|
||||
title: '添加成功',
|
||||
content: `已成功添加 ${userData.username} 为好友`,
|
||||
showCancel: false,
|
||||
confirmText: '确定',
|
||||
success: () => {
|
||||
// Navigate to friends list or chat
|
||||
wx.showActionSheet({
|
||||
itemList: ['查看好友列表', '开始聊天'],
|
||||
success: (actionRes) => {
|
||||
if (actionRes.tapIndex === 0) {
|
||||
wx.navigateTo({
|
||||
url: '/pages/social/friends/friends'
|
||||
});
|
||||
} else if (actionRes.tapIndex === 1) {
|
||||
wx.navigateTo({
|
||||
url: `/pages/message/chat/chat?userId=${userData.userId}&username=${userData.username}`
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Update local friends list if you have one cached
|
||||
this.updateLocalFriendsList(userData);
|
||||
|
||||
} else if (response && (response.code === 40001 || response.message?.includes('已经是好友'))) {
|
||||
// User already a friend (adjust code based on your backend response)
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: `${userData.username} 已经是您的好友了`,
|
||||
showCancel: true,
|
||||
cancelText: '知道了',
|
||||
confirmText: '开始聊天',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
wx.navigateTo({
|
||||
url: `/pages/message/chat/chat?userId=${userData.userId}&username=${userData.username}`
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (response && (response.code === 40002 || response.message?.includes('等待确认'))) {
|
||||
// Friend request sent (pending approval)
|
||||
wx.showToast({
|
||||
title: '好友请求已发送,等待对方确认',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
} else if (response && (response.code === 40404 || response.message?.includes('用户不存在'))) {
|
||||
// User not found
|
||||
wx.showModal({
|
||||
title: '用户不存在',
|
||||
content: '该用户可能已注销或不存在',
|
||||
showCancel: false,
|
||||
confirmText: '知道了'
|
||||
});
|
||||
} else {
|
||||
// Other error
|
||||
const errorMsg = response?.message || '添加好友失败';
|
||||
wx.showToast({
|
||||
title: errorMsg,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
wx.hideLoading();
|
||||
console.error('添加好友出错:', error);
|
||||
|
||||
// Handle specific error messages
|
||||
let errorMsg = '添加好友失败';
|
||||
if (error.message) {
|
||||
if (error.message.includes('目标用户ID不能为空')) {
|
||||
errorMsg = '用户ID无效';
|
||||
} else if (error.message.includes('好友申请留言不能超过100字符')) {
|
||||
errorMsg = '申请留言过长';
|
||||
} else {
|
||||
errorMsg = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
wx.showModal({
|
||||
title: '添加失败',
|
||||
content: errorMsg,
|
||||
showCancel: true,
|
||||
cancelText: '取消',
|
||||
confirmText: '重试',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.addFriend(userData);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Update local friends list cache
|
||||
updateLocalFriendsList: function(newFriend) {
|
||||
try {
|
||||
let friendsList = wx.getStorageSync('friendsList') || [];
|
||||
|
||||
// Check if friend already exists
|
||||
const existingIndex = friendsList.findIndex(friend =>
|
||||
friend.userId === newFriend.userId || friend.customId === newFriend.userId
|
||||
);
|
||||
|
||||
if (existingIndex === -1) {
|
||||
// Add new friend to local cache
|
||||
friendsList.push({
|
||||
userId: newFriend.userId,
|
||||
customId: newFriend.userId,
|
||||
nickname: newFriend.username,
|
||||
avatar: newFriend.avatar || '',
|
||||
addedAt: new Date().toISOString(),
|
||||
status: 'accepted' // or 'pending' if requires approval
|
||||
});
|
||||
|
||||
wx.setStorageSync('friendsList', friendsList);
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('更新本地好友列表失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// Handle generic QR code scan
|
||||
handleGenericScan: function(result) {
|
||||
|
||||
wx.showModal({
|
||||
title: '二维码结果',
|
||||
content: result,
|
||||
showCancel: false,
|
||||
confirmText: 'OK'
|
||||
});
|
||||
}
|
||||
});
|
||||
8
subpackages/qr/qr-code/qr-code.json
Normal file
8
subpackages/qr/qr-code/qr-code.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"navigationBarTitleText": "我的二维码",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#000000",
|
||||
"disableScroll": false,
|
||||
"navigationStyle": "custom",
|
||||
"pageOrientation": "portrait"
|
||||
}
|
||||
87
subpackages/qr/qr-code/qr-code.wxml
Normal file
87
subpackages/qr/qr-code/qr-code.wxml
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<!-- 页面根容器,设置整体样式 -->
|
||||
<view class="qr-code-container">
|
||||
<!-- <image class="codeBng" src="../../images/code-img.png" mode=""/> -->
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<!-- 返回按钮 -->
|
||||
<view class="back-btn" bindtap="navigateBack">
|
||||
<text class="back-icon">↩</text>
|
||||
</view>
|
||||
<!-- 菜单按钮 -->
|
||||
<view class="menu-btn" bindtap="showMenu">
|
||||
<text class="menu-icon">⋯</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主体内容区域 -->
|
||||
<view class="main-content">
|
||||
|
||||
<!-- 用户信息区域 -->
|
||||
<view class="user-info">
|
||||
<!-- 用户名显示 -->
|
||||
<text class="username">{{username}}</text>
|
||||
<!-- 用户ID显示 -->
|
||||
<text class="user-id">ID: {{userId}}</text>
|
||||
<!-- 加载状态指示器 -->
|
||||
<view wx:if="{{isLoading}}" class="loading-indicator">
|
||||
<text class="loading-text">Loading...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 二维码容器 -->
|
||||
<view class="qr-code-box">
|
||||
<!-- 加载状态 -->
|
||||
<view wx:if="{{isLoading}}" class="qr-loading">
|
||||
<text class="loading-text">Generating QR Code...</text>
|
||||
</view>
|
||||
|
||||
<!-- 二维码图片显示 -->
|
||||
<image
|
||||
wx:elif="{{qrCodeUrl}}"
|
||||
class="qr-code-image"
|
||||
src="{{qrCodeUrl}}"
|
||||
mode="aspectFit"
|
||||
bindload="onQRCodeLoad"
|
||||
binderror="onQRCodeError"
|
||||
></image>
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<view wx:else class="qr-error">
|
||||
<text class="error-text">QR Code unavailable</text>
|
||||
<button class="retry-btn" bindtap="generateQRCode">Retry</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 刷新二维码按钮 -->
|
||||
<view class="refresh-btn" bindtap="refreshQRCode" hover-class="btn-hover">
|
||||
<!-- 刷新图标 -->
|
||||
<!-- <text >🔄</text> -->
|
||||
|
||||
<image class="refresh-icon" src="/images/refresh.png" mode=""/>
|
||||
|
||||
<!-- 刷新按钮文字 -->
|
||||
<text class="refresh-text">换一换</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="action-buttons">
|
||||
<!-- 保存二维码按钮 -->
|
||||
<view bindtap="saveQRCode" class="action-btn-out">
|
||||
<!-- 下载图标 -->
|
||||
<view class="action-btn">
|
||||
<image class="action-icon" src="/images/download.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="action-icon-text" >下载</text>
|
||||
</view>
|
||||
|
||||
<!-- 扫描二维码按钮 -->
|
||||
<view class="action-btn-out">
|
||||
<view class="action-btn" bindtap="scanQRCode">
|
||||
<!-- 扫描图标 -->
|
||||
<image class="action-icon" src="/images/scan.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="action-icon-text" >扫一扫</text>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
202
subpackages/qr/qr-code/qr-code.wxss
Normal file
202
subpackages/qr/qr-code/qr-code.wxss
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* 全局渐变背景 */
|
||||
.qr-code-container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #64516A 0%, #553B73 30%, #103055 100%);
|
||||
/* background-image: url(/images/code-img.png); */
|
||||
background-size: cover; /* 图片自适应容器(常用) */
|
||||
background-repeat: no-repeat; /* 禁止图片重复 */
|
||||
background-position: center; /* 图片居中显示 */
|
||||
color: #ffffff;
|
||||
/* padding-bottom: 100rpx; */
|
||||
}
|
||||
|
||||
/* 导航栏样式 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 100rpx 30rpx 30rpx;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.right-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.menu-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 主体内容样式 */
|
||||
.main-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 80rpx 30rpx 50rpx;
|
||||
/* border: 1px solid red; */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* 用户信息样式 */
|
||||
.user-info {
|
||||
color: #202020;
|
||||
width: 73%;
|
||||
/* border: 1px solid red; */
|
||||
text-align: start;
|
||||
margin-bottom: 70rpx;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 44rpx;
|
||||
font-weight: 700;
|
||||
/* color: #ffffff; */
|
||||
margin-bottom: 15rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.user-id {
|
||||
font-size: 30rpx;
|
||||
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 二维码容器样式 */
|
||||
.qr-code-box {
|
||||
width: 460rpx;
|
||||
height: 460rpx;
|
||||
background-color: #000000;
|
||||
border-radius: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.3);
|
||||
margin-bottom: 40rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.qr-code-image {
|
||||
width: 420rpx;
|
||||
height: 420rpx;
|
||||
/* position: relative; */
|
||||
/* background: linear-gradient(135deg, #ff6b6b, #feca57, #48dbfb, #1dd1a1, #5f27cd); */
|
||||
padding: 25rpx;
|
||||
/* border-radius: 16rpx; */
|
||||
}
|
||||
|
||||
/* 刷新按钮样式 */
|
||||
.refresh-btn {
|
||||
|
||||
width: 200rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
background-color: rgba(255, 255, 255, .5);
|
||||
border-radius: 12rpx;
|
||||
padding: 16rpx 8rpx;
|
||||
margin-bottom: 120rpx;
|
||||
/* border: 1px solid rgba(255, 255, 255, .4); */
|
||||
/* border: 1px solid red; */
|
||||
}
|
||||
|
||||
.refresh-icon {
|
||||
color: #ffffff;
|
||||
margin-left: 10rpx;
|
||||
width:35rpx;
|
||||
height:35rpx;
|
||||
/* height: 40rpx; */
|
||||
/* border: 1px solid red; */
|
||||
}
|
||||
|
||||
.refresh-text {
|
||||
width: 160rpx;
|
||||
height: 48rpx;
|
||||
line-height: 48rpx;
|
||||
text-align: center;
|
||||
/* border: 1px solid red; */
|
||||
font-size: 28rpx;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* 底部操作按钮区域样式 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
padding: 0 60rpx;
|
||||
position: fixed;
|
||||
bottom: 80rpx;
|
||||
left: 0;
|
||||
}
|
||||
.action-btn-out{
|
||||
/* border: 1px solid red; */
|
||||
width: 120rpx;
|
||||
height: 180rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.action-btn {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
background-color: #202529;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.2),
|
||||
0rpx -3rpx 2rpx rgba(255, 255, 255, 0.2);
|
||||
/* border: 1px solid rgba(255, 255, 255, 0.15); */
|
||||
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
width: 70rpx;
|
||||
height:70rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
262
subpackages/qr/qr-scan/qr-scan.js
Normal file
262
subpackages/qr/qr-scan/qr-scan.js
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
import jsQR from '../utils/jsQR';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// 相机相关配置
|
||||
cameraContext: null,
|
||||
devicePosition: 'back', // 后置摄像头, 'front' : 'back';
|
||||
flashMode: 'off', // 闪光灯默认关闭,'off' : 'torch'
|
||||
// 扫码框相关配置
|
||||
scanBoxWidth: 580, // 扫码框宽度(rpx)
|
||||
scanBoxHeight: 580, // 扫码框高度(rpx)
|
||||
scanLineTop: 0, // 扫描线初始位置
|
||||
scanLineSpeed: 2, // 扫描线移动速度
|
||||
// 屏幕适配相关
|
||||
windowWidth: 375, // 屏幕宽度(rpx)
|
||||
windowHeight: 667, // 屏幕高度(rpx)
|
||||
scanBoxLeft: 0, // 扫码框左侧偏移
|
||||
scanBoxTop: 0, // 扫码框顶部偏移
|
||||
isScanning: false, // 扫描状态标记,防止重复扫描
|
||||
},
|
||||
// 页面每次显示时(包括跳转返回后)触发
|
||||
onShow() {
|
||||
// 强制重置扫描状态,避免返回后无法扫码
|
||||
this.setData({
|
||||
isScanning: false
|
||||
});
|
||||
this.scanLock = false; // 同步释放全局锁
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const sis = wx.getWindowInfo();
|
||||
// 初始化相机上下文
|
||||
this.setData({
|
||||
cameraContext: wx.createCameraContext(this),
|
||||
// 获取屏幕尺寸用于适配
|
||||
windowWidth: 750,
|
||||
windowHeight: sis.windowHeight / sis.windowWidth * 750,
|
||||
}, () => {
|
||||
// 计算扫码框位置(居中显示)
|
||||
this.calcScanBoxPosition();
|
||||
// 启动扫描线动画
|
||||
this.startScanLineAnimation();
|
||||
});
|
||||
},
|
||||
|
||||
// 计算扫码框居中位置
|
||||
calcScanBoxPosition() {
|
||||
const { windowWidth, windowHeight, scanBoxWidth, scanBoxHeight } = this.data;
|
||||
const scanBoxLeft = (windowWidth - scanBoxWidth) / 2;
|
||||
const scanBoxTop = (windowHeight - scanBoxHeight) / 2 - 100; // 向上偏移100rpx
|
||||
this.setData({ scanBoxLeft, scanBoxTop });
|
||||
},
|
||||
|
||||
|
||||
// 扫描线动画
|
||||
startScanLineAnimation() {
|
||||
const { scanBoxHeight, scanLineSpeed } = this.data;
|
||||
let scanLineTop = 0;
|
||||
this.animationInterval = setInterval(() => {
|
||||
scanLineTop += scanLineSpeed;
|
||||
if (scanLineTop >= scanBoxHeight) {
|
||||
scanLineTop = 0;
|
||||
}
|
||||
this.setData({ scanLineTop });
|
||||
}, 30);
|
||||
},
|
||||
|
||||
// 打开/关闭闪光灯
|
||||
toggleFlash() {
|
||||
this.setData({
|
||||
flashMode: this.data.flashMode === 'torch' ? 'off' : 'torch',
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 扫码结果
|
||||
* @param {*} e
|
||||
*/
|
||||
handleScanCode(e) {
|
||||
// 记录当前时间戳
|
||||
const now = Date.now();
|
||||
// 限制 2 秒内只能处理一次扫码
|
||||
if (this.lastScanTime && now - this.lastScanTime < 2000) {
|
||||
return;
|
||||
}
|
||||
this.lastScanTime = now;
|
||||
|
||||
// 双重锁机制确保不会重复处理
|
||||
if (this.data.isScanning || this.scanLock) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setData({ isScanning: true });
|
||||
this.scanLock = true;
|
||||
|
||||
console.log("扫码结果123-------", e);
|
||||
|
||||
// 处理扫码逻辑
|
||||
this.triggerEvent('scancode', e.detail);
|
||||
this.onCode(e.detail.result)
|
||||
},
|
||||
/**
|
||||
* 点击跳转到我的二维码页面
|
||||
* @param {*} e
|
||||
*/
|
||||
goCode(e) {
|
||||
wx.showToast({ title: "跳转到我的二维码页面" });
|
||||
wx.reLaunch({
|
||||
url: '/subpackages/qr/qr-code/qr-code',
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 手动选择照片后扫码
|
||||
*/
|
||||
chooseImageFirst() {
|
||||
const that = this;
|
||||
wx.chooseMedia({
|
||||
count: 1,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album'],
|
||||
maxDuration: 30,
|
||||
camera: 'back',
|
||||
fail(){
|
||||
wx.showToast({ title: "请选择二维码" });
|
||||
},
|
||||
success(res) {
|
||||
const src = res.tempFiles[0].tempFilePath
|
||||
|
||||
that.setData({
|
||||
imgPath :src
|
||||
})
|
||||
wx.showLoading({
|
||||
title: '识别中...',
|
||||
})
|
||||
console.log("图片地址",src);
|
||||
wx.getImageInfo({
|
||||
src: src,
|
||||
success(res) {
|
||||
console.log("图片信息",res);
|
||||
// 转Canvas像素数据
|
||||
const canvas = wx.createOffscreenCanvas({ type: '2d', width: res.width, height: res.height });
|
||||
const canvasCtx = canvas.getContext('2d');
|
||||
const img = canvas.createImage();
|
||||
img.onload = () => {
|
||||
canvasCtx.drawImage(img, 0, 0, res.width, res.height);
|
||||
const imageData = canvasCtx.getImageData(0, 0, res.width, res.height);
|
||||
// jsQR识别
|
||||
const code = jsQR(imageData.data, res.width, res.height);
|
||||
console.log('识别结果', code);
|
||||
if(code){
|
||||
wx.hideLoading()
|
||||
that.onCode(code.data)
|
||||
}else{
|
||||
wx.showToast({ title: "未识别到二维码" });
|
||||
}
|
||||
|
||||
};
|
||||
img.src = src;
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 扫码后获得的二维码内容
|
||||
* @param {*} data
|
||||
*/
|
||||
onCode(data){
|
||||
try {
|
||||
console.log("扫码结果:", data);
|
||||
if(!data || !data.startsWith('FINDME:')){
|
||||
wx.showToast({
|
||||
title: '无效的二维码,这不是FindMe的用户二维码',
|
||||
icon:"none"
|
||||
})
|
||||
setTimeout(()=>this.setData({imgPath : "" })
|
||||
,1500)
|
||||
this.setData({isScanning:false});
|
||||
this.scanLock = false; // 同步释放全局锁
|
||||
return
|
||||
}
|
||||
const customId = data.split(':')[1]
|
||||
console.log('dataJson',customId);
|
||||
this.handleCode(customId)
|
||||
} catch (error) {
|
||||
wx.showToast({ title: "无法解析二维码内容"});
|
||||
console.log("无法解析二维码内容",error);
|
||||
}finally{
|
||||
this.setData({isScanning:false});
|
||||
this.scanLock = false; // 同步释放全局锁
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
async handleCode(customId){
|
||||
let userInfo = wx.getStorageSync('userInfo');
|
||||
if(customId == userInfo.customId){
|
||||
wx.showToast({
|
||||
title: '不能添加自己为好友',
|
||||
icon:'none'
|
||||
})
|
||||
setTimeout(()=>this.setData({imgPath : "" })
|
||||
,1500)
|
||||
this.setData({isScanning:false});
|
||||
this.scanLock = false; // 同步释放全局锁
|
||||
return
|
||||
}
|
||||
// 检查是否是好友关系
|
||||
await this.checkFriendRelationFromScan(customId);
|
||||
},
|
||||
|
||||
// 检查好友关系(从扫码调用)
|
||||
async checkFriendRelationFromScan(customId) {
|
||||
const friendAPI = require('../../../utils/friend-api.js');
|
||||
wx.showLoading({ title: '加载中...', mask: true });
|
||||
|
||||
const friendDetailResponse = await friendAPI.getFriendDetail(customId).catch(() => null);
|
||||
wx.hideLoading();
|
||||
|
||||
const isFriend = friendDetailResponse?.code === 0 && friendDetailResponse?.data;
|
||||
this.navigateToUserDetail(customId, isFriend);
|
||||
},
|
||||
|
||||
// 跳转到用户详情页面
|
||||
navigateToUserDetail(customId, isFriend = false) {
|
||||
const url = isFriend
|
||||
? `/subpackages/social/friend-detail/friend-detail?customId=${customId}`
|
||||
: `/subpackages/social/user-preview/user-preview?customId=${customId}`;
|
||||
|
||||
wx.navigateTo({
|
||||
url: url,
|
||||
success: () => {
|
||||
this.setData({ imgPath: "" });
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('跳转失败:', err);
|
||||
wx.showToast({ title: '跳转失败,请重试', icon: 'none' });
|
||||
this.setData({ imgPath: "",isScanning: false});
|
||||
this.scanLock = false; // 同步释放全局锁
|
||||
}
|
||||
});
|
||||
},
|
||||
onUnload() {
|
||||
// 页面卸载时停止扫码和动画
|
||||
if (this.animationInterval) {
|
||||
clearInterval(this.animationInterval);
|
||||
}
|
||||
},
|
||||
|
||||
// 授权回调
|
||||
handleGetUserInfo(e) {
|
||||
if (e.detail.userInfo) {
|
||||
// 授权成功后重新初始化
|
||||
this.onLoad();
|
||||
} else {
|
||||
wx.showToast({ title: '需要授权相机权限才能使用扫码功能', icon: 'none' });
|
||||
}
|
||||
}
|
||||
});
|
||||
3
subpackages/qr/qr-scan/qr-scan.json
Normal file
3
subpackages/qr/qr-scan/qr-scan.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"usingComponents": {}
|
||||
}
|
||||
52
subpackages/qr/qr-scan/qr-scan.wxml
Normal file
52
subpackages/qr/qr-scan/qr-scan.wxml
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<!-- 扫码容器 -->
|
||||
<view class="scan-container">
|
||||
|
||||
<!-- 相机组件 -->
|
||||
<camera class="camera" mode="scanCode" device-position="{{devicePosition}}" flash="{{flashMode}}" binderror="handleCameraError" bindscancode="handleScanCode"></camera>
|
||||
<view class="scan-box off" wx:if="{{!!(imgPath)}}">
|
||||
<image class="tempFilePath scan-box"style="width:{{scanBoxWidth}}rpx;height:{{scanBoxHeight}}rpx;left:{{scanBoxLeft}}rpx;top:{{scanBoxTop}}rpx;" src="{{imgPath}}" mode="aspectFill" />
|
||||
</view>
|
||||
<!-- 扫码框 -->
|
||||
<view class="scan-box"style="width:{{scanBoxWidth}}rpx;height:{{scanBoxHeight}}rpx;left:{{scanBoxLeft}}rpx;top:{{scanBoxTop}}rpx;">
|
||||
|
||||
<!-- 扫码框边框 -->
|
||||
<view class="scan-box-border">
|
||||
|
||||
</view>
|
||||
<!-- 扫描线 -->
|
||||
<view class="scan-line"style="top:{{scanLineTop}}rpx;"></view>
|
||||
<!-- 角落标记 -->
|
||||
<view class="scan-corner top-left"></view>
|
||||
<view class="scan-corner top-right"></view>
|
||||
<view class="scan-corner bottom-left"></view>
|
||||
<view class="scan-corner bottom-right"></view>
|
||||
</view>
|
||||
<view class="action-buttons">
|
||||
<!-- 保存二维码按钮 -->
|
||||
<view bindtap="goCode" class="action-btn-out">
|
||||
<!-- 下载图标 -->
|
||||
<view class="action-btn">
|
||||
<image class="action-icon" src="../../../images/qr-code.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="action-icon-text" >二维码</text>
|
||||
</view>
|
||||
|
||||
<!-- 扫描二维码按钮 -->
|
||||
<view class="action-btn-out" >
|
||||
<view class="action-btn" bindtap="chooseImageFirst" >
|
||||
<!-- 扫描图标 -->
|
||||
<image class="action-icon" src="../../../images/Album.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="action-icon-text" >相册</text>
|
||||
|
||||
</view>
|
||||
<!-- 分享二维码按钮 -->
|
||||
<view class="action-btn-out">
|
||||
<view class="action-btn" bindtap="toggleFlash" >
|
||||
<!-- 分享图标 -->
|
||||
<image class="action-icon" src="../../../images/flashlight.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="action-icon-text" >手电筒</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
252
subpackages/qr/qr-scan/qr-scan.wxss
Normal file
252
subpackages/qr/qr-scan/qr-scan.wxss
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/* 扫码容器 */
|
||||
.scan-container {
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #000;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* 相机组件 */
|
||||
.camera {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 扫码框 */
|
||||
.scan-box {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.tempFilePath{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
/* 扫码框边框(半透明) */
|
||||
.scan-box-border {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* border: 1px dashed rgba(0, 255, 0, 0.15); */
|
||||
box-sizing: border-box;
|
||||
/* border: 1px solid red; */
|
||||
}
|
||||
|
||||
/* 扫描线 */
|
||||
.scan-line {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 2rpx;
|
||||
background: linear-gradient(90deg, transparent, #00ffaa, transparent);
|
||||
/* background-color: #00ffaa; */
|
||||
border-radius: 100%;
|
||||
/* box-shadow: 100rpx 100rpx 1px 2px #0f0; */
|
||||
}
|
||||
|
||||
/* 角落标记 */
|
||||
.scan-corner {
|
||||
position: absolute;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border: 3px solid rgba(255, 255, 255, 1);
|
||||
opacity:0;
|
||||
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.scan-corner.top-left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.scan-corner.top-right {
|
||||
top: 0;
|
||||
right: 0;
|
||||
border-left: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.scan-corner.bottom-left {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.scan-corner.bottom-right {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
/* 提示文字 */
|
||||
.scan-tip {
|
||||
position: absolute;
|
||||
bottom: 200rpx;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
text-shadow: 0 0 5px rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
/* 功能按钮区 */
|
||||
.scan-controls {
|
||||
position: absolute;
|
||||
bottom: 60rpx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 结果弹窗 */
|
||||
.result-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0,0,0,0.7);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
width: 600rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.result-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
padding: 20rpx;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 30rpx;
|
||||
word-break: break-all;
|
||||
max-height: 300rpx;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.result-btns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.result-btn {
|
||||
width: 240rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background-color: #07c160;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.rescan-btn {
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
|
||||
/* 底部操作按钮区域样式 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
/* padding: 0 60rpx; */
|
||||
position: fixed;
|
||||
bottom: 50rpx;
|
||||
left: 0;
|
||||
color: white;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.action-btn-out{
|
||||
/* border: 1px solid red; */
|
||||
width: 120rpx;
|
||||
height: 180rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.action-btn {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
background-color: #0a0a0a;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 1),
|
||||
-3rpx -3rpx 2rpx rgba(255, 255, 255, 0.3),
|
||||
inset -3rpx -3rpx 2rpx rgba(255, 255, 255, 0.3);
|
||||
/* border-top-right-radius:; */
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
width: 70rpx;
|
||||
height:70rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.imgPath{
|
||||
height: 80%;
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
.off {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
left: 0rpx;
|
||||
top: 0rpx;
|
||||
/* border: 1px solid rgb(0, 255, 0); */
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
}
|
||||
10100
subpackages/qr/utils/jsQR.js
Normal file
10100
subpackages/qr/utils/jsQR.js
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue