Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
vridge-docs
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
JIRA
JIRA
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
Gyeongho Park
vridge-docs
Commits
7f195dc7
Commit
7f195dc7
authored
Feb 24, 2023
by
shj
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[IMPLEMENT] 인덱스 관리 dialog 구현
parent
68d538d8
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
324 additions
and
100 deletions
+324
-100
GuideTestController.java
...com/vazil/vridge/docs/controller/GuideTestController.java
+10
-10
GuideTestService.java
.../java/com/vazil/vridge/docs/service/GuideTestService.java
+22
-4
guide.scss
docs-front/assets/scss/guide.scss
+2
-2
tree.vue
docs-front/components/tree.vue
+15
-16
default.vue
docs-front/layouts/default.vue
+275
-68
No files found.
docs-back/src/main/java/com/vazil/vridge/docs/controller/GuideTestController.java
View file @
7f195dc7
...
...
@@ -23,6 +23,12 @@ public class GuideTestController {
GuideService
guideService
;
GuideTestService
guideTestService
;
@GetMapping
public
ResponseEntity
<?>
getGuide
(
@RequestParam
(
value
=
"guideId"
)
String
guideId
)
throws
Exception
{
return
new
ResponseEntity
<>(
guideTestService
.
findGuideById
(
guideId
),
HttpStatus
.
OK
);
}
@ApiOperation
(
value
=
"모든 가이드 목차 조회"
)
@GetMapping
(
"/index"
)
public
ResponseEntity
<?>
findIndex
()
throws
Exception
{
...
...
@@ -45,8 +51,8 @@ public class GuideTestController {
}
@PutMapping
(
"/index"
)
public
ResponseEntity
<?>
updateGuideIndex
(
@RequestBody
Guide
guide
)
throws
Exception
{
return
new
ResponseEntity
<>(
guideService
.
updateGuide
(
guide
),
HttpStatus
.
OK
);
public
ResponseEntity
<?>
updateGuideIndex
(
@RequestBody
Guide
Test
guide
)
throws
Exception
{
return
new
ResponseEntity
<>(
guide
Test
Service
.
updateGuide
(
guide
),
HttpStatus
.
OK
);
}
...
...
@@ -56,16 +62,10 @@ public class GuideTestController {
}
@GetMapping
public
ResponseEntity
<?>
getGuide
(
@RequestParam
(
value
=
"guideId"
)
String
guideId
)
throws
Exception
{
return
new
ResponseEntity
<>(
guideTestService
.
findGuideById
(
guideId
),
HttpStatus
.
OK
);
}
@DeleteMapping
public
ResponseEntity
<?>
deleteGuideIndex
(
@RequestParam
(
value
=
"guideId"
)
String
guideId
,
@RequestParam
(
value
=
"cascade"
,
required
=
false
)
boolean
cascade
)
throws
Exception
{
return
new
ResponseEntity
<>(
guideService
.
deleteGuide
(
guideId
,
cascade
),
HttpStatus
.
OK
);
public
ResponseEntity
<?>
deleteGuideIndex
(
@RequestParam
(
value
=
"guideId"
)
String
guideId
)
throws
Exception
{
return
new
ResponseEntity
<>(
guideTestService
.
deleteGuide
(
guideId
),
HttpStatus
.
OK
);
}
@PutMapping
...
...
docs-back/src/main/java/com/vazil/vridge/docs/service/GuideTestService.java
View file @
7f195dc7
package
com
.
vazil
.
vridge
.
docs
.
service
;
import
com.vazil.vridge.docs.model.Guide
;
import
com.vazil.vridge.docs.model.GuideTest
;
import
com.vazil.vridge.docs.repository.GuideTestRepository
;
import
com.vazil.vridge.docs.utils.TimeManager
;
...
...
@@ -100,6 +101,10 @@ public class GuideTestService {
@Transactional
public
GuideTest
createGuide
(
GuideTest
guide
)
throws
Exception
{
// guide.setOrder(guide.getOrder() == null ? getLastOrder(guide.getParentId()) : 0);
// todo : 기존 인덱스와 위치가 중복될 경우, 기존 인덱스들 order + 1
guide
.
setLike
(
0
);
guide
.
setView
(
0
);
guide
.
setCreateDate
(
TimeManager
.
now
());
...
...
@@ -108,11 +113,24 @@ public class GuideTestService {
@Transactional
public
GuideTest
updateGuide
(
GuideTest
guide
)
throws
Exception
{
GuideTest
savedGuide
=
repository
.
findById
(
guide
.
getId
()).
orElseThrow
(
NoSuchElementException:
:
new
);
GuideTest
db
=
repository
.
findById
(
guide
.
getId
()).
orElseThrow
(
NoSuchElementException:
:
new
);
// todo : 기존 인덱스와 위치가 중복될 경우, 기존 인덱스들 order + 1
savedGuide
.
setOrder
(
guide
.
getOrder
());
savedGuide
.
setUpdateDate
(
TimeManager
.
now
());
return
repository
.
save
(
savedGuide
);
db
.
setLocale
(
guide
.
getLocale
());
db
.
setTitle
(
guide
.
getTitle
());
db
.
setOrder
(
guide
.
getOrder
());
db
.
setPath
(
guide
.
getPath
());
db
.
setDepth
(
guide
.
getDepth
());
return
repository
.
save
(
db
);
}
@Transactional
public
String
deleteGuide
(
String
guideId
)
throws
Exception
{
GuideTest
db
=
repository
.
findById
(
guideId
).
orElseThrow
(
NoSuchElementException:
:
new
);
repository
.
delete
(
db
);
return
db
.
getId
();
}
}
docs-front/assets/scss/guide.scss
View file @
7f195dc7
...
...
@@ -12,7 +12,7 @@
padding-right
:
240px
;
}
.guide-wrap
{
min-height
:
10
0vh
!
important
;
min-height
:
9
0vh
!
important
;
max-width
:
1280px
!
important
;
margin
:
0
auto
;
margin-top
:
36px
;
...
...
@@ -46,7 +46,7 @@
}
.guide-content
{
margin-top
:
20
0px
;
margin-top
:
13
0px
;
width
:
100%
!
important
;
h2
{
...
...
docs-front/components/tree.vue
View file @
7f195dc7
...
...
@@ -8,10 +8,10 @@
>
<v-list-item
v-if=
"item.children.length === 0"
@
click=
"click
Child
Event(item)"
@
click=
"click
Item
Event(item)"
:ripple=
"false"
:
class=
"isActiveMenu(item.id) ? 'active' : ''
"
:
style=
"isActiveMenu(item.id) ? 'border-left: 2px solid #1E88E5;' : 'border-left: 1px solid rgba(0,0,0,0.15);
'"
:
style=
"
{'border-left': depth !== 1
&&
'1px solid rgba(0,0,0,0.15)'}
"
:
class="selectedItem === item ? 'active' : '
'"
>
<v-list-item-title
v-text=
"item.title"
/>
</v-list-item>
...
...
@@ -20,9 +20,9 @@
v-else
v-model=
"item.active"
:ripple=
"false"
:class=
"isActiveMenu(item.id) ? 'active' : ''"
:sub-group=
"depth > 1 ? true : false"
:style=
"isActiveMenu(item.id) ? 'border-left: 2px solid #1E88E5;' : 'border-left: 1px solid rgba(0,0,0,0.15);'"
:class=
"selectedItem === item ? 'active' : ''"
:style=
"
{'border-left': depth !== 1
&&
'1px solid rgba(0,0,0,0.15)'}"
prepend-icon=""
>
<template
v-slot:appendIcon
>
...
...
@@ -32,7 +32,7 @@
<
template
v-slot:activator
>
<v-list-item-content
v-if=
"item"
@
click=
"click
Parent
Event(item)"
@
click=
"click
Item
Event(item)"
active-class=
"active"
>
<v-list-item-title
v-text=
"item.title"
/>
...
...
@@ -42,8 +42,8 @@
<tree
:items=
"item.children"
:depth=
"depth+1"
@
clickParentEvent=
"clickParentEvent
"
@
click
ChildEvent=
"clickChild
Event"
:selectedItem=
"selectedItem
"
@
click
ItemEvent=
"clickItem
Event"
/>
</v-list-group>
</div>
...
...
@@ -58,6 +58,9 @@ export default {
type
:
Array
,
required
:
true
,
},
selectedItem
:
{
type
:
Object
,
},
depth
:
{
type
:
[
String
,
Number
],
default
:
1
...
...
@@ -67,15 +70,11 @@ export default {
active
:
[],
}),
methods
:
{
clickParentEvent
(
item
){
this
.
$emit
(
'clickParentEvent'
,
item
)
},
clickChildEvent
(
item
){
this
.
$emit
(
'clickChildEvent'
,
item
)
clickItemEvent
(
item
){
this
.
$emit
(
'clickItemEvent'
,
item
)
},
isActiveMenu
(
id
)
{
return
this
.
$route
.
path
.
indexOf
(
id
)
>
-
1
},
watch
:
{
},
mounted
()
{
},
...
...
docs-front/layouts/default.vue
View file @
7f195dc7
...
...
@@ -65,8 +65,8 @@
<v-list
v-if=
"guideIndex && !loadingPageList"
>
<tree
:items=
"guideIndex"
@
clickParentEvent=
"openGuide
"
@
click
Child
Event=
"openGuide"
:selectedItem=
"selectedIndex
"
@
click
Item
Event=
"openGuide"
/>
</v-list>
...
...
@@ -98,11 +98,28 @@
<img
@
click=
"$router.push('/')"
src=
"/logo_v2/01.png"
cover
height=
"28"
/>
</span>
<v-btn
v-if=
"$store.state.editMode"
@
click=
"editDialogActive = true"
elevation=
"0"
color=
"primary"
class=
"ml-4"
>
인덱스 관리
</v-btn>
<v-btn
v-if=
"$store.state.editMode"
@
click=
"$store.state.editMode = false"
elevation=
"0"
color=
"primary"
class=
"ml-4"
>
편집모드 해제
</v-btn>
<v-spacer/>
<div
class=
"search-container"
v-show=
"showSearch"
>
<v-text-field
v-model=
"keyword"
@
keydown
.
enter=
"editOn"
:maxlength=
"50"
solo
dense
...
...
@@ -138,50 +155,108 @@
</v-row>
<v-progress-linear
absolute
bottom
:value=
"scrollPositionRate"
height=
"1"
background-color=
"#eee"
color=
"primary"
style=
"transition:none !important;"
></v-progress-linear>
</v-app-bar>
<v-main>
<client-only>
<Nuxt/>
<v-dialog
v-model=
"editDialogActive"
>
<div
class=
"edit-dialog-container"
>
<h2
class=
"pa-4 text-center"
>
인덱스 관리
</h2>
<!-- <vridge-dialog
ref="newPageDialog"
<v-icon
@
click=
"formData = null, editFormMethod = '', editDialogActive = false"
class=
"close-btn"
>
mdi-close
</v-icon>
<v-row
class=
"pa-10 pt-0 justify-space-around"
no-gutters
style=
"width: 100%; height: calc(100% - 68px);"
>
<v-col
cols=
"5"
style=
"height: 100%;"
>
<v-subheader>
인덱스 목록
</v-subheader>
<v-list
v-if=
"editModeList && !loadingPageList"
>
<tree
:items=
"editModeList"
:selectedItem=
"formData"
@
clickItemEvent=
"setEditTarget"
/>
</v-list>
</v-col>
<v-col
cols=
"5"
style=
"height: 100%;"
>
<div
style=
"height: 35%;"
>
<v-subheader>
인덱스 이상치
</v-subheader>
<v-list>
<v-list-item
v-for=
"(item, index) in editModeRemainList"
:key=
"index"
@
click=
"setEditTarget(item)"
>
<template
v-slot:content
>
<v-text-field v-model="createGuideTitle" label="타이틀" :maxlength="50"></v-text-field
>
<v-list-item-title>
{{item.title}}
</v-list-item-title>
</v-list-item>
</v-list
>
</div
>
<v-tooltip
bottom>
<template v-slot:activator="{on,attrs}">
<!-- form -->
<div
v-if=
"formData"
style=
"height: 60%;"
>
<v-subheader>
{{editFormMethod}}
<v-spacer></v-spacer>
<v-icon
size=
"16"
@
click=
"formData = null, editFormMethod = ''"
>
mdi-close
</v-icon>
</v-subheader>
<v-form>
<v-container
class=
"pt-6 text-center"
>
<v-select
:items=
"['ko','en']"
v-model=
"formData.locale"
label=
"Locale"
dense
/>
<v-text-field
v-model=
"formData.title"
label=
"Title"
type=
"text"
/>
<v-text-field
v-model=
"formData.order"
label=
"Order"
type=
"number"
/>
<v-text-field
v-model=
"formData.path"
label=
"Path (ex: 'propect/dashboard/')"
@
input=
"setDepthValue"
type=
"text"
/>
<v-text-field
v-model=
"formData.depth"
:disabled=
"editFormMethod !== '수정'"
label=
"Depth"
type=
"number"
/>
<v-btn
v-bind="attrs"
v-on="on"
@
click=
"editFormMethod === '추가' ? createIndex() : updateIndex()"
elevation=
"0"
icon
>
<v-icon small>mdi-comment-question</v-icon>
</v-btn>
</template>
<span>
예시)
https://api.github.com/repos/vazilcompany/vridge-docs/contents/guide/ko/overview/overview.md 일경우
>> overview/overview.md 입력
</span>
</v-tooltip>
<v-text-field v-model="createGuideContentKey" label="가이드 github 경로"></v-text-field>
color=
"primary"
>
저장
</v-btn>
<v-btn
@
click=
"removeIndex"
elevation=
"0"
color=
"primary"
:disabled=
"editFormMethod !== '수정'"
>
삭제
</v-btn>
</v-container>
</v-form>
</div>
</template>
<template v-slot:actionButton>
<v-btn
v-else
@
click=
"editFormMethod = '추가', formData={locale: 'ko', order: 0, depth: 1}"
elevation=
"0"
color=
"primary"
>
인덱스 추가
</v-btn>
</v-col>
</v-row>
</div>
</v-dialog>
</v-app-bar>
<v-btn @click="createGuide()">등록</v-bt
n>
</template
>
<
/vridge-dialog> --
>
<v-mai
n>
<client-only
>
<
Nuxt/
>
</client-only>
</v-main>
...
...
@@ -207,6 +282,7 @@ export default{
clipped
:
false
,
rawIndexList
:[],
guideIndex
:[],
selectedIndex
:
null
,
loadingPageList
:
false
,
targetParentId
:
null
,
createGuideTitle
:
''
,
...
...
@@ -215,6 +291,13 @@ export default{
call
:
0
,
scrollPositionRate
:
0
,
showSearch
:
false
,
// 인덱스 관리
editDialogActive
:
false
,
editModeList
:
[],
editModeRemainList
:
[],
editFormMethod
:
''
,
formData
:
null
,
}),
computed
:
{
searchedList
()
{
...
...
@@ -244,6 +327,7 @@ export default{
// 가이드 페이지 이동
openGuide
(
guide
)
{
this
.
selectedIndex
=
guide
this
.
$router
.
push
(
'/'
+
guide
.
id
)
},
...
...
@@ -252,10 +336,12 @@ export default{
await
this
.
$axios
.
get
(
'http://localhost:5000/test/index'
)
.
then
(
res
=>
{
this
.
rawIndexList
=
res
.
data
this
.
rawIndexList
=
JSON
.
parse
(
JSON
.
stringify
(
res
.
data
));
let
unclassifiedList
=
JSON
.
parse
(
JSON
.
stringify
(
res
.
data
));
this
.
guideIndex
=
[]
// 최상위 목차(depth==1) 추출(path에 슬래시 갯수가 1개)
this
.
rawIndex
List
.
forEach
(
e
=>
{
unclassified
List
.
forEach
(
e
=>
{
if
(
e
.
path
.
split
(
'/'
).
length
===
2
)
this
.
guideIndex
.
push
(
e
)
});
...
...
@@ -264,7 +350,7 @@ export default{
parentEl
.
children
=
[]
// depth==2 추출
this
.
rawIndex
List
.
forEach
(
rawEl
=>
{
unclassified
List
.
forEach
(
rawEl
=>
{
if
(
rawEl
.
path
!==
parentEl
.
path
&&
rawEl
.
path
.
includes
(
parentEl
.
path
)){
parentEl
.
children
.
push
(
rawEl
)
}
...
...
@@ -284,6 +370,7 @@ export default{
this
.
activeNav
()
this
.
loadingPageList
=
false
this
.
createEditModeList
()
// this.activeGuideTreeIndex(this.guideIndex)
},
...
...
@@ -358,50 +445,112 @@ export default{
},
// 가이드 생성
createGuide
()
{
if
(
!
this
.
createGuideTitle
||
!
this
.
createGuideContentKey
)
{
alert
(
'내용 누락'
)
return
editOn
(){
if
(
this
.
keyword
===
'열려라 문'
){
this
.
editDialogActive
=
true
this
.
$store
.
state
.
editMode
=
true
}
},
createEditModeList
(){
let
unclassifiedList
=
JSON
.
parse
(
JSON
.
stringify
(
this
.
rawIndexList
));
this
.
editModeList
=
[]
this
.
editModeRemainList
=
[]
// depth 이상치 확인
unclassifiedList
.
forEach
(
index
=>
{
if
(
!
index
.
depth
||
index
.
depth
<
1
){
this
.
editModeRemainList
.
push
(
index
)
delete
unclassifiedList
[
unclassifiedList
.
indexOf
(
index
)]
}
})
unclassifiedList
=
unclassifiedList
.
filter
(
el
=>
el
)
// empty 값 삭제
// depth 1 리스트 생성
unclassifiedList
.
forEach
(
index
=>
{
if
(
index
.
depth
===
1
){
this
.
editModeList
.
push
(
index
)
delete
unclassifiedList
[
unclassifiedList
.
indexOf
(
index
)]
// empty 처리
}
let
guide
=
{
parentId
:
this
.
targetParentId
,
title
:
this
.
createGuideTitle
,
contentKey
:
this
.
createGuideContentKey
})
unclassifiedList
=
unclassifiedList
.
filter
(
el
=>
el
)
// empty 값 삭제
// depth 2 이상인 인덱스들 트리구조 생성
this
.
setEditModeChildren
(
this
.
editModeList
,
unclassifiedList
)
unclassifiedList
=
unclassifiedList
.
filter
(
el
=>
el
)
// empty 값 삭제
this
.
editModeRemainList
.
push
(...
unclassifiedList
)
},
setEditModeChildren
(
targetList
,
unclassifiedList
){
targetList
.
forEach
(
el
=>
{
el
.
children
=
[]
unclassifiedList
.
forEach
(
ucsfEl
=>
{
if
(
ucsfEl
.
path
.
indexOf
(
el
.
path
)
>
-
1
&&
ucsfEl
.
depth
===
el
.
depth
+
1
){
el
.
children
.
push
(
ucsfEl
)
delete
unclassifiedList
[
unclassifiedList
.
indexOf
(
ucsfEl
)]
}
})
this
.
$axios
.
post
(
'/guide/index'
,
guide
)
if
(
el
.
children
.
length
>
0
){
this
.
setEditModeChildren
(
el
.
children
,
unclassifiedList
)
}
})
},
setEditTarget
(
item
){
this
.
formData
=
item
this
.
editFormMethod
=
'수정'
},
setDepthValue
(){
this
.
formData
.
depth
=
this
.
formData
.
path
.
split
(
'/'
).
length
-
1
},
createIndex
()
{
const
keys
=
[
'locale'
,
'title'
,
'order'
,
'path'
,
'depth'
]
if
(
keys
.
some
(
key
=>
this
.
formData
[
key
]
===
(
undefined
||
null
))){
alert
(
'값을 모두 입력해주세요.'
)
return
}
this
.
$axios
.
post
(
'http://localhost:5000/test'
,
this
.
formData
)
.
then
(
res
=>
{
this
.
$refs
.
newPageDialog
.
dialog
=
false
this
.
getGuideIndex
()
})
.
catch
(
err
=>
{
console
.
log
(
err
)
})
},
updateIndex
()
{
const
keys
=
[
'locale'
,
'title'
,
'order'
,
'path'
,
'depth'
]
openCreateDialog
(
parentId
,
prefix
)
{
this
.
$refs
.
newPageDialog
.
dialog
=
true
this
.
createGuideTitle
=
''
this
.
createGuideContentKey
=
prefix
?
prefix
+
'/'
:
''
this
.
targetParentId
=
parentId
},
if
(
keys
.
some
(
key
=>
this
.
formData
[
key
]
===
(
undefined
||
null
))){
alert
(
'값을 모두 입력해주세요.'
)
return
}
removeGuide
(
guideId
){
this
.
$axios
.
delete
(
"/guide"
,
{
this
.
$axios
.
put
(
'http://localhost:5000/test/index'
,
this
.
formData
)
.
then
(
res
=>
{
this
.
getGuideIndex
()
})
.
catch
(
err
=>
{
console
.
log
(
err
)
})
},
removeIndex
(){
this
.
$axios
.
delete
(
"http://localhost:5000/test"
,
{
params
:{
guideId
:
guideId
,
cascade
:
true
guideId
:
this
.
formData
.
id
,
}
})
.
then
(
res
=>
{
this
.
formData
=
null
this
.
editFormMethod
=
''
this
.
getGuideIndex
()
})
.
catch
(
err
=>
{
console
.
log
(
err
)
})
},
//가이드 네비게이션 이동
goGuideNav
(
position
)
{
window
.
scrollTo
({
top
:
position
,
behavior
:
'smooth'
})
...
...
@@ -488,13 +637,15 @@ export default{
for
(
let
i
=
0
;
i
<
activeTargetPaths
.
length
;
++
i
){
for
(
let
j
=
0
;
j
<
guideList
.
length
;
++
j
){
if
(
guideList
[
j
].
path
===
activeTargetPaths
[
i
]){
this
.
selectedIndex
=
guideList
[
j
]
guideList
[
j
].
active
=
true
guideList
=
guideList
[
j
].
children
}
break
}
}
}
},
},
mounted
(){
this
.
getGuideIndex
()
window
.
addEventListener
(
"scroll"
,
this
.
scrollEvent
);
...
...
@@ -528,6 +679,62 @@ export default{
</
script
>
<
style
lang=
"scss"
>
.v-dialog
{
width
:
70vw
!
important
;
height
:
80vh
;
background-color
:
white
;
.edit-dialog-container
{
height
:
100%
;
position
:
relative
;
.close-btn
{
font-size
:
26px
;
position
:
absolute
;
top
:
20px
;
right
:
20px
;
}
.col
{
.v-subheader
{
font-size
:
1rem
;
}
.v-list
{
height
:
calc
(
100%
-
48px
);
background-color
:
rgba
(
0
,
0
,
0
,
0
.034
);
border-radius
:
10px
;
overflow
:
auto
;
.v-list-item
{
&
.active
,
&
.v-list-item--active
{
*
{
font-weight
:
bold
!
important
;
color
:
#1E88E5
;
}
background-color
:
rgba
(
128
,
128
,
128
,
0
.07
)
!
important
;
}
}
}
&
:nth-child
(
2
)
{
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-between
;
.v-list
{
height
:
calc
(
100%
-
48px
);
}
}
.v-form
{
height
:
calc
(
100%
-
48px
);
background-color
:
rgba
(
0
,
0
,
0
,
0
.034
);
border-radius
:
10px
;
overflow
:
auto
;
}
}
}
}
html
.search-on
{
@media
screen
and
(
max-width
:
600px
)
{
overflow
:
hidden
!
important
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment