feat(团队收入页): 搜索框设置本地搜索功能

This commit is contained in:
hzj 2025-11-21 16:14:34 +08:00
parent 21e6e017e3
commit c0a85cbb67
2 changed files with 105 additions and 71 deletions

View File

@ -24,7 +24,7 @@
>
<div style="display: flex; flex-direction: column; gap: 10px">
<div>{{ t('history_team_income') }}</div>
<div style="font-weight: 600">$000</div>
<div style="font-weight: 600">$0</div>
</div>
<div
style="
@ -143,11 +143,7 @@
<!-- 账单详情模态框 (使用 TeamBillMore 组件) -->
<div v-if="showDetailModal" style="position: fixed; bottom: 0; width: 100%" @click.stop>
<TeamBillMore
:billData="selectedBill"
@close="closeModal"
@propsReceived="handlePropsReceived"
/>
<TeamBillMore :billData="selectedBill" />
</div>
</div>
</div>
@ -156,9 +152,6 @@
<script setup>
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import GeneralHeader from '@/components/GeneralHeader.vue'
import TeamBillMore from '@/components/team/TeamBillMore.vue'
import { getUserIdentity } from '@/api/wallet.js'
import {
getTeamBill,
getTeamBillWorkMembers,
@ -167,6 +160,9 @@ import {
} from '@/api/teamBill.js'
import { getTeamId } from '@/utils/userStore.js'
import { setDocumentDirection } from '@/locales/i18n'
import GeneralHeader from '@/components/GeneralHeader.vue'
import TeamBillMore from './components/TeamBillMore.vue'
import workReportBox from './components/workReportBox.vue'
const { t, locale } = useI18n()

View File

@ -33,11 +33,13 @@
</div>
</div>
<!-- 搜索 -->
<!-- 搜索 -->
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px">
<!-- 搜索框 -->
<div
style="
flex: 1;
min-width: 0;
border-radius: 32px;
border: 1px solid #fff;
@ -49,11 +51,10 @@
height: max-content;
"
>
<img src="../../assets/icon/search.png" alt="" width="24px" style="opacity: 0.6" />
<img src="../../../assets/icon/search.png" alt="" width="24px" style="opacity: 0.6" />
<input
v-model="searchQuery"
type="text"
name=""
id=""
:placeholder="t('enter_host_id')"
style="
width: 100%;
@ -63,10 +64,15 @@
color: black;
padding: 10px 8px;
"
v-model="targetUserId"
@keyup.enter="performSearch"
/>
</div>
<div style="font-weight: 600" @click="$emit('close')">{{ t('cancel') }}</div>
<!-- 按钮 -->
<div v-if="!hasSearched && searchQuery" style="font-weight: 600" @click="performSearch">
{{ t('search') }}
</div>
<div v-else style="font-weight: 600" @click="clearSearch">{{ t('cancel') }}</div>
</div>
<!-- 加载成员 -->
@ -76,9 +82,9 @@
</div>
<!-- 成员列表 -->
<div v-if="members && members.length > 0" class="members-list">
<div v-if="searchResults && searchResults.length > 0" class="members-list">
<div
v-for="(member, index) in members"
v-for="(member, index) in searchResults"
:key="member.memberProfile?.id || index"
style="
background-color: white;
@ -88,34 +94,41 @@
display: flex;
align-items: center;
justify-content: space-between;
gap: 4px;
"
>
<!-- 用户信息 -->
<div style="width: 70%; display: flex; align-items: center; justify-content: space-around">
<div
style="
flex: 1;
min-width: 0;
display: flex;
align-items: center;
justify-content: space-around;
gap: 4px;
"
>
<!-- 头像 -->
<div
style="
position: relative;
margin-right: 4px;
width: 20%;
aspect-ratio: 1/1;
border-radius: 50%;
"
>
<div style="width: 20%">
<img
v-smart-img
:src="member.memberProfile?.userAvatar || ''"
:alt="member.memberProfile?.userNickname"
@error="defaultAvatarUrl"
style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover"
style="
display: block;
width: 100%;
aspect-ratio: 1/1;
border-radius: 50%;
object-fit: cover;
"
/>
</div>
<!-- 账号信息 -->
<div style="width: 35%; display: flex; flex-direction: column; gap: 4px">
<div style="flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px">
<div
style="
font-weight: 700;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@ -163,18 +176,24 @@
</div>
<!-- 数据区域 -->
<div style="width: 30%; display: flex; justify-content: flex-end">
<div style="min-width: 0; display: flex; justify-content: flex-end">
<div style="width: 100%; display: flex; flex-direction: column">
<div style="width: 100%; display: flex; align-items: center">
<img src="../../assets/icon/coin.png" alt="" height="16px" style="display: block" />
<img
src="../../../assets/icon/coin.png"
alt=""
height="16px"
style="display: block"
/>
<div
style="
flex: 1;
min-width: 0;
color: #ffb627;
font-weight: 510;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
font-size: 0.9em;
"
>
@ -183,12 +202,13 @@
</div>
<div
style="
width: 100%;
min-width: 0;
color: rgba(0, 0, 0, 0.4);
font-weight: 510;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
font-size: 0.7em;
"
>
@ -208,11 +228,12 @@
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useLangStore } from '@/stores/lang'
import { formatBillStatus, getTeamReleasePolicy } from '../../api/teamBill.js'
import { getTeamId } from '../../utils/userStore.js'
import { useI18n } from 'vue-i18n'
import { setDocumentDirection } from '@/locales/i18n'
import { getTeamId } from '@/utils/userStore.js'
import { getTeamReleasePolicy } from '@/api/teamBill.js'
const { t, locale } = useI18n()
@ -232,7 +253,10 @@ const loading = ref(false)
const policyData = ref(null)
const loadingPolicy = ref(false)
const targetUserId = ref('') //id
const searchQuery = ref('') //id
const searchResults = ref([]) //
const isSearching = ref(false)
const hasSearched = ref(false)
//
const color = ref('')
@ -245,6 +269,39 @@ const members = computed(() => {
return props.billData?.members || []
})
//
const performSearch = async () => {
console.log('searchQuery.value:', searchQuery.value)
if (!searchQuery.value.trim()) {
searchResults.value = members.value
return
}
isSearching.value = true
hasSearched.value = false
let ResultUser = members.value.find(
(member) => member.memberProfile.account == searchQuery.value.trim()
)
if (ResultUser) {
searchResults.value = [ResultUser]
} else {
searchResults.value = []
}
isSearching.value = false
hasSearched.value = true
}
//
const clearSearch = () => {
hasSearched.value = false
searchQuery.value = ''
searchResults.value = members.value
}
//
const fetchPolicyData = async () => {
try {
@ -283,9 +340,6 @@ const getTargetLevel = (giftValue) => {
return parseFloat(b.target) - parseFloat(a.target)
})
// console.log('sortedPolicy', JSON.stringify(sortedPolicy))
// console.log('numValue', JSON.stringify(numValue))
for (const policy of sortedPolicy) {
if (numValue >= parseFloat(policy.target)) {
return policy.level
@ -309,7 +363,6 @@ const formatTargetDisplay = (giftValue) => {
//
const getStatusText = (status) => {
// const statusInfo = formatBillStatus(status)
// 使
switch (status) {
case 'Completed':
@ -325,12 +378,6 @@ const getStatusText = (status) => {
}
}
//
const getStatusClass = (status) => {
const statusInfo = formatBillStatus(status)
return statusInfo.class
}
//
const formatDiamondValue = (value) => {
if (!value || value === '0') return '0'
@ -346,25 +393,6 @@ const formatDiamondValue = (value) => {
return numValue.toLocaleString()
}
//
const calculateEffectiveDays = (onlineTime) => {
if (!onlineTime) return '14'
// 线601
const minutes = parseInt(onlineTime)
if (isNaN(minutes)) return '14'
// 601
const days = Math.floor(minutes / 60)
return days > 0 ? days.toString() : '14'
}
//
const getAvatarPlaceholder = (nickname) => {
if (!nickname) return 'U'
return nickname.charAt(0).toUpperCase()
}
//
const defaultAvatarUrl = (e) => {
console.log('头像资源出错')
@ -397,12 +425,22 @@ watch(
{ deep: true }
)
watch(
() => searchQuery.value,
(newQuery) => {
if (hasSearched.value) {
hasSearched.value = false
}
if (!newQuery.trim()) {
searchResults.value = members.value
}
},
{ immediate: true }
)
watch(
() => props.billData?.status,
(newType) => {
console.log('newType:', newType)
// let newStatus = getStatusText(status)
updateTypeShowText()
if (newType == 'In Progress') {
boxShadow.value = '0 0 1px 0 #6085FF'