feat: add IM file policy and org exports
This commit is contained in:
@@ -135,11 +135,12 @@
|
|||||||
6. 成员添加、编辑、调岗、移除。
|
6. 成员添加、编辑、调岗、移除。
|
||||||
7. 组织搜索基础接口,覆盖部门和成员。
|
7. 组织搜索基础接口,覆盖部门和成员。
|
||||||
8. 组织操作写入 IM 后台审计。
|
8. 组织操作写入 IM 后台审计。
|
||||||
|
9. 部门导出和成员导出。
|
||||||
|
|
||||||
待实现/配合:
|
待实现/配合:
|
||||||
|
|
||||||
1. 组织数据权限:当前后台按租户选库隔离,部门级 `org_admin` 数据范围尚未建模。
|
1. 组织数据权限:当前后台按租户选库隔离,部门级 `org_admin` 数据范围尚未建模。
|
||||||
2. 导入/导出:需要定义 Excel 字段、失败明细和幂等更新策略。
|
2. 组织导入:需要定义 Excel 字段、冲突覆盖规则、失败明细和幂等更新策略。
|
||||||
3. 离职用户处理策略:当前支持 `employment_state` 和 `contactable`,客户端/IM 服务端需按该状态裁剪通讯录和可联系入口。
|
3. 离职用户处理策略:当前支持 `employment_state` 和 `contactable`,客户端/IM 服务端需按该状态裁剪通讯录和可联系入口。
|
||||||
4. 组织版本增量同步:当前维护部门/成员版本号,客户端增量同步协议需由 IM 服务端继续承接。
|
4. 组织版本增量同步:当前维护部门/成员版本号,客户端增量同步协议需由 IM 服务端继续承接。
|
||||||
|
|
||||||
@@ -179,13 +180,15 @@
|
|||||||
4. 文件详情查看审计。
|
4. 文件详情查看审计。
|
||||||
5. 孤儿文件手动清理。
|
5. 孤儿文件手动清理。
|
||||||
6. 孤儿文件定时任务 `imFileCleanupOrphan`,参数为保留天数、单租户处理上限。
|
6. 孤儿文件定时任务 `imFileCleanupOrphan`,参数为保留天数、单租户处理上限。
|
||||||
|
7. 租户级文件用途策略配置:大小上限、保留天数、MIME 白名单、自动清理开关、病毒扫描开关。
|
||||||
|
|
||||||
待实现/配合:
|
待实现/配合:
|
||||||
|
|
||||||
1. 下载审计:需要 IM 服务端文件下载链路或后台下载代理在实际下载时写入审计。
|
1. 下载审计:需要 IM 服务端文件下载链路或后台下载代理在实际下载时写入审计。
|
||||||
2. 底层对象物理删除:当前后台将 `im_fileuploads.status` 标记为已删除,物理对象清理需由文件服务或 IM 服务端清理器消费 `location`。
|
2. 底层对象物理删除:当前后台将 `im_fileuploads.status` 标记为已删除,物理对象清理需由文件服务或 IM 服务端清理器消费 `location`。
|
||||||
3. 过期文件策略:需要先定义不同 `purpose` 的保留周期和是否允许自动删除。
|
3. 过期文件执行:后台已提供策略配置,IM 服务端/文件清理器需读取 `im_file_policy` 后执行过期扫描、状态更新和物理清理。
|
||||||
4. 文件用途识别目前基于 `im_filemsglinks`/群文件索引做基础归类,头像、举报证据、反馈附件等用途需要 IM 服务端补齐绑定关系或独立索引。
|
4. 文件用途识别目前基于 `im_filemsglinks`/群文件索引做基础归类,头像、举报证据、反馈附件等用途需要 IM 服务端补齐绑定关系或独立索引。
|
||||||
|
5. 病毒扫描执行:后台已提供开关,实际扫描任务、扫描结果回写和用户通知需要 IM 服务端或文件服务承接。
|
||||||
|
|
||||||
### P1-3 外部服务配置
|
### P1-3 外部服务配置
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package imfilepolicy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hotgo/internal/model/input/form"
|
||||||
|
"hotgo/internal/model/input/sysin"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListReq struct {
|
||||||
|
g.Meta `path:"/imFilePolicy/list" method:"get" tags:"IM文件策略" summary:"获取IM文件策略列表"`
|
||||||
|
sysin.ImFilePolicyListInp
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListRes struct {
|
||||||
|
form.PageRes
|
||||||
|
List []*sysin.ImFilePolicyListModel `json:"list" dc:"数据列表"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateReq struct {
|
||||||
|
g.Meta `path:"/imFilePolicy/update" method:"post" tags:"IM文件策略" summary:"更新IM文件策略"`
|
||||||
|
sysin.ImFilePolicyUpdateInp
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateRes struct{}
|
||||||
@@ -17,6 +17,13 @@ type DepartmentListRes struct {
|
|||||||
List []*sysin.ImOrgDepartmentModel `json:"list" dc:"数据列表"`
|
List []*sysin.ImOrgDepartmentModel `json:"list" dc:"数据列表"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DepartmentExportReq struct {
|
||||||
|
g.Meta `path:"/imOrg/departmentExport" method:"get" tags:"IM组织架构" summary:"导出IM组织部门列表"`
|
||||||
|
sysin.ImOrgDepartmentListInp
|
||||||
|
}
|
||||||
|
|
||||||
|
type DepartmentExportRes struct{}
|
||||||
|
|
||||||
type DepartmentEditReq struct {
|
type DepartmentEditReq struct {
|
||||||
g.Meta `path:"/imOrg/departmentEdit" method:"post" tags:"IM组织架构" summary:"新增/编辑IM组织部门"`
|
g.Meta `path:"/imOrg/departmentEdit" method:"post" tags:"IM组织架构" summary:"新增/编辑IM组织部门"`
|
||||||
sysin.ImOrgDepartmentEditInp
|
sysin.ImOrgDepartmentEditInp
|
||||||
@@ -41,6 +48,13 @@ type MemberListRes struct {
|
|||||||
List []*sysin.ImOrgMemberModel `json:"list" dc:"数据列表"`
|
List []*sysin.ImOrgMemberModel `json:"list" dc:"数据列表"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MemberExportReq struct {
|
||||||
|
g.Meta `path:"/imOrg/memberExport" method:"get" tags:"IM组织架构" summary:"导出IM组织成员列表"`
|
||||||
|
sysin.ImOrgMemberListInp
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemberExportRes struct{}
|
||||||
|
|
||||||
type MemberEditReq struct {
|
type MemberEditReq struct {
|
||||||
g.Meta `path:"/imOrg/memberEdit" method:"post" tags:"IM组织架构" summary:"新增/编辑IM组织成员"`
|
g.Meta `path:"/imOrg/memberEdit" method:"post" tags:"IM组织架构" summary:"新增/编辑IM组织成员"`
|
||||||
sysin.ImOrgMemberEditInp
|
sysin.ImOrgMemberEditInp
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package consts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hotgo/internal/library/dict"
|
||||||
|
"hotgo/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dict.RegisterEnums("ImFilePolicySwitchOptions", "IM文件策略开关选项", ImFilePolicySwitchOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ImFilePolicySwitchOff = 0
|
||||||
|
ImFilePolicySwitchOn = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var ImFilePolicySwitchOptions = []*model.Option{
|
||||||
|
dict.GenWarningOption(ImFilePolicySwitchOff, "关闭"),
|
||||||
|
dict.GenSuccessOption(ImFilePolicySwitchOn, "开启"),
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package sys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hotgo/api/admin/imfilepolicy"
|
||||||
|
"hotgo/internal/model/input/sysin"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ImFilePolicy = cImFilePolicy{}
|
||||||
|
|
||||||
|
type cImFilePolicy struct{}
|
||||||
|
|
||||||
|
func (c *cImFilePolicy) List(ctx context.Context, req *imfilepolicy.ListReq) (res *imfilepolicy.ListRes, err error) {
|
||||||
|
list, totalCount, err := service.SysImFilePolicy().List(ctx, &req.ImFilePolicyListInp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if list == nil {
|
||||||
|
list = []*sysin.ImFilePolicyListModel{}
|
||||||
|
}
|
||||||
|
res = new(imfilepolicy.ListRes)
|
||||||
|
res.List = list
|
||||||
|
res.PageRes.Pack(req, totalCount)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cImFilePolicy) Update(ctx context.Context, req *imfilepolicy.UpdateReq) (res *imfilepolicy.UpdateRes, err error) {
|
||||||
|
err = service.SysImFilePolicy().Update(ctx, &req.ImFilePolicyUpdateInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -30,6 +30,11 @@ func (c *cImOrg) DepartmentEdit(ctx context.Context, req *imorg.DepartmentEditRe
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *cImOrg) DepartmentExport(ctx context.Context, req *imorg.DepartmentExportReq) (res *imorg.DepartmentExportRes, err error) {
|
||||||
|
err = service.SysImOrg().DepartmentExport(ctx, &req.ImOrgDepartmentListInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (c *cImOrg) DepartmentDelete(ctx context.Context, req *imorg.DepartmentDeleteReq) (res *imorg.DepartmentDeleteRes, err error) {
|
func (c *cImOrg) DepartmentDelete(ctx context.Context, req *imorg.DepartmentDeleteReq) (res *imorg.DepartmentDeleteRes, err error) {
|
||||||
err = service.SysImOrg().DepartmentDelete(ctx, &req.ImOrgDepartmentDeleteInp)
|
err = service.SysImOrg().DepartmentDelete(ctx, &req.ImOrgDepartmentDeleteInp)
|
||||||
return
|
return
|
||||||
@@ -54,6 +59,11 @@ func (c *cImOrg) MemberEdit(ctx context.Context, req *imorg.MemberEditReq) (res
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *cImOrg) MemberExport(ctx context.Context, req *imorg.MemberExportReq) (res *imorg.MemberExportRes, err error) {
|
||||||
|
err = service.SysImOrg().MemberExport(ctx, &req.ImOrgMemberListInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (c *cImOrg) MemberDelete(ctx context.Context, req *imorg.MemberDeleteReq) (res *imorg.MemberDeleteRes, err error) {
|
func (c *cImOrg) MemberDelete(ctx context.Context, req *imorg.MemberDeleteReq) (res *imorg.MemberDeleteRes, err error) {
|
||||||
err = service.SysImOrg().MemberDelete(ctx, &req.ImOrgMemberDeleteInp)
|
err = service.SysImOrg().MemberDelete(ctx, &req.ImOrgMemberDeleteInp)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
package sys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/dao"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/internal/library/imtenant"
|
||||||
|
"hotgo/internal/model/entity"
|
||||||
|
"hotgo/internal/model/input/sysin"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/database/gdb"
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sSysImFilePolicy struct{}
|
||||||
|
|
||||||
|
func NewSysImFilePolicy() *sSysImFilePolicy {
|
||||||
|
return &sSysImFilePolicy{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterSysImFilePolicy(NewSysImFilePolicy())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImFilePolicy) List(ctx context.Context, in *sysin.ImFilePolicyListInp) (list []*sysin.ImFilePolicyListModel, totalCount int, err error) {
|
||||||
|
if err = in.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tenant, err := s.resolveTenant(ctx, in.TenantId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = s.ensureRows(ctx, tenant.Id); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mod := s.model(ctx).Where("tenant_id", tenant.Id)
|
||||||
|
if in.Purpose != "" {
|
||||||
|
mod = mod.Where("purpose", in.Purpose)
|
||||||
|
}
|
||||||
|
if in.AutoCleanup != nil {
|
||||||
|
mod = mod.Where("auto_cleanup", *in.AutoCleanup)
|
||||||
|
}
|
||||||
|
if in.ScanEnabled != nil {
|
||||||
|
mod = mod.Where("scan_enabled", *in.ScanEnabled)
|
||||||
|
}
|
||||||
|
mod = mod.Page(in.Page, in.PerPage).OrderAsc("id")
|
||||||
|
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||||
|
err = gerror.Wrap(err, "获取IM文件策略失败,请稍后重试!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, item := range list {
|
||||||
|
item.TenantName = tenant.Name
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImFilePolicy) Update(ctx context.Context, in *sysin.ImFilePolicyUpdateInp) (err error) {
|
||||||
|
if err = in.Filter(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tenant, err := s.resolveTenant(ctx, in.TenantId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = s.ensureRows(ctx, tenant.Id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
allowedMimes := "[]"
|
||||||
|
if in.AllowedMimes != nil && in.AllowedMimes.String() != "" && in.AllowedMimes.String() != "null" {
|
||||||
|
allowedMimes = in.AllowedMimes.String()
|
||||||
|
}
|
||||||
|
if _, err = imtenant.RegistryDB().Model("im_file_policy").Safe().Ctx(ctx).
|
||||||
|
Where("tenant_id", tenant.Id).
|
||||||
|
Where("purpose", in.Purpose).
|
||||||
|
Data(g.Map{
|
||||||
|
"max_size": in.MaxSize,
|
||||||
|
"retention_days": in.RetentionDays,
|
||||||
|
"allowed_mimes": allowedMimes,
|
||||||
|
"auto_cleanup": in.AutoCleanup,
|
||||||
|
"scan_enabled": in.ScanEnabled,
|
||||||
|
"remark": in.Remark,
|
||||||
|
"updated_at": gtime.Now(),
|
||||||
|
"updated_by": contexts.GetUserId(ctx),
|
||||||
|
}).Update(); err != nil {
|
||||||
|
return gerror.Wrap(err, "更新IM文件策略失败,请稍后重试!")
|
||||||
|
}
|
||||||
|
_ = writeImAdminAudit(ctx, tenant.Id, "im_file_policy_update", "im_file_policy", 0, "success", g.Map{
|
||||||
|
"purpose": in.Purpose, "auto_cleanup": in.AutoCleanup, "scan_enabled": in.ScanEnabled,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImFilePolicy) model(ctx context.Context) *gdb.Model {
|
||||||
|
return imtenant.RegistryDB().Model("im_file_policy").Safe().Ctx(ctx).Fields(
|
||||||
|
"id",
|
||||||
|
"tenant_id",
|
||||||
|
"purpose",
|
||||||
|
"max_size",
|
||||||
|
"retention_days",
|
||||||
|
"CAST(allowed_mimes AS CHAR) allowed_mimes",
|
||||||
|
"auto_cleanup",
|
||||||
|
"scan_enabled",
|
||||||
|
"remark",
|
||||||
|
"updated_at",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImFilePolicy) resolveTenant(ctx context.Context, selectedTenantId int64) (tenant *entity.ImTenant, err error) {
|
||||||
|
scope, err := resolveImAdminScope(ctx, selectedTenantId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = imtenant.RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx).WherePri(scope.TenantId).Scan(&tenant); err != nil {
|
||||||
|
return nil, gerror.Wrap(err, "获取IM租户失败")
|
||||||
|
}
|
||||||
|
if tenant == nil {
|
||||||
|
return nil, gerror.New("IM租户不存在")
|
||||||
|
}
|
||||||
|
return tenant, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sSysImFilePolicy) ensureRows(ctx context.Context, tenantId int64) error {
|
||||||
|
for _, item := range defaultFilePolicies() {
|
||||||
|
count, err := imtenant.RegistryDB().Model("im_file_policy").Safe().Ctx(ctx).
|
||||||
|
Where("tenant_id", tenantId).
|
||||||
|
Where("purpose", item.Purpose).
|
||||||
|
Count()
|
||||||
|
if err != nil {
|
||||||
|
return gerror.Wrap(err, "检查IM文件策略失败")
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, err = imtenant.RegistryDB().Exec(ctx, `INSERT INTO im_file_policy
|
||||||
|
(tenant_id,purpose,max_size,retention_days,allowed_mimes,auto_cleanup,scan_enabled,remark,created_by,updated_by)
|
||||||
|
VALUES(?,?,?,?,CAST(? AS JSON),?,?,?,?,?)`,
|
||||||
|
tenantId, item.Purpose, item.MaxSize, item.RetentionDays, item.AllowedMimes, item.AutoCleanup, item.ScanEnabled, item.Remark, contexts.GetUserId(ctx), contexts.GetUserId(ctx))
|
||||||
|
if err != nil {
|
||||||
|
return gerror.Wrap(err, "初始化IM文件策略失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type filePolicySeed struct {
|
||||||
|
Purpose string
|
||||||
|
MaxSize int64
|
||||||
|
RetentionDays int
|
||||||
|
AllowedMimes string
|
||||||
|
AutoCleanup int
|
||||||
|
ScanEnabled int
|
||||||
|
Remark string
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultFilePolicies() []*filePolicySeed {
|
||||||
|
const mb = 1024 * 1024
|
||||||
|
return []*filePolicySeed{
|
||||||
|
{Purpose: consts.ImFilePurposeChat, MaxSize: 100 * mb, RetentionDays: 365, AllowedMimes: "[]", AutoCleanup: 0, ScanEnabled: 0, Remark: "聊天附件默认策略"},
|
||||||
|
{Purpose: consts.ImFilePurposeUserAvatar, MaxSize: 5 * mb, RetentionDays: 0, AllowedMimes: `["image/jpeg","image/png","image/webp"]`, AutoCleanup: 0, ScanEnabled: 0, Remark: "用户头像长期保留"},
|
||||||
|
{Purpose: consts.ImFilePurposeGroupAvatar, MaxSize: 5 * mb, RetentionDays: 0, AllowedMimes: `["image/jpeg","image/png","image/webp"]`, AutoCleanup: 0, ScanEnabled: 0, Remark: "群头像长期保留"},
|
||||||
|
{Purpose: consts.ImFilePurposeGroupFile, MaxSize: 200 * mb, RetentionDays: 365, AllowedMimes: "[]", AutoCleanup: 0, ScanEnabled: 0, Remark: "群文件默认策略"},
|
||||||
|
{Purpose: consts.ImFilePurposeAbuseEvidence, MaxSize: 50 * mb, RetentionDays: 730, AllowedMimes: "[]", AutoCleanup: 0, ScanEnabled: 0, Remark: "举报证据默认策略"},
|
||||||
|
{Purpose: consts.ImFilePurposeFeedback, MaxSize: 50 * mb, RetentionDays: 365, AllowedMimes: "[]", AutoCleanup: 0, ScanEnabled: 0, Remark: "反馈附件默认策略"},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,16 +2,21 @@ package sys
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"hotgo/internal/consts"
|
"hotgo/internal/consts"
|
||||||
"hotgo/internal/model/input/form"
|
"hotgo/internal/model/input/form"
|
||||||
"hotgo/internal/model/input/sysin"
|
"hotgo/internal/model/input/sysin"
|
||||||
"hotgo/internal/service"
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/convert"
|
||||||
|
"hotgo/utility/excel"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/database/gdb"
|
"github.com/gogf/gf/v2/database/gdb"
|
||||||
"github.com/gogf/gf/v2/errors/gerror"
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
"github.com/gogf/gf/v2/os/gtime"
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
"github.com/gogf/gf/v2/util/guid"
|
"github.com/gogf/gf/v2/util/guid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -58,6 +63,27 @@ func (s *sSysImOrg) DepartmentList(ctx context.Context, in *sysin.ImOrgDepartmen
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *sSysImOrg) DepartmentExport(ctx context.Context, in *sysin.ImOrgDepartmentListInp) (err error) {
|
||||||
|
list, totalCount, err := s.DepartmentList(ctx, in)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tags, err := convert.GetEntityDescTags(sysin.ImOrgDepartmentExportModel{})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var exports []sysin.ImOrgDepartmentExportModel
|
||||||
|
if err = gconv.Scan(list, &exports); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileName := "导出IM组织部门-" + gctx.CtxId(ctx)
|
||||||
|
sheetName := fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
|
||||||
|
if len(list) > 0 {
|
||||||
|
_ = writeImAdminAudit(ctx, list[0].TenantId, "im_org_department_export", "im_org_department", 0, "success", g.Map{"count": len(list)})
|
||||||
|
}
|
||||||
|
return excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *sSysImOrg) DepartmentEdit(ctx context.Context, in *sysin.ImOrgDepartmentEditInp) (err error) {
|
func (s *sSysImOrg) DepartmentEdit(ctx context.Context, in *sysin.ImOrgDepartmentEditInp) (err error) {
|
||||||
if err = in.Filter(ctx); err != nil {
|
if err = in.Filter(ctx); err != nil {
|
||||||
return
|
return
|
||||||
@@ -199,6 +225,27 @@ func (s *sSysImOrg) MemberList(ctx context.Context, in *sysin.ImOrgMemberListInp
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *sSysImOrg) MemberExport(ctx context.Context, in *sysin.ImOrgMemberListInp) (err error) {
|
||||||
|
list, totalCount, err := s.MemberList(ctx, in)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tags, err := convert.GetEntityDescTags(sysin.ImOrgMemberExportModel{})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var exports []sysin.ImOrgMemberExportModel
|
||||||
|
if err = gconv.Scan(list, &exports); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileName := "导出IM组织成员-" + gctx.CtxId(ctx)
|
||||||
|
sheetName := fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
|
||||||
|
if len(list) > 0 {
|
||||||
|
_ = writeImAdminAudit(ctx, list[0].TenantId, "im_org_member_export", "im_org_member", 0, "success", g.Map{"count": len(list), "department_id": in.DepartmentId})
|
||||||
|
}
|
||||||
|
return excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *sSysImOrg) MemberEdit(ctx context.Context, in *sysin.ImOrgMemberEditInp) (err error) {
|
func (s *sSysImOrg) MemberEdit(ctx context.Context, in *sysin.ImOrgMemberEditInp) (err error) {
|
||||||
if err = in.Filter(ctx); err != nil {
|
if err = in.Filter(ctx); err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package sysin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/library/dict"
|
||||||
|
"hotgo/internal/model/input/form"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ImFilePolicyListInp struct {
|
||||||
|
form.PageReq
|
||||||
|
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||||
|
Purpose string `json:"purpose" dc:"用途"`
|
||||||
|
AutoCleanup *int `json:"autoCleanup" dc:"自动清理"`
|
||||||
|
ScanEnabled *int `json:"scanEnabled" dc:"病毒扫描"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *ImFilePolicyListInp) Filter(ctx context.Context) (err error) { return }
|
||||||
|
|
||||||
|
type ImFilePolicyListModel struct {
|
||||||
|
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||||
|
TenantName string `json:"tenantName" dc:"租户名称"`
|
||||||
|
Id int64 `json:"id" dc:"ID"`
|
||||||
|
Purpose string `json:"purpose" dc:"用途"`
|
||||||
|
MaxSize int64 `json:"maxSize" dc:"单文件大小上限"`
|
||||||
|
RetentionDays int `json:"retentionDays" dc:"保留天数"`
|
||||||
|
AllowedMimes string `json:"allowedMimes" dc:"允许MIME JSON"`
|
||||||
|
AutoCleanup int `json:"autoCleanup" dc:"自动清理"`
|
||||||
|
ScanEnabled int `json:"scanEnabled" dc:"病毒扫描"`
|
||||||
|
Remark string `json:"remark" dc:"备注"`
|
||||||
|
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImFilePolicyUpdateInp struct {
|
||||||
|
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||||
|
Purpose string `json:"purpose" v:"required#用途不能为空" dc:"用途"`
|
||||||
|
MaxSize int64 `json:"maxSize" dc:"单文件大小上限"`
|
||||||
|
RetentionDays int `json:"retentionDays" dc:"保留天数"`
|
||||||
|
AllowedMimes *gjson.Json `json:"allowedMimes" dc:"允许MIME JSON"`
|
||||||
|
AutoCleanup int `json:"autoCleanup" dc:"自动清理"`
|
||||||
|
ScanEnabled int `json:"scanEnabled" dc:"病毒扫描"`
|
||||||
|
Remark string `json:"remark" dc:"备注"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *ImFilePolicyUpdateInp) Filter(ctx context.Context) (err error) {
|
||||||
|
in.Purpose = strings.TrimSpace(in.Purpose)
|
||||||
|
in.Remark = strings.TrimSpace(in.Remark)
|
||||||
|
if !dict.HasOptionKey(consts.ImFilePurposeOptions, in.Purpose) {
|
||||||
|
return gerror.New("文件用途不合法")
|
||||||
|
}
|
||||||
|
if in.Purpose == consts.ImFilePurposeUnknown || in.Purpose == consts.ImFilePurposeOrphan {
|
||||||
|
return gerror.New("该文件用途不允许配置策略")
|
||||||
|
}
|
||||||
|
if in.MaxSize <= 0 {
|
||||||
|
return gerror.New("单文件大小上限必须大于0")
|
||||||
|
}
|
||||||
|
if in.RetentionDays < 0 {
|
||||||
|
return gerror.New("保留天数不能小于0")
|
||||||
|
}
|
||||||
|
if !dict.HasOptionKey(consts.ImFilePolicySwitchOptions, in.AutoCleanup) {
|
||||||
|
return gerror.New("自动清理开关不合法")
|
||||||
|
}
|
||||||
|
if !dict.HasOptionKey(consts.ImFilePolicySwitchOptions, in.ScanEnabled) {
|
||||||
|
return gerror.New("病毒扫描开关不合法")
|
||||||
|
}
|
||||||
|
if len([]rune(in.Remark)) > 200 {
|
||||||
|
return gerror.New("备注不能超过200个字符")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -38,6 +38,20 @@ type ImOrgDepartmentModel struct {
|
|||||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImOrgDepartmentExportModel struct {
|
||||||
|
TenantName string `json:"tenantName" dc:"租户名称"`
|
||||||
|
DepartmentId string `json:"departmentId" dc:"部门ID"`
|
||||||
|
Name string `json:"name" dc:"部门名称"`
|
||||||
|
ParentId string `json:"parentId" dc:"上级部门ID"`
|
||||||
|
SortOrder int `json:"sortOrder" dc:"排序"`
|
||||||
|
Version int `json:"version" dc:"版本"`
|
||||||
|
State string `json:"state" dc:"状态"`
|
||||||
|
ChildCount int `json:"childCount" dc:"子部门数"`
|
||||||
|
MemberCount int `json:"memberCount" dc:"成员数"`
|
||||||
|
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||||
|
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||||
|
}
|
||||||
|
|
||||||
type ImOrgDepartmentEditInp struct {
|
type ImOrgDepartmentEditInp struct {
|
||||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||||
Id int64 `json:"id" dc:"ID"`
|
Id int64 `json:"id" dc:"ID"`
|
||||||
@@ -112,6 +126,23 @@ type ImOrgMemberModel struct {
|
|||||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImOrgMemberExportModel struct {
|
||||||
|
TenantName string `json:"tenantName" dc:"租户名称"`
|
||||||
|
DepartmentId string `json:"departmentId" dc:"部门ID"`
|
||||||
|
DepartmentName string `json:"departmentName" dc:"部门名称"`
|
||||||
|
UserId int64 `json:"userId" dc:"用户ID"`
|
||||||
|
DisplayName string `json:"displayName" dc:"展示名称"`
|
||||||
|
Position string `json:"position" dc:"岗位"`
|
||||||
|
Mobile string `json:"mobile" dc:"手机号"`
|
||||||
|
Email string `json:"email" dc:"邮箱"`
|
||||||
|
EmploymentState string `json:"employmentState" dc:"在职状态"`
|
||||||
|
Contactable int `json:"contactable" dc:"是否可联系"`
|
||||||
|
SortOrder int `json:"sortOrder" dc:"排序"`
|
||||||
|
Version int `json:"version" dc:"版本"`
|
||||||
|
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||||
|
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||||
|
}
|
||||||
|
|
||||||
type ImOrgMemberEditInp struct {
|
type ImOrgMemberEditInp struct {
|
||||||
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
TenantId int64 `json:"tenantId" dc:"租户ID"`
|
||||||
Id int64 `json:"id" dc:"ID"`
|
Id int64 `json:"id" dc:"ID"`
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package genrouter
|
||||||
|
|
||||||
|
import "hotgo/internal/controller/admin/sys"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
LoginRequiredRouter = append(LoginRequiredRouter, sys.ImFilePolicy) // IM文件策略
|
||||||
|
}
|
||||||
@@ -296,12 +296,16 @@ type (
|
|||||||
ISysImOrg interface {
|
ISysImOrg interface {
|
||||||
// DepartmentList 获取IM组织部门列表
|
// DepartmentList 获取IM组织部门列表
|
||||||
DepartmentList(ctx context.Context, in *sysin.ImOrgDepartmentListInp) (list []*sysin.ImOrgDepartmentModel, totalCount int, err error)
|
DepartmentList(ctx context.Context, in *sysin.ImOrgDepartmentListInp) (list []*sysin.ImOrgDepartmentModel, totalCount int, err error)
|
||||||
|
// DepartmentExport 导出IM组织部门
|
||||||
|
DepartmentExport(ctx context.Context, in *sysin.ImOrgDepartmentListInp) (err error)
|
||||||
// DepartmentEdit 新增/编辑IM组织部门
|
// DepartmentEdit 新增/编辑IM组织部门
|
||||||
DepartmentEdit(ctx context.Context, in *sysin.ImOrgDepartmentEditInp) (err error)
|
DepartmentEdit(ctx context.Context, in *sysin.ImOrgDepartmentEditInp) (err error)
|
||||||
// DepartmentDelete 删除IM组织部门
|
// DepartmentDelete 删除IM组织部门
|
||||||
DepartmentDelete(ctx context.Context, in *sysin.ImOrgDepartmentDeleteInp) (err error)
|
DepartmentDelete(ctx context.Context, in *sysin.ImOrgDepartmentDeleteInp) (err error)
|
||||||
// MemberList 获取IM组织成员列表
|
// MemberList 获取IM组织成员列表
|
||||||
MemberList(ctx context.Context, in *sysin.ImOrgMemberListInp) (list []*sysin.ImOrgMemberModel, totalCount int, err error)
|
MemberList(ctx context.Context, in *sysin.ImOrgMemberListInp) (list []*sysin.ImOrgMemberModel, totalCount int, err error)
|
||||||
|
// MemberExport 导出IM组织成员
|
||||||
|
MemberExport(ctx context.Context, in *sysin.ImOrgMemberListInp) (err error)
|
||||||
// MemberEdit 新增/编辑IM组织成员
|
// MemberEdit 新增/编辑IM组织成员
|
||||||
MemberEdit(ctx context.Context, in *sysin.ImOrgMemberEditInp) (err error)
|
MemberEdit(ctx context.Context, in *sysin.ImOrgMemberEditInp) (err error)
|
||||||
// MemberDelete 删除IM组织成员
|
// MemberDelete 删除IM组织成员
|
||||||
@@ -321,6 +325,12 @@ type (
|
|||||||
// CleanupOrphan 清理IM孤儿文件
|
// CleanupOrphan 清理IM孤儿文件
|
||||||
CleanupOrphan(ctx context.Context, in *sysin.ImFileCleanupOrphanInp) (res *sysin.ImFileCleanupOrphanModel, err error)
|
CleanupOrphan(ctx context.Context, in *sysin.ImFileCleanupOrphanInp) (res *sysin.ImFileCleanupOrphanModel, err error)
|
||||||
}
|
}
|
||||||
|
ISysImFilePolicy interface {
|
||||||
|
// List 获取IM文件策略列表
|
||||||
|
List(ctx context.Context, in *sysin.ImFilePolicyListInp) (list []*sysin.ImFilePolicyListModel, totalCount int, err error)
|
||||||
|
// Update 更新IM文件策略
|
||||||
|
Update(ctx context.Context, in *sysin.ImFilePolicyUpdateInp) (err error)
|
||||||
|
}
|
||||||
ISysImExternalConfig interface {
|
ISysImExternalConfig interface {
|
||||||
// List 获取IM外部服务配置列表
|
// List 获取IM外部服务配置列表
|
||||||
List(ctx context.Context, in *sysin.ImExternalConfigListInp) (list []*sysin.ImExternalConfigListModel, totalCount int, err error)
|
List(ctx context.Context, in *sysin.ImExternalConfigListInp) (list []*sysin.ImExternalConfigListModel, totalCount int, err error)
|
||||||
@@ -602,6 +612,7 @@ var (
|
|||||||
localSysImOutbox ISysImOutbox
|
localSysImOutbox ISysImOutbox
|
||||||
localSysImOrg ISysImOrg
|
localSysImOrg ISysImOrg
|
||||||
localSysImFile ISysImFile
|
localSysImFile ISysImFile
|
||||||
|
localSysImFilePolicy ISysImFilePolicy
|
||||||
localSysImExternalConfig ISysImExternalConfig
|
localSysImExternalConfig ISysImExternalConfig
|
||||||
localSysImNotification ISysImNotification
|
localSysImNotification ISysImNotification
|
||||||
localSysImTaskDashboard ISysImTaskDashboard
|
localSysImTaskDashboard ISysImTaskDashboard
|
||||||
@@ -819,6 +830,17 @@ func RegisterSysImFile(i ISysImFile) {
|
|||||||
localSysImFile = i
|
localSysImFile = i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SysImFilePolicy() ISysImFilePolicy {
|
||||||
|
if localSysImFilePolicy == nil {
|
||||||
|
panic("implement not found for interface ISysImFilePolicy, forgot register?")
|
||||||
|
}
|
||||||
|
return localSysImFilePolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterSysImFilePolicy(i ISysImFilePolicy) {
|
||||||
|
localSysImFilePolicy = i
|
||||||
|
}
|
||||||
|
|
||||||
func SysImExternalConfig() ISysImExternalConfig {
|
func SysImExternalConfig() ISysImExternalConfig {
|
||||||
if localSysImExternalConfig == nil {
|
if localSysImExternalConfig == nil {
|
||||||
panic("implement not found for interface ISysImExternalConfig, forgot register?")
|
panic("implement not found for interface ISysImExternalConfig, forgot register?")
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
SET @now := NOW();
|
||||||
|
SET @imRootId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `im_file_policy` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`tenant_id` BIGINT NOT NULL,
|
||||||
|
`purpose` VARCHAR(64) NOT NULL,
|
||||||
|
`max_size` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`retention_days` INT NOT NULL DEFAULT 0,
|
||||||
|
`allowed_mimes` JSON DEFAULT NULL,
|
||||||
|
`auto_cleanup` TINYINT NOT NULL DEFAULT 0,
|
||||||
|
`scan_enabled` TINYINT NOT NULL DEFAULT 0,
|
||||||
|
`remark` VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
|
`created_by` BIGINT DEFAULT NULL,
|
||||||
|
`updated_by` BIGINT DEFAULT NULL,
|
||||||
|
`created_at` DATETIME DEFAULT NULL,
|
||||||
|
`updated_at` DATETIME DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_im_file_policy_tenant_purpose` (`tenant_id`, `purpose`),
|
||||||
|
KEY `idx_im_file_policy_tenant_cleanup` (`tenant_id`, `auto_cleanup`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IM文件用途策略';
|
||||||
|
|
||||||
|
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `component`, `route`, `hidden`, `active_menu`, `is_frame`, `is_cache`, `affix`, `sort`, `status`, `created_by`, `updated_by`, `remark`, `level`, `tree`, `created_at`, `updated_at`)
|
||||||
|
SELECT NULL, @imRootId, '文件策略', 'imFilePolicyIndex', 'file-policy/index', '', '2', '', '/imFilePolicy/list', '', '/imFilePolicy/index', '1', 'imManager', '0', '0', '', '0', '0', '0', '2', CONCAT('tr_', @imRootId, ' '), '73', '', '1', @now, @now
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imFilePolicyIndex');
|
||||||
|
|
||||||
|
SET @imFilePolicyId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imFilePolicyIndex' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `component`, `route`, `hidden`, `active_menu`, `is_frame`, `is_cache`, `affix`, `sort`, `status`, `created_by`, `updated_by`, `remark`, `level`, `tree`, `created_at`, `updated_at`)
|
||||||
|
SELECT NULL, @imFilePolicyId, '策略更新', 'imFilePolicyUpdate', '', '', '3', '', '/imFilePolicy/update', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imFilePolicyId, ' '), '10', '', '1', @now, @now
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imFilePolicyUpdate');
|
||||||
@@ -11,6 +11,10 @@ INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type
|
|||||||
SELECT NULL, @imOrgId, '部门编辑', 'imOrgDepartmentEdit', '', '', '3', '', '/imOrg/departmentEdit', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '10', '', '1', @now, @now
|
SELECT NULL, @imOrgId, '部门编辑', 'imOrgDepartmentEdit', '', '', '3', '', '/imOrg/departmentEdit', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '10', '', '1', @now, @now
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgDepartmentEdit');
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgDepartmentEdit');
|
||||||
|
|
||||||
|
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `component`, `route`, `hidden`, `active_menu`, `is_frame`, `is_cache`, `affix`, `sort`, `status`, `created_by`, `updated_by`, `remark`, `level`, `tree`, `created_at`, `updated_at`)
|
||||||
|
SELECT NULL, @imOrgId, '部门导出', 'imOrgDepartmentExport', '', '', '3', '', '/imOrg/departmentExport', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '15', '', '1', @now, @now
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgDepartmentExport');
|
||||||
|
|
||||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `component`, `route`, `hidden`, `active_menu`, `is_frame`, `is_cache`, `affix`, `sort`, `status`, `created_by`, `updated_by`, `remark`, `level`, `tree`, `created_at`, `updated_at`)
|
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `component`, `route`, `hidden`, `active_menu`, `is_frame`, `is_cache`, `affix`, `sort`, `status`, `created_by`, `updated_by`, `remark`, `level`, `tree`, `created_at`, `updated_at`)
|
||||||
SELECT NULL, @imOrgId, '部门删除', 'imOrgDepartmentDelete', '', '', '3', '', '/imOrg/departmentDelete', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '20', '', '1', @now, @now
|
SELECT NULL, @imOrgId, '部门删除', 'imOrgDepartmentDelete', '', '', '3', '', '/imOrg/departmentDelete', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '20', '', '1', @now, @now
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgDepartmentDelete');
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgDepartmentDelete');
|
||||||
@@ -23,6 +27,10 @@ INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type
|
|||||||
SELECT NULL, @imOrgId, '成员编辑', 'imOrgMemberEdit', '', '', '3', '', '/imOrg/memberEdit', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '40', '', '1', @now, @now
|
SELECT NULL, @imOrgId, '成员编辑', 'imOrgMemberEdit', '', '', '3', '', '/imOrg/memberEdit', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '40', '', '1', @now, @now
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgMemberEdit');
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgMemberEdit');
|
||||||
|
|
||||||
|
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `component`, `route`, `hidden`, `active_menu`, `is_frame`, `is_cache`, `affix`, `sort`, `status`, `created_by`, `updated_by`, `remark`, `level`, `tree`, `created_at`, `updated_at`)
|
||||||
|
SELECT NULL, @imOrgId, '成员导出', 'imOrgMemberExport', '', '', '3', '', '/imOrg/memberExport', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '45', '', '1', @now, @now
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgMemberExport');
|
||||||
|
|
||||||
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `component`, `route`, `hidden`, `active_menu`, `is_frame`, `is_cache`, `affix`, `sort`, `status`, `created_by`, `updated_by`, `remark`, `level`, `tree`, `created_at`, `updated_at`)
|
INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `component`, `route`, `hidden`, `active_menu`, `is_frame`, `is_cache`, `affix`, `sort`, `status`, `created_by`, `updated_by`, `remark`, `level`, `tree`, `created_at`, `updated_at`)
|
||||||
SELECT NULL, @imOrgId, '成员移除', 'imOrgMemberDelete', '', '', '3', '', '/imOrg/memberDelete', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '50', '', '1', @now, @now
|
SELECT NULL, @imOrgId, '成员移除', 'imOrgMemberDelete', '', '', '3', '', '/imOrg/memberDelete', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @imRootId, ' tr_', @imOrgId, ' '), '50', '', '1', @now, @now
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgMemberDelete');
|
WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imOrgMemberDelete');
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { http } from '@/utils/http/axios';
|
||||||
|
|
||||||
|
export function List(params) {
|
||||||
|
return http.request({ url: '/imFilePolicy/list', method: 'GET', params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Update(params) {
|
||||||
|
return http.request({ url: '/imFilePolicy/update', method: 'POST', params });
|
||||||
|
}
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
import { http } from '@/utils/http/axios';
|
import { http, jumpExport } from '@/utils/http/axios';
|
||||||
|
|
||||||
export function DepartmentList(params) {
|
export function DepartmentList(params) {
|
||||||
return http.request({ url: '/imOrg/departmentList', method: 'GET', params });
|
return http.request({ url: '/imOrg/departmentList', method: 'GET', params });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function DepartmentExport(params) {
|
||||||
|
jumpExport('/imOrg/departmentExport', params);
|
||||||
|
}
|
||||||
|
|
||||||
export function DepartmentEdit(params) {
|
export function DepartmentEdit(params) {
|
||||||
return http.request({ url: '/imOrg/departmentEdit', method: 'POST', params });
|
return http.request({ url: '/imOrg/departmentEdit', method: 'POST', params });
|
||||||
}
|
}
|
||||||
@@ -16,6 +20,10 @@ export function MemberList(params) {
|
|||||||
return http.request({ url: '/imOrg/memberList', method: 'GET', params });
|
return http.request({ url: '/imOrg/memberList', method: 'GET', params });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function MemberExport(params) {
|
||||||
|
jumpExport('/imOrg/memberExport', params);
|
||||||
|
}
|
||||||
|
|
||||||
export function MemberEdit(params) {
|
export function MemberEdit(params) {
|
||||||
return http.request({ url: '/imOrg/memberEdit', method: 'POST', params });
|
return http.request({ url: '/imOrg/memberEdit', method: 'POST', params });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="n-layout-page-header">
|
||||||
|
<n-card :bordered="false" title="IM文件策略" />
|
||||||
|
</div>
|
||||||
|
<n-card :bordered="false" class="proCard">
|
||||||
|
<BasicForm
|
||||||
|
ref="searchFormRef"
|
||||||
|
@register="register"
|
||||||
|
@submit="reloadTable"
|
||||||
|
@reset="handleReset"
|
||||||
|
@keyup.enter="reloadTable"
|
||||||
|
/>
|
||||||
|
<BasicTable
|
||||||
|
ref="actionRef"
|
||||||
|
:columns="columns"
|
||||||
|
:request="loadDataTable"
|
||||||
|
:row-key="(row) => row.tenantId + '-' + row.purpose"
|
||||||
|
:actionColumn="actionColumn"
|
||||||
|
:scroll-x="scrollX"
|
||||||
|
:resizeHeightOffset="-10000"
|
||||||
|
/>
|
||||||
|
</n-card>
|
||||||
|
|
||||||
|
<n-modal
|
||||||
|
v-model:show="showModal"
|
||||||
|
preset="dialog"
|
||||||
|
:show-icon="false"
|
||||||
|
:title="'文件策略 - ' + purposeLabel"
|
||||||
|
:style="{ width: dialogWidth }"
|
||||||
|
>
|
||||||
|
<n-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formValue"
|
||||||
|
:rules="rules"
|
||||||
|
label-placement="left"
|
||||||
|
label-width="100"
|
||||||
|
>
|
||||||
|
<n-form-item label="用途" path="purpose">
|
||||||
|
<n-select
|
||||||
|
v-model:value="formValue.purpose"
|
||||||
|
:disabled="true"
|
||||||
|
:options="purposeOptions"
|
||||||
|
placeholder="请选择用途"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="大小上限" path="maxSize">
|
||||||
|
<n-input-number v-model:value="formValue.maxSize" :min="1" />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="保留天数">
|
||||||
|
<n-input-number v-model:value="formValue.retentionDays" :min="0" />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="自动清理">
|
||||||
|
<n-switch v-model:value="autoCleanupEnabled" />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="病毒扫描">
|
||||||
|
<n-switch v-model:value="scanEnabledValue" />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="MIME白名单">
|
||||||
|
<n-input
|
||||||
|
v-model:value="formValue.allowedMimes"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{ minRows: 4, maxRows: 8 }"
|
||||||
|
placeholder="请输入JSON数组,空数组表示不限"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="备注">
|
||||||
|
<n-input v-model:value="formValue.remark" placeholder="请输入备注" />
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
<template #action>
|
||||||
|
<n-space>
|
||||||
|
<n-button @click="showModal = false">取消</n-button>
|
||||||
|
<n-button type="primary" :loading="submitLoading" @click="handleSubmit">保存</n-button>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
</n-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, h, onMounted, reactive, ref } from 'vue';
|
||||||
|
import { useMessage } from 'naive-ui';
|
||||||
|
import { useDictStore } from '@/store/modules/dict';
|
||||||
|
import { BasicForm, useForm } from '@/components/Form/index';
|
||||||
|
import { BasicTable, TableAction } from '@/components/Table';
|
||||||
|
import { usePermission } from '@/hooks/web/usePermission';
|
||||||
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
import { List, Update } from '@/api/imFilePolicy';
|
||||||
|
import { adaModalWidth, adaTableScrollX } from '@/utils/hotgo';
|
||||||
|
import {
|
||||||
|
columns,
|
||||||
|
loadOptions,
|
||||||
|
loadTenantOptions,
|
||||||
|
newState,
|
||||||
|
rules,
|
||||||
|
schemas,
|
||||||
|
State,
|
||||||
|
tenantOptions,
|
||||||
|
} from './model';
|
||||||
|
|
||||||
|
const dict = useDictStore();
|
||||||
|
const message = useMessage();
|
||||||
|
const { hasPermission } = usePermission();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const isPlatformAdmin = computed(() => userStore.isCompanyDept);
|
||||||
|
const actionRef = ref();
|
||||||
|
const searchFormRef = ref<any>({});
|
||||||
|
const formRef = ref();
|
||||||
|
const showModal = ref(false);
|
||||||
|
const submitLoading = ref(false);
|
||||||
|
const formValue = ref<State>(newState(null));
|
||||||
|
const dialogWidth = computed(() => adaModalWidth(720));
|
||||||
|
const purposeOptions = computed(() =>
|
||||||
|
dict
|
||||||
|
.getOptions('ImFilePurposeOptions')
|
||||||
|
.filter((item) => item.value !== 'unknown' && item.value !== 'orphan')
|
||||||
|
);
|
||||||
|
const purposeLabel = computed(
|
||||||
|
() => dict.getLabel('ImFilePurposeOptions', formValue.value.purpose) || formValue.value.purpose
|
||||||
|
);
|
||||||
|
|
||||||
|
const tenantSchema = schemas.value.find((item) => item.field === 'tenantId');
|
||||||
|
if (tenantSchema) {
|
||||||
|
tenantSchema.ifShow = () => isPlatformAdmin.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const autoCleanupEnabled = computed({
|
||||||
|
get: () => formValue.value.autoCleanup === 1,
|
||||||
|
set: (value: boolean) => {
|
||||||
|
formValue.value.autoCleanup = value ? 1 : 0;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const scanEnabledValue = computed({
|
||||||
|
get: () => formValue.value.scanEnabled === 1,
|
||||||
|
set: (value: boolean) => {
|
||||||
|
formValue.value.scanEnabled = value ? 1 : 0;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionColumn = reactive({
|
||||||
|
width: 100,
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
render(record: State) {
|
||||||
|
return h(TableAction as any, {
|
||||||
|
style: 'button',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
auth: ['/imFilePolicy/update'],
|
||||||
|
onClick: () => handleEdit(record),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const scrollX = computed(() => adaTableScrollX(columns, actionColumn.width));
|
||||||
|
|
||||||
|
const [register, { setFieldsValue }] = useForm({
|
||||||
|
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
|
||||||
|
labelWidth: 90,
|
||||||
|
schemas,
|
||||||
|
});
|
||||||
|
|
||||||
|
const loadDataTable = async (res) => {
|
||||||
|
const params = { ...searchFormRef.value?.formModel, ...res };
|
||||||
|
if (isPlatformAdmin.value && !params.tenantId)
|
||||||
|
return { list: [], page: 1, pageCount: 0, totalCount: 0 };
|
||||||
|
return await List(params);
|
||||||
|
};
|
||||||
|
|
||||||
|
function reloadTable() {
|
||||||
|
actionRef.value?.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleReset() {
|
||||||
|
const defaults: Recordable = {};
|
||||||
|
if (isPlatformAdmin.value && tenantOptions.value.length > 0)
|
||||||
|
defaults.tenantId = tenantOptions.value[0].value;
|
||||||
|
await setFieldsValue(defaults);
|
||||||
|
reloadTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(record: State) {
|
||||||
|
formValue.value = newState(record);
|
||||||
|
if (!formValue.value.allowedMimes) formValue.value.allowedMimes = '[]';
|
||||||
|
showModal.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
submitLoading.value = true;
|
||||||
|
try {
|
||||||
|
await formRef.value?.validate();
|
||||||
|
let allowedMimes: any[] = [];
|
||||||
|
const text = (formValue.value.allowedMimes || '').trim();
|
||||||
|
if (text) {
|
||||||
|
allowedMimes = JSON.parse(text);
|
||||||
|
if (!Array.isArray(allowedMimes)) throw new Error('MIME白名单必须是JSON数组');
|
||||||
|
}
|
||||||
|
await Update({ ...formValue.value, allowedMimes });
|
||||||
|
message.success('操作成功');
|
||||||
|
showModal.value = false;
|
||||||
|
reloadTable();
|
||||||
|
} catch (e: any) {
|
||||||
|
message.error(e.message || '操作失败');
|
||||||
|
} finally {
|
||||||
|
submitLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
loadOptions();
|
||||||
|
if (isPlatformAdmin.value) {
|
||||||
|
await loadTenantOptions();
|
||||||
|
if (tenantOptions.value.length > 0) {
|
||||||
|
await setFieldsValue({ tenantId: tenantOptions.value[0].value });
|
||||||
|
} else {
|
||||||
|
message.warning('暂无可用租户');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reloadTable();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
import { FormSchema } from '@/components/Form';
|
||||||
|
import { useDictStore } from '@/store/modules/dict';
|
||||||
|
import { List as TenantList } from '@/api/imTenant';
|
||||||
|
|
||||||
|
const dict = useDictStore();
|
||||||
|
|
||||||
|
export const tenantOptions = ref<any[]>([]);
|
||||||
|
|
||||||
|
export class State {
|
||||||
|
public tenantId = 0;
|
||||||
|
public tenantName = '';
|
||||||
|
public id = 0;
|
||||||
|
public purpose = '';
|
||||||
|
public maxSize = 0;
|
||||||
|
public retentionDays = 0;
|
||||||
|
public allowedMimes = '[]';
|
||||||
|
public autoCleanup = 0;
|
||||||
|
public scanEnabled = 0;
|
||||||
|
public remark = '';
|
||||||
|
public updatedAt = '';
|
||||||
|
|
||||||
|
constructor(state?: Partial<State>) {
|
||||||
|
if (state) Object.assign(this, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newState(state: State | Record<string, any> | null): State {
|
||||||
|
if (state !== null) {
|
||||||
|
if (state instanceof State) return cloneDeep(state);
|
||||||
|
return new State(state);
|
||||||
|
}
|
||||||
|
return new State();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const schemas = ref<FormSchema[]>([
|
||||||
|
{
|
||||||
|
field: 'tenantId',
|
||||||
|
component: 'NSelect',
|
||||||
|
label: '租户',
|
||||||
|
componentProps: {
|
||||||
|
clearable: false,
|
||||||
|
options: computed(() => tenantOptions.value),
|
||||||
|
placeholder: '请选择租户',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'purpose',
|
||||||
|
component: 'NSelect',
|
||||||
|
label: '用途',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
options: computed(() => dict.getOptions('ImFilePurposeOptions')),
|
||||||
|
placeholder: '请选择用途',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'autoCleanup',
|
||||||
|
component: 'NSelect',
|
||||||
|
label: '自动清理',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
options: computed(() => dict.getOptions('ImFilePolicySwitchOptions')),
|
||||||
|
placeholder: '请选择开关',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'scanEnabled',
|
||||||
|
component: 'NSelect',
|
||||||
|
label: '病毒扫描',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
options: computed(() => dict.getOptions('ImFilePolicySwitchOptions')),
|
||||||
|
placeholder: '请选择开关',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const columns = [
|
||||||
|
{
|
||||||
|
title: '用途',
|
||||||
|
key: 'purpose',
|
||||||
|
align: 'left',
|
||||||
|
width: 130,
|
||||||
|
render(row: State) {
|
||||||
|
return dict.getLabel('ImFilePurposeOptions', row.purpose) || row.purpose;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: '大小上限', key: 'maxSize', align: 'left', width: 120 },
|
||||||
|
{ title: '保留天数', key: 'retentionDays', align: 'left', width: 100 },
|
||||||
|
{
|
||||||
|
title: '自动清理',
|
||||||
|
key: 'autoCleanup',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
render(row: State) {
|
||||||
|
return dict.getLabel('ImFilePolicySwitchOptions', row.autoCleanup);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '病毒扫描',
|
||||||
|
key: 'scanEnabled',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
render(row: State) {
|
||||||
|
return dict.getLabel('ImFilePolicySwitchOptions', row.scanEnabled);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: 'MIME白名单', key: 'allowedMimes', align: 'left', width: 260 },
|
||||||
|
{ title: '备注', key: 'remark', align: 'left', width: 180 },
|
||||||
|
{ title: '更新时间', key: 'updatedAt', align: 'left', width: 180 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const rules = {
|
||||||
|
purpose: { required: true, trigger: ['blur', 'change'], type: 'string', message: '请选择用途' },
|
||||||
|
maxSize: {
|
||||||
|
required: true,
|
||||||
|
trigger: ['blur', 'input'],
|
||||||
|
type: 'number',
|
||||||
|
message: '请输入大小上限',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function loadOptions() {
|
||||||
|
dict.loadOptions(['ImFilePurposeOptions', 'ImFilePolicySwitchOptions']);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadTenantOptions() {
|
||||||
|
const res = await TenantList({ page: 1, perPage: 100 });
|
||||||
|
tenantOptions.value = (res?.list || []).map((item) => ({
|
||||||
|
label: `${item.name}(${item.code})`,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
return tenantOptions.value;
|
||||||
|
}
|
||||||
@@ -21,6 +21,13 @@
|
|||||||
>
|
>
|
||||||
新增部门
|
新增部门
|
||||||
</n-button>
|
</n-button>
|
||||||
|
<n-button
|
||||||
|
v-if="hasPermission(['/imOrg/departmentExport'])"
|
||||||
|
type="primary"
|
||||||
|
@click="handleDepartmentExport"
|
||||||
|
>
|
||||||
|
导出部门
|
||||||
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
<BasicTable
|
<BasicTable
|
||||||
ref="departmentTableRef"
|
ref="departmentTableRef"
|
||||||
@@ -50,6 +57,14 @@
|
|||||||
>
|
>
|
||||||
添加成员
|
添加成员
|
||||||
</n-button>
|
</n-button>
|
||||||
|
<n-button
|
||||||
|
v-if="hasPermission(['/imOrg/memberExport'])"
|
||||||
|
type="primary"
|
||||||
|
:disabled="!selectedDepartment.departmentId"
|
||||||
|
@click="handleMemberExport"
|
||||||
|
>
|
||||||
|
导出成员
|
||||||
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
<BasicTable
|
<BasicTable
|
||||||
ref="memberTableRef"
|
ref="memberTableRef"
|
||||||
@@ -190,9 +205,11 @@
|
|||||||
import {
|
import {
|
||||||
DepartmentDelete,
|
DepartmentDelete,
|
||||||
DepartmentEdit,
|
DepartmentEdit,
|
||||||
|
DepartmentExport,
|
||||||
DepartmentList,
|
DepartmentList,
|
||||||
MemberDelete,
|
MemberDelete,
|
||||||
MemberEdit,
|
MemberEdit,
|
||||||
|
MemberExport,
|
||||||
MemberList,
|
MemberList,
|
||||||
} from '@/api/imOrg';
|
} from '@/api/imOrg';
|
||||||
import {
|
import {
|
||||||
@@ -364,6 +381,27 @@
|
|||||||
memberTableRef.value?.reload();
|
memberTableRef.value?.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleDepartmentExport() {
|
||||||
|
const params = { ...departmentSearchRef.value?.formModel };
|
||||||
|
if (isPlatformAdmin.value && !params.tenantId) {
|
||||||
|
message.warning('请先选择租户');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DepartmentExport(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMemberExport() {
|
||||||
|
if (!selectedDepartment.value.departmentId) {
|
||||||
|
message.warning('请先选择部门');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MemberExport({
|
||||||
|
...memberSearchRef.value?.formModel,
|
||||||
|
tenantId: currentTenantId.value,
|
||||||
|
departmentId: selectedDepartment.value.departmentId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function handleDepartmentReset() {
|
async function handleDepartmentReset() {
|
||||||
const defaults: Recordable = {};
|
const defaults: Recordable = {};
|
||||||
if (isPlatformAdmin.value && tenantOptions.value.length > 0)
|
if (isPlatformAdmin.value && tenantOptions.value.length > 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user