diff --git a/packages/ui/certd-client/src/router/source/modules/certd.ts b/packages/ui/certd-client/src/router/source/modules/certd.ts
index e38745825..d2b17c166 100644
--- a/packages/ui/certd-client/src/router/source/modules/certd.ts
+++ b/packages/ui/certd-client/src/router/source/modules/certd.ts
@@ -127,6 +127,21 @@ export const certdResources = [
keepAlive: true,
},
},
+ {
+ title: "certd.sysResources.currentProject",
+ name: "ProjectMemberManager",
+ path: "/certd/project/detail",
+ component: "/certd/project/detail/index.vue",
+ meta: {
+ show: () => {
+ const projectStore = useProjectStore();
+ return projectStore.isEnterprise;
+ },
+ isMenu: true,
+ icon: "ion:apps",
+ auth: true,
+ },
+ },
{
title: "certd.settings",
name: "MineSetting",
@@ -263,21 +278,6 @@ export const certdResources = [
isMenu: false,
},
},
- {
- title: "certd.sysResources.projectMemberManager",
- name: "ProjectMemberManager",
- path: "/certd/project/detail",
- component: "/certd/project/detail/index.vue",
- meta: {
- show: () => {
- const projectStore = useProjectStore();
- return projectStore.isEnterprise;
- },
- isMenu: true,
- icon: "ion:apps",
- auth: true,
- },
- },
],
},
{
diff --git a/packages/ui/certd-client/src/use/use-dialog.ts b/packages/ui/certd-client/src/use/use-dialog.ts
index 741de8a6c..71421b0e5 100644
--- a/packages/ui/certd-client/src/use/use-dialog.ts
+++ b/packages/ui/certd-client/src/use/use-dialog.ts
@@ -1,4 +1,5 @@
import { useFormWrapper } from "@fast-crud/fast-crud";
+import { merge } from "lodash-es";
export type FormOptionReq = {
title: string;
@@ -7,6 +8,7 @@ export type FormOptionReq = {
body?: any;
initialForm?: any;
zIndex?: number;
+ wrapper?: any;
};
export function useFormDialog() {
@@ -14,19 +16,23 @@ export function useFormDialog() {
async function openFormDialog(req: FormOptionReq) {
function createCrudOptions() {
+ const warpper = merge(
+ {
+ zIndex: req.zIndex,
+ title: req.title,
+ saveRemind: false,
+ slots: {
+ "form-body-top": req.body,
+ },
+ },
+ req.wrapper
+ );
return {
crudOptions: {
columns: req.columns,
form: {
initialForm: req.initialForm,
- wrapper: {
- zIndex: req.zIndex,
- title: req.title,
- saveRemind: false,
- slots: {
- "form-body-top": req.body,
- },
- },
+ wrapper: warpper,
async afterSubmit() {},
async doSubmit({ form }: any) {
if (req.onSubmit) {
diff --git a/packages/ui/certd-client/src/views/certd/project/detail/api.ts b/packages/ui/certd-client/src/views/certd/project/detail/api.ts
index 2a2c8af83..eca4c5b82 100644
--- a/packages/ui/certd-client/src/views/certd/project/detail/api.ts
+++ b/packages/ui/certd-client/src/views/certd/project/detail/api.ts
@@ -73,3 +73,17 @@ export async function ApproveJoin(form: any) {
data: form,
});
}
+
+export async function GetSelfResources() {
+ return await request({
+ url: "/enterprise/transfer/selfResources",
+ method: "post",
+ });
+}
+
+export async function TransferResources() {
+ return await request({
+ url: "/enterprise/transfer/doTransfer",
+ method: "post",
+ });
+}
diff --git a/packages/ui/certd-client/src/views/certd/project/detail/index.vue b/packages/ui/certd-client/src/views/certd/project/detail/index.vue
index 6358c644b..f3cce0f7c 100644
--- a/packages/ui/certd-client/src/views/certd/project/detail/index.vue
+++ b/packages/ui/certd-client/src/views/certd/project/detail/index.vue
@@ -9,8 +9,12 @@
-->
+
+ 个人数据迁移
+ {{ t("certd.project.projectManage") }}
+
@@ -29,11 +33,13 @@ import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
-import { useRoute } from "vue-router";
+import { useRoute, useRouter } from "vue-router";
import { useProjectStore } from "/@/store/project";
import { request } from "/@/api/service";
import { useDicts } from "../../dicts";
import { useCrudPermission } from "/@/plugin/permission";
+import { useUserStore } from "/@/store/user";
+import { useTransfer } from "./use";
const { t } = useI18n();
@@ -49,6 +55,14 @@ if (!projectId) {
projectId = projectStore.currentProject?.id;
}
+const router = useRouter();
+const userStore = useUserStore();
+function goProjectManager() {
+ router.push(`/sys/enterprise/project`);
+}
+
+const { openTransferDialog } = useTransfer();
+
const { projectPermissionDict, projectMemberStatusDict, userDict } = useDicts();
const project: Ref = ref({});
diff --git a/packages/ui/certd-client/src/views/certd/project/detail/use.ts b/packages/ui/certd-client/src/views/certd/project/detail/use.ts
deleted file mode 100644
index fbcae7782..000000000
--- a/packages/ui/certd-client/src/views/certd/project/detail/use.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { dict } from "@fast-crud/fast-crud";
-import { useDicts } from "../../dicts";
-import { useFormDialog } from "/@/use/use-dialog";
-
-export function useApprove() {
- const { openFormDialog } = useFormDialog();
- const { projectPermissionDict, projectMemberStatusDict, userDict } = useDicts();
- function openApproveDialog({ id, permission, onSubmit }: { id: any; permission: any; onSubmit: any }) {
- openFormDialog({
- title: "审批加入申请",
- columns: {
- permission: {
- title: "成员权限",
- type: "dict-select",
- dict: projectPermissionDict,
- },
- status: {
- title: "审批结果",
- type: "dict-radio",
- dict: dict({
- data: [
- {
- label: "通过",
- value: "approved",
- },
- {
- label: "拒绝",
- value: "rejected",
- },
- ],
- }),
- },
- },
- onSubmit: onSubmit,
- initialForm: {
- id: id,
- permission: permission,
- status: "approved",
- },
- });
- }
-
- return {
- openApproveDialog,
- };
-}
diff --git a/packages/ui/certd-client/src/views/certd/project/detail/use.tsx b/packages/ui/certd-client/src/views/certd/project/detail/use.tsx
new file mode 100644
index 000000000..95b2922a8
--- /dev/null
+++ b/packages/ui/certd-client/src/views/certd/project/detail/use.tsx
@@ -0,0 +1,131 @@
+import { dict } from "@fast-crud/fast-crud";
+import { useDicts } from "../../dicts";
+import { useFormDialog } from "/@/use/use-dialog";
+import * as api from "./api";
+import { useProjectStore } from "/@/store/project";
+import { message, Modal } from "ant-design-vue";
+import { Ref, ref } from "vue";
+export function useApprove() {
+ const { openFormDialog } = useFormDialog();
+ const { projectPermissionDict, projectMemberStatusDict, userDict } = useDicts();
+ function openApproveDialog({ id, permission, onSubmit }: { id: any; permission: any; onSubmit: any }) {
+ openFormDialog({
+ title: "审批加入申请",
+ columns: {
+ permission: {
+ title: "成员权限",
+ type: "dict-select",
+ dict: projectPermissionDict,
+ },
+ status: {
+ title: "审批结果",
+ type: "dict-radio",
+ dict: dict({
+ data: [
+ {
+ label: "通过",
+ value: "approved",
+ },
+ {
+ label: "拒绝",
+ value: "rejected",
+ },
+ ],
+ }),
+ },
+ },
+ onSubmit: onSubmit,
+ initialForm: {
+ id: id,
+ permission: permission,
+ status: "approved",
+ },
+ });
+ }
+
+ return {
+ openApproveDialog,
+ };
+}
+
+export function useTransfer() {
+ const { openFormDialog } = useFormDialog();
+
+ async function doTransfer() {
+ Modal.confirm({
+ title: "请确认",
+ content: () => (
+
+
确认迁移个人资源数据到当前项目?
+
注意;此操作不可逆,一旦迁移,数据将无法还原回个人用户名下。
+
+ ),
+ okText: "确认",
+ okType: "primary",
+ onOk: async () => {
+ await api.TransferResources();
+ message.success("迁移成功");
+ await loadMyResources();
+ },
+ });
+ }
+
+ const selfResources: Ref = ref({});
+
+ const projectStore = useProjectStore();
+
+ async function loadMyResources() {
+ selfResources.value = await api.GetSelfResources();
+ }
+ async function openTransferDialog() {
+ await loadMyResources();
+ openFormDialog({
+ title: "迁移我的个人资源到当前企业项目",
+ wrapper: {
+ buttons: {
+ ok: {
+ show: false,
+ },
+ reset: {
+ show: false,
+ },
+ },
+ },
+ body() {
+ return (
+
+
+
+
我原有的个人资源数量
+
+
流水线:{selfResources.value.pipeline}
+
证书:{selfResources.value.certInfo}
+
授权:{selfResources.value.access}
+
站点监控:{selfResources.value.siteMonitor}
+
通知:{selfResources.value.notification}
+
站点监控分组:{selfResources.value.group}
+
流水线分组:{selfResources.value.pipelineGroup}
+
流水线模版:{selfResources.value.template}
+
域名:{selfResources.value.domain}
+
子域名托管:{selfResources.value.subdomain}
+
cname记录:{selfResources.value.cnameRecord}
+
其他:{selfResources.value.addon}
+
+
+
迁移到→
+
项目:"{projectStore.currentProject?.name}"
+
+
+
+ );
+ },
+ });
+ }
+ return {
+ openTransferDialog,
+ };
+}
diff --git a/packages/ui/certd-server/src/controller/user/enterprise/transfer-controller.ts b/packages/ui/certd-server/src/controller/user/enterprise/transfer-controller.ts
new file mode 100644
index 000000000..2570c8baf
--- /dev/null
+++ b/packages/ui/certd-server/src/controller/user/enterprise/transfer-controller.ts
@@ -0,0 +1,42 @@
+import { BaseController, Constants } from '@certd/lib-server';
+import { Controller, Inject, Post, Provide } from '@midwayjs/core';
+import { TransferService } from '../../../modules/sys/enterprise/service/transfer-service.js';
+
+/**
+ */
+@Provide()
+@Controller('/api/enterprise/transfer')
+export class TransferController extends BaseController {
+ @Inject()
+ service: TransferService;
+
+
+ getService(): TransferService {
+ return this.service;
+ }
+
+ /**
+ * 我自己的资源
+ * @param body
+ * @returns
+ */
+ @Post('/selfResources', { summary: Constants.per.authOnly })
+ async selfResources() {
+ const userId = this.getUserId();
+ const res = await this.service.getUserResources(userId);
+ return this.ok(res);
+ }
+
+ /**
+ * 迁移项目
+ * @param body
+ * @returns
+ */
+ @Post('/doTransfer', { summary: Constants.per.authOnly })
+ async doTransfer() {
+ const {projectId} = await this.getProjectUserIdRead();
+ const userId = this.getUserId();
+ await this.service.transferAll(userId,projectId);
+ return this.ok();
+ }
+}
diff --git a/packages/ui/certd-server/src/modules/sys/enterprise/service/transfer-service.ts b/packages/ui/certd-server/src/modules/sys/enterprise/service/transfer-service.ts
new file mode 100644
index 000000000..1c20cc01a
--- /dev/null
+++ b/packages/ui/certd-server/src/modules/sys/enterprise/service/transfer-service.ts
@@ -0,0 +1,90 @@
+import { isEnterprise } from "@certd/lib-server";
+import { ApplicationContext, IMidwayContainer, Provide, Scope, ScopeEnum } from "@midwayjs/core";
+
+@Provide()
+@Scope(ScopeEnum.Request, { allowDowngrade: true })
+export class TransferService {
+ @ApplicationContext()
+ appCtx: IMidwayContainer;
+
+
+ async getServices() {
+ const getService = async (key: string) => {
+ return await this.appCtx.getAsync(key);
+ }
+ const serviceNames = [
+ "pipeline",
+ "certInfo",
+ "siteInfo",
+ "domain",
+ "cnameRecord",
+ "group",
+ "pipelineGroup",
+ "notification",
+ "subDomain",
+ "template",
+ "openKey",
+ "siteIp",
+ "access",
+ ]
+
+ const services: any = {}
+ for (const key of serviceNames) {
+ services[key] = await getService(`${key}Service`);
+ }
+ return services;
+ }
+
+ /**
+ * 获取用户资源
+ * @param userId
+ * @returns
+ */
+ async getUserResources(userId: number) {
+
+ const query = {
+ userId,
+ }
+
+ const services = await this.getServices();
+
+ const counts: any = {}
+ let totalCount = 0;
+ for (const key of Object.keys(services)) {
+ const count = await services[key].repository.count({ where: query });
+ counts[key] = count;
+ totalCount += count;
+ }
+
+
+ return {
+ ...counts,
+ totalCount,
+ }
+ }
+
+ async transferAll(userId: number, projectId: number) {
+
+ if (!isEnterprise()) {
+ throw new Error('当前非企业模式,不支持资源迁移到项目');
+ }
+ if (projectId === 0) {
+ throw new Error('项目ID不能为0');
+ }
+ if (userId == null) {
+ throw new Error('用户ID不能为空');
+ }
+
+ const query = {
+ userId,
+ }
+ const services = await this.getServices();
+ for (const key of Object.keys(services)) {
+ await services[key].repository.update(query, {
+ userId: -1,
+ projectId,
+ });
+ }
+ }
+
+}
\ No newline at end of file