mirror of https://github.com/certd/certd
chore: suite
parent
8814ffeda6
commit
45839f227a
|
@ -49,4 +49,10 @@ export abstract class CrudController<T> extends BaseController {
|
|||
await this.getService().delete([id]);
|
||||
return this.ok(null);
|
||||
}
|
||||
|
||||
@Post('/deleteByIds')
|
||||
async deleteByIds(@Body('ids') ids: number[]) {
|
||||
await this.getService().delete(ids);
|
||||
return this.ok(null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,16 @@ export const certdResources = [
|
|||
auth: true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "套餐购买",
|
||||
name: "SuiteProductBuy",
|
||||
path: "/certd/suite/buy",
|
||||
component: "/certd/suite/buy.vue",
|
||||
meta: {
|
||||
icon: "mdi:format-list-group",
|
||||
auth: true
|
||||
}
|
||||
},
|
||||
// {
|
||||
// title: "邮箱设置",
|
||||
// name: "EmailSetting",
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import { request } from "/@/api/service";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
export const durationDict = dict({
|
||||
data: [
|
||||
{ label: "1年", value: 365 },
|
||||
{ label: "2年", value: 730 },
|
||||
{ label: "3年", value: 1095 },
|
||||
{ label: "4年", value: 1460 },
|
||||
{ label: "5年", value: 1825 },
|
||||
{ label: "6年", value: 2190 },
|
||||
{ label: "7年", value: 2555 },
|
||||
{ label: "8年", value: 2920 },
|
||||
{ label: "9年", value: 3285 },
|
||||
{ label: "10年", value: 3650 },
|
||||
{ label: "永久", value: -1 }
|
||||
]
|
||||
});
|
||||
|
||||
export async function ProductList() {
|
||||
return await request({
|
||||
url: "/suite/product/list",
|
||||
method: "POST"
|
||||
});
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
<template>
|
||||
<fs-page class="page-suite-buy">
|
||||
<template #header>
|
||||
<div class="title">套餐购买</div>
|
||||
</template>
|
||||
<div class="suite-buy-content">
|
||||
<a-row :gutter="12">
|
||||
<a-col v-for="item of suites" :key="item.id" class="mb-10" :xs="12" :sm="12" :md="8" :lg="6" :xl="6" :xxl="4">
|
||||
<a-card :title="item.title">
|
||||
<template #extra>
|
||||
<a-tag>{{ item.type }}</a-tag>
|
||||
</template>
|
||||
|
||||
<div>{{ item.intro }}</div>
|
||||
<div class="hr">
|
||||
<div class="flex-between mt-5">流水线条数:<suite-value :model-value="item.content.maxPipelineCount" /></div>
|
||||
<div class="flex-between mt-5">域名数量: <suite-value :model-value="item.content.maxDomainCount" /></div>
|
||||
<div class="flex-between mt-5">部署次数: <suite-value :model-value="item.content.maxDeployCount" /></div>
|
||||
<div class="flex-between mt-5">
|
||||
证书监控:
|
||||
<span v-if="item.content.siteMonitor">支持</span>
|
||||
<span v-else>不支持</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="duration flex-between mt-5 hr">
|
||||
<div class="flex-o">时长</div>
|
||||
<div class="duration-list">
|
||||
<div
|
||||
v-for="dp of item.durationPrices"
|
||||
:key="dp.duration"
|
||||
class="duration-item"
|
||||
:class="{ active: item._selected.duration === dp.duration }"
|
||||
@click="item._selected = dp"
|
||||
>
|
||||
{{ durationDict.dataMap[dp.duration]?.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="price flex-between mt-5 hr">
|
||||
<div class="flex-o">价格</div>
|
||||
<div class="flex-o price-text">
|
||||
<price-input style="font-size: 18px; color: red" :model-value="item._selected?.price" :edit="false" />
|
||||
<span class="ml-5" style="font-size: 12px"> / {{ durationDict.dataMap[item._selected.duration]?.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #actions>
|
||||
<setting-outlined key="setting" />
|
||||
<a-button type="primary">立即购买</a-button>
|
||||
</template>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import * as api from "./api";
|
||||
import { durationDict } from "./api";
|
||||
import PriceInput from "/@/views/sys/suite/product/price-input.vue";
|
||||
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
const suites = ref([]);
|
||||
|
||||
const optionsDict = dict({
|
||||
data: []
|
||||
});
|
||||
|
||||
async function loadSuites() {
|
||||
suites.value = await api.ProductList();
|
||||
|
||||
for (const item of suites.value) {
|
||||
item._selected = item.durationPrices[0];
|
||||
}
|
||||
}
|
||||
|
||||
loadSuites();
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.page-suite-buy {
|
||||
.title {
|
||||
background-color: #fff;
|
||||
}
|
||||
background: #f0f2f5;
|
||||
.suite-buy-content {
|
||||
padding: 20px;
|
||||
|
||||
.duration-list {
|
||||
display: flex;
|
||||
.duration-item {
|
||||
width: 50px;
|
||||
border: 1px solid #cdcdcd;
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
margin: 2px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
&.active {
|
||||
border-color: #a6fba3;
|
||||
background-color: #c1eafb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hr {
|
||||
border-top: 1px solid #cdcdcd;
|
||||
margin-top: 5px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.price-text {
|
||||
align-items: baseline;
|
||||
font-family: "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
|
||||
.prices {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
margin-top: 20px;
|
||||
.price-item {
|
||||
border: 1px solid #c6c6c6;
|
||||
background-color: #f8ebda;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
&:hover {
|
||||
border-color: #38a0fb;
|
||||
}
|
||||
&.active {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,3 @@
|
|||
<div>
|
||||
|
||||
</div>
|
|
@ -1,6 +1,9 @@
|
|||
import { request } from "/src/api/service";
|
||||
|
||||
const apiPrefix = "/sys/suite/product";
|
||||
export type PriceItem = {
|
||||
duration: number;
|
||||
price: number;
|
||||
};
|
||||
|
||||
export async function GetList(query: any) {
|
||||
return await request({
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useUserStore } from "/@/store/modules/user";
|
|||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import SuiteValue from "./suite-value.vue";
|
||||
import SuiteValueEdit from "./suite-value-edit.vue";
|
||||
import PriceEdit from "./price-edit.vue";
|
||||
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const router = useRouter();
|
||||
|
@ -24,9 +25,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
form.content = JSON.stringify({
|
||||
title: form.title
|
||||
});
|
||||
const res = await api.AddObj(form);
|
||||
return res;
|
||||
};
|
||||
|
@ -69,11 +67,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
groups: {
|
||||
base: {
|
||||
header: "基础信息",
|
||||
columns: ["title", "type", "price", "originPrice", "duration", "isBootstrap", "intro"]
|
||||
columns: ["title", "type", "isBootstrap", "disabled", "order", "intro"]
|
||||
},
|
||||
content: {
|
||||
header: "套餐内容",
|
||||
columns: ["maxDomainCount", "maxPipelineCount", "maxDeployCount", "siteMonitor"]
|
||||
columns: ["content.maxDomainCount", "content.maxPipelineCount", "content.maxDeployCount", "content.siteMonitor"]
|
||||
},
|
||||
price: {
|
||||
header: "价格",
|
||||
columns: ["durationPrices"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +98,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
search: {
|
||||
show: true
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: "此项必填" }]
|
||||
},
|
||||
column: {
|
||||
width: 200
|
||||
}
|
||||
|
@ -114,19 +119,41 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
{ label: "加量包", value: "addon" }
|
||||
]
|
||||
}),
|
||||
form: {
|
||||
rules: [{ required: true, message: "此项必填" }]
|
||||
},
|
||||
column: {
|
||||
width: 100
|
||||
},
|
||||
valueBuilder: ({ row }) => {
|
||||
if (row.content) {
|
||||
row.content = JSON.parse(row.content);
|
||||
}
|
||||
if (row.durationPrices) {
|
||||
row.durationPrices = JSON.parse(row.durationPrices);
|
||||
}
|
||||
},
|
||||
valueResolve: ({ form }) => {
|
||||
debugger;
|
||||
if (form.content) {
|
||||
form.content = JSON.stringify(form.content);
|
||||
}
|
||||
if (form.durationPrices) {
|
||||
form.durationPrices = JSON.stringify(form.durationPrices);
|
||||
}
|
||||
}
|
||||
},
|
||||
maxDomainCount: {
|
||||
"content.maxDomainCount": {
|
||||
title: "域名数量",
|
||||
type: "number",
|
||||
form: {
|
||||
key: ["content", "maxDomainCount"],
|
||||
component: {
|
||||
name: SuiteValueEdit,
|
||||
vModel: "modelValue",
|
||||
unit: "个"
|
||||
}
|
||||
},
|
||||
rules: [{ required: true, message: "此项必填" }]
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
|
@ -135,15 +162,17 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
}
|
||||
}
|
||||
},
|
||||
maxPipelineCount: {
|
||||
"content.maxPipelineCount": {
|
||||
title: "流水线数量",
|
||||
type: "number",
|
||||
form: {
|
||||
key: ["content", "maxPipelineCount"],
|
||||
component: {
|
||||
name: SuiteValueEdit,
|
||||
vModel: "modelValue",
|
||||
unit: "条"
|
||||
}
|
||||
},
|
||||
rules: [{ required: true, message: "此项必填" }]
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
|
@ -152,15 +181,17 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
}
|
||||
}
|
||||
},
|
||||
maxDeployCount: {
|
||||
"content.maxDeployCount": {
|
||||
title: "部署次数",
|
||||
type: "number",
|
||||
form: {
|
||||
key: ["content", "maxDeployCount"],
|
||||
component: {
|
||||
name: SuiteValueEdit,
|
||||
vModel: "modelValue",
|
||||
unit: "次"
|
||||
}
|
||||
},
|
||||
rules: [{ required: true, message: "此项必填" }]
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
|
@ -169,7 +200,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
}
|
||||
}
|
||||
},
|
||||
siteMonitor: {
|
||||
"content.siteMonitor": {
|
||||
title: "支持证书监控",
|
||||
type: "dict-switch",
|
||||
dict: dict({
|
||||
|
@ -178,10 +209,40 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
{ label: "否", value: false, color: "error" }
|
||||
]
|
||||
}),
|
||||
form: {
|
||||
key: ["content", "siteMonitor"],
|
||||
value: false
|
||||
},
|
||||
column: {
|
||||
width: 120
|
||||
}
|
||||
},
|
||||
durationPrices: {
|
||||
title: "时长及价格",
|
||||
type: "text",
|
||||
form: {
|
||||
title: "选择时长",
|
||||
component: {
|
||||
name: PriceEdit,
|
||||
vModel: "modelValue",
|
||||
edit: true,
|
||||
style: {
|
||||
minHeight: "120px"
|
||||
}
|
||||
},
|
||||
col: {
|
||||
span: 24
|
||||
},
|
||||
rules: [{ required: true, message: "此项必填" }]
|
||||
},
|
||||
column: {
|
||||
component: {
|
||||
name: PriceEdit,
|
||||
vModel: "modelValue",
|
||||
edit: false
|
||||
}
|
||||
}
|
||||
},
|
||||
isBootstrap: {
|
||||
title: "是否初始套餐",
|
||||
type: "dict-switch",
|
||||
|
@ -191,27 +252,36 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
{ label: "否", value: false, color: "error" }
|
||||
]
|
||||
}),
|
||||
form: {
|
||||
value: false
|
||||
},
|
||||
column: {
|
||||
width: 120
|
||||
}
|
||||
},
|
||||
price: {
|
||||
title: "单价",
|
||||
type: "number",
|
||||
disabled: {
|
||||
title: "上下架",
|
||||
type: "dict-radio",
|
||||
dict: dict({
|
||||
data: [
|
||||
{ value: false, label: "上架" },
|
||||
{ value: true, label: "下架" }
|
||||
]
|
||||
}),
|
||||
form: {
|
||||
value: false
|
||||
},
|
||||
column: {
|
||||
width: 100
|
||||
}
|
||||
},
|
||||
originPrice: {
|
||||
title: "原价",
|
||||
order: {
|
||||
title: "排序",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 100
|
||||
}
|
||||
},
|
||||
duration: {
|
||||
title: "有效时长",
|
||||
type: "dict-select",
|
||||
form: {
|
||||
helper: "越小越靠前",
|
||||
value: 0
|
||||
},
|
||||
column: {
|
||||
width: 100
|
||||
}
|
||||
|
@ -223,16 +293,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
width: 200
|
||||
}
|
||||
},
|
||||
order: {
|
||||
title: "排序",
|
||||
type: "number",
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
width: 100
|
||||
}
|
||||
},
|
||||
createTime: {
|
||||
title: "创建时间",
|
||||
type: "datetime",
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
<template>
|
||||
<div class="cd-suite-value-edit flex-o">
|
||||
<div class="flex- 1"><a-checkbox :checked="modelValue === -1" @update:checked="onCheckedChange">无限制</a-checkbox><span class="ml-5"></span></div>
|
||||
<div class="ml-10 w-50%">
|
||||
<a-input-number v-if="modelValue >= 0" :value="modelValue" class="ml-5" @update:value="onValueChange">
|
||||
<template v-if="unit" #addonAfter>{{ unit }}</template>
|
||||
</a-input-number>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Form } from "ant-design-vue";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: number;
|
||||
}>();
|
||||
|
||||
const durationDict = dict({
|
||||
data: [
|
||||
{ label: "1年", value: 365 },
|
||||
{ label: "2年", value: 730 },
|
||||
{ label: "3年", value: 1095 },
|
||||
{ label: "4年", value: 1460 },
|
||||
{ label: "5年", value: 1825 },
|
||||
{ label: "6年", value: 2190 },
|
||||
{ label: "7年", value: 2555 },
|
||||
{ label: "8年", value: 2920 },
|
||||
{ label: "9年", value: 3285 },
|
||||
{ label: "10年", value: 3650 },
|
||||
{ label: "永久", value: -1 }
|
||||
]
|
||||
});
|
||||
const formItemContext = Form.useInjectFormItemContext();
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const onCheckedChange = (checked: boolean) => {
|
||||
const value = checked ? -1 : 1;
|
||||
onValueChange(value);
|
||||
};
|
||||
|
||||
function onValueChange(value: number) {
|
||||
emit("update:modelValue", value);
|
||||
formItemContext.onFieldChange();
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,94 @@
|
|||
<template>
|
||||
<div class="cd-price-edit">
|
||||
<div v-if="edit" class="duration-list flex-o">
|
||||
<div v-for="item of durationDict.data" :key="item.value" :class="{ active: isActive(item) }" class="duration-item" @click="onDurationClicked(item)">
|
||||
{{ item.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="price-group-list">
|
||||
<div v-for="item of modelValue" :key="item.duration" class="flex-o price-group-item">
|
||||
<div style="width: 50px">{{ durationDict.dataMap[item.duration]?.label }}:</div>
|
||||
<price-input v-model="item.price" style="width: 150px" :edit="edit" class="mr-10" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Form } from "ant-design-vue";
|
||||
import { PriceItem } from "./api";
|
||||
import PriceInput from "./price-input.vue";
|
||||
import { durationDict } from "../../../certd/suite/api";
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue?: PriceItem[];
|
||||
edit: boolean;
|
||||
}>(),
|
||||
{
|
||||
modelValue: () => {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const formItemContext = Form.useInjectFormItemContext();
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
function doEmit(value: PriceItem[]) {
|
||||
emit("update:modelValue", value);
|
||||
formItemContext.onFieldChange();
|
||||
}
|
||||
|
||||
function isActive(item: any) {
|
||||
return props.modelValue.some((v) => v.duration === item.value);
|
||||
}
|
||||
|
||||
function onDurationClicked(item: any) {
|
||||
const has = props.modelValue.some((v) => v.duration === item.value);
|
||||
if (has) {
|
||||
// remove
|
||||
const newValue = props.modelValue.filter((v) => v.duration !== item.value);
|
||||
doEmit(newValue);
|
||||
} else {
|
||||
// add
|
||||
const newValue = [...props.modelValue, { duration: item.value, price: 0.0 }];
|
||||
//sort
|
||||
newValue.sort((a, b) => a.duration - b.duration);
|
||||
if (newValue.length > 0) {
|
||||
const first = newValue[0];
|
||||
if (first.duration === -1) {
|
||||
//挪到后面去
|
||||
newValue.shift();
|
||||
newValue.push(first);
|
||||
}
|
||||
}
|
||||
doEmit(newValue);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.cd-price-edit {
|
||||
.duration-item {
|
||||
border: 1px solid #eee;
|
||||
padding: 2px;
|
||||
width: 35px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.price-group-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.price-group-item {
|
||||
margin-right: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,35 @@
|
|||
<template>
|
||||
<div class="flex-o">
|
||||
<a-input-number v-if="edit" prefix="¥" :value="priceValue" :precision="2" class="ml-5" @update:value="onPriceChange"> </a-input-number>
|
||||
<span v-else>{{ priceLabel }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: number;
|
||||
edit: boolean;
|
||||
}>();
|
||||
|
||||
const priceValue = computed(() => {
|
||||
if (props.modelValue == null) {
|
||||
return 0;
|
||||
}
|
||||
return (props.modelValue / 100.0).toFixed(2);
|
||||
});
|
||||
|
||||
const priceLabel = computed(() => {
|
||||
if (priceValue.value === 0 || priceValue.value === "0.00") {
|
||||
return "免费";
|
||||
}
|
||||
return `¥${priceValue.value}`;
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const onPriceChange = (price: number) => {
|
||||
emit("update:modelValue", price * 100);
|
||||
};
|
||||
</script>
|
|
@ -2,7 +2,7 @@
|
|||
<div class="cd-suite-value-edit flex-o">
|
||||
<div class="flex- 1"><a-checkbox :checked="modelValue === -1" @update:checked="onCheckedChange">无限制</a-checkbox><span class="ml-5"></span></div>
|
||||
<div class="ml-10 w-50%">
|
||||
<a-input-number v-if="modelValue >= 0" :value="modelValue" class="ml-5" @update:value="onValueChange">
|
||||
<a-input-number v-if="modelValue == null || modelValue >= 0" :value="modelValue" class="ml-5" @update:value="onValueChange">
|
||||
<template v-if="unit" #addonAfter>{{ unit }}</template>
|
||||
</a-input-number>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { request } from "/src/api/service";
|
||||
|
||||
const apiPrefix = "/sys/suite/order";
|
||||
const apiPrefix = "/sys/suite/trade";
|
||||
|
||||
export async function GetList(query: any) {
|
||||
return await request({
|
|
@ -55,8 +55,8 @@ export class CnameRecordController extends CrudController<CnameProviderService>
|
|||
}
|
||||
|
||||
@Post('/deleteByIds', { summary: 'sys:settings:edit' })
|
||||
async deleteByIds(@Body(ALL) body: { ids: number[] }) {
|
||||
const res = await this.service.delete(body.ids);
|
||||
async deleteByIds(@Body('ids') ids: number[]) {
|
||||
const res = await this.service.delete(ids);
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,8 +57,8 @@ export class PluginController extends CrudController<PluginService> {
|
|||
}
|
||||
|
||||
@Post('/deleteByIds', { summary: 'sys:settings:edit' })
|
||||
async deleteByIds(@Body(ALL) body: { ids: number[] }) {
|
||||
const res = await this.service.delete(body.ids);
|
||||
async deleteByIds(@Body('ids') ids: number[]) {
|
||||
const res = await this.service.delete(ids);
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue