Compare commits

...

4 Commits

Author SHA1 Message Date
xiaojunnuo 8d8304e859 chore: 1 2026-02-04 19:03:03 +08:00
xiaojunnuo 6ddc23e2aa chore: project member 2026-02-04 18:54:00 +08:00
xiaojunnuo 2fc491015e chore: project manager 2026-02-04 18:24:15 +08:00
xiaojunnuo bb0afe1fa7 perf: 当域名管理中没有域名时,创建流水线时不展开域名选择框 2026-02-04 17:01:08 +08:00
23 changed files with 1305 additions and 10 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
],
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.tabSize": 2,
"explorer.autoReveal": false,
@@ -9,6 +9,7 @@
:options="optionsRef"
:value="value"
v-bind="attrs"
:open="openProp"
@click="onClick"
@update:value="emit('update:value', $event)"
>
@@ -56,11 +57,11 @@
</template>
<script setup lang="ts">
import { computed, defineComponent, ref, Ref, useAttrs } from "vue";
import { request } from "/@/api/service";
import { Dicts } from "../lib/dicts";
import { useRouter } from "vue-router";
import { useDomainImport, useDomainImportManage } from "/@/views/certd/cert/domain/use";
import { Dicts } from "../lib/dicts";
import { request } from "/@/api/service";
import { openRouteInNewWindow } from "/@/vben/utils";
import { useDomainImportManage } from "/@/views/certd/cert/domain/use";
defineOptions({
name: "DomainSelector",
@@ -82,6 +83,7 @@ const props = defineProps<{
search?: boolean;
pager?: boolean;
value?: any[];
open?: boolean;
}>();
const emit = defineEmits<{
@@ -90,6 +92,15 @@ const emit = defineEmits<{
const attrs = useAttrs();
const hasOptions: Ref = ref(null);
const openProp = computed(() => {
if (hasOptions.value == null) {
return false;
}
return hasOptions.value;
});
const searchKeyRef = ref("");
const optionsRef = ref([]);
const message = ref("");
@@ -143,6 +154,13 @@ const getOptions = async () => {
}
optionsRef.value = options;
if (hasOptions.value == null) {
if (options.length > 0) {
hasOptions.value = true;
} else {
hasOptions.value = false;
}
}
pagerRef.value.total = list.length;
if (props.pager) {
if (res.total != null) {
@@ -208,6 +208,10 @@ export default {
orderManager: "Order Management",
userSuites: "User Suites",
netTest: "Network Test",
enterpriseSetting: "Enterprise Settings",
projectManager: "Project Management",
projectUserManager: "Project User Management",
},
certificateRepo: {
title: "Certificate Repository",
@@ -214,6 +214,9 @@ export default {
orderManager: "订单管理",
userSuites: "用户套餐",
netTest: "网络测试",
enterpriseSetting: "企业管理设置",
projectManager: "项目管理",
projectUserManager: "项目用户管理",
},
certificateRepo: {
title: "证书仓库",
@@ -880,4 +883,7 @@ export default {
enterpriseMode: "企业模式",
saasMode: "SaaS模式",
},
ent: {
projectName: "项目名称",
},
};
@@ -187,7 +187,59 @@ export const sysResources = [
keepAlive: true,
},
},
{
title: "certd.sysResources.enterpriseManager",
name: "EnterpriseManager",
path: "/sys/enterprise",
redirect: "/sys/enterprise/project",
meta: {
icon: "ion:cart-outline",
permission: "sys:settings:edit",
show: () => {
const settingStore = useSettingStore();
return settingStore.isEnterprise;
},
keepAlive: true,
},
children: [
{
title: "certd.sysResources.projectManager",
name: "ProjectManager",
path: "/sys/enterprise/project",
component: "/sys/enterprise/project/index.vue",
meta: {
show: true,
icon: "ion:cart",
permission: "sys:settings:edit",
keepAlive: true,
},
},
{
title: "certd.sysResources.projectMemberManager",
name: "ProjectMemberManager",
path: "/sys/enterprise/project/member",
component: "/sys/enterprise/project/member/index.vue",
meta: {
isMenu: false,
show: true,
icon: "ion:cart",
permission: "sys:settings:edit",
},
},
{
title: "certd.sysResources.enterpriseSetting",
name: "EnterpriseSetting",
path: "/sys/enterprise/setting",
redirect: "/sys/settings?tab=enterprise",
meta: {
isMenu: true,
show: true,
icon: "ion:cart",
permission: "sys:settings:edit",
},
},
],
},
{
title: "certd.sysResources.suiteManager",
name: "SuiteManager",
@@ -141,6 +141,9 @@ export const useSettingStore = defineStore({
isComm(): boolean {
return this.plusInfo?.isComm && (this.plusInfo?.expireTime === -1 || this.plusInfo?.expireTime > new Date().getTime());
},
isEnterprise(): boolean {
return this.isPlus && this.sysPublic.adminMode === "enterprise";
},
isAgent(): boolean {
return this.siteEnv?.agent?.enabled === true;
},
@@ -0,0 +1,75 @@
import { request } from "/src/api/service";
const apiPrefix = "/sys/project/provider";
export async function GetList(query: any) {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query,
});
}
export async function AddObj(obj: any) {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj,
});
}
export async function UpdateObj(obj: any) {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj,
});
}
export async function DelObj(id: any) {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id },
});
}
export async function GetObj(id: any) {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id },
});
}
export async function GetDetail(id: any) {
return await request({
url: apiPrefix + "/detail",
method: "post",
params: { id },
});
}
export async function DeleteBatch(ids: any[]) {
return await request({
url: apiPrefix + "/deleteByIds",
method: "post",
data: { ids },
});
}
export async function SetDefault(id: any) {
return await request({
url: apiPrefix + "/setDefault",
method: "post",
data: { id },
});
}
export async function SetDisabled(id: any, disabled: boolean) {
return await request({
url: apiPrefix + "/setDisabled",
method: "post",
data: { id, disabled },
});
}
@@ -0,0 +1,254 @@
import * as api from "./api";
import { useI18n } from "/src/locales";
import { computed, Ref, ref } from "vue";
import { useRouter } from "vue-router";
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import { useUserStore } from "/@/store/user";
import { useSettingStore } from "/@/store/settings";
import { Modal } from "ant-design-vue";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const router = useRouter();
const { t } = useI18n();
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
const res = await api.UpdateObj(form);
return res;
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
const res = await api.AddObj(form);
return res;
};
const userStore = useUserStore();
const settingStore = useSettingStore();
const selectedRowKeys: Ref<any[]> = ref([]);
context.selectedRowKeys = selectedRowKeys;
return {
crudOptions: {
settings: {
plugins: {
//这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并
rowSelection: {
enabled: true,
order: -2,
before: true,
// handle: (pluginProps,useCrudProps)=>CrudOptions,
props: {
multiple: true,
crossPage: true,
selectedRowKeys,
},
},
},
},
request: {
pageRequest,
addRequest,
editRequest,
delRequest,
},
rowHandle: {
minWidth: 200,
fixed: "right",
},
columns: {
id: {
title: "ID",
key: "id",
type: "number",
column: {
width: 100,
},
form: {
show: false,
},
},
domain: {
title: t("certd.cnameDomain"),
type: "text",
editForm: {
component: {
disabled: true,
},
},
search: {
show: true,
},
form: {
component: {
placeholder: t("certd.cnameDomainPlaceholder"),
},
helper: t("certd.cnameDomainHelper"),
rules: [
{ required: true, message: t("certd.requiredField") },
{ pattern: /^[^*]+$/, message: t("certd.cnameDomainPattern") },
],
},
column: {
width: 200,
},
},
dnsProviderType: {
title: t("certd.dnsProvider"),
type: "dict-select",
search: {
show: true,
},
dict: dict({
url: "pi/dnsProvider/list",
value: "key",
label: "title",
}),
form: {
rules: [{ required: true, message: t("certd.requiredField") }],
},
column: {
width: 150,
component: {
color: "auto",
},
},
},
accessId: {
title: t("certd.dnsProviderAuthorization"),
type: "dict-select",
dict: dict({
url: "/pi/access/list",
value: "id",
label: "name",
}),
form: {
component: {
name: "access-selector",
vModel: "modelValue",
type: compute(({ form }) => {
return form.dnsProviderType;
}),
},
rules: [{ required: true, message: t("certd.requiredField") }],
},
column: {
width: 150,
component: {
color: "auto",
},
},
},
isDefault: {
title: t("certd.isDefault"),
type: "dict-switch",
dict: dict({
data: [
{ label: t("certd.yes"), value: true, color: "success" },
{ label: t("certd.no"), value: false, color: "default" },
],
}),
form: {
value: false,
rules: [{ required: true, message: t("certd.selectIsDefault") }],
},
column: {
align: "center",
width: 100,
},
},
setDefault: {
title: t("certd.setDefault"),
type: "text",
form: {
show: false,
},
column: {
width: 100,
align: "center",
conditionalRenderDisabled: true,
cellRender: ({ row }) => {
if (row.isDefault) {
return;
}
const onClick = async () => {
Modal.confirm({
title: t("certd.prompt"),
content: t("certd.confirmSetDefault"),
onOk: async () => {
await api.SetDefault(row.id);
await crudExpose.doRefresh();
},
});
};
return (
<a-button type={"link"} size={"small"} onClick={onClick}>
{t("certd.setAsDefault")}
</a-button>
);
},
},
},
disabled: {
title: t("certd.disabled"),
type: "dict-switch",
dict: dict({
data: [
{ label: t("certd.enabled"), value: false, color: "success" },
{ label: t("certd.disabledLabel"), value: true, color: "error" },
],
}),
form: {
value: false,
},
column: {
width: 100,
component: {
title: t("certd.clickToToggle"),
on: {
async click({ value, row }) {
Modal.confirm({
title: t("certd.prompt"),
content: t("certd.confirmToggleStatus", { action: !value ? t("certd.disable") : t("certd.enable") }),
onOk: async () => {
await api.SetDisabled(row.id, !value);
await crudExpose.doRefresh();
},
});
},
},
},
},
},
createTime: {
title: t("certd.createTime"),
type: "datetime",
form: {
show: false,
},
column: {
sorter: true,
width: 160,
align: "center",
},
},
updateTime: {
title: t("certd.updateTime"),
type: "datetime",
form: {
show: false,
},
column: {
show: true,
width: 160,
},
},
},
},
};
}
@@ -0,0 +1,65 @@
<template>
<fs-page class="page-cert">
<template #header>
<div class="title">
{{ t("certd.cnameTitle") }}
<span class="sub">
{{ t("certd.cnameDescription") }}
<a href="https://certd.docmirror.cn/guide/feature/cname/" target="_blank">
{{ t("certd.cnameLinkText") }}
</a>
</span>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding">
<template #pagination-left>
<a-tooltip :title="t('certd.batchDelete')">
<fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>
</a-tooltip>
</template>
</fs-crud>
</fs-page>
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
const { t } = useI18n();
defineOptions({
name: "CnameProvider",
});
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
const selectedRowKeys = context.selectedRowKeys;
const handleBatchDelete = () => {
if (selectedRowKeys.value?.length > 0) {
Modal.confirm({
title: t("certd.confirmTitle"),
content: t("certd.confirmDeleteBatch", { count: selectedRowKeys.value.length }),
async onOk() {
await DeleteBatch(selectedRowKeys.value);
message.info(t("certd.deleteSuccess"));
crudExpose.doRefresh();
selectedRowKeys.value = [];
},
});
} else {
message.error(t("certd.selectRecordsFirst"));
}
};
// 页面打开后获取列表数据
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh();
});
</script>
<style lang="less"></style>
@@ -0,0 +1,68 @@
import { request } from "/src/api/service";
const apiPrefix = "/sys/enterprise/project";
const userApiPrefix = "/sys/authority/user";
export async function GetList(query: any) {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query,
});
}
export async function AddObj(obj: any) {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj,
});
}
export async function UpdateObj(obj: any) {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj,
});
}
export async function DelObj(id: any) {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id },
});
}
export async function GetObj(id: any) {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id },
});
}
export async function GetDetail(id: any) {
return await request({
url: apiPrefix + "/detail",
method: "post",
params: { id },
});
}
export async function DeleteBatch(ids: any[]) {
return await request({
url: apiPrefix + "/deleteByIds",
method: "post",
data: { ids },
});
}
export async function SetDisabled(id: any, disabled: boolean) {
return await request({
url: apiPrefix + "/setDisabled",
method: "post",
data: { id, disabled },
});
}
@@ -0,0 +1,147 @@
import * as api from "./api";
import { useI18n } from "/src/locales";
import { computed, Ref, ref } from "vue";
import { useRouter } from "vue-router";
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import { useUserStore } from "/@/store/user";
import { useSettingStore } from "/@/store/settings";
import { Modal } from "ant-design-vue";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const router = useRouter();
const { t } = useI18n();
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
const res = await api.UpdateObj(form);
return res;
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
const res = await api.AddObj(form);
return res;
};
const userStore = useUserStore();
const settingStore = useSettingStore();
const selectedRowKeys: Ref<any[]> = ref([]);
context.selectedRowKeys = selectedRowKeys;
return {
crudOptions: {
settings: {
plugins: {
//这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并
rowSelection: {
enabled: true,
order: -2,
before: true,
// handle: (pluginProps,useCrudProps)=>CrudOptions,
props: {
multiple: true,
crossPage: true,
selectedRowKeys,
},
},
},
},
request: {
pageRequest,
addRequest,
editRequest,
delRequest,
},
rowHandle: {
minWidth: 200,
fixed: "right",
},
columns: {
id: {
title: "ID",
key: "id",
type: "number",
column: {
width: 100,
},
form: {
show: false,
},
},
name: {
title: t("certd.ent.projectName"),
type: "text",
search: {
show: true,
},
form: {
component: {},
helper: t("certd.ent.projectNameHelper"),
rules: [{ required: true, message: t("certd.requiredField") }],
},
column: {
width: 200,
},
},
disabled: {
title: t("certd.disabled"),
type: "dict-switch",
dict: dict({
data: [
{ label: t("certd.enabled"), value: false, color: "success" },
{ label: t("certd.disabledLabel"), value: true, color: "error" },
],
}),
form: {
value: false,
},
column: {
width: 100,
component: {
title: t("certd.clickToToggle"),
on: {
async click({ value, row }) {
Modal.confirm({
title: t("certd.prompt"),
content: t("certd.confirmToggleStatus", { action: !value ? t("certd.disable") : t("certd.enable") }),
onOk: async () => {
await api.SetDisabled(row.id, !value);
await crudExpose.doRefresh();
},
});
},
},
},
},
},
createTime: {
title: t("certd.createTime"),
type: "datetime",
form: {
show: false,
},
column: {
sorter: true,
width: 160,
align: "center",
},
},
updateTime: {
title: t("certd.updateTime"),
type: "datetime",
form: {
show: false,
},
column: {
show: true,
width: 160,
},
},
},
},
};
}
@@ -0,0 +1,59 @@
<template>
<fs-page class="page-cert">
<template #header>
<div class="title">
{{ t("certd.sysResources.projectManager") }}
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding">
<template #pagination-left>
<a-tooltip :title="t('certd.batchDelete')">
<fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>
</a-tooltip>
</template>
</fs-crud>
</fs-page>
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
const { t } = useI18n();
defineOptions({
name: "ProjectManager",
});
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
const selectedRowKeys = context.selectedRowKeys;
const handleBatchDelete = () => {
if (selectedRowKeys.value?.length > 0) {
Modal.confirm({
title: t("certd.confirmTitle"),
content: t("certd.confirmDeleteBatch", { count: selectedRowKeys.value.length }),
async onOk() {
await DeleteBatch(selectedRowKeys.value);
message.info(t("certd.deleteSuccess"));
crudExpose.doRefresh();
selectedRowKeys.value = [];
},
});
} else {
message.error(t("certd.selectRecordsFirst"));
}
};
// 页面打开后获取列表数据
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh();
});
</script>
<style lang="less"></style>
@@ -0,0 +1,67 @@
import { request } from "/src/api/service";
const apiPrefix = "/sys/enterprise/projectMember";
const userApiPrefix = "/sys/authority/user";
export async function GetList(query: any) {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query,
});
}
export async function AddObj(obj: any) {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj,
});
}
export async function UpdateObj(obj: any) {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj,
});
}
export async function DelObj(id: any) {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id },
});
}
export async function GetObj(id: any) {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id },
});
}
export async function GetDetail(id: any) {
return await request({
url: apiPrefix + "/detail",
method: "post",
params: { id },
});
}
export async function DeleteBatch(ids: any[]) {
return await request({
url: apiPrefix + "/deleteByIds",
method: "post",
data: { ids },
});
}
export async function GetUserSimpleByIds(query: any) {
return await request({
url: userApiPrefix + "/getSimpleByIds",
method: "post",
data: query,
});
}
@@ -0,0 +1,155 @@
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { Modal } from "ant-design-vue";
import { Ref, ref } from "vue";
import { useRouter } from "vue-router";
import * as api from "./api";
import { useSettingStore } from "/@/store/settings";
import { useUserStore } from "/@/store/user";
import { useI18n } from "/src/locales";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const router = useRouter();
const { t } = useI18n();
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
const res = await api.UpdateObj(form);
return res;
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
const res = await api.AddObj(form);
return res;
};
const userStore = useUserStore();
const settingStore = useSettingStore();
const selectedRowKeys: Ref<any[]> = ref([]);
context.selectedRowKeys = selectedRowKeys;
return {
crudOptions: {
settings: {
plugins: {
//这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并
rowSelection: {
enabled: true,
order: -2,
before: true,
// handle: (pluginProps,useCrudProps)=>CrudOptions,
props: {
multiple: true,
crossPage: true,
selectedRowKeys,
},
},
},
},
request: {
pageRequest,
addRequest,
editRequest,
delRequest,
},
rowHandle: {
minWidth: 200,
fixed: "right",
},
columns: {
id: {
title: "ID",
key: "id",
type: "number",
column: {
width: 100,
},
form: {
show: false,
},
},
projectId: {
title: "项目ID",
type: "text",
search: {
show: false,
},
form: {
show: false,
},
column: {
width: 200,
show: false,
},
},
userId: {
title: "用户ID",
type: "dict-select",
dict: dict({
url: "/sys/authority/user/getSimpleUsers",
value: "id",
label: "nickName",
labelBuilder: (item: any) => item.nickName || item.username || item.phoneCode + item.mobile,
}),
search: {
show: false,
},
form: {
show: false,
},
column: {
width: 200,
show: false,
},
},
permission: {
title: t("certd.ent.projectPermission"),
type: "dict-select",
dict: dict({
data: [
{ label: t("certd.read"), value: "read", color: "cyan" },
{ label: t("certd.write"), value: "write", color: "blue" },
{ label: t("certd.admin"), value: "admin", color: "green" },
],
}),
search: {
show: true,
},
form: {
show: true,
},
column: {
width: 200,
},
},
createTime: {
title: t("certd.createTime"),
type: "datetime",
form: {
show: false,
},
column: {
sorter: true,
width: 160,
align: "center",
},
},
updateTime: {
title: t("certd.updateTime"),
type: "datetime",
form: {
show: false,
},
column: {
show: true,
width: 160,
},
},
},
},
};
}
@@ -0,0 +1,71 @@
<template>
<fs-page class="page-project-member">
<template #header>
<div class="title">
{{ t("certd.projectMemberManager") }}
<span class="sub">
{{ t("certd.projectMemberDescription") }}
</span>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding">
<template #pagination-left>
<a-tooltip :title="t('certd.batchDelete')">
<fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>
</a-tooltip>
</template>
</fs-crud>
</fs-page>
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
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";
const { t } = useI18n();
defineOptions({
name: "ProjectMemberManager",
});
const route = useRoute();
const projectIdStr = route.query.projectId as string;
const projectId = Number(projectIdStr);
const context: any = {
projectId,
};
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
const selectedRowKeys = context.selectedRowKeys;
const handleBatchDelete = () => {
if (selectedRowKeys.value?.length > 0) {
Modal.confirm({
title: t("certd.confirmTitle"),
content: t("certd.confirmDeleteBatch", { count: selectedRowKeys.value.length }),
async onOk() {
await DeleteBatch(selectedRowKeys.value);
message.info(t("certd.deleteSuccess"));
crudExpose.doRefresh();
selectedRowKeys.value = [];
},
});
} else {
message.error(t("certd.selectRecordsFirst"));
}
};
// 页面打开后获取列表数据
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh();
});
</script>
<style lang="less"></style>
@@ -28,7 +28,7 @@ export class UserController extends CrudController<UserService> {
return this.service;
}
@Post('/getSimpleUserByIds', {summary: 'sys:auth:user:add'})
@Post('/getSimpleUserByIds', {summary: 'sys:auth:user:view'})
async getSimpleUserByIds(@Body('ids') ids: number[]) {
const users = await this.service.find({
select: {
@@ -46,6 +46,21 @@ export class UserController extends CrudController<UserService> {
return this.ok(users);
}
@Post('/getSimpleUsers', {summary: 'sys:auth:user:view'})
async getSimpleUsers() {
const users = await this.service.find({
select: {
id: true,
username: true,
nickName: true,
mobile: true,
phoneCode: true,
},
});
return this.ok(users);
}
@Post('/page', {summary: 'sys:auth:user:view'})
async page(
@Body(ALL)
@@ -153,4 +168,6 @@ export class UserController extends CrudController<UserService> {
}
}
@@ -0,0 +1,70 @@
import { CrudController, SysSettingsService } from "@certd/lib-server";
import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core";
import { ProjectService } from "../../../modules/sys/enterprise/service/project-service.js";
import { ProjectEntity } from "../../../modules/sys/enterprise/entity/project.js";
import { merge } from "lodash-es";
/**
*/
@Provide()
@Controller("/api/sys/enterprise/project")
export class SysProjectController extends CrudController<ProjectEntity> {
@Inject()
service: ProjectService;
@Inject()
sysSettingsService: SysSettingsService;
getService<T>() {
return this.service;
}
@Post("/page", { summary: "sys:settings:view" })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
return await super.page(body);
}
@Post("/list", { summary: "sys:settings:view" })
async list(@Body(ALL) body: any) {
return super.list(body);
}
@Post("/add", { summary: "sys:settings:edit" })
async add(@Body(ALL) bean: any) {
const def: any = {
isDefault: false,
disabled: false,
};
merge(bean, def);
bean.userId = this.getUserId();
return super.add(bean);
}
@Post("/update", { summary: "sys:settings:edit" })
async update(@Body(ALL) bean: any) {
bean.userId = this.getUserId();
return super.update(bean);
}
@Post("/info", { summary: "sys:settings:view" })
async info(@Query("id") id: number) {
return super.info(id);
}
@Post("/delete", { summary: "sys:settings:edit" })
async delete(@Query("id") id: number) {
return super.delete(id);
}
@Post("/deleteByIds", { summary: "sys:settings:edit" })
async deleteByIds(@Body("ids") ids: number[]) {
const res = await this.service.delete(ids);
return this.ok(res);
}
@Post("/setDisabled", { summary: "sys:settings:edit" })
async setDisabled(@Body("id") id: number, @Body("disabled") disabled: boolean) {
await this.service.setDisabled(id, disabled);
return this.ok();
}
}
@@ -0,0 +1,65 @@
import { CrudController, SysSettingsService } from "@certd/lib-server";
import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core";
import { ProjectMemberEntity } from "../../../modules/sys/enterprise/entity/project-member.js";
import { ProjectMemberService } from "../../../modules/sys/enterprise/service/project-member-service.js";
import { merge } from "lodash-es";
/**
*/
@Provide()
@Controller("/api/sys/enterprise/projectMember")
export class SysProjectMemberController extends CrudController<ProjectMemberEntity> {
@Inject()
service: ProjectMemberService;
@Inject()
sysSettingsService: SysSettingsService;
getService<T>() {
return this.service;
}
@Post("/page", { summary: "sys:settings:view" })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
return await super.page(body);
}
@Post("/list", { summary: "sys:settings:view" })
async list(@Body(ALL) body: any) {
return super.list(body);
}
@Post("/add", { summary: "sys:settings:edit" })
async add(@Body(ALL) bean: any) {
const def: any = {
isDefault: false,
disabled: false,
};
merge(bean, def);
bean.userId = this.getUserId();
return super.add(bean);
}
@Post("/update", { summary: "sys:settings:edit" })
async update(@Body(ALL) bean: any) {
bean.userId = this.getUserId();
return super.update(bean);
}
@Post("/info", { summary: "sys:settings:view" })
async info(@Query("id") id: number) {
return super.info(id);
}
@Post("/delete", { summary: "sys:settings:edit" })
async delete(@Query("id") id: number) {
return super.delete(id);
}
@Post("/deleteByIds", { summary: "sys:settings:edit" })
async deleteByIds(@Body("ids") ids: number[]) {
const res = await this.service.delete(ids);
return this.ok(res);
}
}
@@ -54,7 +54,6 @@ export class SubDomainService extends BaseService<SubDomainEntity> {
throw new Error('域名已存在');
}
return await super.add(bean)
}
}
@@ -2,8 +2,8 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
/**
*/
@Entity('cd_project_user')
export class ProjectUserEntity {
@Entity('cd_project_member')
export class ProjectMemberEntity {
@PrimaryGeneratedColumn()
id: number;
@@ -14,7 +14,7 @@ export class ProjectUserEntity {
projectId: number;
@Column({ name: 'permission', comment: '权限' })
permission: string; // read / write
permission: string; // read / write / admin
@Column({
name: 'create_time',
@@ -0,0 +1,41 @@
import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core';
import {BaseService, SysSettingsService} from '@certd/lib-server';
import {InjectEntityModel} from '@midwayjs/typeorm';
import {Repository} from 'typeorm';
import { ProjectMemberEntity } from '../entity/project-member.js';
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class ProjectMemberService extends BaseService<ProjectMemberEntity> {
@InjectEntityModel(ProjectMemberEntity)
repository: Repository<ProjectMemberEntity>;
@Inject()
sysSettingsService: SysSettingsService;
//@ts-ignore
getRepository() {
return this.repository;
}
async add(bean: ProjectMemberEntity) {
const {projectId, userId} = bean;
if (!projectId) {
throw new Error('项目ID不能为空');
}
if (!userId) {
throw new Error('用户ID不能为空');
}
const exist = await this.repository.findOne({
where: {
projectId,
userId,
},
});
if (exist) {
throw new Error('项目用户已存在');
}
return await super.add(bean)
}
}
@@ -0,0 +1,59 @@
import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core';
import {BaseService, SysSettingsService} from '@certd/lib-server';
import {InjectEntityModel} from '@midwayjs/typeorm';
import {Repository} from 'typeorm';
import { ProjectEntity } from '../entity/project.js';
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class ProjectService extends BaseService<ProjectEntity> {
@InjectEntityModel(ProjectEntity)
repository: Repository<ProjectEntity>;
@Inject()
sysSettingsService: SysSettingsService;
//@ts-ignore
getRepository() {
return this.repository;
}
async add(bean: ProjectEntity) {
const {name} = bean;
if (!name) {
throw new Error('项目名称不能为空');
}
const exist = await this.repository.findOne({
where: {
name,
userId:0,
},
});
if (exist) {
throw new Error('项目名称已存在');
}
bean.userId = 0
bean.disabled = false
return await super.add(bean)
}
async setDisabled(id: number, disabled: boolean) {
const project = await this.repository.findOne({
where: {
id,
userId:0,
},
});
if (!project) {
throw new Error('项目不存在');
}
await this.repository.update({
userId:0,
}, {
disabled,
});
project.disabled = disabled;
await this.repository.save(project);
}
}