From 8a546e1b185edd399b83431c8f8aff96c8fd123d Mon Sep 17 00:00:00 2001 From: tianfeng <769204422@qq.com> Date: Fri, 15 Aug 2025 13:12:36 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=84=E5=8F=91=E5=B8=83=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/teamBill.js | 2 +- src/api/wallet.js | 58 +++++++- src/utils/appBridge.js | 12 +- src/views/AgencyCenterView.vue | 26 +++- src/views/CoinSellerSearchView.vue | 62 ++++++-- src/views/CoinSellerView.vue | 224 ++++++++++++++++++++++++++--- src/views/HostCenterView.vue | 86 ++++++++--- src/views/SellerRecordsView.vue | 105 ++++++++++++-- src/views/TeamBillView.vue | 37 ++--- 9 files changed, 512 insertions(+), 100 deletions(-) diff --git a/src/api/teamBill.js b/src/api/teamBill.js index 2ed9e65..74c2817 100644 --- a/src/api/teamBill.js +++ b/src/api/teamBill.js @@ -22,7 +22,7 @@ export const getTeamMembers = async (teamId) => { */ export const deleteMember = async (memberId) => { try { - const response = await post('/team/member/remove', { + const response = await post('/team-member/client/removeMemberTeam', { memberId: memberId }) return response diff --git a/src/api/wallet.js b/src/api/wallet.js index e740783..5c13bd7 100644 --- a/src/api/wallet.js +++ b/src/api/wallet.js @@ -55,10 +55,64 @@ export function userBankSearchUserProfile(type, account) { /** * 获取用户身份信息 + * @param {string} userId - 用户ID * @returns {Promise} 返回用户身份权限信息 */ -export function getUserIdentity() { - return get('/app/h5/1911661502909562881/identity') +export function getUserIdentity(userId) { + return get(`/app/h5/${userId}/identity`) +} + +/** + * 获取团队入口信息 + * @returns {Promise} 返回团队和用户信息 + */ +export function getTeamEntry() { + return get('/team/entry') +} + +/** + * 校验金币销售商权限 + * @returns {Promise} 返回是否具有销售商权限 + */ +export function checkFreightDealer() { + return get('/wallet/freight/seller/check/dealer') +} + +/** + * 获取金币销售商余额 + * @returns {Promise} 返回销售商金币余额 + */ +export function getFreightBalance() { + return get('/wallet/freight/balance') +} + +/** + * 搜索用户 + * @param {string} account - 用户账号 + * @returns {Promise} 返回用户信息 + */ +export function searchFreightUser(account) { + return get(`/wallet/freight/search-user?account=${account}`) +} + +/** + * 金币充值 + * @param {Object} data - 充值数据 + * @param {number} data.acceptUserId - 接收用户ID + * @param {number} data.quantity - 充值数量 + * @returns {Promise} 返回剩余可用余额 + */ +export function freightRecharge(data) { + return post('/wallet/freight/ship', data) +} + +/** + * 获取金币交易流水 + * @param {string} userId - 用户ID + * @returns {Promise} 返回交易流水记录 + */ +export function getFreightWaterFlow(userId) { + return get(`/wallet/freight/balance/running/water/client/flowByUserId?userId=${userId}`) } /** diff --git a/src/utils/appBridge.js b/src/utils/appBridge.js index 32f0704..9a8d2f6 100644 --- a/src/utils/appBridge.js +++ b/src/utils/appBridge.js @@ -35,7 +35,7 @@ export function connectApplication(renderFun) { window.renderData = renderFun // 兼容历史版本 window.getIosAccessOriginParam = renderFun - + try { if (ios()) { // iOS可能需要特殊处理,这里先使用通用方法 @@ -55,10 +55,10 @@ export function connectApplication(renderFun) { // 开发环境可以提供模拟数据 if (process.env.NODE_ENV === 'development') { const mockAccess = JSON.stringify({ - 'Authorization': 'Bearer mock-token-for-development', + 'Authorization': 'Bearer F77126B08E13987C3AD88B6015C5B777.djIlM0ExOTU0MDU5NzkzOTYzMzkzMDI1JTNBTElLRUklM0ExNzU3NzYzODU5NzE1JTNBMTc1NTE3MTg1OTcxNQ==', 'Req-Lang': 'en', 'Req-App-Intel': 'build=1.0;version=2.1;channel=official;Req-Imei=mock-imei', - 'Req-Sys-Origin': 'origin=web;child=browser' + 'Req-Sys-Origin': 'origin=LIKEI;originChild=LIKEI' }) setTimeout(() => renderFun(mockAccess), 100) } @@ -102,13 +102,13 @@ export function parseHeader(head) { /** * 解析键值对字符串 (如: "key1=value1;key2=value2") - * @param {string} values + * @param {string} values * @returns {Object} */ function getKeyValObj(values) { const keyValBody = {} if (!values) return keyValBody - + const valueArray = values.split(';') for (let index = 0; index < valueArray.length; index++) { const keyVal = valueArray[index] @@ -150,7 +150,7 @@ export function setHttpHeaders(headerInfo) { }).catch(error => { console.error('Failed to update HTTP headers:', error) }) - + console.log('🔑 Authorization token set:', headerInfo.authorization) console.log('🌍 Language set:', headerInfo.reqLang) console.log('📱 App info:', { diff --git a/src/views/AgencyCenterView.vue b/src/views/AgencyCenterView.vue index 3ab8473..42c8fcb 100644 --- a/src/views/AgencyCenterView.vue +++ b/src/views/AgencyCenterView.vue @@ -21,7 +21,6 @@
🏢 Agency - 💰 Salary
@@ -636,9 +635,7 @@ onMounted(() => { .team-bill-card .card-content { padding: 16px; - display: flex; - flex-direction: column; - gap: 12px; + position: relative; } /* 通用卡片内容样式 */ @@ -650,20 +647,37 @@ onMounted(() => { font-size: 18px; font-weight: 600; color: #1F2937; + margin-bottom: 8px; } .card-link { + position: absolute; + top: 16px; + right: 16px; display: flex; - justify-content: space-between; align-items: center; - color: #6B7280; + gap: 4px; + color: #8B5CF6; font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: color 0.2s; +} + +.card-link:hover { + color: #7C3AED; +} + +.card-link .arrow { + font-size: 16px; + font-weight: bold; } .card-details { display: flex; flex-direction: column; gap: 8px; + margin-top: 12px; } .detail-row { diff --git a/src/views/CoinSellerSearchView.vue b/src/views/CoinSellerSearchView.vue index 34bb31f..3d99a87 100644 --- a/src/views/CoinSellerSearchView.vue +++ b/src/views/CoinSellerSearchView.vue @@ -40,7 +40,14 @@ >
- {{ user.name.charAt(0) }} + + {{ user.name.charAt(0) }}

{{ user.name }}

@@ -81,6 +88,7 @@ import { ref } from 'vue' import { useRouter } from 'vue-router' import MobileHeader from '../components/MobileHeader.vue' import { setSelectedUser } from '../utils/coinSellerStore.js' +import { searchFreightUser } from '../api/wallet.js' const router = useRouter() const searchQuery = ref('') @@ -104,7 +112,7 @@ const mockUsers = [ ] // 执行搜索 -const performSearch = () => { +const performSearch = async () => { if (!searchQuery.value.trim()) { searchResults.value = [] return @@ -112,15 +120,28 @@ const performSearch = () => { isSearching.value = true - // 模拟搜索延迟 - setTimeout(() => { - const query = searchQuery.value.toLowerCase() - searchResults.value = mockUsers.filter(user => - user.name.toLowerCase().includes(query) || - user.id.includes(query) - ) + try { + const response = await searchFreightUser(searchQuery.value.trim()) + + if (response && response.status && response.body && response.body.userProfile) { + const profile = response.body.userProfile + searchResults.value = [{ + id: profile.account || profile.id, + userId: profile.id, // 保存数值型ID用于接口调用 + name: profile.userNickname || 'User', + userNickname: profile.userNickname, + account: profile.account, + userAvatar: profile.userAvatar + }] + } else { + searchResults.value = [] + } + } catch (error) { + console.error('搜索用户失败:', error) + searchResults.value = [] + } finally { isSearching.value = false - }, 500) + } } // 清除搜索 @@ -148,7 +169,11 @@ const selectUser = (user) => { // 将选中的用户信息保存到全局状态 setSelectedUser({ id: user.id, - name: user.name + userId: user.userId, // 数值型ID + name: user.name, + userNickname: user.userNickname, + account: user.account, + userAvatar: user.userAvatar }) console.log('Selected user:', user) @@ -267,6 +292,21 @@ const selectUser = (user) => { font-size: 18px; font-weight: 600; margin-right: 12px; + overflow: hidden; + position: relative; +} + +.avatar-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 25px; +} + +.avatar-text { + color: white; + font-size: 18px; + font-weight: 600; } .user-details h4 { diff --git a/src/views/CoinSellerView.vue b/src/views/CoinSellerView.vue index 499b76b..7b2634b 100644 --- a/src/views/CoinSellerView.vue +++ b/src/views/CoinSellerView.vue @@ -2,25 +2,39 @@
-
+ +
+
+

正在校验权限...

+
+ + +
@@ -29,7 +43,8 @@

My coins:

🪙 - {{ coinsAmount }} + Loading... + {{ coinsAmount }}
@@ -67,10 +82,10 @@
@@ -82,17 +97,26 @@ import { ref, computed, onMounted } from 'vue' import { useRouter } from 'vue-router' import MobileHeader from '../components/MobileHeader.vue' import { getSelectedUser, clearSelectedUser } from '../utils/coinSellerStore.js' +import { checkFreightDealer, getTeamEntry, getFreightBalance, searchFreightUser, freightRecharge } from '../api/wallet.js' const router = useRouter() // 用户信息 const userInfo = ref({ name: 'User1', - id: '1234567890' + id: '1234567890', + userNickname: 'User1', + account: '1234567890', + userAvatar: null }) +// 权限校验状态 +const dealerAccess = ref(false) +const checkingAccess = ref(true) + // 金币数量 -const coinsAmount = ref('123456478') +const coinsAmount = ref('0') +const loadingBalance = ref(false) // 选中的用户 const selectedUser = ref({ @@ -100,6 +124,73 @@ const selectedUser = ref({ name: '' }) +// 校验销售商权限 +const checkDealerAccess = async () => { + try { + checkingAccess.value = true + const response = await checkFreightDealer() + + if (response && response.status !== undefined) { + dealerAccess.value = response.body === true + + if (!dealerAccess.value) { + // 没有权限,跳转回上一页或首页 + alert('您没有金币销售商权限') + router.go(-1) + return + } + } + } catch (error) { + console.error('权限校验失败:', error) + dealerAccess.value = false + router.go(-1) + } finally { + checkingAccess.value = false + } +} + +// 获取用户信息 +const fetchUserInfo = async () => { + try { + const response = await getTeamEntry() + + if (response && response.status && response.body && response.body.memberProfile) { + const profile = response.body.memberProfile + userInfo.value = { + name: profile.userNickname || 'User', + id: profile.account || profile.id, + userNickname: profile.userNickname, + account: profile.account, + userAvatar: profile.userAvatar + } + } + } catch (error) { + console.error('获取用户信息失败:', error) + } +} + +// 获取金币余额 +const fetchFreightBalance = async () => { + try { + loadingBalance.value = true + const response = await getFreightBalance() + + if (response && response.status && response.body !== undefined) { + coinsAmount.value = response.body.toString() + } + } catch (error) { + console.error('获取金币余额失败:', error) + } finally { + loadingBalance.value = false + } +} + +// 处理头像加载错误 +const handleAvatarError = () => { + console.log('头像加载失败,使用默认显示') + userInfo.value.userAvatar = null +} + // 初始化用户信息 const initializeUser = () => { const savedUser = getSelectedUser() @@ -110,10 +201,11 @@ const initializeUser = () => { // 充值金额 const rechargeAmount = ref('') +const isRecharging = ref(false) // 是否可以充值 const canRecharge = computed(() => { - return selectedUser.value.id && rechargeAmount.value && parseFloat(rechargeAmount.value) > 0 + return selectedUser.value.id && rechargeAmount.value && parseFloat(rechargeAmount.value) > 0 && !isRecharging.value }) // 跳转到设置 @@ -127,19 +219,64 @@ const searchUser = () => { } // 立即充值 -const rechargeNow = () => { - if (!canRecharge.value) return +const rechargeNow = async () => { + if (!canRecharge.value || isRecharging.value) return - alert(`Recharge ${rechargeAmount.value} coins to ${selectedUser.value.name}`) + const amount = parseFloat(rechargeAmount.value) + if (amount <= 0) { + alert('请输入正确的充值金额') + return + } - // 重置表单 - rechargeAmount.value = '' - // 可以选择是否清除用户选择 + // 需要获取接收用户的ID(从用户搜索结果中获取) + const acceptUserId = selectedUser.value.userId || selectedUser.value.id + if (!acceptUserId) { + alert('无法获取用户ID,请重新选择用户') + return + } + + try { + isRecharging.value = true + + const rechargeData = { + acceptUserId: parseInt(acceptUserId), + quantity: amount + } + + const response = await freightRecharge(rechargeData) + + if (response && response.status) { + // 充值成功,更新余额 + if (response.body !== undefined) { + coinsAmount.value = response.body.toString() + } + + alert(`成功向 ${selectedUser.value.name} 充值 ${amount} 金币`) + + // 重置表单 + rechargeAmount.value = '' + } else { + alert('充值失败,请重试') + } + } catch (error) { + console.error('充值失败:', error) + alert('充值失败,请检查网络后重试') + } finally { + isRecharging.value = false + } } -// 页面加载时初始化用户信息 -onMounted(() => { - initializeUser() +// 页面加载时校验权限和初始化用户信息 +onMounted(async () => { + // 先校验权限 + await checkDealerAccess() + + // 权限校验通过后获取用户信息和金币余额 + if (dealerAccess.value) { + await fetchUserInfo() + await fetchFreightBalance() + initializeUser() + } }) @@ -148,6 +285,30 @@ onMounted(() => { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } +/* 加载状态 */ +.loading-container { + display: flex; + flex-direction: column; + align-items: center; + padding: 40px 20px; + color: #666; +} + +.loading-spinner { + width: 32px; + height: 32px; + border: 3px solid #f3f3f3; + border-top: 3px solid #F59E0B; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 12px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + .content { padding: 16px; position: relative; @@ -181,6 +342,21 @@ onMounted(() => { font-size: 20px; font-weight: 600; margin-right: 12px; + overflow: hidden; + position: relative; +} + +.avatar-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 25px; +} + +.avatar-text { + color: white; + font-size: 20px; + font-weight: 600; } .user-details { @@ -258,6 +434,12 @@ onMounted(() => { color: #F59E0B; } +.coins-loading { + font-size: 16px; + color: #9ca3af; + font-style: italic; +} + /* 用户搜索部分 */ .user-section { background-color: white; diff --git a/src/views/HostCenterView.vue b/src/views/HostCenterView.vue index e94e183..3d06227 100644 --- a/src/views/HostCenterView.vue +++ b/src/views/HostCenterView.vue @@ -19,11 +19,11 @@
- {{ userInfo.name.charAt(0) }} + {{ (userInfo.userNickname || userInfo.name).charAt(0) }}
-

{{ userInfo.name }}

-

ID: {{ userInfo.id }}

+

{{ userInfo.userNickname || userInfo.name }}

+

ID: {{ userInfo.account || userInfo.id }}

{{ headerInfo.sysOrigin }} @@ -132,7 +132,7 @@ import { ref, reactive, onMounted } from 'vue' import { useRouter } from 'vue-router' import MobileHeader from '../components/MobileHeader.vue' -import { getBankBalance, getUserIdentity } from '../api/wallet.js' +import { getBankBalance, getUserIdentity, getTeamEntry } from '../api/wallet.js' import { connectApplication, parseAccessOrigin, parseHeader, setHttpHeaders, isInApp } from '../utils/appBridge.js' const router = useRouter() @@ -142,6 +142,8 @@ const currentIdentity = ref('host') const loading = ref(false) const appConnected = ref(false) const headerInfo = ref({}) +const userProfile = ref(null) +const identityChecked = ref(false) // 身份选项(保留显示用,但不再用于切换) const identities = [ @@ -151,16 +153,16 @@ const identities = [ ] // 连接APP并获取认证信息 -const connectToApp = () => { +const connectToApp = async () => { if (!isInApp()) { console.warn('Not running in APP environment') // 非APP环境,直接执行后续逻辑 appConnected.value = true - checkUserIdentity() + await fetchTeamEntry() return } - connectApplication(access => { + connectApplication(async (access) => { console.log('Received access from APP:', access) const result = parseAccessOrigin(access) @@ -176,8 +178,8 @@ const connectToApp = () => { console.log('APP connection established:', headerInfo.value) - // 连接成功后获取用户身份和银行余额 - checkUserIdentity() + // 连接成功后获取团队信息和银行余额 + await fetchTeamEntry() fetchBankBalance() } else { console.error('Failed to parse access origin:', result.error) @@ -213,7 +215,9 @@ const fetchBankBalance = async () => { // 用户信息 const userInfo = reactive({ name: 'User1', - id: '1234567890' + id: '1234567890', + userNickname: 'User1', + account: '1234567890' }) // 收入信息 @@ -250,24 +254,70 @@ const goToNotification = () => { router.push('/host-setting') } -// 获取用户身份并自动跳转 -const checkUserIdentity = async () => { +// 获取团队入口信息 +const fetchTeamEntry = async () => { try { - const response = await getUserIdentity() + const response = await getTeamEntry() + + if (response && response.status && response.body) { + userProfile.value = response.body + + // 更新用户信息显示 + if (response.body.memberProfile) { + userInfo.userNickname = response.body.memberProfile.userNickname + userInfo.account = response.body.memberProfile.account + } + + // 获取用户ID后检查身份 + const userId = response.body.memberProfile?.id + if (userId) { + await checkUserIdentity(userId) + } + + return userId + } + } catch (error) { + console.error('获取团队信息失败:', error) + } + return null +} +// 获取用户身份并自动跳转 +const checkUserIdentity = async (userId) => { + if (!userId) { + console.error('用户ID不存在') + return + } + + // 防止重复检查 + if (identityChecked.value) { + console.log('身份已检查,跳过') + return + } + + try { + identityChecked.value = true + const response = await getUserIdentity(userId) + if (response && response.status && response.body) { const { freightAgent, anchor, agent } = response.body - + + console.log('用户身份权限:', { freightAgent, anchor, agent }) + // 根据优先级判断:freightAgent > anchor > agent if (freightAgent) { - router.push('/coin-seller') + console.log('跳转到 coin-seller') + router.replace('/coin-seller') } else if (anchor) { - router.push('/agency-center') + console.log('跳转到 agency-center') + router.replace('/agency-center') } else if (agent) { // 保持在当前Host页面 + console.log('保持在 host-center') currentIdentity.value = 'host' } else { // 如果没有任何权限,默认保持在Host页面 + console.log('无特殊权限,保持在 host-center') currentIdentity.value = 'host' } } @@ -288,9 +338,9 @@ const transfer = () => { } // 页面加载时先连接APP,然后获取数据 -onMounted(() => { +onMounted(async () => { // 先连接APP获取认证信息 - connectToApp() + await connectToApp() // 如果不是APP环境,直接获取银行余额 if (!isInApp()) { diff --git a/src/views/SellerRecordsView.vue b/src/views/SellerRecordsView.vue index fda0df5..9789830 100644 --- a/src/views/SellerRecordsView.vue +++ b/src/views/SellerRecordsView.vue @@ -25,7 +25,8 @@

- {{ record.amount }} coins + {{ record.amount }} coins + {{ getTransactionType(record.type, record.origin) }}
@@ -43,10 +44,12 @@ import { ref, onMounted } from 'vue' import { useRouter } from 'vue-router' import MobileHeader from '../components/MobileHeader.vue' +import { getFreightWaterFlow, getTeamEntry } from '../api/wallet.js' const router = useRouter() const loading = ref(false) const records = ref([]) +const currentUserId = ref(null) // 模拟记录数据 const mockRecords = [ @@ -73,24 +76,93 @@ const mockRecords = [ } ] +// 获取当前用户ID +const fetchCurrentUserId = async () => { + try { + const response = await getTeamEntry() + if (response && response.status && response.body && response.body.memberProfile) { + currentUserId.value = response.body.memberProfile.id + return response.body.memberProfile.id + } + } catch (error) { + console.error('获取用户ID失败:', error) + } + return null +} + +// 格式化时间 +const formatTime = (timestamp) => { + const date = new Date(timestamp) + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + const seconds = String(date.getSeconds()).padStart(2, '0') + + return `${year}.${month}.${day} ${hours}:${minutes}:${seconds}` +} + +// 获取交易类型显示 +const getTransactionType = (type, origin) => { + if (type === 0) { + return '进货' // 入账 + } else if (type === 1) { + return '出货' // 出账 + } + return origin || '未知' +} + +// 获取金额显示 +const getAmountDisplay = (type, quantity) => { + const prefix = type === 0 ? '+' : '-' + return `${prefix}${quantity}` +} + // 获取记录 const fetchRecords = async () => { loading.value = true try { - // 模拟API调用 - setTimeout(() => { - records.value = mockRecords - loading.value = false - }, 800) + // 先获取用户ID + const userId = currentUserId.value || await fetchCurrentUserId() + + if (!userId) { + console.error('无法获取用户ID') + records.value = [] + return + } + + const response = await getFreightWaterFlow(userId) + + if (response && response.status && response.body) { + // 处理返回的数据 + records.value = response.body.map(item => ({ + id: item.id, + userId: item.acceptUserId, + userName: `User${item.acceptUserId.toString().slice(-6)}...`, // 显示最后6位 + amount: getAmountDisplay(item.type, item.quantity), + time: formatTime(item.createTime), + type: item.type, + quantity: item.quantity, + balance: item.balance, + origin: item.origin, + remark: item.remark, + rechargeType: item.rechargeType + })) + } else { + records.value = [] + } } catch (error) { - console.error('Failed to fetch records:', error) + console.error('获取记录失败:', error) + records.value = [] + } finally { loading.value = false } } -onMounted(() => { - fetchRecords() +onMounted(async () => { + await fetchRecords() }) @@ -203,7 +275,20 @@ onMounted(() => { .coins-amount { font-size: 14px; font-weight: 600; - color: #EF4444; +} + +.coins-amount.positive { + color: #10B981; /* 绿色表示进货/入账 */ +} + +.coins-amount.negative { + color: #EF4444; /* 红色表示出货/出账 */ +} + +.transaction-type { + font-size: 12px; + color: #9ca3af; + margin-top: 2px; } /* 空状态 */ diff --git a/src/views/TeamBillView.vue b/src/views/TeamBillView.vue index c420739..6de23ca 100644 --- a/src/views/TeamBillView.vue +++ b/src/views/TeamBillView.vue @@ -4,19 +4,6 @@
-
-
-
- Avatar - {{ memberProfile.countryCode }} -
-
-
{{ memberProfile.userNickname }}
-
ID: {{ memberProfile.account }}
-
{{ memberProfile.accountStatusName }}
-
-
-
@@ -32,8 +19,8 @@
-
- +
Host Salary: ${{ formatSalaryValue(bill.target?.ownSalary) }}
- +
Agent Salary: ${{ formatSalaryValue(bill.target?.memberSalary) }} @@ -61,7 +48,7 @@ ${{ formatSalaryValue(bill.target?.totalSalry) }}
- +
More > @@ -83,14 +70,14 @@

Work Report Help

- +

Below is the work report of the team members. Click "More" to view detailed daily work records.

- +

Status Description:

@@ -118,8 +105,8 @@