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

View File

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