mirror of
				https://github.com/1Panel-dev/1Panel.git
				synced 2025-11-01 03:37:19 +08:00 
			
		
		
		
	feat: 增加网站分组管理
This commit is contained in:
		
							parent
							
								
									9621d6ea1f
								
							
						
					
					
						commit
						42134e315b
					
				
					 9 changed files with 198 additions and 22 deletions
				
			
		|  | @ -5,10 +5,10 @@ import ( | |||
| 	"github.com/1Panel-dev/1Panel/backend/app/dto" | ||||
| 	"github.com/1Panel-dev/1Panel/backend/constant" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| func (b *BaseApi) GetWebGroups(c *gin.Context) { | ||||
| 
 | ||||
| 	list, err := websiteGroupService.GetGroups() | ||||
| 	if err != nil { | ||||
| 		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | ||||
|  | @ -18,16 +18,46 @@ func (b *BaseApi) GetWebGroups(c *gin.Context) { | |||
| } | ||||
| 
 | ||||
| func (b *BaseApi) CreateWebGroup(c *gin.Context) { | ||||
| 
 | ||||
| 	var req dto.WebSiteGroupCreate | ||||
| 	if err := c.ShouldBindJSON(&req); err != nil { | ||||
| 		helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err := websiteGroupService.CreateGroup(req); err != nil { | ||||
| 		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | ||||
| 		return | ||||
| 	} | ||||
| 	helper.SuccessWithData(c, nil) | ||||
| } | ||||
| 
 | ||||
| func (b *BaseApi) UpdateWebGroup(c *gin.Context) { | ||||
| 	var req dto.WebSiteGroupUpdate | ||||
| 	if err := c.ShouldBindJSON(&req); err != nil { | ||||
| 		helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) | ||||
| 		return | ||||
| 	} | ||||
| 	if err := websiteGroupService.UpdateGroup(req); err != nil { | ||||
| 		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | ||||
| 		return | ||||
| 	} | ||||
| 	helper.SuccessWithData(c, nil) | ||||
| } | ||||
| 
 | ||||
| func (b *BaseApi) DeleteWebGroup(c *gin.Context) { | ||||
| 
 | ||||
| 	groupId := c.Param("groupId") | ||||
| 	if groupId == "" { | ||||
| 		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, nil) | ||||
| 		return | ||||
| 	} | ||||
| 	id, err := strconv.Atoi(groupId) | ||||
| 	if err != nil { | ||||
| 		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | ||||
| 		return | ||||
| 	} | ||||
| 	if err := websiteGroupService.DeleteGroup(uint(id)); err != nil { | ||||
| 		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | ||||
| 		return | ||||
| 	} | ||||
| 	helper.SuccessWithData(c, nil) | ||||
| } | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ func (a *WebsiteGroupRouter) InitWebsiteGroupRouter(Router *gin.RouterGroup) { | |||
| 	baseApi := v1.ApiGroupApp.BaseApi | ||||
| 	{ | ||||
| 		groupRouter.GET("", baseApi.GetWebGroups) | ||||
| 		//groupRouter.GET("", baseApi.GetGroups) | ||||
| 		groupRouter.POST("", baseApi.CreateWebGroup) | ||||
| 		groupRouter.PUT("", baseApi.UpdateWebGroup) | ||||
| 		groupRouter.DELETE("/:groupId", baseApi.DeleteWebGroup) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -46,4 +46,9 @@ export namespace WebSite { | |||
|         name: string; | ||||
|         default: boolean; | ||||
|     } | ||||
| 
 | ||||
|     export interface GroupOp { | ||||
|         name: string; | ||||
|         id?: number; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -6,10 +6,6 @@ export const SearchWebSites = (req: WebSite.WebSiteSearch) => { | |||
|     return http.post<ResPage<WebSite.WebSite>>(`/websites/search`, req); | ||||
| }; | ||||
| 
 | ||||
| export const ListGroups = () => { | ||||
|     return http.get<WebSite.Group[]>(`/websites/groups`); | ||||
| }; | ||||
| 
 | ||||
| export const CreateWebsite = (req: WebSite.WebSiteCreateReq) => { | ||||
|     return http.post<any>(`/websites`, req); | ||||
| }; | ||||
|  | @ -17,3 +13,19 @@ export const CreateWebsite = (req: WebSite.WebSiteCreateReq) => { | |||
| export const DeleteWebsite = (req: WebSite.WebSiteDel) => { | ||||
|     return http.post<any>(`/websites/del`, req); | ||||
| }; | ||||
| 
 | ||||
| export const ListGroups = () => { | ||||
|     return http.get<WebSite.Group[]>(`/websites/groups`); | ||||
| }; | ||||
| 
 | ||||
| export const CreateGroup = (req: WebSite.GroupOp) => { | ||||
|     return http.post<any>(`/websites/groups`, req); | ||||
| }; | ||||
| 
 | ||||
| export const UpdateGroup = (req: WebSite.GroupOp) => { | ||||
|     return http.put<any>(`/websites/groups`, req); | ||||
| }; | ||||
| 
 | ||||
| export const DeleteGroup = (id: number) => { | ||||
|     return http.delete<any>(`/websites/groups/${id}`); | ||||
| }; | ||||
|  |  | |||
|  | @ -44,6 +44,7 @@ const checkName = (rule: any, value: any, callback: any) => { | |||
| interface CommonRule { | ||||
|     requiredInput: FormItemRule; | ||||
|     requiredSelect: FormItemRule; | ||||
|     requiredSelectBusiness: FormItemRule; | ||||
|     name: FormItemRule; | ||||
|     password: FormItemRule; | ||||
|     email: FormItemRule; | ||||
|  | @ -63,6 +64,14 @@ export const Rules: CommonRule = { | |||
|         message: i18n.global.t('commons.rule.requiredSelect'), | ||||
|         trigger: 'change', | ||||
|     }, | ||||
|     requiredSelectBusiness: { | ||||
|         required: true, | ||||
|         min: 1, | ||||
|         max: 65535, | ||||
|         type: 'number', | ||||
|         message: i18n.global.t('commons.rule.requiredSelect'), | ||||
|         trigger: 'change', | ||||
|     }, | ||||
|     name: { | ||||
|         required: true, | ||||
|         validator: checkName, | ||||
|  |  | |||
|  | @ -673,12 +673,14 @@ export default { | |||
|         alias: '代号', | ||||
|         remark: '备注', | ||||
|         group: '分组', | ||||
|         groupSetting: '分组管理', | ||||
|         app: '应用', | ||||
|         app_new: '新装应用', | ||||
|         app_installed: '已装应用', | ||||
|         appNew: '新装应用', | ||||
|         appInstalled: '已装应用', | ||||
|         create: '创建网站', | ||||
|         delete: '删除网站', | ||||
|         deleteApp: '删除应用', | ||||
|         deleteBackup: '删除备份', | ||||
|         domain: '域名', | ||||
|     }, | ||||
| }; | ||||
|  |  | |||
|  | @ -21,16 +21,16 @@ | |||
|                 <el-form-item prop="appType"> | ||||
|                     <el-radio-group v-model="website.appType" @change="changeAppType(website.appType)"> | ||||
|                         <el-radio :label="'installed'" :value="'installed'"> | ||||
|                             {{ $t('website.app_installed') }} | ||||
|                             {{ $t('website.appInstalled') }} | ||||
|                         </el-radio> | ||||
|                         <el-radio :label="'new'"> | ||||
|                             {{ $t('website.app_new') }} | ||||
|                             {{ $t('website.appNew') }} | ||||
|                         </el-radio> | ||||
|                     </el-radio-group> | ||||
|                 </el-form-item> | ||||
|                 <el-form-item | ||||
|                     v-if="website.appType == 'installed'" | ||||
|                     :label="$t('website.app_installed')" | ||||
|                     :label="$t('website.appInstalled')" | ||||
|                     prop="appInstallID" | ||||
|                 > | ||||
|                     <el-select v-model="website.appInstallID"> | ||||
|  | @ -125,13 +125,13 @@ const website = ref({ | |||
|     remark: '', | ||||
|     domains: [], | ||||
|     appType: 'installed', | ||||
|     appInstallID: null, | ||||
|     appInstallID: 0, | ||||
|     webSiteGroupID: 1, | ||||
|     otherDomains: '', | ||||
|     appinstall: { | ||||
|         appID: null, | ||||
|         appID: 0, | ||||
|         name: '', | ||||
|         appDetailID: null, | ||||
|         appDetailID: 0, | ||||
|         params: {}, | ||||
|         version: '', | ||||
|     }, | ||||
|  | @ -140,12 +140,12 @@ let rules = ref({ | |||
|     primaryDomain: [Rules.requiredInput], | ||||
|     alias: [Rules.requiredInput], | ||||
|     type: [Rules.requiredInput], | ||||
|     webSiteGroupID: [Rules.requiredInput], | ||||
|     appInstallID: [Rules.requiredInput], | ||||
|     webSiteGroupID: [Rules.requiredSelectBusiness], | ||||
|     appInstallID: [Rules.requiredSelectBusiness], | ||||
|     appType: [Rules.requiredInput], | ||||
|     appinstall: { | ||||
|         name: [Rules.requiredInput], | ||||
|         appID: [Rules.requiredInput], | ||||
|         appID: [Rules.requiredSelectBusiness], | ||||
|         params: {}, | ||||
|     }, | ||||
| }); | ||||
|  |  | |||
							
								
								
									
										107
									
								
								frontend/src/views/website/project/group/index.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								frontend/src/views/website/project/group/index.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,107 @@ | |||
| <template> | ||||
|     <el-dialog v-model="open" :title="$t('website.groupSetting')" width="40%" :before-close="handleClose"> | ||||
|         <ComplexTable :data="data" @search="search()"> | ||||
|             <template #toolbar> | ||||
|                 <el-button type="primary" plain @click="openCreate">{{ $t('commons.button.create') }}</el-button> | ||||
|             </template> | ||||
|             <el-table-column :label="$t('commons.table.name')" prop="name"> | ||||
|                 <template #default="{ row }"> | ||||
|                     <span v-if="!row.edit" @click="row.edit = true">{{ row.name }}</span> | ||||
|                     <el-input v-if="row.edit" v-model="row.name" @blur="row.edit = false"></el-input> | ||||
|                 </template> | ||||
|             </el-table-column> | ||||
|             <el-table-column :label="$t('commons.table.operate')"> | ||||
|                 <template #default="{ row, $index }"> | ||||
|                     <el-button link :disabled="row.default" type="primary" @click="saveGroup(row)"> | ||||
|                         {{ $t('commons.button.save') }} | ||||
|                     </el-button> | ||||
|                     <el-button link :disabled="row.default" type="primary" @click="deleteGroup($index)"> | ||||
|                         {{ $t('commons.button.delete') }} | ||||
|                     </el-button> | ||||
|                 </template> | ||||
|             </el-table-column> | ||||
|         </ComplexTable> | ||||
|     </el-dialog> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { ref } from 'vue'; | ||||
| import i18n from '@/lang'; | ||||
| import ComplexTable from '@/components/complex-table/index.vue'; | ||||
| import { ListGroups, CreateGroup, DeleteGroup, UpdateGroup } from '@/api/modules/website'; | ||||
| import { ElMessage } from 'element-plus'; | ||||
| 
 | ||||
| interface groupData { | ||||
|     id: number; | ||||
|     name: string; | ||||
|     edit: boolean; | ||||
|     default: boolean; | ||||
| } | ||||
| 
 | ||||
| let open = ref(false); | ||||
| let data = ref<groupData[]>([]); | ||||
| 
 | ||||
| const handleClose = () => { | ||||
|     open.value = false; | ||||
|     data.value = []; | ||||
| }; | ||||
| 
 | ||||
| const search = () => { | ||||
|     ListGroups().then((res) => { | ||||
|         for (const d of res.data) { | ||||
|             const g = { | ||||
|                 id: d.id, | ||||
|                 name: d.name, | ||||
|                 default: d.default, | ||||
|                 edit: false, | ||||
|             }; | ||||
|             data.value.push(g); | ||||
|         } | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| const saveGroup = (create: groupData) => { | ||||
|     const group = { | ||||
|         name: create.name, | ||||
|         id: create.id, | ||||
|     }; | ||||
|     if (create.id == 0) { | ||||
|         CreateGroup(group).then(() => { | ||||
|             ElMessage.success(i18n.global.t('commons.msg.createSuccess')); | ||||
|         }); | ||||
|     } else { | ||||
|         UpdateGroup(group).then(() => { | ||||
|             ElMessage.success(i18n.global.t('commons.msg.updateSuccess')); | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| const acceptParams = async () => { | ||||
|     open.value = true; | ||||
|     search(); | ||||
| }; | ||||
| 
 | ||||
| const openCreate = () => { | ||||
|     const g = { | ||||
|         id: 0, | ||||
|         name: '', | ||||
|         default: false, | ||||
|         edit: true, | ||||
|     }; | ||||
|     data.value.push(g); | ||||
| }; | ||||
| 
 | ||||
| const deleteGroup = (index: number) => { | ||||
|     const group = data.value[index]; | ||||
| 
 | ||||
|     if (group.id > 0) { | ||||
|         DeleteGroup(group.id).then(() => { | ||||
|             data.value.splice(index, 1); | ||||
|             ElMessage.success(i18n.global.t('commons.msg.deleteSuccess')); | ||||
|         }); | ||||
|     } else { | ||||
|         data.value.splice(index, 1); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| defineExpose({ acceptParams }); | ||||
| </script> | ||||
|  | @ -2,7 +2,8 @@ | |||
|     <LayoutContent :header="'网站'"> | ||||
|         <ComplexTable :data="data" @search="search()"> | ||||
|             <template #toolbar> | ||||
|                 <el-button type="primary" plain @click="openCreate">{{ '新建网站' }}</el-button> | ||||
|                 <el-button type="primary" plain @click="openCreate">{{ $t('commons.button.create') }}</el-button> | ||||
|                 <el-button type="primary" plain @click="openGroup">{{ $t('website.group') }}</el-button> | ||||
|                 <el-button type="primary" plain>{{ '修改默认页' }}</el-button> | ||||
|                 <el-button type="primary" plain>{{ '默认站点' }}</el-button> | ||||
|             </template> | ||||
|  | @ -25,6 +26,7 @@ | |||
|         </ComplexTable> | ||||
|         <CreateWebSite ref="createRef" @close="search"></CreateWebSite> | ||||
|         <DeleteWebsite ref="deleteRef" @close="search"></DeleteWebsite> | ||||
|         <WebSiteGroup ref="groupRef"></WebSiteGroup> | ||||
|     </LayoutContent> | ||||
| </template> | ||||
| 
 | ||||
|  | @ -32,15 +34,18 @@ | |||
| import LayoutContent from '@/layout/layout-content.vue'; | ||||
| import ComplexTable from '@/components/complex-table/index.vue'; | ||||
| import { onMounted, reactive, ref } from '@vue/runtime-core'; | ||||
| import router from '@/routers'; | ||||
| import CreateWebSite from './create/index.vue'; | ||||
| import DeleteWebsite from './delete/index.vue'; | ||||
| import WebSiteGroup from './group/index.vue'; | ||||
| import { SearchWebSites } from '@/api/modules/website'; | ||||
| import i18n from '@/lang'; | ||||
| import { WebSite } from '@/api/interface/website'; | ||||
| 
 | ||||
| import i18n from '@/lang'; | ||||
| import router from '@/routers'; | ||||
| 
 | ||||
| const createRef = ref(); | ||||
| const deleteRef = ref(); | ||||
| const groupRef = ref(); | ||||
| 
 | ||||
| const paginationConfig = reactive({ | ||||
|     currentPage: 1, | ||||
|  | @ -86,6 +91,10 @@ const openCreate = () => { | |||
|     createRef.value.acceptParams(); | ||||
| }; | ||||
| 
 | ||||
| const openGroup = () => { | ||||
|     groupRef.value.acceptParams(); | ||||
| }; | ||||
| 
 | ||||
| onMounted(() => { | ||||
|     search(); | ||||
| }); | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue