diff --git a/blog-backend/src/main/java/cn/hamster3/application/blog/controller/SettingController.java b/blog-backend/src/main/java/cn/hamster3/application/blog/controller/SettingController.java
index 9598d5e..1e92459 100644
--- a/blog-backend/src/main/java/cn/hamster3/application/blog/controller/SettingController.java
+++ b/blog-backend/src/main/java/cn/hamster3/application/blog/controller/SettingController.java
@@ -4,6 +4,7 @@ import cn.hamster3.application.blog.service.ISettingService;
import cn.hamster3.application.blog.vo.PageableVO;
import cn.hamster3.application.blog.vo.ResponseVO;
import cn.hamster3.application.blog.vo.setting.SettingInfoResponseVO;
+import cn.hamster3.application.blog.vo.setting.SettingUpdateRequireVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -12,6 +13,8 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
+import java.util.Map;
+
@Tag(name = "SettingController", description = "网站设置相关接口")
@RestController
@RequestMapping(value = "/api/v1/settings", produces = MediaType.APPLICATION_JSON_VALUE)
@@ -40,13 +43,19 @@ public class SettingController {
return settingService.getSettingInfoList(PageRequest.of(page, Math.min(size, 100)));
}
- @PutMapping(value = "/{id}/", consumes = MediaType.TEXT_PLAIN_VALUE)
+ @PutMapping(value = "/{id}/")
@Operation(summary = "更改网站设置")
public ResponseVO updateSetting(
@Parameter(description = "设置ID") @PathVariable String id,
- @Parameter(description = "设置内容") @RequestBody String content
+ @Parameter(description = "设置内容") @RequestBody SettingUpdateRequireVO requireVO
) {
- return settingService.updateSetting(id, content);
+ return settingService.updateSetting(id, requireVO);
+ }
+
+ @PutMapping(value = "/")
+ @Operation(summary = "批量更改网站设置")
+ public ResponseVO updateSettings(@Parameter(description = "设置内容") @RequestBody Map data) {
+ return settingService.updateSettings(data);
}
@DeleteMapping("/{id}/")
diff --git a/blog-backend/src/main/java/cn/hamster3/application/blog/service/ISettingService.java b/blog-backend/src/main/java/cn/hamster3/application/blog/service/ISettingService.java
index c7ffd94..b227a92 100644
--- a/blog-backend/src/main/java/cn/hamster3/application/blog/service/ISettingService.java
+++ b/blog-backend/src/main/java/cn/hamster3/application/blog/service/ISettingService.java
@@ -3,9 +3,12 @@ package cn.hamster3.application.blog.service;
import cn.hamster3.application.blog.vo.PageableVO;
import cn.hamster3.application.blog.vo.ResponseVO;
import cn.hamster3.application.blog.vo.setting.SettingInfoResponseVO;
+import cn.hamster3.application.blog.vo.setting.SettingUpdateRequireVO;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.domain.Pageable;
+import java.util.Map;
+
public interface ISettingService {
@NotNull ResponseVO getSettingInfo(@NotNull String id);
@@ -13,7 +16,9 @@ public interface ISettingService {
@NotNull ResponseVO> getSettingInfoList(@NotNull Pageable pageable);
- @NotNull ResponseVO updateSetting(@NotNull String id, @NotNull String content);
+ @NotNull ResponseVO updateSetting(@NotNull String id, @NotNull SettingUpdateRequireVO requireVO);
+
+ @NotNull ResponseVO updateSettings(@NotNull Map data);
@NotNull ResponseVO deleteSetting(@NotNull String id);
}
diff --git a/blog-backend/src/main/java/cn/hamster3/application/blog/service/impl/SettingService.java b/blog-backend/src/main/java/cn/hamster3/application/blog/service/impl/SettingService.java
index dc18558..42963f5 100644
--- a/blog-backend/src/main/java/cn/hamster3/application/blog/service/impl/SettingService.java
+++ b/blog-backend/src/main/java/cn/hamster3/application/blog/service/impl/SettingService.java
@@ -8,12 +8,15 @@ import cn.hamster3.application.blog.util.BlogUtils;
import cn.hamster3.application.blog.vo.PageableVO;
import cn.hamster3.application.blog.vo.ResponseVO;
import cn.hamster3.application.blog.vo.setting.SettingInfoResponseVO;
+import cn.hamster3.application.blog.vo.setting.SettingUpdateRequireVO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
+import java.util.Map;
+
@Slf4j
@Service
public class SettingService implements ISettingService {
@@ -49,7 +52,8 @@ public class SettingService implements ISettingService {
}
@Override
- public @NotNull ResponseVO updateSetting(@NotNull String id, @NotNull String content) {
+ public @NotNull ResponseVO updateSetting(@NotNull String id, @NotNull SettingUpdateRequireVO requireVO) {
+ String content = requireVO.getContent();
ResponseVO check = BlogUtils.checkAdminPermission();
if (check != null) {
return check;
@@ -65,6 +69,27 @@ public class SettingService implements ISettingService {
return ResponseVO.success();
}
+ @Override
+ public @NotNull ResponseVO updateSettings(@NotNull Map data) {
+ ResponseVO check = BlogUtils.checkAdminPermission();
+ if (check != null) {
+ return check;
+ }
+ for (Map.Entry entry : data.entrySet()) {
+ String id = entry.getKey();
+ String content = entry.getValue();
+ if (!settingRepo.existsByIdIgnoreCase(id)) {
+ SettingEntity entity = new SettingEntity();
+ entity.setId(id);
+ entity.setContent(content);
+ settingRepo.save(entity);
+ } else {
+ settingRepo.updateContentByIdIgnoreCase(content, id);
+ }
+ }
+ return ResponseVO.success();
+ }
+
@Override
public @NotNull ResponseVO deleteSetting(@NotNull String id) {
ResponseVO check = BlogUtils.checkAdminPermission();
diff --git a/blog-backend/src/main/java/cn/hamster3/application/blog/vo/setting/SettingUpdateRequireVO.java b/blog-backend/src/main/java/cn/hamster3/application/blog/vo/setting/SettingUpdateRequireVO.java
index f7cc021..564f281 100644
--- a/blog-backend/src/main/java/cn/hamster3/application/blog/vo/setting/SettingUpdateRequireVO.java
+++ b/blog-backend/src/main/java/cn/hamster3/application/blog/vo/setting/SettingUpdateRequireVO.java
@@ -1,6 +1,5 @@
package cn.hamster3.application.blog.vo.setting;
-import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -9,6 +8,5 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
public class SettingUpdateRequireVO {
- @NotBlank(message = "网站设置内容不能为空!")
private String content;
}
diff --git a/blog-frontend/index.html b/blog-frontend/index.html
index b2514fd..d0e3fd4 100644
--- a/blog-frontend/index.html
+++ b/blog-frontend/index.html
@@ -7,6 +7,8 @@
网站标题
+
diff --git a/blog-frontend/src/App.vue b/blog-frontend/src/App.vue
index 2cbe318..a063db6 100644
--- a/blog-frontend/src/App.vue
+++ b/blog-frontend/src/App.vue
@@ -1,22 +1,36 @@
-
+
-
+
-
+
diff --git a/blog-frontend/src/api/api.ts b/blog-frontend/src/api/api.ts
index 63d7c1e..e51705d 100644
--- a/blog-frontend/src/api/api.ts
+++ b/blog-frontend/src/api/api.ts
@@ -1,8 +1,14 @@
-import { UserControllerApiFactory, BlogControllerApiFactory, SettingControllerApiFactory, AttachControllerApiFactory } from "@/swagger"
+import { UserControllerApiFactory, BlogControllerApiFactory, SettingControllerApiFactory, AttachControllerApiFactory, Configuration } from "@/swagger"
+
+export const apiConfig: Configuration = {
+ baseOptions: {
+ withCredentials: true
+ }
+}
export const api = {
- AttachController: AttachControllerApiFactory(),
- UserController: UserControllerApiFactory(),
- BlogController: BlogControllerApiFactory(),
- SettingController: SettingControllerApiFactory()
+ AttachController: AttachControllerApiFactory(apiConfig),
+ UserController: UserControllerApiFactory(apiConfig),
+ BlogController: BlogControllerApiFactory(apiConfig),
+ SettingController: SettingControllerApiFactory(apiConfig)
}
diff --git a/blog-frontend/src/api/global-store.ts b/blog-frontend/src/api/global-store.ts
index be90438..c4afd37 100644
--- a/blog-frontend/src/api/global-store.ts
+++ b/blog-frontend/src/api/global-store.ts
@@ -1,25 +1,38 @@
import { reactive, ref } from "vue"
+
import type { UserInfoResponseVO } from "@/swagger"
-export const globalStore = {
- currentUserInfo: ref({})
-}
+export const globalStore = reactive<{
+ currentUserInfo?: UserInfoResponseVO
+}>({})
-export const siteSetting = {
- title: ref("网站标题"),
- footer: ref(""),
- customNavigationMenu: ref>()
+ }>>(),
+})
+
+export function loadGlobalStore() {
+ globalStore.currentUserInfo = JSON.parse(window.localStorage.getItem("currentUserInfo") || "{}")
+ console.log("local storage user info: ", globalStore.currentUserInfo)
}
-export function load() {
- globalStore.currentUserInfo.value = JSON.parse(window.localStorage.getItem("currentUserInfo") || "{}")
- console.log("local storage user info: ", globalStore.currentUserInfo.value)
-}
-
-export function save() {
- window.localStorage.setItem("currentUserInfo", JSON.stringify(globalStore.currentUserInfo.value))
- console.log(window.localStorage)
+export function saveGlobalStore() {
+ if (globalStore.currentUserInfo?.id) {
+ window.localStorage.setItem("currentUserInfo", JSON.stringify(globalStore.currentUserInfo))
+ } else {
+ window.localStorage.removeItem("currentUserInfo")
+ }
}
\ No newline at end of file
diff --git a/blog-frontend/src/components/FooterComponent.vue b/blog-frontend/src/components/FooterComponent.vue
index 8e5e00a..da07646 100644
--- a/blog-frontend/src/components/FooterComponent.vue
+++ b/blog-frontend/src/components/FooterComponent.vue
@@ -1,6 +1,19 @@
@@ -13,7 +26,11 @@ import { siteSetting } from '@/api';
|
Editor
- {{ siteSetting.footer.value }}
+ {{ siteSetting.footer }}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/blog-frontend/src/components/HeaderComponent.vue b/blog-frontend/src/components/HeaderComponent.vue
index 057eae8..09e844f 100644
--- a/blog-frontend/src/components/HeaderComponent.vue
+++ b/blog-frontend/src/components/HeaderComponent.vue
@@ -6,6 +6,7 @@ import { RouterLink } from 'vue-router'
import { UserInfoResponseVORoleEnum } from "@/swagger";
import { api, globalStore, siteSetting } from "@/api"
import { updateCurrentUserInfo } from '@/utils';
+import router from '@/router';
const currentUserInfo = globalStore.currentUserInfo
const navigationMenu = siteSetting.customNavigationMenu
@@ -14,53 +15,48 @@ const menuIndex = ref(document.location.pathname)
onMounted(() => {
// 获取站点标题
- api.SettingController.getSettingContent("site.title")
+ api.SettingController.getSettingContent(siteSetting.keys.site.title)
.then(response => {
let vo = response.data;
if (vo.code === 200) {
let title = vo.data ?? "网站标题"
- siteSetting.title.value = title
+ siteSetting.title = title
document.title = title
}
})
- // 获取站点页脚
- api.SettingController.getSettingContent("site.footer")
- .then(response => {
- let vo = response.data;
- if (vo.code === 200) {
- let title = vo.data ?? ""
- siteSetting.footer.value = title
- }
- })
- if (!currentUserInfo.value.id) {
+ if (currentUserInfo?.id) {
// 获取当前登录的用户信息
updateCurrentUserInfo();
}
})
+
+function onSelectMenu(index: string) {
+ router.push(index)
+}
-
+
- {{ siteSetting.title.value }}
+ {{ siteSetting.title }}
- 标签
+ 标签
-
- {{ customMenu.text }}
+
+ {{ customMenu.text }}
-
- 登录
+
+ 登录
-
- 注册
+
+ 注册
- 管理面板
+ 管理面板
diff --git a/blog-frontend/src/components/manage/SiteManageComponent.vue b/blog-frontend/src/components/manage/SiteManageComponent.vue
index e1e8e9d..3e6ccba 100644
--- a/blog-frontend/src/components/manage/SiteManageComponent.vue
+++ b/blog-frontend/src/components/manage/SiteManageComponent.vue
@@ -2,14 +2,27 @@
-
-
+
+
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/blog-frontend/src/swagger/apis/setting-controller-api.ts b/blog-frontend/src/swagger/apis/setting-controller-api.ts
index b00e6fa..e70f9c0 100644
--- a/blog-frontend/src/swagger/apis/setting-controller-api.ts
+++ b/blog-frontend/src/swagger/apis/setting-controller-api.ts
@@ -20,6 +20,7 @@ import { ResponseVOPageableVOSettingInfoResponseVO } from '../models';
import { ResponseVOSettingInfoResponseVO } from '../models';
import { ResponseVOString } from '../models';
import { ResponseVOVoid } from '../models';
+import { SettingUpdateRequireVO } from '../models';
/**
* SettingControllerApi - axios parameter creator
* @export
@@ -201,12 +202,12 @@ export const SettingControllerApiAxiosParamCreator = function (configuration?: C
/**
*
* @summary 更改网站设置
- * @param {string} body
+ * @param {SettingUpdateRequireVO} body
* @param {string} id 设置ID
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- updateSetting: async (body: string, id: string, options: AxiosRequestConfig = {}): Promise => {
+ updateSetting: async (body: SettingUpdateRequireVO, id: string, options: AxiosRequestConfig = {}): Promise => {
// verify required parameter 'body' is not null or undefined
if (body === null || body === undefined) {
throw new RequiredError('body','Required parameter body was null or undefined when calling updateSetting.');
@@ -227,7 +228,50 @@ export const SettingControllerApiAxiosParamCreator = function (configuration?: C
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
- localVarHeaderParameter['Content-Type'] = 'text/plain';
+ localVarHeaderParameter['Content-Type'] = 'application/json';
+
+ const query = new URLSearchParams(localVarUrlObj.search);
+ for (const key in localVarQueryParameter) {
+ query.set(key, localVarQueryParameter[key]);
+ }
+ for (const key in options.params) {
+ query.set(key, options.params[key]);
+ }
+ localVarUrlObj.search = (new URLSearchParams(query)).toString();
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+ localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+ const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+ localVarRequestOptions.data = needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+ return {
+ url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+ options: localVarRequestOptions,
+ };
+ },
+ /**
+ *
+ * @summary 批量更改网站设置
+ * @param {{ [key: string]: string; }} body
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ updateSettings: async (body: { [key: string]: string; }, options: AxiosRequestConfig = {}): Promise => {
+ // verify required parameter 'body' is not null or undefined
+ if (body === null || body === undefined) {
+ throw new RequiredError('body','Required parameter body was null or undefined when calling updateSettings.');
+ }
+ const localVarPath = `/api/v1/settings/`;
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
+ const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+ let baseOptions;
+ if (configuration) {
+ baseOptions = configuration.baseOptions;
+ }
+ const localVarRequestOptions :AxiosRequestConfig = { method: 'PUT', ...baseOptions, ...options};
+ const localVarHeaderParameter = {} as any;
+ const localVarQueryParameter = {} as any;
+
+ localVarHeaderParameter['Content-Type'] = 'application/json';
const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) {
@@ -316,18 +360,32 @@ export const SettingControllerApiFp = function(configuration?: Configuration) {
/**
*
* @summary 更改网站设置
- * @param {string} body
+ * @param {SettingUpdateRequireVO} body
* @param {string} id 设置ID
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- async updateSetting(body: string, id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> {
+ async updateSetting(body: SettingUpdateRequireVO, id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> {
const localVarAxiosArgs = await SettingControllerApiAxiosParamCreator(configuration).updateSetting(body, id, options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs);
};
},
+ /**
+ *
+ * @summary 批量更改网站设置
+ * @param {{ [key: string]: string; }} body
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async updateSettings(body: { [key: string]: string; }, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> {
+ const localVarAxiosArgs = await SettingControllerApiAxiosParamCreator(configuration).updateSettings(body, options);
+ return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+ const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+ return axios.request(axiosRequestArgs);
+ };
+ },
}
};
@@ -381,14 +439,24 @@ export const SettingControllerApiFactory = function (configuration?: Configurati
/**
*
* @summary 更改网站设置
- * @param {string} body
+ * @param {SettingUpdateRequireVO} body
* @param {string} id 设置ID
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- async updateSetting(body: string, id: string, options?: AxiosRequestConfig): Promise> {
+ async updateSetting(body: SettingUpdateRequireVO, id: string, options?: AxiosRequestConfig): Promise> {
return SettingControllerApiFp(configuration).updateSetting(body, id, options).then((request) => request(axios, basePath));
},
+ /**
+ *
+ * @summary 批量更改网站设置
+ * @param {{ [key: string]: string; }} body
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async updateSettings(body: { [key: string]: string; }, options?: AxiosRequestConfig): Promise> {
+ return SettingControllerApiFp(configuration).updateSettings(body, options).then((request) => request(axios, basePath));
+ },
};
};
@@ -447,13 +515,24 @@ export class SettingControllerApi extends BaseAPI {
/**
*
* @summary 更改网站设置
- * @param {string} body
+ * @param {SettingUpdateRequireVO} body
* @param {string} id 设置ID
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SettingControllerApi
*/
- public async updateSetting(body: string, id: string, options?: AxiosRequestConfig) : Promise> {
+ public async updateSetting(body: SettingUpdateRequireVO, id: string, options?: AxiosRequestConfig) : Promise> {
return SettingControllerApiFp(this.configuration).updateSetting(body, id, options).then((request) => request(this.axios, this.basePath));
}
+ /**
+ *
+ * @summary 批量更改网站设置
+ * @param {{ [key: string]: string; }} body
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ * @memberof SettingControllerApi
+ */
+ public async updateSettings(body: { [key: string]: string; }, options?: AxiosRequestConfig) : Promise> {
+ return SettingControllerApiFp(this.configuration).updateSettings(body, options).then((request) => request(this.axios, this.basePath));
+ }
}
diff --git a/blog-frontend/src/swagger/models/index.ts b/blog-frontend/src/swagger/models/index.ts
index 44d139e..193a3a0 100644
--- a/blog-frontend/src/swagger/models/index.ts
+++ b/blog-frontend/src/swagger/models/index.ts
@@ -21,6 +21,7 @@ export * from './response-vostring';
export * from './response-vouser-info-response-vo';
export * from './response-vovoid';
export * from './setting-info-response-vo';
+export * from './setting-update-require-vo';
export * from './user-create-require-vo';
export * from './user-entity';
export * from './user-info-response-vo';
diff --git a/blog-frontend/src/swagger/models/setting-update-require-vo.ts b/blog-frontend/src/swagger/models/setting-update-require-vo.ts
new file mode 100644
index 0000000..a4dbb47
--- /dev/null
+++ b/blog-frontend/src/swagger/models/setting-update-require-vo.ts
@@ -0,0 +1,26 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * OpenAPI definition
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * OpenAPI spec version: v0
+ *
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+/**
+ * 设置内容
+ * @export
+ * @interface SettingUpdateRequireVO
+ */
+export interface SettingUpdateRequireVO {
+ /**
+ *
+ * @type {string}
+ * @memberof SettingUpdateRequireVO
+ */
+ content?: string;
+}
diff --git a/blog-frontend/src/utils/index.ts b/blog-frontend/src/utils/index.ts
index 7904d1e..e953c2a 100644
--- a/blog-frontend/src/utils/index.ts
+++ b/blog-frontend/src/utils/index.ts
@@ -1,16 +1,16 @@
-import { api, globalStore, save } from "@/api";
-
+import { api, globalStore, saveGlobalStore } from "@/api";
export function updateCurrentUserInfo() {
- api.UserController.getCurrentUserInfo({ withCredentials: true })
+ api.UserController.getCurrentUserInfo()
.then(resp => {
let vo = resp.data
if (vo.code === 200) {
- globalStore.currentUserInfo.value = vo.data || {}
+ globalStore.currentUserInfo = vo.data
console.log("current user info: ", vo.data)
} else {
- console.error("ckeck current user info failed!")
+ globalStore.currentUserInfo = undefined
+ console.warn("ckeck current user info failed!")
}
- save()
+ saveGlobalStore()
})
}
\ No newline at end of file
diff --git a/blog-frontend/src/views/LoginView.vue b/blog-frontend/src/views/LoginView.vue
index 762eff0..39ea492 100644
--- a/blog-frontend/src/views/LoginView.vue
+++ b/blog-frontend/src/views/LoginView.vue
@@ -9,7 +9,7 @@ const form = reactive({
})
const onSubmit = () => {
- api.UserController.loginUser(form, { withCredentials: true })
+ api.UserController.loginUser(form)
.then(resp => {
let vo = resp.data;
if (vo.code === 200) {
@@ -31,7 +31,7 @@ const onSubmit = () => {
-
+
登录
diff --git a/blog-frontend/src/views/ManagePaneView.vue b/blog-frontend/src/views/ManagePaneView.vue
index cb2795e..9e37baa 100644
--- a/blog-frontend/src/views/ManagePaneView.vue
+++ b/blog-frontend/src/views/ManagePaneView.vue
@@ -1,9 +1,16 @@