mirror of
https://github.com/cuigh/swirl
synced 2024-12-27 22:32:49 +00:00
Make event objects more extensible
This commit is contained in:
parent
308d3ddc05
commit
752ddff01f
@ -63,7 +63,7 @@ func (b *containerBiz) Search(node, name, status string, pageIndex, pageSize int
|
||||
func (b *containerBiz) Delete(node, id, name string, user web.User) (err error) {
|
||||
err = b.d.ContainerRemove(context.TODO(), node, id)
|
||||
if err == nil {
|
||||
b.eb.CreateContainer(EventActionDelete, id, name, user)
|
||||
b.eb.CreateContainer(EventActionDelete, node, id, name, user)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -92,7 +92,7 @@ func (b *containerBiz) Prune(node string, user web.User) (count int, size uint64
|
||||
var report types.ContainersPruneReport
|
||||
if report, err = b.d.ContainerPrune(context.TODO(), node); err == nil {
|
||||
count, size = len(report.ContainersDeleted), report.SpaceReclaimed
|
||||
b.eb.CreateContainer(EventActionPrune, "", "", user)
|
||||
b.eb.CreateContainer(EventActionPrune, node, "", "", user)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
107
biz/event.go
107
biz/event.go
@ -3,6 +3,7 @@ package biz
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cuigh/auxo/data"
|
||||
"github.com/cuigh/auxo/log"
|
||||
"github.com/cuigh/auxo/net/web"
|
||||
"github.com/cuigh/swirl/dao"
|
||||
@ -12,21 +13,20 @@ import (
|
||||
type EventType string
|
||||
|
||||
const (
|
||||
EventTypeRegistry EventType = "Registry"
|
||||
EventTypeNode EventType = "Node"
|
||||
EventTypeNetwork EventType = "Network"
|
||||
EventTypeService EventType = "Service"
|
||||
EventTypeServiceTemplate EventType = "Template"
|
||||
EventTypeStack EventType = "Stack"
|
||||
EventTypeConfig EventType = "Config"
|
||||
EventTypeSecret EventType = "Secret"
|
||||
EventTypeImage EventType = "Image"
|
||||
EventTypeContainer EventType = "Container"
|
||||
EventTypeVolume EventType = "Volume"
|
||||
EventTypeUser EventType = "User"
|
||||
EventTypeRole EventType = "Role"
|
||||
EventTypeChart EventType = "Chart"
|
||||
EventTypeSetting EventType = "Setting"
|
||||
EventTypeRegistry EventType = "Registry"
|
||||
EventTypeNode EventType = "Node"
|
||||
EventTypeNetwork EventType = "Network"
|
||||
EventTypeService EventType = "Service"
|
||||
EventTypeStack EventType = "Stack"
|
||||
EventTypeConfig EventType = "Config"
|
||||
EventTypeSecret EventType = "Secret"
|
||||
EventTypeImage EventType = "Image"
|
||||
EventTypeContainer EventType = "Container"
|
||||
EventTypeVolume EventType = "Volume"
|
||||
EventTypeUser EventType = "User"
|
||||
EventTypeRole EventType = "Role"
|
||||
EventTypeChart EventType = "Chart"
|
||||
EventTypeSetting EventType = "Setting"
|
||||
)
|
||||
|
||||
type EventAction string
|
||||
@ -51,13 +51,12 @@ type EventBiz interface {
|
||||
CreateNode(action EventAction, id, name string, user web.User)
|
||||
CreateNetwork(action EventAction, id, name string, user web.User)
|
||||
CreateService(action EventAction, name string, user web.User)
|
||||
CreateTemplate(action EventAction, id, name string, user web.User)
|
||||
CreateConfig(action EventAction, id, name string, user web.User)
|
||||
CreateSecret(action EventAction, id, name string, user web.User)
|
||||
CreateStack(action EventAction, name string, user web.User)
|
||||
CreateImage(action EventAction, id string, user web.User)
|
||||
CreateContainer(action EventAction, id, name string, user web.User)
|
||||
CreateVolume(action EventAction, name string, user web.User)
|
||||
CreateImage(action EventAction, node, id string, user web.User)
|
||||
CreateContainer(action EventAction, node, id, name string, user web.User)
|
||||
CreateVolume(action EventAction, node, name string, user web.User)
|
||||
CreateUser(action EventAction, id, name string, user web.User)
|
||||
CreateRole(action EventAction, id, name string, user web.User)
|
||||
CreateChart(action EventAction, id, title string, user web.User)
|
||||
@ -76,79 +75,99 @@ func (b *eventBiz) Search(args *dao.EventSearchArgs) (events []*dao.Event, total
|
||||
return b.d.EventSearch(context.TODO(), args)
|
||||
}
|
||||
|
||||
func (b *eventBiz) create(et EventType, ea EventAction, code, name string, user web.User) {
|
||||
func (b *eventBiz) create(et EventType, ea EventAction, args data.Map, user web.User) {
|
||||
event := &dao.Event{
|
||||
ID: primitive.NewObjectID(),
|
||||
Type: string(et),
|
||||
Action: string(ea),
|
||||
Code: code,
|
||||
Name: name,
|
||||
Args: args,
|
||||
UserID: user.ID(),
|
||||
Username: user.Name(),
|
||||
Time: now(),
|
||||
}
|
||||
err := b.d.EventCreate(context.TODO(), event)
|
||||
if err != nil {
|
||||
log.Get("event").Errorf("failed to create event `%+v`: %v", event, err)
|
||||
log.Get("event").Errorf("failed to create event `%+v`: %s", event, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateRegistry(action EventAction, id, name string, user web.User) {
|
||||
b.create(EventTypeRegistry, action, id, name, user)
|
||||
args := data.Map{"id": id, "name": name}
|
||||
b.create(EventTypeRegistry, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateService(action EventAction, name string, user web.User) {
|
||||
b.create(EventTypeService, action, name, name, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateTemplate(action EventAction, id, name string, user web.User) {
|
||||
b.create(EventTypeServiceTemplate, action, id, name, user)
|
||||
args := data.Map{"name": name}
|
||||
b.create(EventTypeService, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateNetwork(action EventAction, id, name string, user web.User) {
|
||||
b.create(EventTypeNetwork, action, id, name, user)
|
||||
args := data.Map{"id": id, "name": name}
|
||||
b.create(EventTypeNetwork, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateNode(action EventAction, id, name string, user web.User) {
|
||||
b.create(EventTypeNode, action, id, name, user)
|
||||
args := data.Map{"id": id, "name": name}
|
||||
b.create(EventTypeNode, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateImage(action EventAction, id string, user web.User) {
|
||||
b.create(EventTypeImage, action, id, "", user)
|
||||
func (b *eventBiz) CreateImage(action EventAction, node, id string, user web.User) {
|
||||
args := data.Map{"node": node}
|
||||
if id != "" {
|
||||
args["id"] = id
|
||||
}
|
||||
b.create(EventTypeImage, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateContainer(action EventAction, id, name string, user web.User) {
|
||||
b.create(EventTypeContainer, action, id, name, user)
|
||||
func (b *eventBiz) CreateContainer(action EventAction, node, id, name string, user web.User) {
|
||||
args := data.Map{"node": node}
|
||||
if id != "" {
|
||||
args["id"] = id
|
||||
}
|
||||
if name != "" {
|
||||
args["name"] = name
|
||||
}
|
||||
b.create(EventTypeContainer, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateVolume(action EventAction, name string, user web.User) {
|
||||
b.create(EventTypeVolume, action, name, name, user)
|
||||
func (b *eventBiz) CreateVolume(action EventAction, node, name string, user web.User) {
|
||||
args := data.Map{"node": node}
|
||||
if name != "" {
|
||||
args["name"] = name
|
||||
}
|
||||
b.create(EventTypeVolume, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateStack(action EventAction, name string, user web.User) {
|
||||
b.create(EventTypeStack, action, name, name, user)
|
||||
args := data.Map{"name": name}
|
||||
b.create(EventTypeStack, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateSecret(action EventAction, id, name string, user web.User) {
|
||||
b.create(EventTypeSecret, action, id, name, user)
|
||||
args := data.Map{"id": id, "name": name}
|
||||
b.create(EventTypeSecret, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateConfig(action EventAction, id, name string, user web.User) {
|
||||
b.create(EventTypeConfig, action, id, name, user)
|
||||
args := data.Map{"id": id, "name": name}
|
||||
b.create(EventTypeConfig, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateRole(action EventAction, id, name string, user web.User) {
|
||||
b.create(EventTypeRole, action, id, name, user)
|
||||
args := data.Map{"id": id, "name": name}
|
||||
b.create(EventTypeRole, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateSetting(action EventAction, user web.User) {
|
||||
b.create(EventTypeSetting, action, "", "Setting", user)
|
||||
b.create(EventTypeSetting, action, nil, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateUser(action EventAction, id, name string, user web.User) {
|
||||
b.create(EventTypeUser, action, id, name, user)
|
||||
args := data.Map{"id": id, "name": name}
|
||||
b.create(EventTypeUser, action, args, user)
|
||||
}
|
||||
|
||||
func (b *eventBiz) CreateChart(action EventAction, id, title string, user web.User) {
|
||||
b.create(EventTypeChart, action, id, title, user)
|
||||
args := data.Map{"id": id, "name": title}
|
||||
b.create(EventTypeChart, action, args, user)
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ func (b *imageBiz) Search(node, name string, pageIndex, pageSize int) (images []
|
||||
func (b *imageBiz) Delete(node, id string, user web.User) (err error) {
|
||||
err = b.d.ImageRemove(context.TODO(), node, id)
|
||||
if err == nil {
|
||||
b.eb.CreateImage(EventActionDelete, id, user)
|
||||
b.eb.CreateImage(EventActionDelete, node, id, user)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -74,7 +74,7 @@ func (b *imageBiz) Prune(node string, user web.User) (count int, size uint64, er
|
||||
var report types.ImagesPruneReport
|
||||
if report, err = b.d.ImagePrune(context.TODO(), node); err == nil {
|
||||
count, size = len(report.ImagesDeleted), report.SpaceReclaimed
|
||||
b.eb.CreateImage(EventActionPrune, "", user)
|
||||
b.eb.CreateImage(EventActionPrune, node, "", user)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func (b *volumeBiz) Search(node, name string, pageIndex, pageSize int) (volumes
|
||||
func (b *volumeBiz) Delete(node, name string, user web.User) (err error) {
|
||||
err = b.d.VolumeRemove(context.TODO(), node, name)
|
||||
if err == nil {
|
||||
b.eb.CreateVolume(EventActionDelete, name, user)
|
||||
b.eb.CreateVolume(EventActionDelete, node, name, user)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -79,8 +79,8 @@ func (b *volumeBiz) Create(vol *Volume, user web.User) (err error) {
|
||||
}
|
||||
|
||||
err = b.d.VolumeCreate(context.TODO(), vol.Node, options)
|
||||
if err != nil {
|
||||
b.eb.CreateVolume(EventActionDelete, vol.Name, user)
|
||||
if err == nil {
|
||||
b.eb.CreateVolume(EventActionDelete, vol.Node, vol.Name, user)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -90,7 +90,7 @@ func (b *volumeBiz) Prune(node string, user web.User) (count int, size uint64, e
|
||||
report, err = b.d.VolumePrune(context.TODO(), node)
|
||||
if err == nil {
|
||||
count, size = len(report.VolumesDeleted), report.SpaceReclaimed
|
||||
b.eb.CreateVolume(EventActionPrune, "", user)
|
||||
b.eb.CreateVolume(EventActionPrune, node, "", user)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/cuigh/auxo/util/cast"
|
||||
"github.com/cuigh/swirl/dao"
|
||||
"github.com/cuigh/swirl/misc"
|
||||
)
|
||||
@ -21,7 +22,7 @@ func (d *Dao) EventSearch(ctx context.Context, args *dao.EventSearchArgs) (event
|
||||
|
||||
match := true
|
||||
if args.Name != "" {
|
||||
match = matchAny(args.Name, event.Name)
|
||||
match = event.Args != nil && matchAny(args.Name, cast.ToString(event.Args["name"]))
|
||||
}
|
||||
if match && args.Type != "" {
|
||||
match = event.Type == args.Type
|
||||
|
@ -140,8 +140,7 @@ type Event struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id"`
|
||||
Type string `json:"type" bson:"type"`
|
||||
Action string `json:"action" bson:"action"`
|
||||
Code string `json:"code" bson:"code"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
Args data.Map `json:"args" bson:"args"`
|
||||
UserID string `json:"userId" bson:"user_id"`
|
||||
Username string `json:"username" bson:"username"`
|
||||
Time Time `json:"time" bson:"time"`
|
||||
|
@ -15,7 +15,7 @@ func (d *Dao) EventSearch(ctx context.Context, args *dao.EventSearchArgs) (event
|
||||
filter["type"] = args.Type
|
||||
}
|
||||
if args.Name != "" {
|
||||
filter["name"] = args.Name
|
||||
filter["args.name"] = args.Name
|
||||
}
|
||||
opts := searchOptions{filter: filter, sorter: bson.M{"_id": -1}, pageIndex: args.PageIndex, pageSize: args.PageSize}
|
||||
events = []*dao.Event{}
|
||||
|
@ -33,7 +33,7 @@ var indexes = map[string][]mongo.IndexModel{
|
||||
},
|
||||
"event": {
|
||||
mongo.IndexModel{Keys: bson.D{{"type", 1}}},
|
||||
mongo.IndexModel{Keys: bson.D{{"name", 1}}},
|
||||
mongo.IndexModel{Keys: bson.D{{"args.name", 1}}},
|
||||
},
|
||||
"session": {
|
||||
mongo.IndexModel{
|
||||
|
@ -4,8 +4,9 @@ export interface Event {
|
||||
id: string;
|
||||
type: string;
|
||||
action: string;
|
||||
code: number;
|
||||
name: string;
|
||||
args: {
|
||||
[key: string]: string;
|
||||
};
|
||||
userId: string;
|
||||
username: string;
|
||||
time: number;
|
||||
|
@ -291,236 +291,3 @@ export function createChart(dom: HTMLElement, info: ChartInfo): Chart {
|
||||
throw new Error('unknown chart type: ' + info.type)
|
||||
}
|
||||
}
|
||||
|
||||
// export class ChartDashboardOptions {
|
||||
// name: string;
|
||||
// key?: string;
|
||||
// period?: number = 30;
|
||||
// refreshInterval?: number = 15; // seconds
|
||||
// }
|
||||
|
||||
// export class ChartDashboard {
|
||||
// private $panel: JQuery;
|
||||
// private readonly opts: ChartDashboardOptions;
|
||||
// private charts: Chart[] = [];
|
||||
// private timer: number;
|
||||
// private dlg: ChartDialog;
|
||||
|
||||
// constructor(elem: string | Element | JQuery, charts: ChartData[], opts?: ChartDashboardOptions) {
|
||||
// this.opts = $.extend(new ChartDashboardOptions(), opts);
|
||||
// this.$panel = $(elem);
|
||||
// this.dlg = new ChartDialog(this);
|
||||
|
||||
// charts.forEach(opts => this.createGraph(opts));
|
||||
|
||||
// // events
|
||||
// Dispatcher.bind(this.$panel).on("remove-chart", e => {
|
||||
// let name = $(e.target).closest("div.column").data("name");
|
||||
// Modal.confirm(`Are you sure to delete chart: <strong>${name}</strong>?`, "Delete chart", dlg => {
|
||||
// this.removeGraph(name);
|
||||
// dlg.close();
|
||||
// });
|
||||
// });
|
||||
// $(window).resize(e => {
|
||||
// $.each(this.charts, (i: number, g: Chart) => {
|
||||
// g.resize();
|
||||
// });
|
||||
// });
|
||||
|
||||
// this.refresh();
|
||||
// }
|
||||
|
||||
// refresh() {
|
||||
// if (!this.timer) {
|
||||
// this.loadData();
|
||||
// if (this.opts.refreshInterval > 0) {
|
||||
// this.timer = setTimeout(this.refreshData.bind(this), this.opts.refreshInterval * 1000);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// private refreshData() {
|
||||
// this.loadData();
|
||||
// if (this.opts.refreshInterval > 0) {
|
||||
// this.timer = setTimeout(this.refreshData.bind(this), this.opts.refreshInterval * 1000);
|
||||
// }
|
||||
// }
|
||||
|
||||
// stop() {
|
||||
// clearTimeout(this.timer);
|
||||
// this.timer = 0;
|
||||
// }
|
||||
|
||||
// setPeriod(period: number) {
|
||||
// this.opts.period = period;
|
||||
// this.loadData();
|
||||
// }
|
||||
|
||||
// addGraph(opts: ChartData) {
|
||||
// this.createGraph(opts);
|
||||
// this.loadData();
|
||||
// }
|
||||
|
||||
// private createGraph(opts: ChartData) {
|
||||
// for (let i = 0; i < this.charts.length; i++) {
|
||||
// let chart = this.charts[i];
|
||||
// if (chart.getOptions().name === opts.name) {
|
||||
// // chart already added.
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// let chart = ChartFactory.create(opts);
|
||||
// if (chart != null) {
|
||||
// this.$panel.append(chart.getElem());
|
||||
// chart.init();
|
||||
// this.charts.push(chart);
|
||||
// }
|
||||
// }
|
||||
|
||||
// removeGraph(name: string) {
|
||||
// let index = -1;
|
||||
// for (let i = 0; i < this.charts.length; i++) {
|
||||
// let c = this.charts[i];
|
||||
// if (c.getOptions().name === name) {
|
||||
// index = i;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (index >= 0) {
|
||||
// let $elem = this.charts[index].getElem();
|
||||
// this.charts.splice(index, 1);
|
||||
// $elem.remove();
|
||||
// }
|
||||
// }
|
||||
|
||||
// save(asDefault: boolean = false) {
|
||||
// let charts: any = [];
|
||||
// this.$panel.children().each((index: number, elem: Element) => {
|
||||
// let name = $(elem).data("name");
|
||||
// for (let i = 0; i < this.charts.length; i++) {
|
||||
// let c = this.charts[i];
|
||||
// if (c.getOptions().name === name) {
|
||||
// charts.push({
|
||||
// name: c.getOptions().name,
|
||||
// width: c.getOptions().width,
|
||||
// height: c.getOptions().height,
|
||||
// });
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// let args = {
|
||||
// name: this.opts.name,
|
||||
// key: asDefault ? '' : (this.opts.key || ''),
|
||||
// charts: charts,
|
||||
// };
|
||||
// $ajax.post(`/system/chart/save_dashboard`, args).json<AjaxResult>((r: AjaxResult) => {
|
||||
// if (r.success) {
|
||||
// Notification.show("success", "Successfully saved.");
|
||||
// } else {
|
||||
// Notification.show("danger", r.message);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// getOptions(): ChartDashboardOptions {
|
||||
// return this.opts;
|
||||
// }
|
||||
|
||||
// private loadData() {
|
||||
// if (this.charts.length == 0) {
|
||||
// return
|
||||
// }
|
||||
|
||||
// let args: any = {
|
||||
// charts: this.charts.map(c => c.getOptions().name).join(","),
|
||||
// period: this.opts.period,
|
||||
// };
|
||||
// if (this.opts.key) {
|
||||
// args.key = this.opts.key;
|
||||
// }
|
||||
// $ajax.get(`/system/chart/data`, args).json((d: { [index: string]: any[] }) => {
|
||||
// $.each(this.charts, (i: number, g: Chart) => {
|
||||
// if (d[g.getOptions().name]) {
|
||||
// g.setData(d[g.getOptions().name]);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// class ChartDialog {
|
||||
// private dashboard: ChartDashboard;
|
||||
// private fb: FilterBox;
|
||||
// private charts: any;
|
||||
// private $charts: JQuery;
|
||||
|
||||
// constructor(dashboard: ChartDashboard) {
|
||||
// this.dashboard = dashboard;
|
||||
// this.fb = new FilterBox("#txt-query", this.filterCharts.bind(this));
|
||||
// $("#btn-add").click(this.showAddDlg.bind(this));
|
||||
// $("#btn-add-chart").click(this.addChart.bind(this));
|
||||
// $("#btn-save").click(() => {
|
||||
// this.dashboard.save();
|
||||
// });
|
||||
// $("#btn-save-as-default").click(() => {
|
||||
// this.dashboard.save(true);
|
||||
// });
|
||||
// }
|
||||
|
||||
// private showAddDlg() {
|
||||
// let $panel = $("#nav-charts");
|
||||
// $panel.find("label.panel-block").remove();
|
||||
|
||||
// // load charts
|
||||
// $ajax.get(`/system/chart/query`, { dashboard: this.dashboard.getOptions().name }).json((charts: any) => {
|
||||
// for (let i = 0; i < charts.length; i++) {
|
||||
// let c = charts[i];
|
||||
// $panel.append(`<label class="panel-block">
|
||||
// <input type="checkbox" value="${c.name}" data-index="${i}">${c.name}: ${c.title}
|
||||
// </label>`);
|
||||
// }
|
||||
// this.charts = charts;
|
||||
// this.$charts = $panel.find("label.panel-block");
|
||||
// });
|
||||
|
||||
// let dlg = new Modal("#dlg-add-chart");
|
||||
// dlg.show();
|
||||
// }
|
||||
|
||||
// private filterCharts(text: string) {
|
||||
// if (!text) {
|
||||
// this.$charts.show();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// this.$charts.each((i, elem) => {
|
||||
// let $elem = $(elem);
|
||||
// let texts: string[] = [
|
||||
// this.charts[i].name.toLowerCase(),
|
||||
// this.charts[i].title.toLowerCase(),
|
||||
// this.charts[i].desc.toLowerCase(),
|
||||
// ];
|
||||
// for (let i = 0; i < texts.length; i++) {
|
||||
// let index = texts[i].indexOf(text);
|
||||
// if (index >= 0) {
|
||||
// $elem.show();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// $elem.hide();
|
||||
// })
|
||||
// }
|
||||
|
||||
// private addChart() {
|
||||
// this.$charts.each((i, e) => {
|
||||
// if ($(e).find(":checked").length > 0) {
|
||||
// let c = this.charts[i];
|
||||
// this.dashboard.addGraph(c);
|
||||
// }
|
||||
// });
|
||||
// Modal.close();
|
||||
// }
|
||||
// }
|
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="tab-pane" :class="{active: active}" :style="{borderColor: themeVars.primaryColor}">
|
||||
<div class="tab-pane" :class="{ active: active }" :style="{ borderColor: themeVars.primaryColor }">
|
||||
<router-link
|
||||
:to="href"
|
||||
:style="{color: active ? themeVars.primaryColor : themeVars.textColorBase}"
|
||||
:style="{ color: active ? themeVars.primaryColor : themeVars.textColorBase }"
|
||||
>
|
||||
<slot />
|
||||
</router-link>
|
||||
@ -11,16 +11,15 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useThemeVars } from "naive-ui";
|
||||
import { RouterLink } from "vue-router";
|
||||
import { RouterLink, RouteLocationRaw } from "vue-router";
|
||||
|
||||
const props = defineProps({
|
||||
active: Boolean,
|
||||
title: String,
|
||||
href: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
interface Props {
|
||||
active?: boolean;
|
||||
title?: string;
|
||||
href: RouteLocationRaw;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const themeVars = useThemeVars();
|
||||
</script>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<x-page-header>
|
||||
<template #action>
|
||||
<n-button secondary size="small" @click="$router.push('/system/charts')">
|
||||
<n-button secondary size="small" @click="$router.push({ name: 'chart_list' })">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<back-icon />
|
||||
@ -219,7 +219,7 @@ const rules: any = {
|
||||
const form = ref();
|
||||
const { submit, submiting } = useForm(form, () => chartApi.save(model.value), () => {
|
||||
window.message.info(t('texts.action_success'));
|
||||
router.push("/system/charts")
|
||||
router.push({ name: 'chart_list' })
|
||||
})
|
||||
|
||||
function newMetric() {
|
||||
|
@ -9,18 +9,24 @@
|
||||
</template>
|
||||
{{ t('buttons.import') }}
|
||||
</n-button>
|
||||
<n-button secondary size="small" @click="$router.push('/system/charts/new')">
|
||||
<n-button secondary size="small" @click="$router.push({ name: 'chart_new' })">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<add-icon />
|
||||
</n-icon>
|
||||
</template>{{ t('buttons.new') }}
|
||||
</template>
|
||||
{{ t('buttons.new') }}
|
||||
</n-button>
|
||||
</template>
|
||||
</x-page-header>
|
||||
<n-space class="page-body" vertical :size="12">
|
||||
<n-space :size="12">
|
||||
<n-input size="small" v-model:value="filter.title" :placeholder="t('fields.title')" clearable />
|
||||
<n-input
|
||||
size="small"
|
||||
v-model:value="filter.title"
|
||||
:placeholder="t('fields.title')"
|
||||
clearable
|
||||
/>
|
||||
<n-select
|
||||
size="small"
|
||||
:placeholder="t('fields.dashboard')"
|
||||
@ -84,7 +90,7 @@ const columns = [
|
||||
title: t('fields.title'),
|
||||
key: "title",
|
||||
fixed: "left" as const,
|
||||
render: (c: Chart) => renderLink(`/system/charts/${c.id}`, c.title),
|
||||
render: (c: Chart) => renderLink({ name: 'chart_detail', params: { id: c.id } }, c.title),
|
||||
},
|
||||
{
|
||||
title: t('fields.type'),
|
||||
@ -136,7 +142,7 @@ const columns = [
|
||||
{
|
||||
type: 'warning',
|
||||
text: t('buttons.edit'),
|
||||
action: () => router.push(`/system/charts/${c.id}/edit`),
|
||||
action: () => router.push({ name: 'chart_edit', params: { id: c.id } }),
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<x-page-header :subtitle="model.title">
|
||||
<template #action>
|
||||
<n-button secondary size="small" @click="$router.push('/system/charts')">
|
||||
<n-button secondary size="small" @click="$router.push({ name: 'chart_list' })">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<back-icon />
|
||||
@ -12,7 +12,7 @@
|
||||
<n-button
|
||||
secondary
|
||||
size="small"
|
||||
@click="$router.push(`/system/charts/${model.id}/edit`)"
|
||||
@click="$router.push({ name: 'chart_edit', params: { id: model.id } })"
|
||||
>{{ t('buttons.edit') }}</n-button>
|
||||
</template>
|
||||
</x-page-header>
|
||||
@ -55,13 +55,19 @@
|
||||
<x-description-item :label="t('fields.dashboard')">{{ model.dashboard }}</x-description-item>
|
||||
<x-description-item :label="t('fields.type')">{{ model.type }}</x-description-item>
|
||||
<x-description-item :label="t('fields.created_by')">
|
||||
<x-anchor :url="`/system/users/${model.createdBy?.id}`">{{ model.createdBy?.name }}</x-anchor>
|
||||
<x-anchor
|
||||
:url="{ name: 'user_detail', params: { id: model.createdBy?.id } }"
|
||||
v-if="model.createdBy?.id"
|
||||
>{{ model.createdBy?.name }}</x-anchor>
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.created_at')">
|
||||
<n-time :time="model.createdAt" format="y-MM-dd HH:mm:ss" />
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.updated_by')">
|
||||
<x-anchor :url="`/system/users/${model.updatedBy?.id}`">{{ model.updatedBy?.name }}</x-anchor>
|
||||
<x-anchor
|
||||
:url="{ name: 'user_detail', params: { id: model.updatedBy?.id } }"
|
||||
v-if="model.updatedBy?.id"
|
||||
>{{ model.updatedBy?.name }}</x-anchor>
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.updated_at')">
|
||||
<n-time :time="model.updatedAt" format="y-MM-dd HH:mm:ss" />
|
||||
|
@ -153,7 +153,6 @@ const columns = [
|
||||
key: "id",
|
||||
width: 210,
|
||||
fixed: "left" as const,
|
||||
// render: (e: Event) => renderLink(`/system/events/${e.id}`, e.id),
|
||||
},
|
||||
{
|
||||
title: t('fields.type'),
|
||||
@ -172,15 +171,12 @@ const columns = [
|
||||
{
|
||||
title: t('fields.object'),
|
||||
key: "name",
|
||||
render(e: Event) {
|
||||
const u = url(e)
|
||||
return u ? renderLink(u, e.name) : e.name
|
||||
},
|
||||
render: renderObject,
|
||||
},
|
||||
{
|
||||
title: t('fields.operator'),
|
||||
key: "name",
|
||||
render: (e: Event) => renderLink(`/system/users/${e.userId}`, e.username),
|
||||
render: (e: Event) => e.userId ? renderLink({ name: 'user_detail', params: { id: e.userId } }, e.username) : null,
|
||||
},
|
||||
{
|
||||
title: t('fields.time'),
|
||||
@ -190,40 +186,40 @@ const columns = [
|
||||
];
|
||||
const { state, pagination, fetchData, changePageSize } = useDataTable(eventApi.search, filter)
|
||||
|
||||
function url(e: Event): RouteLocationRaw | null {
|
||||
if (e.type === 'Setting') {
|
||||
return { name: 'setting' }
|
||||
} else if (!e.code) {
|
||||
return null
|
||||
}
|
||||
|
||||
function renderObject(e: Event) {
|
||||
switch (e.type) {
|
||||
case "User":
|
||||
return { name: 'user_detail', params: { id: e.code } }
|
||||
case "Role":
|
||||
return { name: 'role_detail', params: { id: e.code } }
|
||||
case "Chart":
|
||||
return { name: 'chart_detail', params: { id: e.code } }
|
||||
case "Registry":
|
||||
return { name: 'registry_detail', params: { id: e.code } }
|
||||
case "Node":
|
||||
return { name: 'node_detail', params: { id: e.code } }
|
||||
case "Network":
|
||||
return { name: 'network_detail', params: { name: e.code } }
|
||||
case "Service":
|
||||
return { name: 'service_detail', params: { name: e.code } }
|
||||
case "Stack":
|
||||
return { name: 'stack_detail', params: { name: e.code } }
|
||||
case "Config":
|
||||
return { name: 'config_detail', params: { id: e.code } }
|
||||
case "Secret":
|
||||
return { name: 'secret_detail', params: { id: e.code } }
|
||||
return renderLink({ name: e.type.toLowerCase() + '_detail', params: { id: e.args.id } }, e.args.name)
|
||||
case "Network":
|
||||
case "Service":
|
||||
case "Stack":
|
||||
return renderLink({ name: e.type.toLowerCase() + '_detail', params: { name: e.args.name } }, e.args.name)
|
||||
case "Image":
|
||||
return { name: 'image_detail', params: { node: '-', id: e.code } }
|
||||
if (e.args.id) {
|
||||
return renderLink({ name: 'image_detail', params: { node: e.args.node || '-', id: e.args.id } }, e.args.id)
|
||||
} else {
|
||||
return renderLink({ name: 'image_list' }, t('objects.image'))
|
||||
}
|
||||
case "Container":
|
||||
return { name: 'container_detail', params: { node: '-', id: e.code } }
|
||||
if (e.args.id) {
|
||||
return renderLink({ name: 'container_detail', params: { node: e.args.node || '-', id: e.args.id } }, e.args.name)
|
||||
} else {
|
||||
return renderLink({ name: 'container_list' }, t('objects.container'))
|
||||
}
|
||||
case "Volume":
|
||||
return { name: 'volume_detail', params: { node: '-', name: e.code } }
|
||||
if (e.args.name) {
|
||||
return renderLink({ name: 'volume_detail', params: { node: e.args.node || '-', name: e.args.name } }, e.args.name)
|
||||
} else {
|
||||
return renderLink({ name: 'volume_list' }, t('objects.volume'))
|
||||
}
|
||||
case "Setting":
|
||||
return renderLink({ name: 'setting' }, t('objects.setting'))
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
@ -23,13 +23,19 @@
|
||||
<x-description-item :label="t('fields.url')">{{ model.url }}</x-description-item>
|
||||
<x-description-item :label="t('fields.login_name')">{{ model.username }}</x-description-item>
|
||||
<x-description-item :label="t('fields.created_by')">
|
||||
<x-anchor :url="`/system/users/${model.createdBy?.id}`">{{ model.createdBy?.name }}</x-anchor>
|
||||
<x-anchor
|
||||
:url="{ name: 'user_detail', params: { id: model.createdBy?.id } }"
|
||||
v-if="model.createdBy?.id"
|
||||
>{{ model.createdBy?.name }}</x-anchor>
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.created_at')">
|
||||
<n-time :time="model.createdAt" format="y-MM-dd HH:mm:ss" />
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.updated_by')">
|
||||
<x-anchor :url="`/system/users/${model.updatedBy?.id}`">{{ model.updatedBy?.name }}</x-anchor>
|
||||
<x-anchor
|
||||
:url="{ name: 'user_detail', params: { id: model.updatedBy?.id } }"
|
||||
v-if="model.updatedBy?.id"
|
||||
>{{ model.updatedBy?.name }}</x-anchor>
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.updated_at')">
|
||||
<n-time :time="model.updatedAt" format="y-MM-dd HH:mm:ss" />
|
||||
|
@ -111,7 +111,7 @@ const rules: any = {
|
||||
const form = ref();
|
||||
const { submit, submiting } = useForm(form, () => roleApi.save(model.value), () => {
|
||||
window.message.info(t('texts.action_success'));
|
||||
router.push("/system/roles")
|
||||
router.push({ name: 'role_list' })
|
||||
})
|
||||
|
||||
function checkGroup(key: string, checked: boolean = true) {
|
||||
|
@ -29,7 +29,7 @@
|
||||
<tbody>
|
||||
<tr v-for="(r, index) of model.roles" :key="r.id">
|
||||
<td>
|
||||
<x-anchor :url="`/system/roles/${r.id}`">{{ r.id }}</x-anchor>
|
||||
<x-anchor :url="{ name: 'role_detail', params: { id: r.id } }">{{ r.id }}</x-anchor>
|
||||
</td>
|
||||
<td>{{ r.name }}</td>
|
||||
<td>{{ r.desc }}</td>
|
||||
|
@ -22,24 +22,30 @@
|
||||
<x-description-item :label="t('fields.name')">{{ model.name }}</x-description-item>
|
||||
<x-description-item :span="2" :label="t('fields.desc')">{{ model.desc }}</x-description-item>
|
||||
<x-description-item :label="t('fields.created_by')">
|
||||
<x-anchor :url="`/system/users/${model.createdBy?.id}`">{{ model.createdBy?.name }}</x-anchor>
|
||||
<x-anchor
|
||||
:url="{ name: 'user_detail', params: { id: model.createdBy?.id } }"
|
||||
v-if="model.createdBy?.id"
|
||||
>{{ model.createdBy?.name }}</x-anchor>
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.created_at')">
|
||||
<n-time :time="model.createdAt" format="y-MM-dd HH:mm:ss" />
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.updated_by')">
|
||||
<x-anchor :url="`/system/users/${model.updatedBy?.id}`">{{ model.updatedBy?.name }}</x-anchor>
|
||||
<x-anchor
|
||||
:url="{ name: 'user_detail', params: { id: model.updatedBy?.id } }"
|
||||
v-if="model.updatedBy?.id"
|
||||
>{{ model.updatedBy?.name }}</x-anchor>
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.updated_at')">
|
||||
<n-time :time="model.updatedAt" format="y-MM-dd HH:mm:ss" />
|
||||
</x-description-item>
|
||||
</x-description>
|
||||
<x-panel :title="t('fields.perms')">
|
||||
<n-grid cols="1 640:2 960:3 1440:4" x-gap="6" y-gap="6">
|
||||
<n-gi span="1" v-for="g in ps">
|
||||
<x-pair-tag type="warning" :label="g.group" :value="g.items" />
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-grid cols="1 640:2 960:3 1440:4" x-gap="6" y-gap="6">
|
||||
<n-gi span="1" v-for="g in ps">
|
||||
<x-pair-tag type="warning" :label="g.group" :value="g.items" />
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</x-panel>
|
||||
</n-space>
|
||||
</template>
|
||||
|
@ -70,7 +70,7 @@
|
||||
<n-dynamic-input v-model:value="model.labels" #="{ index, value }" :on-create="newLabel">
|
||||
<n-input :placeholder="t('fields.name')" v-model:value="value.name" />
|
||||
<div style="height: 34px; line-height: 34px; margin: 0 8px">=</div>
|
||||
<n-input :placeholder="t('fields.name')" v-model:value="value.value" />
|
||||
<n-input :placeholder="t('fields.value')" v-model:value="value.value" />
|
||||
</n-dynamic-input>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
|
@ -21,13 +21,19 @@
|
||||
<x-description :label-width="90">
|
||||
<x-description-item :label="t('fields.name')" :span="2">{{ model.name }}</x-description-item>
|
||||
<x-description-item :label="t('fields.created_by')">
|
||||
<x-anchor :url="`/system/users/${model.createdBy?.id}`">{{ model.createdBy?.name }}</x-anchor>
|
||||
<x-anchor
|
||||
:url="{ name: 'user_detail', params: { id: model.createdBy?.id } }"
|
||||
v-if="model.createdBy?.id"
|
||||
>{{ model.createdBy?.name }}</x-anchor>
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.created_at')">
|
||||
<n-time :time="model.createdAt" format="y-MM-dd HH:mm:ss" />
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.updated_by')">
|
||||
<x-anchor :url="`/system/users/${model.updatedBy?.id}`">{{ model.updatedBy?.name }}</x-anchor>
|
||||
<x-anchor
|
||||
:url="{ name: 'user_detail', params: { id: model.updatedBy?.id } }"
|
||||
v-if="model.updatedBy?.id"
|
||||
>{{ model.updatedBy?.name }}</x-anchor>
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.updated_at')">
|
||||
<n-time :time="model.updatedAt" format="y-MM-dd HH:mm:ss" />
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<x-page-header :subtitle="user.id">
|
||||
<template #action>
|
||||
<n-button secondary size="small" @click="$router.push('/system/users')">
|
||||
<n-button secondary size="small" @click="$router.push({ name: 'user_list' })">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<back-icon />
|
||||
@ -18,16 +18,28 @@
|
||||
<n-input :placeholder="t('fields.username')" v-model:value="user.name" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :label="t('fields.login_name')" path="loginName">
|
||||
<n-input :placeholder="t('fields.login_name')" v-model:value="user.loginName" />
|
||||
<n-input :placeholder="t('fields.login_name')" v-model:value="user.loginName" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :label="t('fields.password')" path="password" v-if="!user.id">
|
||||
<n-input type="password" :placeholder="t('fields.password')" v-model:value="user.password" />
|
||||
<n-input
|
||||
type="password"
|
||||
:placeholder="t('fields.password')"
|
||||
v-model:value="user.password"
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :label="t('fields.password_confirm')" path="passwordConfirm" v-if="!user.id">
|
||||
<n-input type="password" :placeholder="t('fields.password_confirm')" v-model:value="user.passwordConfirm" />
|
||||
<n-form-item-gi
|
||||
:label="t('fields.password_confirm')"
|
||||
path="passwordConfirm"
|
||||
v-if="!user.id"
|
||||
>
|
||||
<n-input
|
||||
type="password"
|
||||
:placeholder="t('fields.password_confirm')"
|
||||
v-model:value="user.passwordConfirm"
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :label="t('fields.email')" path="email">
|
||||
<n-input :placeholder="t('fields.email')" v-model:value="user.email" />
|
||||
<n-input :placeholder="t('fields.email')" v-model:value="user.email" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :label="t('fields.admin')" path="admin">
|
||||
<n-switch v-model:value="user.admin">
|
||||
@ -47,7 +59,12 @@
|
||||
<n-radio key="ldap" value="ldap">LDAP</n-radio>
|
||||
</n-radio-group>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :label="t('objects.role', 2)" span="2" path="roles" v-if="roles && roles.length">
|
||||
<n-form-item-gi
|
||||
:label="t('objects.role', 2)"
|
||||
span="2"
|
||||
path="roles"
|
||||
v-if="roles && roles.length"
|
||||
>
|
||||
<n-checkbox-group v-model:value="user.roles">
|
||||
<n-space item-style="display: flex;">
|
||||
<n-checkbox :value="r.id" :label="r.name" v-for="r of roles" />
|
||||
@ -119,7 +136,7 @@ const rules: any = {
|
||||
const form = ref();
|
||||
const { submit, submiting } = useForm(form, () => userApi.save(user.value), () => {
|
||||
window.message.info(t('texts.action_success'));
|
||||
router.push("/system/users")
|
||||
router.push({ name: 'user_list' })
|
||||
})
|
||||
|
||||
async function fetchData() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<x-page-header>
|
||||
<template #action>
|
||||
<n-button secondary size="small" @click="$router.push('/system/users/new')">
|
||||
<n-button secondary size="small" @click="$router.push({ name: 'user_new' })">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<add-icon />
|
||||
@ -13,19 +13,12 @@
|
||||
</x-page-header>
|
||||
<n-space class="page-body" vertical :size="12">
|
||||
<x-tab>
|
||||
<x-tab-pane href="/system/users" :active="!$route.query.filter">{{ t('fields.all') }}</x-tab-pane>
|
||||
<x-tab-pane :href="{ name: 'user_list' }" :active="!$route.query.filter">{{ t('fields.all') }}</x-tab-pane>
|
||||
<x-tab-pane
|
||||
href="/system/users?filter=admins"
|
||||
:active="$route.query.filter === 'admins'"
|
||||
>{{ t('fields.admins') }}</x-tab-pane>
|
||||
<x-tab-pane
|
||||
href="/system/users?filter=active"
|
||||
:active="$route.query.filter === 'active'"
|
||||
>{{ t('fields.active') }}</x-tab-pane>
|
||||
<x-tab-pane
|
||||
href="/system/users?filter=blocked"
|
||||
:active="$route.query.filter === 'blocked'"
|
||||
>{{ t('fields.blocked') }}</x-tab-pane>
|
||||
:href="{ name: 'user_list', query: { filter: tab } }"
|
||||
:active="$route.query.filter === tab"
|
||||
v-for="tab in ['admins', 'active', 'blocked']"
|
||||
>{{ t('fields.' + tab) }}</x-tab-pane>
|
||||
</x-tab>
|
||||
<n-space :size="12">
|
||||
<n-input size="small" v-model:value="args.name" :placeholder="t('fields.name')" clearable />
|
||||
@ -84,7 +77,7 @@ const columns = [
|
||||
{
|
||||
title: t('fields.id'),
|
||||
key: "id",
|
||||
render: (row: User) => renderLink(`/system/users/${row.id}`, row.id),
|
||||
render: (row: User) => renderLink({ name: 'user_detail', params: { id: row.id } }, row.id),
|
||||
},
|
||||
{
|
||||
title: t('fields.name'),
|
||||
@ -124,7 +117,7 @@ const columns = [
|
||||
row.status ?
|
||||
{ type: 'warning', text: t('buttons.block'), action: () => setStatus(row, 0), prompt: t('prompts.block'), } :
|
||||
{ type: 'success', text: t('buttons.enable'), action: () => setStatus(row, 1) },
|
||||
{ type: 'warning', text: t('buttons.edit'), action: () => router.push(`/system/users/${row.id}/edit`) },
|
||||
{ type: 'warning', text: t('buttons.edit'), action: () => router.push({ name: 'user_edit', params: { id: row.id } }) },
|
||||
{ type: 'error', text: t('buttons.delete'), action: () => remove(row, index), prompt: t('prompts.delete') },
|
||||
])
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<x-page-header :subtitle="model.user.name">
|
||||
<template #action>
|
||||
<n-button secondary size="small" @click="$router.push('/system/users')">
|
||||
<n-button secondary size="small" @click="$router.push({ name: 'user_list' })">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<back-icon />
|
||||
@ -12,7 +12,7 @@
|
||||
<n-button
|
||||
secondary
|
||||
size="small"
|
||||
@click="$router.push(`/system/users/${model.user.id}/edit`)"
|
||||
@click="$router.push({ name: 'user_edit', params: { id: model.user.id } })"
|
||||
>{{ t('buttons.edit') }}</n-button>
|
||||
</template>
|
||||
</x-page-header>
|
||||
@ -45,7 +45,8 @@
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.created_by')">
|
||||
<x-anchor
|
||||
:url="`/system/users/${model.user.createdBy?.id}`"
|
||||
:url="{ name: 'user_detail', params: { id: model.user.createdBy?.id } }"
|
||||
v-if="model.user.createdBy?.id"
|
||||
>{{ model.user.createdBy?.name }}</x-anchor>
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.created_at')">
|
||||
@ -53,7 +54,8 @@
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.updated_by')">
|
||||
<x-anchor
|
||||
:url="`/system/users/${model.user.updatedBy?.id}`"
|
||||
:url="{ name: 'user_detail', params: { id: model.user.updatedBy?.id } }"
|
||||
v-if="model.user.updatedBy?.id"
|
||||
>{{ model.user.updatedBy?.name }}</x-anchor>
|
||||
</x-description-item>
|
||||
<x-description-item :label="t('fields.updated_at')">
|
||||
|
Loading…
Reference in New Issue
Block a user