aslan-h5/src/views/TeamBillView.vue
2025-08-22 20:29:39 +08:00

640 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="team-bill gradient-background-circles">
<MobileHeader title="Team Bill" />
<div class="content">
<!-- 成员资料信息 -->
<!-- 工作报告标题 -->
<div class="work-report-title">
<span>Work report:</span>
<button class="help-btn" @click="showHelpInfo">?</button>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading-container">
<div class="loading-spinner"></div>
<p>Loading...</p>
</div>
<!-- 账单列表 -->
<div v-else-if="billList.length > 0" class="bill-list">
<div
v-for="bill in billList"
:key="bill.id"
class="bill-item"
@click="showBillDetail(bill)"
>
<div class="bill-header">
<span class="bill-period">{{ bill.billTitle }}</span>
<span :class="['bill-status', getStatusClass(bill.status)]">
{{ getStatusText(bill.status) }}
</span>
</div>
<div class="bill-details">
<div class="bill-row">
<span class="bill-label">Create Time:</span>
<span class="bill-value">{{ bill.createTimeFormatted }}</span>
</div>
<div class="bill-row">
<span class="bill-label">Period:</span>
<span class="bill-value">{{ bill.billBelong }}</span>
</div>
<div class="bill-row">
<span class="bill-label">Last Update:</span>
<span class="bill-value">{{ bill.updateTimeFormatted }}</span>
</div>
</div>
<div class="more-btn">
<span>More</span>
<span class="arrow">></span>
</div>
</div>
</div>
<!-- 空数据状态 -->
<div v-else class="empty-state">
<div class="empty-icon">📋</div>
<p>No data available</p>
</div>
</div>
<!-- 帮助模态框 -->
<div v-if="showHelpModal" class="modal-overlay" @click="closeHelpModal">
<div class="help-modal-content" @click.stop>
<div class="help-modal-header">
<h3>Work Report Help</h3>
<button class="close-btn" @click="closeHelpModal">×</button>
</div>
<div class="help-modal-body">
<div class="help-section">
<p class="help-description">
Below is the work report of the team members. Click "More" to view detailed daily work records.
</p>
</div>
<div class="help-section">
<h4>Status Description:</h4>
<div class="status-list">
<div class="status-item">
<span class="status-number">(1)</span>
<span class="status-name">UNPAID:</span>
<span class="status-desc">Work is currently in progress.</span>
</div>
<div class="status-item">
<span class="status-number">(2)</span>
<span class="status-name">HANG_UP:</span>
<span class="status-desc">Work has been suspended or is pending review.</span>
</div>
<div class="status-item">
<span class="status-number">(3)</span>
<span class="status-name">PAY_OUT:</span>
<span class="status-desc">Work has been completed and paid out.</span>
</div>
<div class="status-item">
<span class="status-number">(4)</span>
<span class="status-name">PAID:</span>
<span class="status-desc">Work has been completed and settled.</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 账单详情模态框 (使用 TeamBillMore 组件) -->
<div v-if="showDetailModal" class="modal-overlay">
<div class="detail-modal-wrapper">
<TeamBillMore
:billData="selectedBill"
@close="closeDetailModal"
@propsReceived="handlePropsReceived"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import MobileHeader from '../components/MobileHeader.vue'
import TeamBillMore from '../components/team/TeamBillMore.vue'
import { getTeamBill, getTeamBillWorkMembers, formatBillStatus } from '../api/teamBill.js'
import { getTeamId } from "@/utils/userStore.js"
const loading = ref(false)
const showHelpModal = ref(false)
const showDetailModal = ref(false)
const selectedBill = ref(null)
// 数据
const billList = ref([])
const selectedBillMembers = ref([])
// 获取团队账单列表
const fetchTeamBill = async (teamId) => {
try {
loading.value = true
const response = await getTeamBill(teamId)
if (response.status && response.body) {
billList.value = response.body.map(bill => ({
...bill,
// 将时间戳转换为可读格式
createTimeFormatted: new Date(bill.createTime).toLocaleDateString(),
updateTimeFormatted: new Date(bill.updateTime).toLocaleDateString()
}))
}
} catch (error) {
console.error('Failed to fetch team bill:', error)
} finally {
loading.value = false
}
}
// 获取账单的工资成员信息
const fetchBillWorkMembers = async (billId) => {
try {
const response = await getTeamBillWorkMembers(billId)
if (response.status && response.body) {
return response.body
}
return []
} catch (error) {
console.error('Failed to fetch bill work members:', error)
return []
}
}
// 格式化状态文本
const getStatusText = (status) => {
const statusInfo = formatBillStatus(status)
return statusInfo.text
}
// 获取状态样式类
const getStatusClass = (status) => {
const statusInfo = formatBillStatus(status)
return statusInfo.class
}
// 移除不再使用的函数
// const formatDate = (dateNumber) => {
// return formatDateDisplay(dateNumber)
// }
// 显示账单详情
const showBillDetail = async (bill) => {
// 获取该账单的成员工作信息
const membersData = await fetchBillWorkMembers(bill.id)
selectedBillMembers.value = membersData
selectedBill.value = {
...bill,
members: membersData
}
showDetailModal.value = true
}
// 关闭详情模态框
const closeDetailModal = () => {
showDetailModal.value = false
selectedBill.value = null
}
// 显示帮助模态框
const showHelpInfo = () => {
showHelpModal.value = true
}
// 关闭帮助模态框
const closeHelpModal = () => {
showHelpModal.value = false
}
// 处理道具领取成功
const handlePropsReceived = () => {
// 可以在这里添加提示或者刷新数据
console.log('Props received successfully')
}
// 组件挂载时获取数据
onMounted(() => {
// 获取团队ID
const teamId = getTeamId()
if (teamId) {
fetchTeamBill(teamId)
} else {
console.warn('未找到团队ID')
}
})
</script>
<style scoped>
.team-bill {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
min-height: 100vh;
background-color: #f1f2f3;
}
.content {
padding: 16px;
position: relative;
z-index: 2;
}
/* 成员资料卡片 */
.member-profile-card {
background-color: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.member-info {
display: flex;
align-items: center;
gap: 12px;
}
.avatar-wrapper {
position: relative;
flex-shrink: 0;
}
.member-avatar {
width: 48px;
height: 48px;
border-radius: 8px;
object-fit: cover;
}
.country-flag {
position: absolute;
bottom: -4px;
left: 2px;
background-color: #333;
color: white;
font-size: 10px;
padding: 1px 3px;
border-radius: 2px;
}
.member-details {
flex: 1;
}
.member-name {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 4px;
}
.member-id {
font-size: 12px;
color: #666;
margin-bottom: 2px;
}
.member-status {
font-size: 12px;
color: #10B981;
}
/* 工作报告标题 */
.work-report-title {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
color: #666;
margin-bottom: 12px;
}
.help-btn {
width: 20px;
height: 20px;
border-radius: 10px;
background-color: #666;
color: white;
border: none;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
}
.help-btn:hover {
background-color: #8B5CF6;
}
/* 加载状态 */
.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 #8B5CF6;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 12px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 账单列表 */
.bill-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.bill-item {
background-color: white;
padding: 16px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
cursor: pointer;
transition: all 0.2s;
}
.bill-item:active {
transform: scale(0.98);
}
.bill-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.bill-period {
font-size: 16px;
font-weight: 600;
color: #333;
}
.bill-status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.bill-status.in-progress {
background-color: #DBEAFE;
color: #1D4ED8;
}
.bill-status.pending {
background-color: #FEF3C7;
color: #D97706;
}
.bill-status.completed {
background-color: #D1FAE5;
color: #059669;
}
.bill-details {
margin-bottom: 12px;
}
.bill-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.bill-row:last-child {
margin-bottom: 0;
}
.bill-label {
font-size: 14px;
color: #666;
}
.bill-value {
font-size: 14px;
font-weight: 600;
color: #333;
}
.total-salary-value {
color: #059669 !important;
font-weight: 700;
}
.more-btn {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 4px;
color: #C084FC;
font-size: 14px;
cursor: pointer;
}
.arrow {
font-weight: bold;
}
/* 空数据状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
padding: 40px 20px;
color: #666;
}
.empty-icon {
font-size: 48px;
margin-bottom: 12px;
}
/* 模态框公共样式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 16px;
}
.help-modal-content,
.detail-modal-content {
background-color: white;
border-radius: 12px;
width: 100%;
max-width: 400px;
max-height: 80vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
border-bottom: 1px solid #e5e7eb;
}
.modal-header h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #333;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
padding: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.modal-body {
padding: 16px;
overflow-y: auto;
}
/* 帮助模态框样式 */
.help-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
border-bottom: 1px solid #e5e7eb;
background-color: #f8f9fa;
}
.help-modal-header h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #333;
}
.help-modal-body {
padding: 20px;
overflow-y: auto;
}
.help-section {
margin-bottom: 20px;
}
.help-section:last-child {
margin-bottom: 0;
}
.help-description {
font-size: 14px;
line-height: 1.5;
color: #555;
margin: 0;
padding: 12px;
background-color: #f0f9ff;
border-radius: 8px;
border-left: 4px solid #3b82f6;
}
.help-section h4 {
margin: 0 0 12px 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.status-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.status-item {
display: flex;
gap: 8px;
padding: 12px;
background-color: #f9fafb;
border-radius: 8px;
border: 1px solid #e5e7eb;
}
.status-number {
font-weight: 600;
color: #8B5CF6;
flex-shrink: 0;
}
.status-name {
font-weight: 600;
color: #333;
flex-shrink: 0;
min-width: 80px;
}
.status-desc {
color: #666;
line-height: 1.4;
flex: 1;
}
/* 详情模态框新样式 */
.detail-modal-wrapper {
width: 100%;
height: 100%;
max-width: 100vw;
max-height: 100vh;
overflow: auto;
background-color: white;
border-radius: 0;
}
/* 在大屏幕上显示为模态框 */
@media (min-width: 768px) {
.detail-modal-wrapper {
width: 90%;
height: 90%;
max-width: 600px;
max-height: 80vh;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
}
</style>