# Adminive Backend-Driven Example Adminive 使用 C++20 模板、成员指针和 `magic_enum` 生成后端描述。前端只负责渲染 `/admin/amis` 返回的 amis Schema,不包含业务字段、枚举映射、列顺序、排序规则或配置联动。 ## 数据名称与界面名称 字段成员名继续作为稳定的数据名称: ```cpp struct Radio_State { Radio_Mode mode{Radio_Mode::receive}; }; ``` 界面标签、列表表头和枚举显示名称在描述端单独配置: ```cpp ADMINIVE_FIELD_LABEL(T, mode, "运行模式") .list_label("模式") .sortable() .enum_label("接收") .enum_label("发送") .enum_label("双工") .enum_label("维护"); ``` 接口仍返回: ```json { "mode": "duplex" } ``` 表格显示为“模式:双工”。`magic_enum` 负责枚举值发现和稳定字符串生成,`.enum_label` 只覆盖界面文字。 ## 列顺序与用户排序 字段的默认列顺序和是否允许按值排序由后端描述: ```cpp ADMINIVE_FIELD_LABEL(T, buffer_count, "缓冲区数量") .list_label("缓冲区") .order(10) .sortable(); ``` 对象描述可以定义默认排序: ```cpp object(/* fields */) .default_sort("buffer_count"); ``` 用户点击可排序表头时,前端发送 `orderBy` 和 `orderDir`,后端只接受声明为 `.sortable()` 的字段。 用户也可以拖拽调整行顺序: ```cpp object(/* fields */) .user_reorderable(); ``` 该配置会生成 `draggable` 和 `saveOrderApi`,后端提供 `POST /order` 保存当前运行期顺序。列选择菜单支持拖拽调整列先后,默认由 `.column_reorderable()` 控制。 ## CRUD 中文文字 CRUD 固定文字也在对象描述中配置: ```cpp object(/* fields */) .label("无线电状态") .id_label("编号") .create_label("新增") .edit_label("编辑") .delete_label("删除") .actions_label("操作") .confirm_label("确认修改") .view_status_label("查看状态 ▾") .management_label("无线电状态列表"); ``` ## 树状配置与持久化 嵌套 C++ 对象直接表达树状配置: ```cpp struct Radio_Service_Config { std::string profile_name{"Primary Radio Profile"}; Radio_Mode mode{Radio_Mode::receive}; Endpoint_Config primary_endpoint{}; Endpoint_Config backup_endpoint{}; Appearance_Config appearance{}; }; ``` 嵌套对象在界面中生成 `fieldset`。字符串、整数、浮点数、布尔、枚举、日期和颜色分别生成对应控件。联动条件仍由后端定义: ```cpp ADMINIVE_FIELD_LABEL(T, backup_endpoint, "备用端点") .editable() .visible_on("${mode == 'duplex'}"); ``` 配置文件位于: ```text config/adminive.json ``` 文件保存以下持久化内容: ```text Application_Config ├── default_device_type ├── radio_service ├── alerts ├── serial_table └── network_table ``` 程序启动时读取该文件。文件不存在时,使用 C++ 默认值自动创建。配置表单点击“确认修改”后,后端先在副本上完成赋值和校验,再写入临时文件并替换正式配置文件,成功后才替换内存对象。 ## 多态类型表格 不同表格类型通过 C++ 虚函数注册: ```cpp class Device_Table_Base { public: virtual ~Device_Table_Base() = default; virtual Device_Table_Type type() const noexcept = 0; virtual std::string_view type_name() const noexcept = 0; virtual std::string_view type_label() const noexcept = 0; virtual Json amis_schema() const = 0; virtual void bind(httplib::Server& server) = 0; }; ``` 示例实现两个子类: ```text Serial_Device_Table Network_Device_Table ``` 界面切换 `device_type` 后,`service.schemaApi` 重新向后端请求对应子类的 Schema: ```text serial -> 串口、波特率、校验位、启用 network -> 地址、协议、超时、TLS ``` 因此切换类型后,表格列、编辑表单、配置表单和接口路径都会一起改变,前端不需要识别任何业务类型。 ## 对象状态 对象状态使用独立类型,并通过成员函数指针注册: ```cpp resource.register_status<&Radio_State::status>(2000); ``` 只有用户打开某一行的“查看状态”弹层后,该行状态才开始请求和刷新。颜色由后端状态值返回。 ## 主要接口 ```text GET /admin/amis GET /admin/status GET /admin/radio_states POST /admin/radio_states PUT /admin/radio_states/{id} DELETE /admin/radio_states/{id} GET /admin/radio_states/{id}/status POST /admin/device_tables/serial/items/order POST /admin/device_tables/network/items/order GET /admin/device_tables/schema?type=serial GET /admin/device_tables/schema?type=network POST /admin/device_tables/default POST /admin/config/radio/data POST /admin/config/alerts/data POST /admin/device_tables/serial/config/data POST /admin/device_tables/network/config/data ``` ## 构建 ```powershell cmake -S . -B build -G Ninja cmake --build build ctest --test-dir build --output-on-failure .\build\backend\Adminive_Server.exe 9999 ``` ## 源码压缩目标 压缩函数参数顺序: ```cmake add_project_zip_target( target_name output_file root_directory include_list_name exclude_list_name ) ``` 调用示例: ```cmake set(adminive_package_includes "/backend/" "/cmake/" "/config/" "/frontend/" "/scripts/" "/CMakeLists.txt" "/README.md" ) set(adminive_package_excludes "/.git/" "/.idea/" "/build/" "/cmake-build-*/" "/doc/" "/node_modules/" "/frontend/frontend.zip" "/frontend_dist/" "*.tsbuildinfo" "*.zip" ) include("${CMAKE_CURRENT_LIST_DIR}/cmake/PackageZip.cmake") add_project_zip_target(package_zip "${CMAKE_CURRENT_LIST_DIR}/Adminive.zip" "${CMAKE_CURRENT_LIST_DIR}" adminive_package_includes adminive_package_excludes) ``` 函数只扫描包含列表指定的目录和文件,然后相对于根目录应用排除列表。