update pay promo

This commit is contained in:
2026-02-03 12:35:22 +08:00
parent 57e5672dc1
commit 28c8fe4283
5 changed files with 72 additions and 44 deletions

5
app.py
View File

@@ -3733,6 +3733,7 @@ def get_invoice_available_orders():
"""获取可开票订单列表(已支付且未申请开票的订单)"""
try:
user_id = session['user_id']
print(f"[发票API] 获取可开票订单 - 用户ID: {user_id}")
# 查询已支付的订单
paid_orders = PaymentOrder.query.filter_by(
@@ -3740,6 +3741,10 @@ def get_invoice_available_orders():
status='paid'
).order_by(PaymentOrder.paid_at.desc()).all()
print(f"[发票API] 查询到已支付订单数: {len(paid_orders)}")
for po in paid_orders:
print(f"[发票API] 订单ID={po.id}, 订单号={po.order_no}, 金额={po.amount}, 支付时间={po.paid_at}")
available_orders = []
for order in paid_orders:
# 检查该订单是否已申请过发票

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -10,7 +10,6 @@ import {
Text,
Button,
Icon,
useColorModeValue,
Divider,
} from '@chakra-ui/react';
import { Download, Eye, X, FileText } from 'lucide-react';
@@ -40,32 +39,29 @@ function InvoiceCard({
onDownload,
onCancel,
}: InvoiceCardProps) {
const bgCard = useColorModeValue('white', 'gray.800');
const borderColor = useColorModeValue('gray.200', 'gray.600');
const headerBg = useColorModeValue('gray.50', 'gray.700');
const canDownload = invoice.status === 'completed' && invoice.invoiceType === 'electronic';
const canCancel = invoice.status === 'pending';
return (
<Box
bg={bgCard}
bg="transparent"
borderRadius="lg"
border="1px solid"
borderColor={borderColor}
borderColor="rgba(212, 175, 55, 0.3)"
overflow="hidden"
transition="all 0.2s"
_hover={{ shadow: 'md' }}
backdropFilter="blur(10px)"
_hover={{ borderColor: 'rgba(212, 175, 55, 0.5)' }}
>
{/* 头部 */}
<HStack justify="space-between" p={4} bg={headerBg}>
<HStack justify="space-between" p={4} bg="rgba(212, 175, 55, 0.05)">
<HStack spacing={3}>
<Icon as={FileText} boxSize={5} color="blue.500" />
<Icon as={FileText} boxSize={5} color="#D4AF37" />
<VStack align="flex-start" spacing={0}>
<Text fontWeight="600" fontSize="sm">
<Text fontWeight="600" fontSize="sm" color="white">
{invoice.title}
</Text>
<Text fontSize="xs" color="gray.500">
<Text fontSize="xs" color="gray.400">
{invoiceTypeMap[invoice.invoiceType]} · {titleTypeMap[invoice.titleType]}
</Text>
</VStack>
@@ -73,74 +69,76 @@ function InvoiceCard({
<InvoiceStatusBadge status={invoice.status} />
</HStack>
<Divider />
<Divider borderColor="rgba(212, 175, 55, 0.2)" />
{/* 内容 */}
<VStack align="stretch" spacing={3} p={4}>
<HStack justify="space-between">
<Text fontSize="sm" color="gray.500">
<Text fontSize="sm" color="gray.400">
</Text>
<Text fontSize="sm">{invoice.orderNo}</Text>
<Text fontSize="sm" color="gray.200">{invoice.orderNo}</Text>
</HStack>
<HStack justify="space-between">
<Text fontSize="sm" color="gray.500">
<Text fontSize="sm" color="gray.400">
</Text>
<Text fontSize="sm" fontWeight="600" color="blue.500">
<Text fontSize="sm" fontWeight="600" color="#D4AF37">
¥{invoice.amount.toFixed(2)}
</Text>
</HStack>
{invoice.invoiceNo && (
<HStack justify="space-between">
<Text fontSize="sm" color="gray.500">
<Text fontSize="sm" color="gray.400">
</Text>
<Text fontSize="sm">{invoice.invoiceNo}</Text>
<Text fontSize="sm" color="gray.200">{invoice.invoiceNo}</Text>
</HStack>
)}
<HStack justify="space-between">
<Text fontSize="sm" color="gray.500">
<Text fontSize="sm" color="gray.400">
</Text>
<Text fontSize="sm">
<Text fontSize="sm" color="gray.200">
{new Date(invoice.createdAt).toLocaleString('zh-CN')}
</Text>
</HStack>
{invoice.completedAt && (
<HStack justify="space-between">
<Text fontSize="sm" color="gray.500">
<Text fontSize="sm" color="gray.400">
</Text>
<Text fontSize="sm">
<Text fontSize="sm" color="gray.200">
{new Date(invoice.completedAt).toLocaleString('zh-CN')}
</Text>
</HStack>
)}
{invoice.rejectReason && (
<Box p={2} bg="red.50" borderRadius="md">
<Text fontSize="xs" color="red.600">
<Box p={2} bg="rgba(229, 62, 62, 0.15)" borderRadius="md">
<Text fontSize="xs" color="red.300">
: {invoice.rejectReason}
</Text>
</Box>
)}
</VStack>
<Divider />
<Divider borderColor="rgba(212, 175, 55, 0.2)" />
{/* 操作按钮 */}
<HStack justify="flex-end" spacing={2} p={3}>
<HStack justify="center" spacing={2} p={3}>
{onView && (
<Button
size="sm"
variant="ghost"
color="gray.300"
leftIcon={<Icon as={Eye} boxSize={4} />}
onClick={onView}
_hover={{ color: 'white', bg: 'whiteAlpha.100' }}
>
</Button>
@@ -148,10 +146,12 @@ function InvoiceCard({
{canDownload && onDownload && (
<Button
size="sm"
colorScheme="blue"
variant="outline"
borderColor="#D4AF37"
color="#D4AF37"
leftIcon={<Icon as={Download} boxSize={4} />}
onClick={onDownload}
_hover={{ bg: 'rgba(212, 175, 55, 0.1)' }}
>
</Button>
@@ -159,10 +159,11 @@ function InvoiceCard({
{canCancel && onCancel && (
<Button
size="sm"
colorScheme="red"
variant="ghost"
color="red.400"
leftIcon={<Icon as={X} boxSize={4} />}
onClick={onCancel}
_hover={{ bg: 'rgba(229, 62, 62, 0.1)' }}
>
</Button>

View File

@@ -18,7 +18,6 @@ import {
SimpleGrid,
Spinner,
Center,
useColorModeValue,
useToast,
useDisclosure,
AlertDialog,
@@ -72,8 +71,6 @@ export default function InvoicePage({ embedded = false }: InvoicePageProps) {
const [cancelingId, setCancelingId] = useState<string | null>(null);
const toast = useToast();
const textColor = useColorModeValue("gray.700", "white");
const bgCard = useColorModeValue("white", "gray.800");
const cancelDialogRef = React.useRef<HTMLButtonElement>(null);
const {
@@ -210,7 +207,7 @@ export default function InvoicePage({ embedded = false }: InvoicePageProps) {
>
<Stat>
<StatLabel color="gray.500"></StatLabel>
<StatNumber color={textColor}>{stats.total}</StatNumber>
<StatNumber color="white">{stats.total}</StatNumber>
<StatHelpText>
<Icon as={FileText} boxSize={3} mr={1} />
@@ -278,8 +275,8 @@ export default function InvoicePage({ embedded = false }: InvoicePageProps) {
<CardHeader>
<Flex justify="space-between" align="center" w="100%" mb={4}>
<HStack>
<Icon as={FileText} boxSize={6} color="blue.500" />
<Text fontSize="xl" fontWeight="bold" color={textColor}>
<Icon as={FileText} boxSize={6} color="#D4AF37" />
<Text fontSize="xl" fontWeight="bold" color="white">
</Text>
</HStack>
@@ -287,20 +284,25 @@ export default function InvoicePage({ embedded = false }: InvoicePageProps) {
<Button
size="sm"
variant="ghost"
color="gray.300"
leftIcon={<Icon as={RefreshCw} />}
onClick={() => {
loadInvoices();
loadStats();
}}
isLoading={loading}
_hover={{ color: 'white', bg: 'whiteAlpha.100' }}
>
</Button>
<Button
size="sm"
colorScheme="blue"
variant="outline"
borderColor="#D4AF37"
color="#D4AF37"
leftIcon={<Icon as={Plus} />}
onClick={onApplyOpen}
_hover={{ bg: 'rgba(212, 175, 55, 0.1)' }}
>
</Button>
@@ -311,33 +313,53 @@ export default function InvoicePage({ embedded = false }: InvoicePageProps) {
<Tabs
index={tabConfig.findIndex((t) => t.key === activeTab)}
onChange={(index) => setActiveTab(tabConfig[index].key)}
variant="unstyled"
>
<TabList>
<TabList borderBottom="1px solid" borderColor="rgba(212, 175, 55, 0.2)" px={4}>
{tabConfig.map((tab) => (
<Tab key={tab.key}>{tab.label}</Tab>
<Tab
key={tab.key}
color="gray.400"
fontWeight="normal"
pb={3}
px={4}
_selected={{
color: '#D4AF37',
fontWeight: '600',
borderBottom: '2px solid',
borderColor: '#D4AF37',
mb: '-1px'
}}
_hover={{ color: 'gray.200' }}
>
{tab.label}
</Tab>
))}
</TabList>
<TabPanels>
{tabConfig.map((tab) => (
<TabPanel key={tab.key} px={0}>
<TabPanel key={tab.key} px={4}>
{loading ? (
<Center py={10}>
<VStack spacing={4}>
<Spinner size="lg" />
<Text color="gray.500">...</Text>
<Spinner size="lg" color="#D4AF37" />
<Text color="gray.400">...</Text>
</VStack>
</Center>
) : invoices.length === 0 ? (
<Center py={10}>
<VStack spacing={4}>
<Icon as={FileText} boxSize={12} color="gray.300" />
<Text color="gray.500"></Text>
<Icon as={FileText} boxSize={12} color="gray.600" />
<Text color="gray.400"></Text>
<Button
size="sm"
colorScheme="blue"
variant="outline"
borderColor="#D4AF37"
color="#D4AF37"
leftIcon={<Icon as={Plus} />}
onClick={onApplyOpen}
_hover={{ bg: 'rgba(212, 175, 55, 0.1)' }}
>
</Button>