PHP项目Laravel邮件通知视图设计

wen PHP项目 3

本文目录导读:

PHP项目Laravel邮件通知视图设计

  1. 项目结构规划
  2. 基础布局设计
  3. 通知视图示例
  4. 使用Laravel Mail类
  5. 使用通知系统
  6. 最佳实践
  7. 个性化模板变量

在Laravel中设计邮件通知视图时,需要兼顾美观、功能性和跨客户端兼容性,以下是系统化的设计指南和最佳实践:

项目结构规划

resources/views/emails/
├── layouts/
│   ├── default.blade.php      # 主布局
│   └── notification.blade.php # 通知特定布局
├── notifications/
│   ├── order-confirmation.blade.php
│   ├── password-reset.blade.php
│   └── welcome.blade.php
├── partials/
│   ├── header.blade.php
│   ├── footer.blade.php
│   └── button.blade.php
└── components/
    └── alert.blade.php

基础布局设计

主布局 (layouts/default.blade.php)

@php
$appName = config('app.name');
$primaryColor = '#3490dc';
@endphp
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">{{ $title ?? $appName }}</title>
    <style>
        /* 响应式设计 */
        @media only screen and (max-width: 600px) {
            .container { width: 100% !important; padding: 0 20px !important; }
            .header { padding: 20px 15px !important; }
            .content { padding: 30px 20px !important; }
            .button { display: block !important; width: 100% !important; }
        }
        /* 主题变量 */
        :root {
            --primary-color: {{ $primaryColor }};
            --text-primary: #333333;
            --text-secondary: #666666;
            --background: #f8f9fa;
            --border: #e9ecef;
        }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
            background-color: var(--background);
            color: var(--text-primary);
            margin: 0;
            padding: 0;
            -webkit-font-smoothing: antialiased;
        }
        /* 容器 */
        .container {
            max-width: 600px;
            margin: 0 auto;
            background-color: #ffffff;
            border-radius: 8px;
            overflow: hidden;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        /* 头部 */
        .header {
            background-color: var(--primary-color);
            padding: 30px 40px;
            text-align: center;
        }
        .header img {
            max-width: 150px;
            max-height: 60px;
            object-fit: contain;
        }
        .header h1 {
            color: #ffffff;
            font-size: 24px;
            margin: 0;
            font-weight: 600;
        }
        /* 内容区域 */
        .content {
            padding: 40px;
            line-height: 1.6;
        }
        h2 {
            color: var(--text-primary);
            font-size: 20px;
            margin-bottom: 20px;
        }
        p {
            margin: 0 0 15px;
            color: var(--text-secondary);
            font-size: 16px;
        }
        /* 按钮样式 */
        .button {
            display: inline-block;
            background-color: var(--primary-color);
            color: #ffffff !important;
            text-decoration: none;
            padding: 12px 30px;
            border-radius: 4px;
            font-weight: 500;
            margin: 20px 0;
            text-align: center;
            font-size: 16px;
            transition: opacity 0.3s;
        }
        .button:hover {
            opacity: 0.9;
        }
        /* 事件块 */
        .event-details {
            background-color: var(--background);
            border-left: 4px solid var(--primary-color);
            padding: 20px;
            margin: 20px 0;
            border-radius: 4px;
        }
        .event-details h3 {
            margin: 0 0 10px;
            font-size: 18px;
            color: var(--text-primary);
        }
        .event-details p {
            margin: 5px 0;
            font-size: 14px;
        }
        /* 表格样式 */
        .table {
            width: 100%;
            border-collapse: collapse;
            margin: 20px 0;
        }
        .table th, .table td {
            padding: 12px;
            border: 1px solid var(--border);
            text-align: left;
        }
        .table th {
            background-color: var(--background);
            font-weight: 600;
        }
        /* 警告框 */
        .alert {
            padding: 15px 20px;
            border-radius: 4px;
            margin: 20px 0;
            font-size: 14px;
        }
        .alert-danger {
            background-color: #f8d7da;
            border: 1px solid #f5c6cb;
            color: #721c24;
        }
        .alert-success {
            background-color: #d4edda;
            border: 1px solid #c3e6cb;
            color: #155724;
        }
        /* 页脚 */
        .footer {
            padding: 20px 40px;
            text-align: center;
            background-color: #f8f9fa;
            border-top: 1px solid var(--border);
            font-size: 13px;
            color: #999;
        }
        .footer a {
            color: #666;
            text-decoration: none;
        }
        .footer a:hover {
            color: var(--primary-color);
        }
    </style>
</head>
<body>
    <table width="100%" cellpadding="0" cellspacing="0" border="0">
        <tr>
            <td align="center" style="padding: 40px 20px;">
                <table class="container" cellpadding="0" cellspacing="0" border="0">
                    <tr>
                        <td class="header">
                            @include('emails.partials.header')
                        </td>
                    </tr>
                    <tr>
                        <td class="content">
                            {{ $slot }}
                        </td>
                    </tr>
                    <tr>
                        <td class="footer">
                            @include('emails.partials.footer')
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>
</body>
</html>

通知视图示例

订单确认通知 (order-confirmation.blade.php)

@component('emails.layouts.notification', [ => '订单确认',
    'user' => $user
])
    <h2>感谢您的购买,{{ $user->name }}!</h2>
    <p>您的订单已成功确认,以下是您的订单详情:</p>
    <div class="event-details">
        <h3>订单 #{{ $order->id }}</h3>
        <p><strong>订单日期:</strong>{{ $order->created_at->format('Y-m-d H:i') }}</p>
        <p><strong>支付方式:</strong>{{ $order->payment_method }}</p>
        <p><strong>预计送达:</strong>{{ $order->expected_delivery_date }}</p>
    </div>
    <h3 style="margin-top: 30px;">商品清单</h3>
    <table class="table">
        <thead>
            <tr>
                <th>商品</th>
                <th>数量</th>
                <th>价格</th>
                <th>小计</th>
            </tr>
        </thead>
        <tbody>
            @foreach($order->items as $item)
            <tr>
                <td>{{ $item->product_name }}</td>
                <td>{{ $item->quantity }}</td>
                <td>{{ number_format($item->price, 2, ',', '.') }} 元</td>
                <td>{{ number_format($item->quantity * $item->price, 2, ',', '.') }} 元</td>
            </tr>
            @endforeach
        </tbody>
        <tfoot>
            <tr>
                <td colspan="3" style="text-align: right;"><strong>总计:</strong></td>
                <td><strong>{{ number_format($order->total, 2, ',', '.') }} 元</strong></td>
            </tr>
        </tfoot>
    </table>
    <div style="text-align: center; margin-top: 30px;">
        <a href="{{ url('/order/'.$order->id) }}" class="button">查看订单详情</a>
    </div>
    <p style="margin-top: 30px; font-size: 14px; color: #999;">
        如对订单有任何疑问,请在 30 天内联系客服 <a href="mailto:{{ config('mail.support_email') }}">{{ config('mail.support_email') }}</a>
    </p>
@endcomponent

响应式按钮组件

{{-- resources/views/emails/components/button.blade.php --}}
@props([
    'url' => '#',
    'color' => '#3490dc',
    'width' => 'auto'
])
<table role="presentation" cellpadding="0" cellspacing="0" style="margin: 20px 0;">
    <tr>
        <td style="border-radius: 4px; background-color: {{ $color }}; padding: 12px 30px;">
            <a href="{{ $url }}" style="color: #ffffff; text-decoration: none; display: inline-block; font-size: 16px;">
                {{ $slot }}
            </a>
        </td>
    </tr>
</table>

使用Laravel Mail类

<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Notifications\Notification;
class OrderConfirmationMail extends Mailable
{
    use Queueable, SerializesModels;
    public $order;
    public $user;
    public function __construct($order, $user)
    {
        $this->order = $order;
        $this->user = $user;
    }
    public function build()
    {
        return $this->from(config('mail.from.address'), config('mail.from.name'))
                    ->subject('订单确认')
                    ->view('emails.notifications.order-confirmation')
                    ->with([
                        'order' => $this->order,
                        'user' => $this->user,
                        'title' => '订单已确认'
                    ]);
    }
}

使用通知系统

<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class OrderShipped extends Notification implements ShouldQueue
{
    use Queueable;
    protected $order;
    public function __construct($order)
    {
        $this->order = $order;
    }
    public function via($notifiable)
    {
        return ['mail'];
    }
    public function toMail($notifiable)
    {
        return (new MailMessage)
                    ->subject('您的订单已发货')
                    ->greeting('您好,' . $notifiable->name . '!')
                    ->line('您的订单已发货,正在运输途中。')
                    ->line('订单号:' . $this->order->tracking_number)
                    ->action('追踪订单', url('/track-order/' . $this->order->tracking_number))
                    ->line('感谢您选择我们的服务!')
                    ->view('emails.notifications.custom-view', [
                        'order' => $this->order,
                        'user' => $notifiable
                    ]);
    }
}

最佳实践

响应式设计检查清单

  • 使用 table 布局确保Outlook兼容
  • 设置 max-width 保证移动端显示
  • 确保按钮在移动端全宽显示
  • 使用 @media 查询处理小屏适配

性能优化

  • 避免使用过大的图片
  • 使用CSS内联样式(可借助CSS inliner工具)
  • 将邮件通知加入队列处理

测试建议

Mail::fake();
Mail::assertSent(OrderConfirmationMail::class, function ($mail) {
    return $mail->hasTo('test@example.com');
});

个性化模板变量

@foreach($items as $item)
    <tr>
        <td>{{ $item->name }}</td>
        <td>{{ $item->price }}</td>
    </tr>
@endforeach
@if($hasDiscount)
    <div class="alert alert-success">
        您获得了 {{ $discount }} 元折扣!
    </div>
@endif
@isset($referral)
    <p>您的邀请码:{{ $referral->code }}</p>
@endisset

这种设计确保了邮件在各种设备和客户端上的良好表现,同时保持代码的可维护性和可复用性,根据项目需求,可以进一步定制样式和组件。

抱歉,评论功能暂时关闭!