Alpha Technology Group Limited, ATGL, $22.40, Change % 10.74

博客

  • 基于vue带目录和文件上传功能的Markdown查看器

    基于 Vue 的 Markdown 查看器项目介绍

    本项目是一款基于 Vue 框架精心打造的 Markdown 查看器,为用户提供了便捷、高效的 Markdown 文件查看体验。

    功能亮点

    • 轻松上传文件:用户能够轻松地从本地选择并上传 .md 格式文件,上传后的文件内容随即在查看器中清晰呈现。
    • 智能动态目录:可自动识别 Markdown 文件内的标题,动态生成目录。点击目录中的任意一项,就能迅速定位到对应内容区域。
    • 贴心固定侧边栏:页面滚动时,侧边栏始终稳稳固定,用户得以随时便捷导航。
    • 便捷回到顶部:当页面滚动到特定位置,“回到顶部”按钮适时出现,一键点击,即可快速返回页面顶端。
    • 适配多元设备:采用响应式设计,页面布局会根据不同设备自动适配,搭配简洁美观的样式,视觉体验极佳。

    项目架构

    本项目由多个关键组件协同搭建而成:
    Markdown 查看组件:专注于将解析后的 Markdown 内容精准无误地展示出来。
    目录组件:负责呈现动态生成的目录,并且在页面滚动过程中,精准高亮当前章节。
    文件上传功能模块:全力支持 Markdown 文件上传,并将解析后的内容完美显示。

    安装指南

    1. 克隆仓库
      git clone https://github.com/jefsky/markdown-viewer.git
      
    2. 进入项目目录
      cd markdown-viewer
      
    3. 安装依赖包
      npm install
      
    4. 启动开发服务器
      npm run serve
      

      启动后,应用将在 `http://localhost:8080` 顺畅运行。

    使用教程

    1. 只需轻点侧边栏的 “选择文件” 按钮,即可上传 Markdown 文件。
    2. 文件上传成功后,内容会立即显示在页面右侧区域。
    3. 侧边栏会依据文件中的标题自动生成目录,点击目录条目,就能精准滚动到相应章节。
    4. 页面向下滚动时,“回到顶部” 按钮会悄然现身,点击它瞬间回到页面顶部。

    代码架构剖析

    • markdown-container:作为核心容器,巧妙容纳侧边栏(集成目录与文件上传按钮)与 Markdown 内容展示区。
    • generateHtml():借助 marked 库细致解析 Markdown 内容,同时提取标题生成目录。
    • scrollTo():实现平滑流畅的滚动效果,精准定位到选中的标题位置。
    • IntersectionObserver:在用户滚动页面时,智能动态高亮对应的目录项。

    个性化定制

    样式定制

    项目运用基础 CSS 样式,样式代码存于各组件的 style 部分,方便按需定制。您可以调整:
    – 侧边栏宽度与背景色调,营造独特视觉氛围。
    – “选择文件” 与 “回到顶部” 按钮的样式细节,凸显个性风格。
    – 目录项字体大小与间距,优化阅读体验。
    – 盒子阴影与圆角效果,增添页面精致感。

    功能拓展

    您还能通过如下方式拓展查看器功能:
    集成 Markdown 编辑器:赋予用户在应用内直接编辑 Markdown 内容的能力。
    增添主题切换功能:轻松支持浅色、深色模式切换,满足不同场景需求。
    拓展文件格式支持:添加对更多文件类型,诸如 .txt.json 等的支持。

    依赖清单

    • Vue.js:强大的 JavaScript 框架,用于搭建灵动的用户界面。
    • Marked.js:专业的 Markdown 解析、编译工具。
    • VueScrollTo:助力实现平滑滚动效果的 Vue 插件。

    许可协议

    本项目基于 MIT 许可证开源,详细许可信息可查阅 LICENSE 文件。

    贡献指引

    热忱欢迎大家 fork 本仓库,并踊跃提交 pull requests。若计划进行重大变更,烦请先开启 issue,深入讨论修改设想。


    感谢您对本项目的关注!要是觉得项目还不错,欢迎前往 GitHub 点亮一颗 🌟。

  • 基于uni-app和 Three.js 开发一款DIY蛋糕应用

    DIYCakeApp:基于uni-app与Three.js的创意蛋糕DIY应用开发指南

    本项目聚焦于打造一款趣味十足的DIY蛋糕应用,借助uni-app和Three.js的强大功能,让用户能够亲手定制专属蛋糕。从项目筹备到最终部署上线,各个环节都将详细展开。

    目录

    1. 项目准备
    2. 环境搭建
    3. 创建uni-app项目
    4. 集成Three.js
    5. 设计3D蛋糕模型
    6. 实现用户交互
    7. UI设计与集成
    8. 测试与调试
    9. 部署与发布
    10. 资源与参考

    项目准备

    开启项目之旅前,您需储备以下知识与备好相关工具:
    必备知识
    – 熟练掌握JavaScript、HTML、CSS,这是构建网页应用的基石。
    – 熟悉Vue.js,鉴于uni-app基于Vue,相关知识不可或缺。
    – 对Three.js有基础认知,以便驾驭3D场景搭建。
    实用工具
    – 一款趁手的代码编辑器,如功能丰富的VS Code。
    – 安装Node.js与npm,构建项目依赖环境。
    – HBuilderX,官方力荐的uni-app开发集成环境。

    环境搭建

    1. 安装Node.js和npm

    首先要确认开发环境中已妥善安装Node.js与npm,前往 Node.js官网 下载并安装最新的长期支持版(LTS),安装完成后,可在终端输入以下命令检查版本:

    node -v
    npm -v
    

    2. 安装HBuilderX

    HBuilderX由DCloud推出,专为uni-app开发量身定制。访问 HBuilderX下载链接 完成下载与安装。

    3. 安装uni-app CLI(可选)

    若倾向于命令行操作,可安装uni-app的CLI工具:

    npm install -g @vue/cli
    npm install -g @dcloudio/uni-app
    

    创建uni-app项目

    1. 使用HBuilderX创建项目

    1. 启动HBuilderX。
    2. 依次点击“文件” > “新建” > “项目”。
    3. 选中“uni-app”模板,填入项目名称,例如DIYCakeApp,再选定项目保存路径,最后点击“创建”。

    2. 使用命令行创建项目(可选)

    要是习惯用命令行来创建项目,执行如下指令:

    vue create -p dcloudio/uni-preset-vue DIYCakeApp
    cd DIYCakeApp
    

    集成Three.js

    1. 安装Three.js

    在项目根目录下,借助npm安装Three.js:

    npm install three
    

    2. 引入Three.js

    在需要启用Three.js的页面或组件里引入:

    import * as THREE from 'three';
    

    设计3D蛋糕模型

    1. 准备3D模型

    既可以用Three.js自带的几何体,像圆柱体、球体等拼搭出蛋糕雏形,也能借助专业3D建模软件,如Blender,塑造复杂模型,再导出成Three.js支持的格式,比如GLTF。

    2. 使用Three.js创建基本蛋糕模型

    以下是一份简易蛋糕模型示例代码:

    // 创建场景
    const scene = new THREE.Scene();
    
    // 创建相机
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 5;
    
    // 创建渲染器
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    
    // 将渲染器的DOM添加到页面
    document.getElementById('three-container').appendChild(renderer.domElement);
    
    // 添加蛋糕主体(圆柱体)
    const cakeGeometry = new THREE.CylinderGeometry(1, 1, 0.5, 32);
    const cakeMaterial = new THREE.MeshBasicMaterial({ color: 0xff69b4 });
    const cake = new THREE.Mesh(cakeGeometry, cakeMaterial);
    scene.add(cake);
    
    // 添加蛋糕装饰(例如球体表示水果)
    const decorationGeometry = new THREE.SphereGeometry(0.1, 16, 16);
    const decorationMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
    const decoration = new THREE.Mesh(decorationGeometry, decorationMaterial);
    decoration.position.set(0.5, 0.3, 0);
    scene.add(decoration);
    
    // 渲染循环
    function animate() {
        requestAnimationFrame(animate);
    
        // 旋转蛋糕
        cake.rotation.y += 0.01;
        decoration.rotation.y += 0.02;
    
        renderer.render(scene, camera);
    }
    animate();
    

    3. 在uni-app中集成3D场景

    在uni-app页面里,添加用于渲染Three.js的容器。以pages/index/index.vue为例:

    <template>
        <view class="container">
            <view id="three-container" class="three-container"></view>
        </view>
    </template>
    
    <script>
    import * as THREE from 'three';
    export default {
        onReady() {
            this.initThree();
        },
        methods: {
            initThree() {
                // 与上述示例相同的Three.js初始化代码
                const scene = new THREE.Scene();
                const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
                camera.position.z = 5;
    
                const renderer = new THREE.WebGLRenderer({ antialias: true });
                renderer.setSize(window.innerWidth, window.innerHeight);
                document.getElementById('three-container').appendChild(renderer.domElement);
    
                const cakeGeometry = new THREE.CylinderGeometry(1, 1, 0.5, 32);
                const cakeMaterial = new THREE.MeshBasicMaterial({ color: 0xff69b4 });
                const cake = new THREE.Mesh(cakeGeometry, cakeMaterial);
                scene.add(cake);
    
                const decorationGeometry = new THREE.SphereGeometry(0.1, 16, 16);
                const decorationMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
                const decoration = new THREE.Mesh(decorationGeometry, decorationMaterial);
                decoration.position.set(0.5, 0.3, 0);
                scene.add(decoration);
    
                function animate() {
                    requestAnimationFrame(animate);
                    cake.rotation.y += 0.01;
                    decoration.rotation.y += 0.02;
                    renderer.render(scene, camera);
                }
                animate();
            }
        }
    }
    </script>
    
    <style>
    .container {
        width: 100%;
        height: 100vh;
    }
    .three-container {
        width: 100%;
        height: 100%;
    }
    </style>
    

    实现用户交互

    为赋予用户DIY蛋糕的乐趣,需落实以下交互功能:
    1. 选择蛋糕基底:提供多样的蛋糕形状与口味选项。
    2. 添加装饰:支持添加水果、奶油、糖霜等装饰元素。
    3. 颜色选择:能够更改蛋糕及装饰的色彩。
    4. 视角控制:方便用户旋转、缩放、移动观察视角。

    1. 添加控制器

    利用OrbitControls达成视角控制。先引入它:

    import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
    

    接着,在初始化Three.js时添加上控制器:

    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true; // 让动画过渡更丝滑
    

    并且在渲染循环里更新控制器状态:

    function animate() {
        requestAnimationFrame(animate);
    
        controls.update(); // 更新控制器
    
        cake.rotation.y += 0.01;
        decoration.rotation.y += 0.02;
    
        renderer.render(scene, camera);
    }
    

    2. 添加UI控件

    在uni-app里,运用<button>或自定义组件充当交互控件。比如,添加颜色选择按钮:

    <template>
        <view class="container">
            <view id="three-container" class="three-container"></view>
            <view class="controls">
                <button>粉红色</button>
                <button>蓝紫色</button>
                <!-- 更多颜色按钮 -->
            </view>
        </view>
    </template>
    
    <script>
    import * as THREE from 'three';
    import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
    export default {
        data() {
            return {
                cakeMaterial: null,
                decorationMaterial: null,
            }
        },
        onReady() {
            this.initThree();
        },
        methods: {
            initThree() {
                const scene = new THREE.Scene();
                const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
                camera.position.z = 5;
    
                const renderer = new THREE.WebGLRenderer({ antialias: true });
                renderer.setSize(window.innerWidth, window.innerHeight);
                document.getElementById('three-container').appendChild(renderer.domElement);
    
                const cakeGeometry = new THREE.CylinderGeometry(1, 1, 0.5, 32);
                this.cakeMaterial = new THREE.MeshBasicMaterial({ color: 0xff69b4 });
                const cake = new THREE.Mesh(cakeGeometry, this.cakeMaterial);
                scene.add(cake);
    
                const decorationGeometry = new THREE.SphereGeometry(0.1, 16, 16);
                this.decorationMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
                const decoration = new THREE.Mesh(decorationGeometry, this.decorationMaterial);
                decoration.position.set(0.5, 0.3, 0);
                scene.add(decoration);
    
                const controls = new OrbitControls(camera, renderer.domElement);
                controls.enableDamping = true;
    
                const animate = () => {
                    requestAnimationFrame(animate);
                    controls.update();
                    cake.rotation.y += 0.01;
                    decoration.rotation.y += 0.02;
                    renderer.render(scene, camera);
                };
                animate();
            },
            changeCakeColor(color) {
                if (this.cakeMaterial) {
                    this.cakeMaterial.color.set(color);
                }
            }
        }
    }
    </script>
    
    <style>
    .container {
        position: relative;
        width: 100%;
        height: 100vh;
    }
    .three-container {
        width: 100%;
        height: 100%;
    }
    .controls {
        position: absolute;
        bottom: 20px;
        left: 50%;
        transform: translateX(-50%);
        display: flex;
        gap: 10px;
    }
    button {
        padding: 10px 20px;
        background-color: #ffffffaa;
        border: none;
        border-radius: 5px;
        cursor: pointer;
    }
    button:hover {
        background-color: #ffffff;
    }
    </style>
    

    3. 动态添加装饰

    支持用户从预设装饰清单里挑选并添加到蛋糕上,示例代码如下:

    methods: {
        //...之前的代码
        addDecoration(color) {
            const decorationGeometry = new THREE.SphereGeometry(0.1, 16, 16);
            const decorationMaterial = new THREE.MeshBasicMaterial({ color });
            const decoration = new THREE.Mesh(decorationGeometry, decorationMaterial);
            decoration.position.set(Math.random() - 0.5, Math.random() * 0.5, Math.random() - 0.5);
            this.$refs.scene.add(decoration);
        }
    }
    

    在UI里添加按钮调用addDecoration方法:

    <button>添加绿色装饰</button>
    <button>添加蓝色装饰</button>
    

    注意:要保证在Three.js初始化代码中,场景对象存于this.$refs.scene或其他可访问之处。

    UI设计与集成

    优质UI设计是提升用户体验的关键,以下是相关要点:

    1. 布局设计

    • 3D视图区:占据屏幕大部,用于展示3D蛋糕并支持交互操作。
    • 控制面板:置于侧边或底部,承载选择蛋糕基底、添加装饰、颜色挑选等功能。
    • 预览与保存:配备预览功能,以及保存定制蛋糕的选项。

    2. 使用uni-app的组件

    借助uni-app丰富组件库搭建界面,例如:
    – 引入uViewVant Weapp等第三方UI库,加速开发进程。
    – 善用<button><picker><slider>等组件实现交互功能。

    3. 响应式设计

    保障应用在各类设备(手机、平板等)上显示效果俱佳,利用CSS的Flex布局或Grid布局达成响应式布局:

    .container {
        display: flex;
        flex-direction: column;
        height: 100vh;
    }
    .three-container {
        flex: 1;
    }
    .controls {
        padding: 10px;
        background-color: #f0f0f0;
    }
    

    测试与调试

    1. 使用HBuilderX的预览功能

    HBuilderX提供多元预览选项,助您实时查看应用效果:
    手机预览:扫码即可在真机上预览效果。
    浏览器预览:在浏览器中打开,便于调试与测试。

    2. 调试Three.js

    借助浏览器开发者工具排查Three.js渲染问题,查看控制台报错,核验渲染效果是否达标。

    3. 性能优化

    • 精简多边形数量:复杂3D模型易拖累性能,需优化模型复杂度。
    • 巧用纹理贴图:合理运用纹理,降低几何体复杂度。
    • 调控渲染器性能选项:按需开启或关闭抗锯齿、阴影等功能。

    部署与发布

    1. 打包应用

    利用HBuilderX的打包功能,将应用打包成Web应用、小程序、APP等不同格式:
    – 点击“发行” > 选定目标平台(如Web、微信小程序、APP等)。
    – 依指引完成打包全流程。

    2. 发布到应用商店或服务器

    依循目标平台规则,发布应用:
    Web应用:上传打包文件至服务器,配置域名与SSL。
    移动应用:提交至对应应用商店,如Apple App Store、Google Play。
    小程序:通过微信公众平台等渠道发布。

    3. 持续更新与维护

    依据用户反馈与需求,持续优化、更新应用,修复漏洞,增添新特性。

    资源与参考

    官方文档

    教程与示例

    资源素材

    社区与支持

  • laravel+filament搭建cms系统

    基于 Laravel 和 Filament 搭建内容管理系统(CMS)指南

    利用 Laravel 与 Filament 搭建 CMS 系统,不仅高效,还兼具灵活性。Filament 所提供的现代化管理后台界面,让内容管理变得既直观又高效。下面是从零开始搭建功能完备的 CMS 系统的详细步骤。

    目录

    1. 项目初始化
    2. 安装 Filament
    3. 定义数据模型
    4. 创建 Filament 资源
    5. 配置权限和角色
    6. 创建内容管理功能
    7. 前端集成(可选)
    8. 额外功能
    9. 部署
    10. 参考资源
    11. 总结

    1. 项目初始化

    1.1 创建新的 Laravel 项目

    要是还没创建Laravel项目,借助Composer就能轻松搞定:

    composer create-project laravel/laravel cms-system
    

    随后进入项目目录:

    cd cms-system
    

    1.2 设置数据库连接

    .env 文件里配置数据库相关参数:

    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=cms_system
    DB_USERNAME=root
    DB_PASSWORD=your_password
    

    接着创建数据库(以MySQL为例):

    CREATE DATABASE cms_system;
    

    1.3 运行迁移

    运行初始迁移,生成必要的数据表:

    php artisan migrate
    

    2. 安装 Filament

    2.1 使用 Composer 安装 Filament

    执行如下命令:

    composer require filament/filament
    

    2.2 运行 Filament 安装命令

    php artisan filament:install
    

    安装期间,Filament会提示创建管理员用户,按提示操作完成安装。

    2.3 构建前端资源

    先安装前端依赖,再构建资源:

    npm install
    npm run build
    

    处于开发环境时,可使用:

    npm run dev
    

    要确保 public/build/manifest.json 文件存在。

    3. 定义数据模型

    依据 CMS 系统的需求,明确要管理的内容,比如文章(Posts)、分类(Categories)、标签(Tags)等。

    3.1 创建 Eloquent 模型和迁移

    以文章(Post)为例:

    php artisan make:model Post -m
    

    编辑迁移文件 database/migrations/xxxx_xx_xx_create_posts_table.php

    public function up()
    {
        Schema::create('posts', function (Blueprint table) {table->id();
            table->string('title');table->text('content');
            table->foreignId('category_id')->constrained()->onDelete('cascade');table->timestamps();
        });
    }
    

    同样地,为分类(Category)创建模型与迁移:

    php artisan make:model Category -m
    

    编辑迁移文件 database/migrations/xxxx_xx_xx_create_categories_table.php

    public function up()
    {
        Schema::create('categories', function (Blueprint table) {table->id();
            table->string('name');table->timestamps();
        });
    }
    

    最后运行迁移:

    php artisan migrate
    

    3.2 定义模型关系

    App\Models\Post.php 里:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    
    class Post extends Model
    {
        use HasFactory;
    
        protected fillable = ['title', 'content', 'category_id'];
    
        public function category()
        {
            returnthis->belongsTo(Category::class);
        }
    }
    

    App\Models\Category.php 中:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    
    class Category extends Model
    {
        use HasFactory;
    
        protected fillable = ['name'];
    
        public function posts()
        {
            returnthis->hasMany(Post::class);
        }
    }
    

    4. 创建 Filament 资源

    4.1 创建 Post 资源

    用 Filament 提供的 Artisan 命令生成资源:

    php artisan make:filament-resource Post
    

    这会在 app/Filament/Resources/PostResource 目录下生成所需文件。

    4.2 配置 Post 资源

    编辑 app/Filament/Resources/PostResource.php

    <?php
    
    namespace App\Filament\Resources;
    
    use App\Filament\Resources\PostResource\Pages;
    use App\Models\Post;
    use Filament\Forms;
    use Filament\Tables;
    use Filament\Resources\Resource;
    
    class PostResource extends Resource
    {
        protected static?string model = Post::class;
        protected static?stringnavigationIcon = 'heroicon-o-document-text';
        protected static?int navigationSort = 1;
    
        public static function form(Forms\Formform): Forms\Form
        {
            return form
                ->schema([
                    Forms\Components\TextInput::make('title')
                        ->required()
                        ->maxLength(255),
                    Forms\Components\Select::make('category_id')
                        ->relationship('category', 'name')
                        ->required(),
                    Forms\Components\RichEditor::make('content')
                        ->required(),
                ]);
        }
    
        public static function table(Tables\Tabletable): Tables\Table
        {
            return $table
                ->columns([
                    Tables\Columns\TextColumn::make('id')->sortable(),
                    Tables\Columns\TextColumn::make('title')->sortable()->searchable(),
                    Tables\Columns\TextColumn::make('category.name')->label('Category')->sortable()->searchable(),
                    Tables\Columns\TextColumn::make('created_at')->dateTime(),
                ])
                ->filters([
                    //
                ])
                ->actions([
                    Tables\Actions\EditAction::make(),
                    Tables\Actions\DeleteAction::make(),
                ])
                ->bulkActions([
                    Tables\Actions\DeleteBulkAction::make(),
                ]);
        }
    
        public static function getPages(): array
        {
            return [
                'index' => Pages\ListPosts::route('/'),
                'create' => Pages\CreatePost::route('/create'),
                'edit' => Pages\EditPost::route('/{record}/edit'),
            ];
        }
    }
    

    4.3 创建 Category 资源

    同样的流程,创建并配置分类资源:

    php artisan make:filament-resource Category
    

    编辑 app/Filament/Resources/CategoryResource.php

    <?php
    
    namespace App\Filament\Resources;
    
    use App\Filament\Resources\CategoryResource\Pages;
    use App\Models\Category;
    use Filament\Forms;
    use Filament\Tables;
    use Filament\Resources\Resource;
    
    class CategoryResource extends Resource
    {
        protected static?string model = Category::class;
        protected static?stringnavigationIcon = 'heroicon-o-collection';
        protected static?int navigationSort = 2;
    
        public static function form(Forms\Formform): Forms\Form
        {
            return form
                ->schema([
                    Forms\Components\TextInput::make('name')
                        ->required()
                        ->maxLength(255),
                ]);
        }
    
        public static function table(Tables\Tabletable): Tables\Table
        {
            return $table
                ->columns([
                    Tables\Columns\TextColumn::make('id')->sortable(),
                    Tables\Columns\TextColumn::make('name')->sortable()->searchable(),
                    Tables\Columns\TextColumn::make('created_at')->dateTime(),
                ])
                ->filters([
                    //
                ])
                ->actions([
                    Tables\Actions\EditAction::make(),
                    Tables\Actions\DeleteAction::make(),
                ])
                ->bulkActions([
                    Tables\Actions\DeleteBulkAction::make(),
                ]);
        }
    
        public static function getPages(): array
        {
            return [
                'index' => Pages\ListCategories::route('/'),
                'create' => Pages\CreateCategory::route('/create'),
                'edit' => Pages\EditCategory::route('/{record}/edit'),
            ];
        }
    }
    

    4.4 创建其他资源

    按需创建更多资源,像是标签(Tags)、用户(Users)、媒体(Media) 等。

    5. 配置权限和角色

    为保障只有授权用户能访问、管理 CMS 内容,推荐使用 Spatie Laravel Permission 包管理角色和权限。

    5.1 安装 Spatie Laravel Permission

    composer require spatie/laravel-permission
    

    5.2 发布配置和迁移

    php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
    php artisan migrate
    

    5.3 配置 User 模型

    App\Models\User.php 中添加 HasRoles trait:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Spatie\Permission\Traits\HasRoles;
    
    class User extends Authenticatable
    {
        use HasRoles;
    
        // 其他代码...
    }
    

    5.4 创建角色和权限

    用 Tinker 或编写 Seeder 来创建角色与权限:

    php artisan tinker
    

    在 Tinker 里:

    use Spatie\Permission\Models\Role;
    use Spatie\Permission\Models\Permission;
    use App\Models\User;
    
    // 创建角色
    adminRole = Role::create(['name' => 'admin']);editorRole = Role::create(['name' => 'editor']);
    
    // 创建权限
    managePosts = Permission::create(['name' => 'manage posts']);manageCategories = Permission::create(['name' => 'manage categories']);
    
    // 分配权限给角色
    adminRole->givePermissionTo(['manage posts', 'manage categories']);editorRole->givePermissionTo(['manage posts']);
    
    // 查找用户并分配角色
    user = User::where('email', 'admin@example.com')->first();
    
    if (user) {
        user->assignRole('admin');
    } else {user = User::create([
            'name' => 'Admin User',
            'email' => 'admin@example.com',
            'password' => bcrypt('password'), // 请用更安全的密码
        ]);
        $user->assignRole('admin');
    }
    

    5.5 配置 Filament 中间件

    config/filament.php 中,限定 Filament 仅允许特定角色用户访问后台:

    'auth' => [
        'guard' => 'web',
        'passwords' => 'users',
        'middleware' => [
            'web',
            'auth',
            function (request,next) {
                if (!request->user() ||!request->user()->hasRole('admin')) {
                    abort(403);
                }
                return next(request);
            },
        ],
    ],
    

    6. 创建内容管理功能

    依据 CMS 需求,创建更多内容管理资源,如页面(Pages)、媒体(Media)、菜单(Menus)等。

    示例:创建 Page 资源

    php artisan make:model Page -m
    php artisan make:filament-resource Page
    

    编辑 database/migrations/xxxx_xx_xx_create_pages_table.php

    public function up()
    {
        Schema::create('pages', function (Blueprint table) {table->id();
            table->string('title');table->string('slug')->unique();
            table->text('content');table->timestamps();
        });
    }
    

    运行迁移:

    php artisan migrate
    

    编辑 app/Filament/Resources/PageResource.php

    <?php
    
    namespace App\Filament\Resources;
    
    use App\Filament\Resources\PageResource\Pages;
    use App\Models\Page;
    use Filament\Forms;
    use Filament\Tables;
    use Filament\Resources\Resource;
    
    class PageResource extends Resource
    {
        protected static?string model = Page::class;
        protected static?stringnavigationIcon = 'heroicon-o-document';
        protected static?int navigationSort = 3;
    
        public static function form(Forms\Formform): Forms\Form
        {
            return form
                ->schema([
                    Forms\Components\TextInput::make('title')
                        ->required()
                        ->maxLength(255),
                    Forms\Components\TextInput::make('slug')
                        ->required()
                        ->maxLength(255)
                        ->unique(ignoreRecord: true),
                    Forms\Components\RichEditor::make('content')
                        ->required(),
                ]);
        }
    
        public static function table(Tables\Tabletable): Tables\Table
        {
            return $table
                ->columns([
                    Tables\Columns\TextColumn::make('id')->sortable(),
                    Tables\Columns\TextColumn::make('title')->sortable()->searchable(),
                    Tables\Columns\TextColumn::make('slug')->sortable()->searchable(),
                    Tables\Columns\TextColumn::make('created_at')->dateTime(),
                ])
                ->filters([
                    //
                ])
                ->actions([
                    Tables\Actions\EditAction::make(),
                    Tables\Actions\DeleteAction::make(),
                ])
                ->bulkActions([
                    Tables\Actions\DeleteBulkAction::make(),
                ]);
        }
    
        public static function getPages(): array
        {
            return [
                'index' => Pages\ListPages::route('/'),
                'create' => Pages\CreatePage::route('/create'),
                'edit' => Pages\EditPage::route('/{record}/edit'),
            ];
        }
    }
    

    7. 前端集成(可选)

    要是需要在前端展示 CMS 内容,能用 Blade 模板,也能构建 API 端点。

    7.1 使用 Blade 模板

    创建前端视图展示 CMS 内容,比如在 resources/views/pages.blade.php 里:

    @extends('layouts.app')
    
    @section('content')
        <h1>Pages</h1>
        @foreach(pages aspage)
            <div>
                <h2>{{ page->title }}</h2>
                <div>{!!page->content!!}</div>
            </div>
        @endforeach
    @endsection
    

    在控制器里获取数据:

    <?php
    
    namespace App\Http\Controllers;
    
    use App\Models\Page;
    
    class PageController extends Controller
    {
        public function index()
        {
            pages = Page::all();
            return view('pages', compact('pages'));
        }
    
        public function show(slug)
        {
            page = Page::where('slug',slug)->firstOrFail();
            return view('page', compact('page'));
        }
    }
    

    定义路由:

    use App\Http\Controllers\PageController;
    
    Route::get('/pages', [PageController::class, 'index']);
    Route::get('/pages/{slug}', [PageController::class, 'show']);
    

    7.2 构建 API

    要是前端用 JavaScript 框架(如 Vue.js、React),就构建 API 端点。
    创建 API 资源:

    php artisan make:controller Api/PageController --api
    

    编辑 app/Http/Controllers/Api/PageController.php

    <?php
    
    namespace App\Http\Controllers\Api;
    
    use App\Http\Controllers\Controller;
    use App\Http\Resources\PageResource;
    use App\Models\Page;
    use Illuminate\Http\Request;
    
    class PageController extends Controller
    {
        public function index()
        {
            return PageResource::collection(Page::all());
        }
    
        public function show(slug)
        {page = Page::where('slug', slug)->firstOrFail();
            return new PageResource(page);
        }
    }
    

    创建资源类:

    php artisan make:resource PageResource
    

    编辑 app/Http/Resources/PageResource.php

    “`php
    this->id,
    ‘title’ =>
    this->title,
    ‘slug’ => this->slug,
    ‘content’ =>
    this->content,
    ‘created_at’ => $this->created_at,
    ‘updated_at’

  • 使用 Laravel 发起HTTP 请求到云函数

    基于 Unipush 2.0 的云函数实践:使用 GuzzleHttp 发起推送请求

    概述

    1. 在当下,uniapp以及众多小程序纷纷采用云函数来实现各类功能,这里我们选取dcloud的unipush云函数作为典型案例展开简述。
    2. 本示例还能当作传统服务器应对unipush 2.0部署方案的参考范例。要知道,unipush 2.0摒弃了传统服务器的身份验证方式,借助云函数url化即可完成相应处理。

    实践

    使用 GuzzleHttp\Client 构建请求

    1. 安装 Guzzle:要开启使用Guzzle HTTP客户端库之旅,首先得通过Composer进行安装,执行如下命令:
      composer require guzzlehttp/guzzle
      
    2. 使用 Guzzle 发起请求:完成安装后,就可以利用Guzzle构建请求了。以下是一段PHP代码示例:
    <?php
    namespace App\Http\Controllers;
    use Illuminate\Http\Request;
    use GuzzleHttp\Client;
    use GuzzleHttp\Exception\RequestException;
    class YourController extends Controller
    {
        public function test_unipush()
        {
            data = [
                "cids" => ['15f2d1111de1ef5aa3f8911dab53ffb2'], // 设备id,注意这里使用数组形式
                "title" => 'Test', // 标题
                "content" => '123', // 内容
                "payload" => '', // 数据
                "force_notification" => 'true', // 自动创建“通知栏消息”
                "request_id" => '12345678982', // 请求唯一标识号
            ];
    
            // 确保数据被正确编码为 JSONjsonData = json_encode(data);
    
            // 输出 JSON 编码后的数据,以便验证
            var_dump(jsonData);
    
            try {
                client = new Client();response = client->request('POST', 'https://your-cloud-function-url.com/send-push-notification', [
                    'headers' => [
                        'Content-Type' => 'application/json',
                    ],
                    'body' =>jsonData,
                ]);
    
                return response()->json(json_decode((string) response->getBody(), true));
            } catch (RequestExceptione) {
                // 处理异常情况
                return response()->json([
                    'error' => 'An error occurred while sending the request.',
                    'message' => $e->getMessage(),
                ], 500);
            }
        }
    }
    

    注意事项

    1. Guzzle 安装:在运行相关代码前,必须再三确认已经借助Composer成功安装Guzzle,否则后续代码会因缺少依赖库而报错。
    2. 请求构造:利用Guzzle的Client类构建请求时,要格外留意请求体的设置。例如,本示例中需将数据准确编码为JSON格式,并正确设置请求头中的 Content-Typeapplication/json,如此才能确保请求被服务端正确解析。
    3. 错误处理:Guzzle提供了一套完善的异常处理机制,代码里通过 catch 捕获 RequestException 异常,以便在请求出现问题时,及时反馈给前端合适的错误信息,提升系统的稳定性与可维护性。

    如何测试

    1. 使用 var_dump():当访问应用中的 /test_unipush 端点时,在控制台能够查看JSON编码后的数据结构输出,通过该输出可以初步判断数据组装是否正确。
    2. 检查请求:还需确认请求是否精准无误地发送到目标URL,以此保障推送通知功能的完整性,排查因网络、URL配置错误等导致的问题。

    示例

    当您访问 /test_unipush 端点时,您将看到类似如下的输出:

    string(135) "{"cids":["15f2d1111de1ef5aa3f8911dab53ffb2"],"title":"Test","content":"123","payload":"","force_notification":"true","request_id":"12345678982"}"
    
  • Animate.css动画快查表

    Animate.css 动画快查表

    基础动画效果

    • bounce:呈现弹跳效果。
    • flash:制造闪烁视觉。
    • pulse:规律地放大、缩小。
    • rubberBand:不仅有放大、缩小,还带有弹簧般的弹性动态。
    • shake:左右来回晃动。
    • headShake:左右进行小幅晃动。
    • swing:左右呈扇形摇摆。
    • tada:综合了放大、左右上下晃动后再缩小的效果。
    • wobble:左右做小幅(圆点较远)扇形摇摆。
    • jello:左右、上下交替晃动。

    淡入淡出动画

    • fadeIn:渐渐显现。
    • fadeInDown:从上方逐渐显现。
    • fadeInDownBig:从上方大幅滑动着渐现。
    • fadeInLeft:从左侧渐现。
    • fadeInLeftBig:从左侧长距离滑动渐现。
    • fadeInRight:从右侧渐现。
    • fadeInRightBig:从右侧长距离滑动渐现。
    • fadeInUp:从下方渐现。
    • fadeInUpBig:从下方大幅滑动渐现。
    • fadeOut:慢慢隐去。
    • fadeOutDown:向下方渐隐。
    • fadeOutDownBig:向下方大幅滑动渐隐。
    • fadeOutLeft:向左侧渐隐。
    • fadeOutLeftBig:向左侧长距离滑动渐隐。
    • fadeOutRight:向右侧渐隐。
    • fadeOutRightBig:向右侧长距离滑动渐隐。
    • fadeOutUp:向上方渐隐。
    • fadeOutUpBig:向上方大幅滑动渐隐。

    翻转动画

    • flip:围绕中心 Y 轴旋转,同时伴有放大、缩小。
    • flipInX:以元素中心 X 轴旋转进入视野。
    • flipInY:以元素中心 Y 轴旋转进入视野。
    • flipOutX:围绕中心 X 轴旋转后消失。
    • flipOutY:围绕中心 Y 轴旋转后消失。

    光速动画

    • lightSpeedIn:从右到左,以平行四边形且左上角突出的样式进入。
    • lightSpeedOut:从左到右,以平行四边形且左上角突出的样式离开。

    旋转动画

    • rotateIn:围绕元素中心顺时针旋转进入画面。
    • rotateInDownLeft:从元素左外长半径处顺时针旋转进入。
    • rotateInDownRight:从元素右外长半径处逆时针旋转进入。
    • rotateInUpLeft:从元素左外长半径处逆时针旋转进入。
    • rotateInUpRight:从元素右外长半径处顺时针旋转进入。
    • rotateOut:围绕元素中心顺时针旋转消失。
    • rotateOutDownLeft:从元素左外长半径处顺时针旋转消失。
    • rotateOutDownRight:从元素右外长半径处逆时针旋转消失。
    • rotateOutUpLeft:从元素左外长半径处逆时针旋转消失。
    • rotateOutUpRight:从元素右外长半径处顺时针旋转消失。

    合页动画

    • hinge:从右上到坐下,按顺时针旋转,随后从上到下消失。

    滚动动画

    • rollIn:从元素左外长半径顺时针旋转,顺滑进入。
    • rollOut:从元素右外长半径顺时针旋转,顺滑离开。

    缩放动画

    • zoomIn:由小变大进入画面。
    • zoomInDown:由小变大,从上方进入。
    • zoomInLeft:由小变大,从左方进入。
    • zoomInRight:由小变大,从右方进入。
    • zoomInUp:由小变大,从下方进入。
    • zoomOut:由大变小直至消失。
    • zoomOutDown:由大变小,向下方消失。
    • zoomOutLeft:由大变小,从左方消失。
    • zoomOutRight:由大变小,从右方消失。
    • zoomOutUp:由大变小,从上方消失。

    滑动动画

    • slideInDown:从上到下,平稳进入。
    • slideInLeft:从左到右,平稳进入。
    • slideInRight:从右到左,平稳进入。
    • slideInUp:从下到上,平稳进入。
    • slideOutDown:从上到下,平稳消失。
    • slideOutLeft:从右到左,平稳消失。
    • slideOutRight:从左到右,平稳消失。
    • slideOutUp:从下到上,平稳消失。
  • axios

    Axios:现代Web开发中的HTTP请求利器

    Axios作为一款基于Promise的HTTP库,在浏览器与Node.js环境都能大放异彩。鉴于其简单上手、功能完备,还支持取消请求、自动转换数据等诸多实用特性,当下已然成为发送AJAX请求的热门之选。下面就来深入了解它的核心亮点与使用范例。

    核心特点

    1. 基于Promise:Axios借助Promise机制,让异步请求处理变得直观清晰,还支持链式调用。以往令人头疼的回调嵌套(回调地狱)问题,在它面前迎刃而解。开发人员可以连贯地书写代码,提升代码可读性与维护性。
    2. 跨平台:不管是前端浏览器环境,还是后端Node.js服务器场景,Axios都能适配,而且提供统一的API接口。如此一来,前后端开发人员无需切换不同的请求处理方式,开发效率大大提高。
    3. 拦截器:它配备请求拦截器与响应拦截器。请求发出前,利用请求拦截器能完成如添加Token这类预处理操作;接收到响应后,响应拦截器又能对数据或错误进行针对性处理,极大增强了请求处理的灵活性。
    4. 自动转换JSON:Axios自带“智能转换”功能,会自动把JavaScript对象转化为JSON字符串发送出去,收到JSON格式的响应数据时,又能无缝变回JavaScript对象,为开发人员省去手动转换的繁琐步骤。
    5. 取消请求:当页面状态发生变化,或是用户操作需要中断正在执行的请求时,Axios允许取消已发出的请求。这不仅优化了用户体验,还避免了不必要的服务器资源浪费。
    6. 上传和下载进度:在涉及文件上传、下载场景时,Axios贴心地支持监控进度,方便开发者给用户呈现直观的进度条反馈。

    使用示例

    发送GET请求

    axios.get('https://api.example.com/data')
     .then(response => {
          console.log(response.data);
        })
     .catch(error => {
          console.error("Error fetching data:", error);
        });
    

    这段代码向指定API发送GET请求,成功获取数据后,在控制台打印响应数据;要是请求过程出错,就输出错误信息。

    发送POST请求

    axios.post('https://api.example.com/data', {
        key: 'value'
      })
     .then(response => {
          console.log(response.data);
        })
     .catch(error => {
          console.error("Error posting data:", error);
        });
    

    这里是向API发送POST请求,携带了一个简单的JavaScript对象作为请求体,后续处理逻辑与GET请求类似,根据响应情况输出对应内容。

    使用拦截器

    // 添加请求拦截器
    axios.interceptors.request.use(config => {
        // 在发送请求之前做些什么,比如添加Tokenn
        config.headers.Authorization = `Bearer ${token}`;
        return config;
      }, error => {
        // 对请求错误做些什么
        return Promise.reject(error);
    });
    
    // 添加响应拦截器
    axios.interceptors.response.use(response => {
        // 对响应数据做点什么
        return response;
      }, error => {
        // 对响应错误做点什么
        return Promise.reject(error);
    });
    

    在这段代码里,请求拦截器给即将发出的请求头部添加授权信息;响应拦截器则可对返回的数据或错误进行预处理,确保进入业务逻辑的信息是经过“把关”的。

    正是凭借这些出彩的特性,Axios成为众多现代Web开发项目处理HTTP请求时的不二之选。

  • Promise 和ajax的关系

    Promise与AJAX:JavaScript异步处理的黄金搭档

    在JavaScript的异步编程世界里,Promise和AJAX占据着举足轻重的地位。它们各司其职,又相辅相成,携手为开发者解决各类异步难题。

    AJAX:网页异步交互的基石

    AJAX,全称“Asynchronous JavaScript and XML”,是一项意义非凡的技术。它赋予网页无需整体刷新,就能与服务器悄然通信的能力。借助JavaScript发起请求,AJAX能够异步地从服务器抓取数据,进而达成页面局部更新的效果。往昔,XML曾是AJAX常用的数据格式,不过当下,JSON因其简洁轻巧、易于解析,已然成为主流之选。

    在处理异步操作结果时,传统的AJAX依赖回调函数。也就是说,当请求大功告成,预先设定的回调函数便会被触发执行。但这种方式在复杂场景下,容易催生“回调地狱”,让代码陷入层层嵌套、晦涩难懂的困境。

    Promise:异步代码的优雅编排器

    随着JavaScript ES6标准的落地,Promise闪亮登场,为异步操作管理带来全新思路。Promise本质上是一种原生对象,专门用于驯服异步代码这头“猛兽”。它仅有两个核心状态:pending(悬而未决,等待中)与settled(尘埃落定,细分又有fulfilled圆满完成、rejected遭遇拒绝 )。

    借助链式调用 .then.catch 方法,Promise为异步流程勾勒出清晰的“成功-失败”处理路径。开发者得以摆脱回调嵌套的泥沼,让异步代码焕然一新,具备极佳的可读性与易维护性。

    Promise与AJAX的协同效应

    尽管AJAX聚焦于与服务器的异步通信技术,Promise侧重于异步编程范式,二者看似各司其职,但搭配起来却相得益彰。

    如今,众多现代AJAX库,像Axios,以及经典框架如jQuery,都已将Promise支持融入其中。当发起AJAX请求时,返回一个Promise对象,后续只需链式调用 .then 处理顺风顺水的响应,用 .catch 捕获并应对错误状况。这般操作,即便AJAX请求天生异步、变幻莫测,代码也能如行云流水般线性编排,可读性与可维护性大幅跃升。

    总而言之,Promise宛如一把精准的手术刀,巧妙化解了AJAX编程长久以来的痛点,让异步交互代码告别杂乱无章,迈向条理清晰的新境界。

  • Laravel+Swoole 实现websocket 主动消息推送

    基于Laravel + Swoole实现后端主动推送功能

    在利用Laravel与Swoole搭建的聊天系统基础上,实现后端主动推送消息,尤其是在订单状态改变时推送至后台系统,是个很实用的需求。

    Swoole的关键特性利用

    Swoole的WebSocket\Server继承自Http\Server,这一特性十分关键,让我们既能使用Http\Server提供的API与配置项,又能处理WebSocket相关的逻辑。例如设置onRequest回调,就可以让WebSocket\Server兼任HTTP服务器,这为接收外部触发信号来推送WebSocket消息创造了条件。

    示例代码剖析

    Swoole服务端示例

    class WebSocketServer {
        public server;
    
        public function __construct() {this->server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
            this->server->on('open', function (Swoole\WebSocket\Serverserver, request) {
                echo "server: handshake success with fd{request->fd}\n";
            });
            this->server->on('message', function (Swoole\WebSocket\Serverserver, frame) {
                echo "receive from {frame->fd}:{frame->data},opcode:{frame->opcode},fin:{frame->finish}\n";server->push(frame->fd, "this is server");
            });this->server->on('close', function (ser,fd) {
                echo "client {fd} closed\n";
            });this->server->on('request', function (request,response) {
                // 接收http请求从get获取message参数的值,给用户推送
                // this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送
                foreach (this->server->connections as fd) {
                    // 需要先判断是否是正确的websocket连接,否则有可能会push失败
                    if (this->server->isEstablished(fd)) {this->server->push(fd,request->get['message']);
                    }
                }
            });
            $this->server->start();
        }
    }
    new WebSocketServer();
    

    这段代码展示了一个完整的Swoole WebSocket服务器搭建过程。on('request')事件监听HTTP请求,当接收到请求时,它会遍历所有WebSocket连接,将请求中的消息推送给合适的连接用户。这是实现后端主动推送的核心逻辑基础。

    项目结合代码

    • 修改ImCommand类:在ImCommand类里,新增$serverInstance作为类成员变量存储Swoole服务器实例,让类内的其他方法能够访问它。handle方法创建服务器实例并赋值给该变量,同时监听request事件,在事件处理中调用pushHomeLogic方法并传入必要参数,确保推送消息时使用正确的服务器实例。
    <?php namespace App\Console\Commands;
    
    class ImCommand extends Command {
        use SocketPush;
        protected serverInstance;
    
        public function handle() {this->serverInstance = new Server("0.0.0.0", 9502);
            this->serverInstance->on('request', function (request, response) {
                echo "request-".request->fd."\n";
                var_dump(request->post);
                call_user_func([this, 'pushHomeLogic'], request,this->serverInstance);
            });
        }
    
        public function pushHomeLogic(request,serv) {
            data =request->post;
            serv->push(data['fd'], this->success(ImService::scene[ImService::PUSH_MESSAGE], $data['data']));
        }
    }
    
    • 创建消息服务ImMessageServicecurl方法利用CURL发起HTTP请求到本地的Swoole服务器,push方法则先从Redis获取目标用户(后台管理客服)的信息,组装参数后调用curl方法,实现主动推送消息。
    public static function curl(data) {curl = curl_init();
        curl_setopt(curl, CURLOPT_URL, "http://127.0.0.1:9502");
        curl_setopt(curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt(curl, CURLOPT_HEADER, 1);
        curl_setopt(curl, CURLOPT_POST, 1);
        curl_setopt(curl, CURLOPT_POSTFIELDS,data);
        curl_exec(curl);
        curl_close(curl);
    }
    
    public static function push(param) {to_user_id = 999;    // 后台管理客服id
        cacheKey = "socket_user:id_".to_user_id;
        to_user_info = Redis::get(cacheKey);
        if (empty(to_user_info)) return;to_user_info = json_decode(to_user_info,true);param['fd'] = to_user_info['fd'];
        self::curl(param);            // 主动推送消息
    }
    
    • 在控制器中调用:在控制器里,只需简单组装消息参数,调用ImMessageService::push方法,就能触发后端主动推送消息至指定的后台客服,整个流程清晰流畅。
    // 系统推送订单消息
    param['msg'] = '用户新订单,请注意查看!';
    ImMessageService::push(param);
    

    总体而言,通过巧妙利用Swoole的特性,整合项目中的各个模块,成功实现了后端主动推送消息的功能,满足系统对于订单状态变更推送的需求。不过在实际应用中,还需留意如异常处理、连接稳定性维护等方面的优化。

  • Laravel 多种方法获取当前 URL、请求路径、参数等信息

    在Laravel中获取URL、请求路径及参数相关信息

    在Laravel项目开发过程中,精准获取当前URL、请求路径以及各类参数,对实现业务逻辑、页面跳转、权限判断等功能极为关键。下面详细介绍多种可行的获取方式:

    使用URL类

    • 获取完整URLURL::full();url()->full();作用相同,都用于获取包含查询字符串的当前请求完整URL 。例如,若当前页面是https://example.com/products?sort=price,执行这两个方法就会返回这个完整链接。
    • 获取不含查询字符串的URLURL::current();url()->current();能给出当前请求的URL,但剔除了查询字符串,以上面例子来说,会返回https://example.com/products
    • 获取前一个URLURL::previous();url()->previous();依赖浏览器发送的Referer头。要是浏览器传递了该信息,就能获取到之前访问的URL;要是没有,就返回null
    • 获取安全资源URLURL::secureAsset('path/to/asset');可智能生成指定资源的HTTPS URL。当应用配置成使用HTTPS时,返回的必然是HTTPS链接;反之,则是普通HTTP URL。

    使用Request类

    • 获取完整URLRequest::url();$request->url();的效果等同于使用URL类获取完整URL的方法,会把查询字符串一块返回。
    • 获取请求路径Request::path();$request->path();专注于提取当前请求的URI路径,过滤掉查询字符串,让开发者拿到纯粹的路径信息。
    • 获取请求URIRequest::getRequestUri();$request->getRequestUri();返回的是请求URI,也就是路径与查询字符串合并后的内容。
    • 获取完整URI字符串Request::getUri();$request->getUri();给出最完整的请求URI,涵盖scheme、host、端口(非默认情况下)、路径以及查询字符串。
    • 获取路由操作信息$request->route()->getAction();常用来深挖当前路由操作背后的信息,像是控制器、方法的具体名称,辅助开发者理清业务流程。

    使用Input类(不推荐)

    在Laravel新的版本生态里,Input类已经过时废弃,官方推荐使用Request类的对应方法获取相关信息,所以不要再使用Input::url();这类写法。

    使用$_SERVER

    • 获取请求URI$_SERVER['REQUEST_URI']保存着当前请求的URI路径,有可能附带查询字符串,开发中常用于快速抓取请求路径。
    • 获取主机名与端口$_SERVER['HTTP_HOST']存有当前请求的主机名,要是端口号不是默认的,也会一并包含在内。

    使用Request类获取$_SERVER信息

    • 获取完整$_SERVER数组Request::server();$request->server();能获取包含$_SERVER数组全部元素的新数组,方便集中查阅。
    • 获取特定键值Request::server('HTTP_HOST');$request->server('HTTP_HOST');则是精准定位,直接提取$_SERVER数组中特定键对应的值。

    注意事项

    • 实例访问便利性:在控制器、路由闭包场景下,Request类实例往往作为参数传入,所以能直接用$request变量调用其各类方法,无需额外获取。
    • 依赖注入优势:得益于Laravel的服务容器与依赖注入机制,获取Request类实例变得轻而易举,减少了从全局帮助函数或URL Facade获取的繁琐操作。
    • 配置准确性:当使用URL Facade或全局帮助函数时,得保证应用的URL配置无误,重点留意.env文件里的APP_URL配置项,避免生成错误链接。
  • thinkcmf 留言版(后端)

    这段PHP代码是基于ThinkCMF框架编写的一个简单的留言模块后端代码,下面详细分析一下:

    1. 命名空间与引入类
      • 代码位于app\portal\controller命名空间下,这是ThinkCMF框架中用于存放门户相关控制器的常见位置。
      • 通过use关键字引入了app\portal\model\GuestBookModelcmf\controller\HomeBaseController,前者用于与留言数据模型交互,后者是ThinkCMF框架中的一个基础控制器,当前控制器继承自它,以便复用一些基础功能。
    2. add方法逻辑
      • 实例化模型:在add方法中,首先实例化了GuestBookModel类,这一步为后续操作数据库、保存留言数据做好准备。
      • 处理POST请求:使用if ($this->request->isPost())判断当前请求是否为POST类型,如果是,则进行以下操作:
        • 获取请求数据:通过$data = $this->request->param();获取前端传来的所有表单数据,包括用户填写的留言内容、验证码等信息。
        • 验证码校验:从$data中取出captcha字段,调用cmf_captcha_check函数进行验证码校验。如果校验失败,直接返回captcha error错误信息给前端。
        • 添加时间戳:给留言数据添加create_time字段,并赋值为当前时间戳time(),用于记录留言的创建时间。
        • 保存数据:调用$guestBookModel->save($data)尝试将留言数据保存到数据库中。如果保存成功,返回success成功信息;否则,返回error错误信息。
      • 处理非POST请求:如果当前请求不是POST类型,直接返回error错误信息,提示前端请求方式不正确。

    整体来看,这段代码实现了一个基础的留言添加功能,涵盖数据获取、验证码校验、时间记录和数据保存等关键步骤,但也存在一些可优化的地方:
    – 错误提示比较笼统,error并没有明确指出是数据库保存失败的哪种具体原因,后续排查问题可能会有困难,可以补充更详细的日志信息。
    – 验证码相关的配置没有展示出来,像验证码的生成规则、有效期等,在代码维护时不够直观。
    – 没有对前端传来的数据做更细致的过滤与校验,存在一定安全风险,例如SQL注入风险,可以引入数据验证机制,如ThinkCMF框架自身的验证规则。