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: try:
user_id = session['user_id'] user_id = session['user_id']
print(f"[发票API] 获取可开票订单 - 用户ID: {user_id}")
# 查询已支付的订单 # 查询已支付的订单
paid_orders = PaymentOrder.query.filter_by( paid_orders = PaymentOrder.query.filter_by(
@@ -3740,6 +3741,10 @@ def get_invoice_available_orders():
status='paid' status='paid'
).order_by(PaymentOrder.paid_at.desc()).all() ).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 = [] available_orders = []
for order in paid_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, Text,
Button, Button,
Icon, Icon,
useColorModeValue,
Divider, Divider,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { Download, Eye, X, FileText } from 'lucide-react'; import { Download, Eye, X, FileText } from 'lucide-react';
@@ -40,32 +39,29 @@ function InvoiceCard({
onDownload, onDownload,
onCancel, onCancel,
}: InvoiceCardProps) { }: 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 canDownload = invoice.status === 'completed' && invoice.invoiceType === 'electronic';
const canCancel = invoice.status === 'pending'; const canCancel = invoice.status === 'pending';
return ( return (
<Box <Box
bg={bgCard} bg="transparent"
borderRadius="lg" borderRadius="lg"
border="1px solid" border="1px solid"
borderColor={borderColor} borderColor="rgba(212, 175, 55, 0.3)"
overflow="hidden" overflow="hidden"
transition="all 0.2s" 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}> <HStack spacing={3}>
<Icon as={FileText} boxSize={5} color="blue.500" /> <Icon as={FileText} boxSize={5} color="#D4AF37" />
<VStack align="flex-start" spacing={0}> <VStack align="flex-start" spacing={0}>
<Text fontWeight="600" fontSize="sm"> <Text fontWeight="600" fontSize="sm" color="white">
{invoice.title} {invoice.title}
</Text> </Text>
<Text fontSize="xs" color="gray.500"> <Text fontSize="xs" color="gray.400">
{invoiceTypeMap[invoice.invoiceType]} · {titleTypeMap[invoice.titleType]} {invoiceTypeMap[invoice.invoiceType]} · {titleTypeMap[invoice.titleType]}
</Text> </Text>
</VStack> </VStack>
@@ -73,74 +69,76 @@ function InvoiceCard({
<InvoiceStatusBadge status={invoice.status} /> <InvoiceStatusBadge status={invoice.status} />
</HStack> </HStack>
<Divider /> <Divider borderColor="rgba(212, 175, 55, 0.2)" />
{/* 内容 */} {/* 内容 */}
<VStack align="stretch" spacing={3} p={4}> <VStack align="stretch" spacing={3} p={4}>
<HStack justify="space-between"> <HStack justify="space-between">
<Text fontSize="sm" color="gray.500"> <Text fontSize="sm" color="gray.400">
</Text> </Text>
<Text fontSize="sm">{invoice.orderNo}</Text> <Text fontSize="sm" color="gray.200">{invoice.orderNo}</Text>
</HStack> </HStack>
<HStack justify="space-between"> <HStack justify="space-between">
<Text fontSize="sm" color="gray.500"> <Text fontSize="sm" color="gray.400">
</Text> </Text>
<Text fontSize="sm" fontWeight="600" color="blue.500"> <Text fontSize="sm" fontWeight="600" color="#D4AF37">
¥{invoice.amount.toFixed(2)} ¥{invoice.amount.toFixed(2)}
</Text> </Text>
</HStack> </HStack>
{invoice.invoiceNo && ( {invoice.invoiceNo && (
<HStack justify="space-between"> <HStack justify="space-between">
<Text fontSize="sm" color="gray.500"> <Text fontSize="sm" color="gray.400">
</Text> </Text>
<Text fontSize="sm">{invoice.invoiceNo}</Text> <Text fontSize="sm" color="gray.200">{invoice.invoiceNo}</Text>
</HStack> </HStack>
)} )}
<HStack justify="space-between"> <HStack justify="space-between">
<Text fontSize="sm" color="gray.500"> <Text fontSize="sm" color="gray.400">
</Text> </Text>
<Text fontSize="sm"> <Text fontSize="sm" color="gray.200">
{new Date(invoice.createdAt).toLocaleString('zh-CN')} {new Date(invoice.createdAt).toLocaleString('zh-CN')}
</Text> </Text>
</HStack> </HStack>
{invoice.completedAt && ( {invoice.completedAt && (
<HStack justify="space-between"> <HStack justify="space-between">
<Text fontSize="sm" color="gray.500"> <Text fontSize="sm" color="gray.400">
</Text> </Text>
<Text fontSize="sm"> <Text fontSize="sm" color="gray.200">
{new Date(invoice.completedAt).toLocaleString('zh-CN')} {new Date(invoice.completedAt).toLocaleString('zh-CN')}
</Text> </Text>
</HStack> </HStack>
)} )}
{invoice.rejectReason && ( {invoice.rejectReason && (
<Box p={2} bg="red.50" borderRadius="md"> <Box p={2} bg="rgba(229, 62, 62, 0.15)" borderRadius="md">
<Text fontSize="xs" color="red.600"> <Text fontSize="xs" color="red.300">
: {invoice.rejectReason} : {invoice.rejectReason}
</Text> </Text>
</Box> </Box>
)} )}
</VStack> </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 && ( {onView && (
<Button <Button
size="sm" size="sm"
variant="ghost" variant="ghost"
color="gray.300"
leftIcon={<Icon as={Eye} boxSize={4} />} leftIcon={<Icon as={Eye} boxSize={4} />}
onClick={onView} onClick={onView}
_hover={{ color: 'white', bg: 'whiteAlpha.100' }}
> >
</Button> </Button>
@@ -148,10 +146,12 @@ function InvoiceCard({
{canDownload && onDownload && ( {canDownload && onDownload && (
<Button <Button
size="sm" size="sm"
colorScheme="blue"
variant="outline" variant="outline"
borderColor="#D4AF37"
color="#D4AF37"
leftIcon={<Icon as={Download} boxSize={4} />} leftIcon={<Icon as={Download} boxSize={4} />}
onClick={onDownload} onClick={onDownload}
_hover={{ bg: 'rgba(212, 175, 55, 0.1)' }}
> >
</Button> </Button>
@@ -159,10 +159,11 @@ function InvoiceCard({
{canCancel && onCancel && ( {canCancel && onCancel && (
<Button <Button
size="sm" size="sm"
colorScheme="red"
variant="ghost" variant="ghost"
color="red.400"
leftIcon={<Icon as={X} boxSize={4} />} leftIcon={<Icon as={X} boxSize={4} />}
onClick={onCancel} onClick={onCancel}
_hover={{ bg: 'rgba(229, 62, 62, 0.1)' }}
> >
</Button> </Button>

View File

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