更新功能

This commit is contained in:
2026-08-06 16:48:57 +08:00
parent a0ca831a00
commit 9a25d37a90
10 changed files with 612 additions and 206 deletions
+50 -14
View File
@@ -7,21 +7,46 @@
#include <utility>
#include <vector>
namespace adminive {
inline Json make_config_tree(std::string name, std::string label, Json children = Json::array()) {
return Json{{"name", std::move(name)}, {"label", std::move(label)}, {"children", std::move(children)}};
inline std::string make_field_name(std::string_view prefix, std::string_view name) {
if(prefix.empty()) {
return std::string(name);
}
return std::string(prefix) + "." + std::string(name);
}
inline Json make_amis_control(const Json& field, std::string_view permission) {
Json control{{"name", field.at("name")}, {"label", field.at("label")}};
inline Json make_amis_form_actions(std::string submit_label = "Apply") {
return Json::array({Json{{"type", "reset"}, {"label", "Reset"}}, Json{{"type", "submit"}, {"label", std::move(submit_label)}, {"level", "primary"}}});
}
inline Json make_amis_control(const Json& field, std::string_view permission, std::string_view prefix = {}) {
const std::string name = field.at("name").get<std::string>();
const std::string full_name = make_field_name(prefix, name);
const std::string value_type = field.at("value_type").get<std::string>();
if(value_type == "object" && field.contains("children")) {
Json body = Json::array();
for(const auto& child : field.at("children")) {
if(child.at("visible").get<bool>()) {
body.push_back(make_amis_control(child, permission, full_name));
}
}
Json control{{"type", "fieldset"}, {"title", field.at("label")}, {"body", std::move(body)}, {"collapsable", true}, {"collapsed", false}};
if(field.contains("description")) {
control["description"] = field.at("description");
}
if(field.contains("visible_on")) {
control["visibleOn"] = field.at("visible_on");
}
return control;
}
Json control{{"name", full_name}, {"label", field.at("label")}};
if(field.contains("description")) {
control["description"] = field.at("description");
}
if(field.contains("visible_on")) {
control["visibleOn"] = field.at("visible_on");
}
if(!field.at(permission).get<bool>()) {
control["type"] = "static";
return control;
}
const std::string value_type = field.at("value_type");
if(field.contains("widget")) {
control["type"] = field.at("widget");
} else if(value_type == "boolean") {
@@ -33,6 +58,13 @@ inline Json make_amis_control(const Json& field, std::string_view permission) {
} else {
control["type"] = "input-text";
}
if(control.at("type") == "input-date") {
control["valueFormat"] = "YYYY-MM-DD";
control["displayFormat"] = "YYYY-MM-DD";
control["clearable"] = true;
} else if(control.at("type") == "input-color") {
control["clearable"] = true;
}
if(field.at("required").get<bool>()) {
control["required"] = true;
}
@@ -64,8 +96,12 @@ inline Json make_amis_form_body(const Json& descriptor, std::string_view permiss
}
inline Json make_amis_column(const Json& field) {
Json column{{"name", field.at("name")}, {"label", field.at("list_label")}};
if(field.contains("order")) { column["order"] = field.at("order"); }
if(field.contains("sortable")) { column["sortable"] = field.at("sortable"); }
if(field.contains("order")) {
column["order"] = field.at("order");
}
if(field.contains("sortable")) {
column["sortable"] = field.at("sortable");
}
const std::string value_type = field.at("value_type");
if(value_type == "boolean") {
column["type"] = "status";
@@ -107,9 +143,9 @@ inline Json make_amis_object_status_column(const Json& descriptor, std::string s
return Json{{"type", "tpl"}, {"label", descriptor.at("label")}, {"tpl", R"(<span class="text-primary">View status ▾</span>)"}, {"popOver", pop_over}};
}
template <Described_Type T>
Json to_amis_form_schema(const T& value, std::string submit_api = {}) {
Json to_amis_form_schema(const T& value, std::string submit_api = {}, std::string submit_label = "Apply") {
const Json descriptor = to_descriptor_json<T>();
Json result{{"type", "form"}, {"title", descriptor.at("label")}, {"data", to_frontend_json(value)}, {"body", make_amis_form_body(descriptor, "editable")}};
Json result{{"type", "form"}, {"title", descriptor.at("label")}, {"data", to_frontend_json(value)}, {"body", make_amis_form_body(descriptor, "editable")}, {"actions", make_amis_form_actions(std::move(submit_label))}, {"affixFooter", true}};
if(!submit_api.empty()) {
result["api"] = Json{{"method", "post"}, {"url", std::move(submit_api)}};
}
@@ -122,7 +158,7 @@ Json to_amis_crud_schema(std::string base_api, const Json* status_descriptor = n
Json columns = Json::array({Json{{"name", "id"}, {"label", "Id"}}});
std::vector<Json> list_fields;
for(const auto& field : descriptor.at("fields")) {
if(field.at("readable").get<bool>() && field.at("list_visible").get<bool>()) {
if(field.at("readable").get<bool>() && field.at("list_visible").get<bool>() && field.at("value_type") != "object") {
list_fields.push_back(field);
}
}
@@ -135,8 +171,8 @@ Json to_amis_crud_schema(std::string base_api, const Json* status_descriptor = n
if(status_descriptor) {
columns.push_back(make_amis_object_status_column(*status_descriptor, base_api + "/${id}/status", status_interval));
}
const Json create_form{{"type", "form"}, {"api", Json{{"method", "post"}, {"url", base_api}}}, {"reload", crud_id}, {"body", make_amis_form_body(descriptor, "creatable")}};
const Json edit_form{{"type", "form"}, {"api", Json{{"method", "put"}, {"url", base_api + "/${id}"}}}, {"reload", crud_id}, {"body", make_amis_form_body(descriptor, "editable")}};
const Json create_form{{"type", "form"}, {"api", Json{{"method", "post"}, {"url", base_api}}}, {"reload", crud_id}, {"body", make_amis_form_body(descriptor, "creatable")}, {"actions", make_amis_form_actions("Create")}};
const Json edit_form{{"type", "form"}, {"api", Json{{"method", "put"}, {"url", base_api + "/${id}"}}}, {"reload", crud_id}, {"body", make_amis_form_body(descriptor, "editable")}, {"actions", make_amis_form_actions("Confirm Changes")}};
const Json create_button{{"type", "button"}, {"label", "Create"}, {"level", "primary"}, {"actionType", "dialog"}, {"dialog", Json{{"title", "Create " + descriptor.at("label").get<std::string>()}, {"body", create_form}}}};
Json actions = Json::array();
actions.push_back(Json{{"type", "button"}, {"label", "Edit"}, {"level", "link"}, {"actionType", "dialog"}, {"dialog", Json{{"title", "Edit " + descriptor.at("label").get<std::string>()}, {"body", edit_form}}}});
+11 -2
View File
@@ -26,8 +26,8 @@ inline std::string make_label(std::string_view name) {
std::string result;
result.reserve(name.size());
bool uppercase = true;
for (const char value : name) {
if (value == '_') {
for(const char value : name) {
if(value == '_') {
result.push_back(' ');
uppercase = true;
continue;
@@ -90,6 +90,11 @@ public:
result.widget_ = std::move(value);
return result;
}
Field_Descriptor visible_on(std::string value) const {
auto result = *this;
result.visible_on_ = std::move(value);
return result;
}
Field_Descriptor list_label(std::string value) const {
auto result = *this;
result.list_label_ = std::move(value);
@@ -120,6 +125,9 @@ public:
const std::string& widget() const noexcept {
return widget_;
}
const std::string& visible_on() const noexcept {
return visible_on_;
}
bool is_editable() const noexcept {
return editable_;
}
@@ -150,6 +158,7 @@ private:
std::string list_label_;
std::string description_;
std::string widget_;
std::string visible_on_;
bool editable_{};
bool creatable_{};
bool readable_{true};
+6 -3
View File
@@ -25,6 +25,10 @@ requires std::copy_constructible<T> && std::assignable_from<T&, T>
class Http_Resource {
public:
Http_Resource(T& value, std::string path) : value_(value), path_(std::move(path)) {}
Json amis_schema() const {
std::scoped_lock lock(mutex_);
return to_amis_form_schema(value_, path_ + "/data", "Confirm Changes");
}
void bind(httplib::Server& server) {
server.Get(path_ + "/descriptor", [this](const httplib::Request&, httplib::Response& response) {
write_http_json(response, Json{{"status", 0}, {"msg", ""}, {"data", to_descriptor_json<T>()}});
@@ -34,8 +38,7 @@ public:
write_http_json(response, Json{{"status", 0}, {"msg", ""}, {"data", to_frontend_json(value_)}});
});
server.Get(path_ + "/amis", [this](const httplib::Request&, httplib::Response& response) {
std::scoped_lock lock(mutex_);
write_http_json(response, Json{{"status", 0}, {"msg", ""}, {"data", to_amis_form_schema(value_, path_ + "/data")}});
write_http_json(response, Json{{"status", 0}, {"msg", ""}, {"data", amis_schema()}});
});
server.Post(path_ + "/data", [this](const httplib::Request& request, httplib::Response& response) {
try {
@@ -56,7 +59,7 @@ public:
private:
T& value_;
std::string path_;
std::mutex mutex_;
mutable std::mutex mutex_;
};
template <Described_Type T>
requires std::default_initializable<T> && std::copy_constructible<T> && std::assignable_from<T&, T>
+10 -1
View File
@@ -36,6 +36,9 @@ Json to_json(const T& value);
template <Described_Type T>
Json to_frontend_json(const T& value);
template <Described_Type T>
requires std::default_initializable<T>
Json to_descriptor_json();
template <Described_Type T>
requires std::default_initializable<T> && std::copy_constructible<T> && std::assignable_from<T&, T>
Update_Result assign_json(T& target, const Json& value);
template <class T>
@@ -190,9 +193,15 @@ Json to_descriptor_json() {
if (!item.description().empty()) {
field_json["description"] = item.description();
}
if (!item.widget().empty()) {
if(!item.widget().empty()) {
field_json["widget"] = item.widget();
}
if(!item.visible_on().empty()) {
field_json["visible_on"] = item.visible_on();
}
if constexpr(Described_Type<Member>) {
field_json["children"] = to_descriptor_json<Member>().at("fields");
}
append_constraints<Member>(field_json);
const auto options = enum_options<Member>();
if (!options.empty()) {