feat: support bach set website group (#10754)

This commit is contained in:
CityFun
2025-10-24 10:00:14 +00:00
committed by GitHub
parent d8e4144d46
commit 08ebbee328
11 changed files with 206 additions and 50 deletions
+20
View File
@@ -1162,6 +1162,26 @@ func (b *BaseApi) BatchOpWebsites(c *gin.Context) {
helper.Success(c)
}
// @Tags Website
// @Summary Batch set website group
// @Accept json
// @Param request body request.BatchSetGroupReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/batch/group [post]
func (b *BaseApi) BatchSetWebsiteGroup(c *gin.Context) {
var req request.BatchWebsiteGroup
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.BatchSetGroup(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Website
// @Summary Get CORS Config
// @Accept json
+5
View File
@@ -109,6 +109,11 @@ type BatchWebsiteOp struct {
TaskID string `json:"taskID" validate:"required"`
}
type BatchWebsiteGroup struct {
IDs []uint `json:"ids" validate:"required"`
GroupID uint `json:"groupID" validate:"required"`
}
type WebsiteRedirectUpdate struct {
WebsiteID uint `json:"websiteId" validate:"required"`
Key string `json:"key" validate:"required"`
+12
View File
@@ -61,6 +61,7 @@ type IWebsiteService interface {
DeleteWebsite(req request.WebsiteDelete) error
GetWebsite(id uint) (response.WebsiteDTO, error)
BatchOpWebsite(req request.BatchWebsiteOp) error
BatchSetGroup(req request.BatchWebsiteGroup) error
CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error)
GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error)
@@ -521,6 +522,17 @@ func (w WebsiteService) OpWebsite(req request.WebsiteOp) error {
return websiteRepo.Save(context.Background(), &website)
}
func (w WebsiteService) BatchSetGroup(req request.BatchWebsiteGroup) error {
websites, _ := websiteRepo.List(repo.WithByIDs(req.IDs))
for _, web := range websites {
web.WebsiteGroupID = req.GroupID
if err := websiteRepo.Save(context.Background(), &web); err != nil {
return err
}
}
return nil
}
func (w WebsiteService) BatchOpWebsite(req request.BatchWebsiteOp) error {
websites, _ := websiteRepo.List(repo.WithByIDs(req.IDs))
opTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("Status"), task.TaskBatch, task.TaskScopeWebsite, req.TaskID, 0)
+1
View File
@@ -26,6 +26,7 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) {
websiteRouter.POST("/default/server", baseApi.ChangeDefaultServer)
websiteRouter.POST("/group/change", baseApi.ChangeWebsiteGroup)
websiteRouter.POST("/batch/operate", baseApi.BatchOpWebsites)
websiteRouter.POST("/batch/group", baseApi.BatchSetWebsiteGroup)
websiteRouter.GET("/domains/:websiteId", baseApi.GetWebDomains)
websiteRouter.POST("/domains/del", baseApi.DeleteWebDomain)
+5
View File
@@ -694,4 +694,9 @@ export namespace Website {
export interface CorsConfigReq extends CorsConfig {
websiteID: number;
}
export interface BatchSetGroup {
ids: number[];
groupID: number;
}
}
+4
View File
@@ -367,3 +367,7 @@ export const getCorsConfig = (id: number) => {
export const updateCorsConfig = (req: Website.CorsConfigReq) => {
return http.post(`/websites/cors/update`, req);
};
export const batchSetGroup = (req: Website.BatchSetGroup) => {
return http.post(`/websites/batch/group`, req);
};
@@ -0,0 +1,65 @@
<template>
<DrawerPro v-model="open" :header="$t('commons.table.group')" size="30%" @close="handleClose">
<el-form ref="websiteForm" label-position="top" :model="form" :rules="rules" v-loading="loading">
<GroupSelect v-model="form.groupID" :prop="'groupID'" :groupType="'website'"></GroupSelect>
</el-form>
<template #footer>
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary" @click="submit()" :disabled="loading">
{{ $t('commons.button.confirm') }}
</el-button>
</template>
</DrawerPro>
</template>
<script lang="ts" setup>
import GroupSelect from '@/views/website/website/components/group/index.vue';
import { batchSetGroup } from '@/api/modules/website';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { FormInstance } from 'element-plus';
const open = ref(false);
const loading = ref(false);
const websiteForm = ref<FormInstance>();
const form = reactive({
ids: [] as number[],
groupID: 0,
});
const rules = ref({
groupID: [Rules.requiredSelect],
});
const handleClose = () => {
open.value = false;
};
const acceptParams = async (ids: []) => {
form.ids = ids;
form.groupID = 0;
open.value = true;
};
const submit = async () => {
loading.value = true;
const valid = await websiteForm.value.validate();
if (!valid) {
loading.value = false;
return;
}
batchSetGroup(form)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
open.value = false;
})
.finally(() => {
loading.value = false;
});
};
defineExpose({
acceptParams,
});
</script>
@@ -0,0 +1,50 @@
<template>
<el-form-item :label="$t('commons.table.group')" :prop="prop">
<el-select v-model="selectedValue">
<el-option
v-for="(group, index) in groups"
:key="index"
:label="group.name === 'Default' ? $t('commons.table.default') : group.name"
:value="group.id"
/>
</el-select>
</el-form-item>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue';
import { Group } from '@/api/interface/group';
import { getAgentGroupList } from '@/api/modules/group';
const props = defineProps({
modelValue: {
type: Number,
required: true,
},
groupType: {
type: String,
default: 'website',
},
prop: {
type: String,
default: '',
},
});
const emit = defineEmits(['update:modelValue']);
const groups = ref<Group.GroupInfo[]>([]);
const selectedValue = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value),
});
onMounted(async () => {
const res = await getAgentGroupList(props.groupType);
groups.value = res.data;
if (groups.value.length > 0 && !props.modelValue && props.modelValue == 0) {
selectedValue.value = groups.value[0].id;
}
});
</script>
@@ -8,16 +8,11 @@
<el-form-item :label="$t('website.alias')" prop="primaryDomain">
<el-input v-model="form.alias" disabled></el-input>
</el-form-item>
<el-form-item :label="$t('commons.table.group')" prop="webSiteGroupID">
<el-select v-model="form.webSiteGroupId">
<el-option
v-for="(group, index) in groups"
:key="index"
:label="group.name == 'Default' ? $t('commons.table.default') : group.name"
:value="group.id"
></el-option>
</el-select>
</el-form-item>
<GroupSelect
v-model="form.webSiteGroupId"
:prop="'webSiteGroupId'"
:groupType="'website'"
></GroupSelect>
<el-form-item :label="$t('website.remark')" prop="remark">
<el-input v-model="form.remark"></el-input>
</el-form-item>
@@ -35,14 +30,14 @@
</template>
<script lang="ts" setup>
import GroupSelect from '@/views/website/website/components/group/index.vue';
import { getWebsite, updateWebsite } from '@/api/modules/website';
import { Rules } from '@/global/form-rules';
import { computed, onMounted, reactive, ref } from 'vue';
import { FormInstance } from 'element-plus';
import i18n from '@/lang';
import { MsgError, MsgSuccess } from '@/utils/message';
import { getAgentGroupList } from '@/api/modules/group';
import { Group } from '@/api/interface/group';
const websiteForm = ref<FormInstance>();
const props = defineProps({
@@ -68,7 +63,6 @@ const rules = ref({
primaryDomain: [Rules.requiredInput],
webSiteGroupId: [Rules.requiredSelect],
});
const groups = ref<Group.GroupInfo[]>([]);
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
@@ -92,9 +86,6 @@ const submit = async (formEl: FormInstance | undefined) => {
});
};
const search = async () => {
const res = await getAgentGroupList('website');
groups.value = res.data;
getWebsite(websiteId.value).then((res) => {
form.primaryDomain = res.data.primaryDomain;
form.remark = res.data.remark;
@@ -28,16 +28,11 @@
:validate-on-rule-change="false"
v-loading="loading"
>
<el-form-item :label="$t('commons.table.group')" prop="webSiteGroupId">
<el-select v-model="website.webSiteGroupId">
<el-option
v-for="(group, index) in groups"
:key="index"
:label="group.name == 'Default' ? $t('commons.table.default') : group.name"
:value="group.id"
></el-option>
</el-select>
</el-form-item>
<GroupSelect
v-model="website.webSiteGroupId"
:prop="'webSiteGroupId'"
:groupType="'website'"
></GroupSelect>
<div v-if="website.type === 'deployment'">
<el-form-item prop="appType">
<el-radio-group v-model="website.appType" @change="changeAppType(website.appType)">
@@ -421,6 +416,13 @@
</template>
<script lang="ts" setup name="CreateWebSite">
import AppInstallForm from '@/views/app-store/detail/form/index.vue';
import SSLAlert from '@/views/website/website/create/site-alert/index.vue';
import DomainCreate from '@/views/website/website/domain-create/index.vue';
import TaskLog from '@/components/log/task/index.vue';
import Check from '../check/index.vue';
import GroupSelect from '@/views/website/website/components/group/index.vue';
import { App } from '@/api/interface/app';
import { searchApp, getAppInstalled } from '@/api/modules/app';
import {
@@ -435,23 +437,16 @@ import { Rules, checkNumberRange } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm, FormInstance } from 'element-plus';
import { reactive, ref } from 'vue';
import Check from '../check/index.vue';
import { MsgError, MsgSuccess } from '@/utils/message';
import { getAgentGroupList } from '@/api/modules/group';
import { Group } from '@/api/interface/group';
import { SearchRuntimes } from '@/api/modules/runtime';
import { Runtime } from '@/api/interface/runtime';
import { getRandomStr, getRuntimeLabel } from '@/utils/util';
import TaskLog from '@/components/log/task/index.vue';
import { getAppService } from '@/api/modules/app';
import { v4 as uuidv4 } from 'uuid';
import { dateFormatSimple, getProvider, getAccountName } from '@/utils/util';
import { Website } from '@/api/interface/website';
import DomainCreate from '@/views/website/website/domain-create/index.vue';
import { getPathByType } from '@/api/modules/files';
import { getWebsiteTypes } from '@/global/mimetype';
import AppInstallForm from '@/views/app-store/detail/form/index.vue';
import SSLAlert from '@/views/website/website/create/site-alert/index.vue';
const websiteForm = ref<FormInstance>();
@@ -462,7 +457,7 @@ const initData = () => ({
remark: '',
appType: 'installed',
appInstallId: undefined,
webSiteGroupId: 1,
webSiteGroupId: 0,
otherDomains: '',
proxy: '',
runtimeID: undefined,
@@ -539,7 +534,6 @@ const rules = ref<any>({
const open = ref(false);
const loading = ref(false);
const groups = ref<Group.GroupInfo[]>([]);
const acmeAccounts = ref();
const appInstalls = ref<App.AppInstalled[]>([]);
const appReq = reactive({
@@ -714,9 +708,6 @@ const acceptParams = async () => {
const dirRes = await getPathByType('websiteDir');
staticPath.value = dirRes.data + '/sites/';
const res = await getAgentGroupList('website');
groups.value = res.data;
website.value.webSiteGroupId = res.data[0].id;
runtimeResource.value = 'appstore';
runtimeReq.value = initRuntimeReq();
changeType(websiteType);
+24 -12
View File
@@ -222,6 +222,10 @@
:label="$t('commons.button.delete') + $t('menu.website')"
value="delete"
></el-option>
<el-option
:label="$t('commons.button.set') + $t('commons.table.group')"
value="group"
></el-option>
</el-select>
<el-button
class="ml-2"
@@ -261,6 +265,7 @@
<DefaultHtml ref="defaultHtmlRef" />
<TaskLog ref="taskLogRef" @close="search" />
<OpDialog ref="opRef" @search="openTaskLog" />
<BatchSetGroup ref="batchSetGroupRef" @close="search" />
</div>
</template>
@@ -276,6 +281,8 @@ import GroupDialog from '@/components/agent-group/index.vue';
import AppStatus from '@/components/app-status/index.vue';
import TaskLog from '@/components/log/task/index.vue';
import Domain from '@/views/website/website/domain/index.vue';
import BatchSetGroup from '@/views/website/website/batch-op/group.vue';
import i18n from '@/lang';
import { onMounted, reactive, ref, computed } from 'vue';
import { batchOpreate, opWebsite, searchWebsites, updateWebsite } from '@/api/modules/website';
@@ -336,6 +343,7 @@ const batchReq = reactive({
});
const taskLogRef = ref();
const opRef = ref();
const batchSetGroupRef = ref();
const paginationConfig = reactive({
cacheSizeKey: 'website-page-size',
@@ -609,18 +617,22 @@ const openTaskLog = () => {
};
const batchOp = () => {
const names = selects.value.map((item) => item.primaryDomain);
batchReq.ids = selects.value.map((item) => item.id);
const taskID = newUUID();
batchReq.taskID = taskID;
opRef.value.acceptParams({
names: names,
title: i18n.global.t('website.batchOpreate'),
api: batchOpreate,
msg: i18n.global.t('website.batchOpreateHelper', [i18n.global.t('commons.button.' + batchReq.operate)]),
params: batchReq,
noMsg: true,
});
if (batchReq.operate == 'group') {
batchSetGroupRef.value.acceptParams(selects.value.map((item) => item.id));
} else {
const names = selects.value.map((item) => item.primaryDomain);
batchReq.ids = selects.value.map((item) => item.id);
const taskID = newUUID();
batchReq.taskID = taskID;
opRef.value.acceptParams({
names: names,
title: i18n.global.t('website.batchOpreate'),
api: batchOpreate,
msg: i18n.global.t('website.batchOpreateHelper', [i18n.global.t('commons.button.' + batchReq.operate)]),
params: batchReq,
noMsg: true,
});
}
};
onMounted(() => {