동적 탭 관리 기능(회원별 탭 데이터 조회,생성 및 삭제)
This commit is contained in:
parent
2c49552a7b
commit
c9f30c4da3
|
|
@ -0,0 +1,52 @@
|
|||
package com.bpgroup.poc.admin.app.tab;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.admin.entity.Admin;
|
||||
import com.bpgroup.poc.admin.domain.admin.service.AdminService;
|
||||
import com.bpgroup.poc.admin.domain.tab.entity.Tab;
|
||||
import com.bpgroup.poc.admin.domain.tab.service.TabAddCommand;
|
||||
import com.bpgroup.poc.admin.domain.tab.service.TabService;
|
||||
import com.bpgroup.poc.admin.web.main.admin.tab.reqres.TabCreate;
|
||||
import com.bpgroup.poc.admin.web.main.admin.tab.reqres.TabDelete;
|
||||
import com.bpgroup.poc.admin.web.main.admin.tab.reqres.TabFind;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class TabAppService {
|
||||
|
||||
private final TabService tabService;
|
||||
private final AdminService adminService;
|
||||
|
||||
|
||||
public List<TabFind.Response> findTabList() {
|
||||
String userName = SecurityContextHolder.getContext().getAuthentication().getName();
|
||||
Admin findAdmin = adminService.find(userName).orElseThrow(() -> new IllegalArgumentException("Not found admin"));
|
||||
return tabService.find(findAdmin.getId());
|
||||
}
|
||||
|
||||
|
||||
public TabCreate.Response addTab(TabCreate.Request request) {
|
||||
String userName = SecurityContextHolder.getContext().getAuthentication().getName();
|
||||
Admin findAdmin = adminService.find(userName).orElseThrow(() -> new IllegalArgumentException("Not found admin"));
|
||||
Tab tab = tabService.create(
|
||||
TabAddCommand.of(
|
||||
request.getName(),
|
||||
request.getUrl(),
|
||||
findAdmin
|
||||
)
|
||||
);
|
||||
|
||||
return TabCreate.Response.success(tab);
|
||||
}
|
||||
|
||||
public TabDelete.Response deleteTab(TabDelete.Request request) {
|
||||
tabService.delete(request.getId());
|
||||
return TabDelete.Response.success();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.bpgroup.poc.admin.domain.tab.entity;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.BaseEntity;
|
||||
import com.bpgroup.poc.admin.domain.admin.entity.Admin;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@Entity
|
||||
@Table(name = "tab")
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
public class Tab extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "name", length = 100, nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "url", length = 100, nullable = false)
|
||||
private String url;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name="admin_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||
private Admin admin;
|
||||
|
||||
private Tab(String name, String url, Admin admin) {
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
this.admin = admin;
|
||||
}
|
||||
|
||||
public static Tab getNewInstanceForCreateOf(String name, String url, Admin admin) {
|
||||
return new Tab(name, url, admin);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.bpgroup.poc.admin.domain.tab.entity;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TabRepository extends JpaRepository<Tab, Long> {
|
||||
|
||||
List<Tab> findByAdminId(long adminId);
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.bpgroup.poc.admin.domain.tab.service;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.admin.entity.Admin;
|
||||
import com.bpgroup.poc.admin.domain.tab.entity.Tab;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class TabAddCommand {
|
||||
|
||||
@NotBlank
|
||||
private final String name;
|
||||
|
||||
@NotBlank
|
||||
private final String url;
|
||||
|
||||
@NotNull
|
||||
private final Admin admin;
|
||||
|
||||
public static TabAddCommand of(String name, String url, Admin admin) {
|
||||
return new TabAddCommand(name, url, admin);
|
||||
}
|
||||
|
||||
public Tab toEntity() {
|
||||
return Tab.getNewInstanceForCreateOf(name, url, admin);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.bpgroup.poc.admin.domain.tab.service;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.tab.entity.Tab;
|
||||
import com.bpgroup.poc.admin.domain.tab.entity.TabRepository;
|
||||
import com.bpgroup.poc.admin.web.main.admin.tab.reqres.TabFind;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Transactional
|
||||
public class TabService {
|
||||
|
||||
private final TabRepository tabRepository;
|
||||
|
||||
public List<TabFind.Response> find(Long adminId) {
|
||||
List<Tab> findTabList = tabRepository.findByAdminId(adminId);
|
||||
return findTabList.stream()
|
||||
.map(TabFind.Response::of)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Tab create(
|
||||
@NotNull @Valid TabAddCommand command
|
||||
) {
|
||||
return tabRepository.save(command.toEntity());
|
||||
}
|
||||
|
||||
public void delete( @NotNull Long id) {
|
||||
tabRepository.deleteById(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,6 +18,8 @@ import org.springframework.security.config.Customizer;
|
|||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
|
|
@ -67,7 +69,7 @@ public class SecurityConfig {
|
|||
|
||||
private void configureAuthorization(HttpSecurity http) throws Exception {
|
||||
http.authorizeHttpRequests(c -> c
|
||||
.requestMatchers("/css/**", "/images/**", "/js/**", "/font/**", "/favicon.ico").permitAll()
|
||||
.requestMatchers("/css/**", "/images/**", "/js/**", "/font/**", "/favicon.ico","/admin/tab/**").permitAll()
|
||||
.requestMatchers("/common/modal/**", "/csrf").permitAll()
|
||||
.requestMatchers(LOGIN_PATH, LOGOUT_PATH, ERROR_PATH).permitAll()
|
||||
.anyRequest()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package com.bpgroup.poc.admin.web.main.admin.tab;
|
||||
|
||||
import com.bpgroup.poc.admin.app.tab.TabAppService;
|
||||
import com.bpgroup.poc.admin.web.main.admin.tab.reqres.TabCreate;
|
||||
import com.bpgroup.poc.admin.web.main.admin.tab.reqres.TabDelete;
|
||||
import com.bpgroup.poc.admin.web.main.admin.tab.reqres.TabFind;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/admin")
|
||||
public class TabRestController {
|
||||
|
||||
private final TabAppService tabAppService;
|
||||
|
||||
@GetMapping("/tab/list")
|
||||
public ResponseEntity<?> findTab() {
|
||||
List<TabFind.Response> responses = tabAppService.findTabList();
|
||||
return ResponseEntity.ok(responses);
|
||||
}
|
||||
|
||||
@PostMapping("/tab/add")
|
||||
public ResponseEntity<?> addTab(
|
||||
@RequestBody @Valid TabCreate.Request request ,
|
||||
BindingResult bindingResult
|
||||
) {
|
||||
TabCreate.Response response = tabAppService.addTab(request);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@PostMapping("/tab/delete")
|
||||
public ResponseEntity<?> deleteTab(
|
||||
@RequestBody @Valid TabDelete.Request request
|
||||
) {
|
||||
TabDelete.Response response = tabAppService.deleteTab(request);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.bpgroup.poc.admin.web.main.admin.tab.reqres;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.tab.entity.Tab;
|
||||
import com.bpgroup.poc.admin.web.common.reqres.CommonResponse;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
|
||||
public class TabCreate {
|
||||
|
||||
@Data
|
||||
public static class Request {
|
||||
|
||||
@NotBlank
|
||||
private String name;
|
||||
|
||||
@NotBlank
|
||||
private String url;
|
||||
|
||||
private String LoginId;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public static class Response extends CommonResponse {
|
||||
public Long id;
|
||||
public static Response success(Tab tab) {
|
||||
Response response = new Response();
|
||||
response.resultCode = "0000";
|
||||
response.resultMessage = "Success";
|
||||
response.id = tab.getId();
|
||||
return response;
|
||||
}
|
||||
|
||||
public static Response fail() {
|
||||
Response response = new Response();
|
||||
response.resultCode = "9999";
|
||||
response.resultMessage = "Fail";
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.bpgroup.poc.admin.web.main.admin.tab.reqres;
|
||||
|
||||
import com.bpgroup.poc.admin.web.common.reqres.CommonResponse;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
public class TabDelete {
|
||||
|
||||
@Data
|
||||
public static class Request {
|
||||
@NotNull
|
||||
private Long id;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public static class Response extends CommonResponse {
|
||||
|
||||
public static TabDelete.Response success() {
|
||||
TabDelete.Response response = new TabDelete.Response();
|
||||
response.resultCode = "0000";
|
||||
response.resultMessage = "Success";
|
||||
return response;
|
||||
}
|
||||
|
||||
public static TabDelete.Response fail(String resultMessage) {
|
||||
TabDelete.Response response = new TabDelete.Response();
|
||||
response.resultCode = "9999";
|
||||
response.resultMessage = resultMessage;
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.bpgroup.poc.admin.web.main.admin.tab.reqres;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.tab.entity.Tab;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TabFind {
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public static class Response {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String url;
|
||||
private String loginId;
|
||||
|
||||
public static Response of(Tab tab) {
|
||||
Response response = new Response();
|
||||
response.id = tab.getId();
|
||||
response.name = tab.getName();
|
||||
response.url = tab.getUrl();
|
||||
response.loginId = tab.getAdmin().getLoginId();
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -876,7 +876,7 @@ body.login select {
|
|||
.content {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
padding: 70px 30px 50px 30px;
|
||||
padding: 15px 15px 15px 15px;
|
||||
}
|
||||
.con_wrap h3 {
|
||||
display: inline-block;
|
||||
|
|
@ -904,6 +904,15 @@ body.login select {
|
|||
|
||||
/* Tab Style */
|
||||
|
||||
.close-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: red;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tab-list {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,152 @@
|
|||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<th:block layout:fragment="script"></th:block>
|
||||
<th:block layout:fragment="tabActionScript">
|
||||
<script th:inline="javascript">
|
||||
let tabContentCache = {};
|
||||
|
||||
Reqhelper.reqGetJson('/admin/tab/list', function(data) {
|
||||
data.forEach(function(item) {
|
||||
loadTab(item.id, item.name, item.url);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
function loadTab(id,tabName, url) {
|
||||
const newTab = $('<li></li>').addClass('tab-item');
|
||||
const newTabLink = $('<a></a>').addClass('tab-link')
|
||||
.attr('id', `${id}-tab`)
|
||||
.attr('href', `#${id}`)
|
||||
.attr('role', 'tab')
|
||||
.attr('aria-controls', `${id}`)
|
||||
.attr('aria-selected', 'false')
|
||||
.text(tabName)
|
||||
.click(function(event) {
|
||||
openTab(event, id);
|
||||
loadTabContent(id, url);
|
||||
});
|
||||
|
||||
const closeButton = $('<span></span>').addClass('close-button')
|
||||
.text('X')
|
||||
.click(function(event) {
|
||||
$(`#${id}`).remove();
|
||||
$(`#${id}-tab`).parent().remove();
|
||||
event.stopPropagation();
|
||||
Reqhelper.reqPostJson('/admin/tab/delete', {
|
||||
id : id
|
||||
}, function() {
|
||||
console.log('Tab deleted successfully');
|
||||
}, function() {
|
||||
console.log('Failed to delete tab');
|
||||
})
|
||||
});
|
||||
|
||||
newTabLink.append(closeButton);
|
||||
newTab.append(newTabLink);
|
||||
$('#myTab').append(newTab);
|
||||
|
||||
const newTabContent = $('<div></div>').addClass('tab-pane')
|
||||
.attr('id', id)
|
||||
.attr('role', 'tabpanel')
|
||||
.attr('aria-labelledby', `${id}-tab`)
|
||||
.on('input', 'input, textarea', function() {
|
||||
tabContentCache[id] = newTabContent.html();
|
||||
});
|
||||
$('#myTabContent').append(newTabContent);
|
||||
}
|
||||
|
||||
function addTab(tabName,url) {
|
||||
Reqhelper.reqPostJson('/admin/tab/add', {
|
||||
name: tabName,
|
||||
url: url
|
||||
}, function(response) {
|
||||
console.log('Tab created successfully');
|
||||
const tabId = response.id;
|
||||
const newTab = $('<li></li>').addClass('tab-item');
|
||||
const newTabLink = $('<a></a>').addClass('tab-link')
|
||||
.attr('id', `${tabId}-tab`)
|
||||
.attr('href', `#${tabId}`)
|
||||
.attr('role', 'tab')
|
||||
.attr('aria-controls', `${tabId}`)
|
||||
.attr('aria-selected', 'false')
|
||||
.text(tabName)
|
||||
.click(function(event) {
|
||||
openTab(event, tabId);
|
||||
loadTabContent(tabId, url);
|
||||
});
|
||||
|
||||
const closeButton = $('<span></span>').addClass('close-button')
|
||||
.text('X')
|
||||
.click(function(event) {
|
||||
$(`#${tabId}`).remove();
|
||||
$(`#${tabId}-tab`).parent().remove();
|
||||
event.stopPropagation();
|
||||
Reqhelper.reqPostJson('/admin/tab/delete', {
|
||||
id : tabId
|
||||
}, function() {
|
||||
console.log('Tab deleted successfully');
|
||||
}, function() {
|
||||
console.log('Failed to delete tab');
|
||||
})
|
||||
});
|
||||
|
||||
newTabLink.append(closeButton);
|
||||
newTab.append(newTabLink);
|
||||
$('#myTab').append(newTab);
|
||||
|
||||
const newTabContent = $('<div></div>').addClass('tab-pane')
|
||||
.attr('id', tabId)
|
||||
.attr('role', 'tabpanel')
|
||||
.attr('aria-labelledby', `${tabId}-tab`)
|
||||
.on('input', 'input, textarea', function() {
|
||||
tabContentCache[tabId] = newTabContent.html();
|
||||
});
|
||||
$('#myTabContent').append(newTabContent);
|
||||
}, function() {
|
||||
console.log('Failed to create tab');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function loadTabContent(tabId, url) {
|
||||
if (tabContentCache[tabId]) {
|
||||
$('#' + tabId).html(tabContentCache[tabId]);
|
||||
} else {
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
tabContentCache[tabId] = data;
|
||||
$('#' + tabId).html(data);
|
||||
},
|
||||
error: function() {
|
||||
alert('Failed to load tab content');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function openTab(evt, tabId) {
|
||||
$('.tab-pane').css('display', 'none');
|
||||
$('.tab-link').each(function() {
|
||||
$(this).removeClass('active');
|
||||
});
|
||||
|
||||
$(`#${tabId}`).css('display', 'block');
|
||||
$(evt.currentTarget).addClass('active');
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$("#myTab").sortable({
|
||||
placeholder: "ui-state-highlight",
|
||||
update: function(event, ui) {
|
||||
|
||||
}
|
||||
});
|
||||
$("#myTab").disableSelection();
|
||||
});
|
||||
|
||||
</script>
|
||||
</th:block>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<ul class="sub_menu">
|
||||
<li th:each="menu : ${menuGroup.getMenuChildren()}">
|
||||
<a th:with="path= ${menu.getUri()}"
|
||||
th:classappend="${pathInfo.activeClass(path)}" onclick="addTab(this.textContent)"><span
|
||||
th:classappend="${pathInfo.activeClass(path)}" href="#" th:attr="data-path=${path}" onclick="addTab(this.textContent, this.getAttribute('data-path'))"><span
|
||||
th:text="${menu.getName()}"></span></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -16,75 +16,6 @@
|
|||
</th:block>
|
||||
</th:block>
|
||||
</body>
|
||||
<th:block layout:fragment="script">
|
||||
<script th:inline="javascript">
|
||||
let tabIdCounter = 1;
|
||||
|
||||
function addTab(tabName) {
|
||||
const tabId = 'tab' + tabIdCounter++;
|
||||
|
||||
const newTab = $('<li></li>').addClass('tab-item');
|
||||
const newTabLink = $('<a></a>').addClass('tab-link')
|
||||
.attr('id', `${tabId}-tab`)
|
||||
.attr('href', `#${tabId}`)
|
||||
.attr('role', 'tab')
|
||||
.attr('aria-controls', `${tabId}`)
|
||||
.attr('aria-selected', 'false')
|
||||
.text(tabName)
|
||||
.click(function(event) {
|
||||
openTab(event, tabId);
|
||||
loadTabContent(tabName, '/admin/management')
|
||||
});
|
||||
newTab.append(newTabLink);
|
||||
$('#myTab').append(newTab);
|
||||
|
||||
const newTabContent = $('<div></div>').addClass('tab-pane')
|
||||
.attr('id', tabId)
|
||||
.attr('role', 'tabpanel')
|
||||
.attr('aria-labelledby', `${tabId}-tab`)
|
||||
$('#myTabContent').append(newTabContent);
|
||||
}
|
||||
|
||||
function openTab(evt, tabId) {
|
||||
$('.tab-pane').css('display', 'none');
|
||||
$('.tab-link').each(function() {
|
||||
$(this).removeClass('active');
|
||||
});
|
||||
|
||||
$(`#${tabId}`).css('display', 'block');
|
||||
$(evt.currentTarget).addClass('active');
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$("#myTab").sortable({
|
||||
placeholder: "ui-state-highlight",
|
||||
update: function(event, ui) {
|
||||
|
||||
}
|
||||
});
|
||||
$("#myTab").disableSelection();
|
||||
});
|
||||
|
||||
function loadTabContent(tabName, url) {
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
success: function(response) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function adjustParentDivWidth() {
|
||||
var tabListWidth = $('.tab-list').width();
|
||||
|
||||
$('.parent-div').css('width', tabListWidth + 'px');
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
adjustParentDivWidth();
|
||||
$('.tab-item').on('click', adjustParentDivWidth);
|
||||
});
|
||||
</script>
|
||||
<th:block layout:fragment="script" th:replace="~{layout/common.html :: tabActionScript}">
|
||||
</th:block>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue