Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
pptist
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
viitto
pptist
Commits
4d0d9ec2
Commit
4d0d9ec2
authored
Jun 25, 2025
by
罗超
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
临时提交
parent
3e0854f4
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
629 additions
and
7 deletions
+629
-7
package.json
package.json
+13
-1
common.css
src/assets/styles/common.css
+54
-0
main.ts
src/main.ts
+1
-0
京都双宿~双温泉+双京都+独家安排:京都夜游+京都和服体验+绝景氛围 (百万湛藍粉蝶花+梦幻海洋紫藤花+富士山芝樱庆典)+独家祕境:京都三千院+世外桃源:美秀美术馆+世界唯一:榻榻米星巴克+东京浅草寺(川航 阪东7日) 【6个正餐+夜游京都】.txt
...院+世外桃源:美秀美术馆+世界唯一:榻榻米星巴克+东京浅草寺(川航 阪东7日) 【6个正餐+夜游京都】.txt
+0
-0
icon.ts
src/plugins/icon.ts
+17
-1
router.ts
src/router/router.ts
+10
-3
common.ts
src/utils/common.ts
+5
-0
Index.vue
src/views/project/Editor/Index.vue
+31
-0
Editor.vue
src/views/project/components/Editor/Editor.vue
+266
-0
EditorDayList.vue
src/views/project/components/Editor/EditorDayList.vue
+92
-0
EditorHeader.vue
src/views/project/components/Editor/EditorHeader.vue
+55
-0
TextEditor.vue
src/views/project/components/Editor/TextEditor.vue
+54
-0
TourItinerary.vue
src/views/project/components/Editor/TourItinerary.vue
+23
-0
vue.config.js
vue.config.js
+8
-2
No files found.
package.json
View file @
4d0d9ec2
...
@@ -15,6 +15,18 @@
...
@@ -15,6 +15,18 @@
"
@hcaptcha/vue3-hcaptcha
"
:
"^1.3.0"
,
"
@hcaptcha/vue3-hcaptcha
"
:
"^1.3.0"
,
"
@icon-park/vue-next
"
:
"^1.4.2"
,
"
@icon-park/vue-next
"
:
"^1.4.2"
,
"
@mapbox/mapbox-gl-language
"
:
"^1.0.1"
,
"
@mapbox/mapbox-gl-language
"
:
"^1.0.1"
,
"
@tiptap/extension-blockquote
"
:
"^2.8.0"
,
"
@tiptap/extension-bubble-menu
"
:
"^2.9.1"
,
"
@tiptap/extension-heading
"
:
"^2.8.0"
,
"
@tiptap/extension-highlight
"
:
"^2.8.0"
,
"
@tiptap/extension-horizontal-rule
"
:
"^2.8.0"
,
"
@tiptap/extension-link
"
:
"^2.9.1"
,
"
@tiptap/extension-text-align
"
:
"^2.8.0"
,
"
@tiptap/extension-typography
"
:
"^2.7.4"
,
"
@tiptap/extension-underline
"
:
"^2.8.0"
,
"
@tiptap/pm
"
:
"^2.7.4"
,
"
@tiptap/starter-kit
"
:
"^2.7.4"
,
"
@tiptap/vue-3
"
:
"^2.7.4"
,
"
@types/ali-oss
"
:
"^6.16.11"
,
"
@types/ali-oss
"
:
"^6.16.11"
,
"
@types/opentype.js
"
:
"^1.3.8"
,
"
@types/opentype.js
"
:
"^1.3.8"
,
"
@types/psd
"
:
"^3.4.3"
,
"
@types/psd
"
:
"^3.4.3"
,
...
@@ -64,7 +76,7 @@
...
@@ -64,7 +76,7 @@
"
svg-pathdata
"
:
"^6.0.3"
,
"
svg-pathdata
"
:
"^6.0.3"
,
"
tinycolor2
"
:
"^1.6.0"
,
"
tinycolor2
"
:
"^1.6.0"
,
"
tippy.js
"
:
"^6.3.7"
,
"
tippy.js
"
:
"^6.3.7"
,
"
vue
"
:
"^3.
4.21
"
,
"
vue
"
:
"^3.
5.9
"
,
"
vue-konva
"
:
"^3.0.2"
,
"
vue-konva
"
:
"^3.0.2"
,
"
vue-picture-cropper
"
:
"^0.7.0"
,
"
vue-picture-cropper
"
:
"^0.7.0"
,
"
vue-router
"
:
"^4.0.13"
,
"
vue-router
"
:
"^4.0.13"
,
...
...
src/assets/styles/common.css
View file @
4d0d9ec2
...
@@ -75,6 +75,21 @@ page {
...
@@ -75,6 +75,21 @@ page {
line-height
:
1.375rem
;
line-height
:
1.375rem
;
letter-spacing
:
0.00714em
;
letter-spacing
:
0.00714em
;
}
}
h3
{
font-size
:
18px
!important
;
}
hr
{
border
:
none
;
border-top
:
1px
solid
#e0e0e0
!important
;
}
blockquote
{
border-left
:
5px
solid
#e6e7e8
!important
;
padding-left
:
30px
!important
;
margin
:
16px
0
!important
;
font-size
:
14px
!important
;
color
:
#999
!important
;
font-style
:
normal
!important
;
}
.text-success
{
.text-success
{
color
:
#20af6e
!important
;
color
:
#20af6e
!important
;
}
}
...
@@ -202,6 +217,38 @@ page {
...
@@ -202,6 +217,38 @@ page {
.borders-light
{
.borders-light
{
border
:
1px
solid
#F1f2f4
;
border
:
1px
solid
#F1f2f4
;
}
}
.card-h2
{
margin
:
0
;
color
:
#000E1A
;
font-weight
:
700
;
font-size
:
24px
;
line-height
:
32px
;
}
.draw-close
{
position
:
absolute
;
width
:
54px
;
height
:
54px
;
line-height
:
54px
;
text-align
:
center
;
cursor
:
pointer
;
border-top-left-radius
:
8px
;
border-bottom-left-radius
:
8px
;
background-color
:
var
(
--el-color-primary
);
left
:
-54px
;
top
:
100px
;
font-size
:
30px
;
color
:
#f1f2f4
;
}
.customer-drawer
{
overflow
:
unset
!important
;
}
.customer-drawer
.el-drawer__header
{
margin
:
0
;
padding
:
20px
;
}
.customer-drawer
.el-drawer__body
{
padding
:
0px
;
}
.transparent-borders
{
.transparent-borders
{
border
:
1px
solid
transparent
;
border
:
1px
solid
transparent
;
}
}
...
@@ -313,6 +360,10 @@ page {
...
@@ -313,6 +360,10 @@ page {
.q-pa-xl
{
.q-pa-xl
{
padding
:
30px
;
padding
:
30px
;
}
}
.q-pa-none
{
padding
:
0px
!important
;
}
.display-none
{
.display-none
{
display
:
none
;
display
:
none
;
}
}
...
@@ -371,6 +422,9 @@ page {
...
@@ -371,6 +422,9 @@ page {
.q-ma-no
{
.q-ma-no
{
padding
:
0
!important
padding
:
0
!important
}
}
.q-ma-none
{
margin
:
0
!important
;
}
.q-ma-md
{
.q-ma-md
{
margin
:
10px
margin
:
10px
}
}
...
...
src/main.ts
View file @
4d0d9ec2
...
@@ -22,6 +22,7 @@ import router from './router'
...
@@ -22,6 +22,7 @@ import router from './router'
import
ElementPlus
from
'element-plus'
import
ElementPlus
from
'element-plus'
import
zhCn
from
'element-plus/es/locale/lang/zh-cn'
import
zhCn
from
'element-plus/es/locale/lang/zh-cn'
const
app
=
createApp
(
App
)
const
app
=
createApp
(
App
)
app
.
use
(
router
)
app
.
use
(
router
)
const
pinia
=
createPinia
()
const
pinia
=
createPinia
()
...
...
src/mocks/京都双宿~双温泉+双京都+独家安排:京都夜游+京都和服体验+绝景氛围 (百万湛藍粉蝶花+梦幻海洋紫藤花+富士山芝樱庆典)+独家祕境:京都三千院+世外桃源:美秀美术馆+世界唯一:榻榻米星巴克+东京浅草寺(川航 阪东7日) 【6个正餐+夜游京都】.txt
0 → 100644
View file @
4d0d9ec2
This diff is collapsed.
Click to expand it.
src/plugins/icon.ts
View file @
4d0d9ec2
...
@@ -154,7 +154,15 @@ import {
...
@@ -154,7 +154,15 @@ import {
WaterfallsH
,
WaterfallsH
,
ListCheckbox
,
ListCheckbox
,
DocAdd
,
DocAdd
,
FileConversion
FileConversion
,
Switch
,
Mark
,
TitleLevel
,
CenterAlignment
,
LinkTwo
,
Unlink
,
DividingLine
,
DeleteFour
}
from
'@icon-park/vue-next'
}
from
'@icon-park/vue-next'
export
interface
Icons
{
export
interface
Icons
{
...
@@ -162,6 +170,13 @@ export interface Icons {
...
@@ -162,6 +170,13 @@ export interface Icons {
}
}
export
const
icons
:
Icons
=
{
export
const
icons
:
Icons
=
{
IconMark
:
Mark
,
IconDividingLine
:
DividingLine
,
IconUnlink
:
Unlink
,
IconLinkTwo
:
LinkTwo
,
IconCenterAlignment
:
CenterAlignment
,
IconTitleLevel
:
TitleLevel
,
IconSwitch
:
Switch
,
IconFileConversion
:
FileConversion
,
IconFileConversion
:
FileConversion
,
IconDocAdd
:
DocAdd
,
IconDocAdd
:
DocAdd
,
IconPlayOne
:
PlayOne
,
IconPlayOne
:
PlayOne
,
...
@@ -314,6 +329,7 @@ export const icons: Icons = {
...
@@ -314,6 +329,7 @@ export const icons: Icons = {
IconGridFour
:
GridFour
,
IconGridFour
:
GridFour
,
IconWaterfallsH
:
WaterfallsH
,
IconWaterfallsH
:
WaterfallsH
,
IconListCheckbox
:
ListCheckbox
,
IconListCheckbox
:
ListCheckbox
,
IconDeleteFour
:
DeleteFour
}
}
export
default
{
export
default
{
...
...
src/router/router.ts
View file @
4d0d9ec2
...
@@ -399,27 +399,34 @@ const routes: RouteRecordRaw[] = [
...
@@ -399,27 +399,34 @@ const routes: RouteRecordRaw[] = [
},
},
{
{
path
:
'/project'
,
path
:
'/project'
,
component
:
()
=>
import
(
'@/views/
p
roject/Layout.vue'
),
component
:
()
=>
import
(
'@/views/
P
roject/Layout.vue'
),
meta
:{
meta
:{
title
:
'工作台'
title
:
'工作台'
},
},
children
:[
children
:[
{
{
path
:
'/project'
,
path
:
'/project'
,
component
:
()
=>
import
(
'@/views/
p
roject/Index.vue'
),
component
:
()
=>
import
(
'@/views/
P
roject/Index.vue'
),
meta
:{
meta
:{
title
:
'概览'
title
:
'概览'
}
}
},
},
{
{
path
:
'/project/create/trip'
,
path
:
'/project/create/trip'
,
component
:
()
=>
import
(
'@/views/
p
roject/trip.vue'
),
component
:
()
=>
import
(
'@/views/
P
roject/trip.vue'
),
meta
:{
meta
:{
title
:
'行程制作'
title
:
'行程制作'
}
}
},
},
]
]
},
},
{
path
:
"/proj/e/:projectid"
,
component
:()
=>
import
(
'@/views/Project/Editor/Index.vue'
),
meta
:{
title
:
'编辑行程'
}
},
{
{
path
:
'/p/show'
,
path
:
'/p/show'
,
component
:
()
=>
import
(
'@/views/Product/Show.vue'
),
component
:
()
=>
import
(
'@/views/Product/Show.vue'
),
...
...
src/utils/common.ts
View file @
4d0d9ec2
...
@@ -196,6 +196,7 @@ export const getHtmlPlainText = (html_str:string) => {
...
@@ -196,6 +196,7 @@ export const getHtmlPlainText = (html_str:string) => {
}
}
export
const
dateFormat
=
(
value
:
number
|
string
|
Date
=
Date
.
now
(),
format
=
'YYYY-MM-DD HH:mm:ss'
):
string
=>
{
export
const
dateFormat
=
(
value
:
number
|
string
|
Date
=
Date
.
now
(),
format
=
'YYYY-MM-DD HH:mm:ss'
):
string
=>
{
const
week
=
[
'日'
,
'一'
,
'二'
,
'三'
,
'四'
,
'五'
,
'六'
];
if
(
typeof
value
===
'number'
||
typeof
value
===
'string'
)
{
if
(
typeof
value
===
'number'
||
typeof
value
===
'string'
)
{
var
date
=
new
Date
(
value
)
var
date
=
new
Date
(
value
)
}
else
{
}
else
{
...
@@ -230,6 +231,10 @@ export const dateFormat = (value: number|string|Date = Date.now(), format = 'YYY
...
@@ -230,6 +231,10 @@ export const dateFormat = (value: number|string|Date = Date.now(), format = 'YYY
const
s
=
String
(
date
.
getSeconds
())
const
s
=
String
(
date
.
getSeconds
())
showTime
=
showTime
.
includes
(
'ss'
)
?
showTime
.
replace
(
'ss'
,
s
.
padStart
(
2
,
'0'
))
:
showTime
.
replace
(
's'
,
s
)
showTime
=
showTime
.
includes
(
'ss'
)
?
showTime
.
replace
(
'ss'
,
s
.
padStart
(
2
,
'0'
))
:
showTime
.
replace
(
's'
,
s
)
}
}
if
(
showTime
.
includes
(
'w'
))
{
const
w
=
`
${
week
[
date
.
getDay
()]}
`
showTime
=
showTime
.
includes
(
'ww'
)
?
showTime
.
replace
(
'ww'
,
`周
${
w
}
`
)
:
showTime
.
replace
(
's'
,
w
)
}
return
showTime
return
showTime
}
}
...
...
src/views/project/Editor/Index.vue
0 → 100644
View file @
4d0d9ec2
<
template
>
<div
class=
"window-width window-height project-full-wrap column"
>
<editor-header></editor-header>
<div
class=
"col full-wdith row"
>
<editor-day-list
v-model:value=
"activeTab"
></editor-day-list>
<div
class=
"q-pa-lg row col"
>
<tour-itinerary
v-if=
"activeTab==-1"
></tour-itinerary>
<div
class=
"col"
v-if=
"activeTab==-1 || activeTab==-2"
></div>
</div>
</div>
</div>
</
template
>
<
script
lang=
"ts"
setup
>
import
EditorHeader
from
'../components/Editor/EditorHeader.vue'
import
EditorDayList
from
'../components/Editor/EditorDayList.vue'
;
import
TourItinerary
from
'../components/Editor/TourItinerary.vue'
;
import
{
ref
}
from
'vue'
;
const
activeTab
=
ref
(
-
1
)
const
setActiveTabHandle
=
(
playload
:
number
)
=>
{
activeTab
.
value
=
playload
}
</
script
>
<
style
>
.project-full-wrap
{
background-color
:
#f7f8f8
;
}
</
style
>
\ No newline at end of file
src/views/project/components/Editor/Editor.vue
0 → 100644
View file @
4d0d9ec2
<
template
>
<div
class=
"full-height column full-width"
>
{{
editor
.
isActive
(
'bold'
)
}}
<div
class=
"row items-center q-my-sm"
>
<div
class=
"editor-item"
@
click=
"setBold"
:class=
"
{'active':editor.isActive('bold')}">
<IconTextBold></IconTextBold>
</div>
<div
class=
"editor-item"
@
click=
"setItalic"
:class=
"
{'active':editor.isActive('italic')}">
<IconTextItalic></IconTextItalic>
</div>
<div
class=
"editor-item"
@
click=
"setUnderline"
:class=
"
{'active':editor.isActive('underline')}">
<IconTextUnderline></IconTextUnderline>
</div>
<el-divider
style=
"height: 100%; margin: 0 16px;"
direction=
"vertical"
/>
<div
class=
"editor-item"
@
click=
"setHeaderTitle"
:class=
"
{'active':editor.isActive('heading', { level: 3 })}">
<IconTitleLevel></IconTitleLevel>
</div>
<div
class=
"editor-item"
@
click=
"setBlockquote"
:class=
"
{'active':editor.isActive('blockquote')}">
<IconQuote></IconQuote>
</div>
<div
class=
"editor-item"
@
click=
"setTextCenter"
:class=
"
{'active':editor.isActive({ textAlign: 'center' })}">
<IconCenterAlignment></IconCenterAlignment>
</div>
<el-divider
style=
"height: 100%; margin: 0 16px;"
direction=
"vertical"
/>
<div
class=
"editor-item"
ref=
"buttonRef"
@
click=
"openLinkHandle"
>
<IconLinkTwo></IconLinkTwo>
</div>
<div
class=
"editor-item"
>
<IconPicture></IconPicture>
</div>
<div
class=
"editor-item"
@
click=
"setDiver"
>
<IconDividingLine></IconDividingLine>
</div>
</div>
<el-divider
class=
"q-ma-none"
/>
<el-scrollbar
class=
"q-pa-lg full-width col editor-text-content"
style=
"padding-bottom: 0;"
>
<bubble-menu
:editor=
"editor"
:tippy-options=
"
{ duration: 100 }" :should-show="shouldShowHandler" v-if="editor">
<div
class=
"bubble-menu row items-center"
v-if=
"!showEditorLink"
>
<span
class=
"link cusor-pointer"
@
click=
"openLink(currentLink?.href)"
>
{{
currentLink
?.
href
}}
</span>
<el-divider
style=
"height: 30px; margin: 0 8px 0 16px;background-color: #e5e5e5;"
direction=
"vertical"
/>
<div
class=
"editor-item"
@
click=
"editorLinkHandle"
>
<IconEdit></IconEdit>
</div>
<div
class=
"editor-item"
@
click=
"removeLink"
>
<IconDeleteFour></IconDeleteFour>
</div>
</div>
<div
class=
"bubble-menu"
style=
"width: 330px;"
v-else
>
<div
class=
"row items-center"
>
<el-input
class=
"link-box"
v-model=
"linkText"
prefix-icon=
"Link"
@
input=
"linkUrlChangeHandler"
></el-input>
<el-button
class=
"q-ml-md"
@
click=
"setLink"
:disabled=
"!validatorUrl"
>
确认
</el-button>
</div>
<div
class=
"q-mt-md"
>
<el-switch
v-model=
"isBlank"
class=
"short-switch"
/>
在新窗口打开
</div>
</div>
</bubble-menu>
<editor-content
:editor=
"editor"
/>
</el-scrollbar>
</div>
<el-popover
ref=
"popoverRef"
placement=
"bottom"
width=
"330px"
:virtual-ref=
"buttonRef"
:visible=
"linkVisible"
virtual-triggering
popper-style=
"border-radius:6px !important;"
>
<div
v-click-outside=
"onClickOutside"
>
<div
class=
"row items-center"
>
<el-input
class=
"link-box"
v-model=
"linkText"
prefix-icon=
"Link"
@
input=
"linkUrlChangeHandler"
></el-input>
<el-button
class=
"q-ml-md"
@
click=
"setLink"
:disabled=
"!validatorUrl"
>
确认
</el-button>
</div>
<div
class=
"q-mt-md"
>
<el-switch
v-model=
"isBlank"
class=
"short-switch"
/>
在新窗口打开
</div>
</div>
</el-popover>
</
template
>
<
script
setup
>
import
{
EditorContent
,
BubbleMenu
}
from
'@tiptap/vue-3'
import
{
Editor
}
from
'@tiptap/core'
import
StarterKit
from
'@tiptap/starter-kit'
import
Document
from
'@tiptap/extension-document'
import
Paragraph
from
'@tiptap/extension-paragraph'
import
Text
from
'@tiptap/extension-text'
import
Highlight
from
'@tiptap/extension-highlight'
import
Underline
from
'@tiptap/extension-underline'
import
Heading
from
'@tiptap/extension-heading'
import
Blockquote
from
'@tiptap/extension-blockquote'
import
HorizontalRule
from
'@tiptap/extension-horizontal-rule'
import
TextAlign
from
'@tiptap/extension-text-align'
import
Link
from
'@tiptap/extension-link'
import
{
ref
}
from
'vue'
import
{
ClickOutside
as
vClickOutside
}
from
'element-plus'
import
{
openNewBlank
}
from
'@/utils/common'
// import BubbleMenu from '@tiptap/extension-bubble-menu'
const
textalign
=
TextAlign
.
configure
({
types
:
[
'heading'
,
'paragraph'
],
alignments
:
[
'left'
,
'right'
,
'center'
,
'justify'
],
})
const
link
=
Link
.
configure
({
openOnClick
:
false
,
defaultProtocol
:
'https'
,
})
const
linkText
=
ref
(
''
)
const
isBlank
=
ref
(
false
)
const
buttonRef
=
ref
()
const
popoverRef
=
ref
()
const
linkVisible
=
ref
(
false
)
const
validatorUrl
=
ref
(
false
)
const
showEditorLink
=
ref
(
false
)
const
currentLink
=
ref
()
const
regex
=
/^
(
https
?
|ftp
)
:
\/\/([
a-zA-Z0-9.-
]
+
(\:[
0-9
]
+
)?)(\/[
a-zA-Z0-9%_.~+-
]
+
)
*
\/?(\?[
a-zA-Z0-9%_.,~+-=&
]
*
)?(
#
[
a-zA-Z0-9_-
]
+
)?
$/
;
const
statusList
=
ref
({
isBold
:
false
,
isItalic
:
false
,
isUnderline
:
false
,
isHeading
:
false
,
isBlockquote
:
false
,
isCenter
:
false
,
})
const
editor
=
new
Editor
({
// register extensions
extensions
:
[
StarterKit
,
Highlight
,
Text
,
Document
,
Paragraph
,
Underline
,
Heading
,
Blockquote
,
textalign
,
HorizontalRule
,
link
],
// set the initial content
content
:
'<p>Example Text</p>'
,
// place the cursor in the editor after initialization
autofocus
:
true
,
// make the text editable (but that’s the default anyway)
editable
:
true
,
// disable the loading of the default CSS (which is not much anyway)
injectCSS
:
true
,
})
const
setBold
=
()
=>
{
editor
.
chain
().
focus
().
toggleBold
().
run
()
}
const
setItalic
=
()
=>
{
editor
.
chain
().
focus
().
toggleItalic
().
run
()
}
const
setUnderline
=
()
=>
{
editor
.
chain
().
focus
().
toggleUnderline
().
run
()
}
const
setHeaderTitle
=
()
=>
{
editor
.
chain
().
focus
().
toggleHeading
({
level
:
3
}).
run
()
}
const
setBlockquote
=
()
=>
{
editor
.
chain
().
focus
().
toggleBlockquote
().
run
()
}
const
setTextCenter
=
()
=>
{
if
(
!
editor
.
isActive
({
textAlign
:
'center'
}))
editor
.
chain
().
focus
().
setTextAlign
(
'center'
).
run
()
else
editor
.
chain
().
focus
().
unsetTextAlign
().
run
()
}
const
setDiver
=
()
=>
{
editor
.
chain
().
focus
().
setHorizontalRule
().
run
()
}
const
onClickOutside
=
()
=>
{
if
(
linkVisible
.
value
)
{
linkVisible
.
value
=
false
editor
.
chain
().
focus
()
}
}
const
openLink
=
(
url
)
=>
{
openNewBlank
(
url
)
}
const
linkUrlChangeHandler
=
()
=>
{
validatorUrl
.
value
=
regex
.
test
(
linkText
.
value
)
}
const
setLink
=
()
=>
{
if
(
editor
.
isActive
(
'link'
)){
const
r
=
editor
.
chain
().
focus
().
extendMarkRange
(
'link'
).
setLink
({
href
:
linkText
.
value
,
target
:
isBlank
.
value
?
'_blank'
:
'_self'
}).
run
()
showEditorLink
.
value
=
false
currentLink
.
value
=
editor
.
getAttributes
(
'link'
)
}
else
{
editor
.
commands
.
setLink
({
href
:
linkText
.
value
,
target
:
isBlank
.
value
?
'_blank'
:
'_self'
})
}
onClickOutside
()
}
const
removeLink
=
()
=>
{
if
(
editor
.
isActive
(
'link'
)){
editor
.
chain
().
focus
().
extendMarkRange
(
'link'
).
unsetLink
().
run
()
}
}
const
openLinkHandle
=
()
=>
{
editor
.
chain
().
focus
()
const
{
from
,
to
}
=
editor
.
state
.
selection
;
linkVisible
.
value
=
from
!=
to
if
(
linkVisible
.
value
)
{
linkText
.
value
=
''
validatorUrl
.
value
=
false
}
}
const
editorLinkHandle
=
()
=>
{
linkText
.
value
=
currentLink
.
value
.
href
showEditorLink
.
value
=
true
isBlank
.
value
=
currentLink
.
value
.
target
==
'_blank'
}
const
shouldShowHandler
=
({
editor
,
view
,
state
,
oldState
,
from
,
to
})
=>
{
if
(
editor
.
isActive
(
'link'
)){
showEditorLink
.
value
=
false
currentLink
.
value
=
editor
.
getAttributes
(
'link'
)
}
return
editor
.
isActive
(
'link'
)
}
</
script
>
<
style
>
.editor-item
{
font-size
:
18px
;
padding
:
5px
10px
;
color
:
#000
;
border-radius
:
4px
;
background-color
:
transparent
;
cursor
:
pointer
;
margin-left
:
5px
;
}
.editor-item
:hover
,
.editor-item.active
{
background-color
:
rgba
(
0
,
0
,
255
,
0.05
);
}
.editor-item.active
{
color
:
var
(
--el-color-primary
);
}
.editor-text-content
.ProseMirror
,
.editor-text-content
.ProseMirror-static
{
font-size
:
14px
!important
;
font-family
:
"Helvetica Neue"
,
Helvetica
,
"PingFang SC"
,
"Hiragino Sans GB"
,
"Microsoft YaHei"
,
"微软雅黑"
,
Arial
,
sans-serif
!important
;
}
.link-box
.el-input__wrapper
{
background-color
:
#f5f5f5
!important
;
color
:
#000
!important
;
border-radius
:
6px
!important
;
box-shadow
:
none
!important
;
}
.link-box
.el-input__prefix
{
color
:
#000
!important
;
}
.tiptap
*
{
line-height
:
1.5
!important
;
}
.el-switch.short-switch
.el-switch__core
{
min-width
:
35px
;
}
.bubble-menu
{
background-color
:
#FFF
;
border
:
1px
solid
#e5e5e5
;
border-radius
:
8px
;
box-shadow
:
0
1px
2px
0
rgba
(
0
,
0
,
0
,
.05
);
padding
:
8px
;
}
.bubble-menu
.link
{
font-size
:
14px
;
color
:
#000
;
text-decoration
:
underline
;
}
</
style
>
\ No newline at end of file
src/views/project/components/Editor/EditorDayList.vue
0 → 100644
View file @
4d0d9ec2
<
template
>
<div
class=
"proj-editor-left full-height column"
>
<div
class=
"q-pa-lg row items-center"
>
<div
class=
"col text-info"
>
行程总览
</div>
<el-link
:underline=
"false"
>
<IconEdit></IconEdit>
<span
style=
"margin-left: 5px;"
class=
"text-small"
>
编辑
</span>
</el-link>
</div>
<el-divider
class=
"q-ma-none"
/>
<el-scrollbar
class=
"full-width col"
>
<div
class=
"day-item"
:class=
"
{'active':activeDayItem==-1}" @click="setCurrentDayItemHandle(-1)">
<span
class=
"day-title"
>
行程总览
</span>
</div>
<div
class=
"day-item"
v-for=
"(x,i) in days"
:key=
"i"
:class=
"
{'active':activeDayItem==i}" @click="setCurrentDayItemHandle(i)">
<div
class=
"row"
>
<span
class=
"day-title col"
>
{{
x
.
day
}}
</span>
<span
class=
"text-info"
>
{{
x
.
dateStr
}}
</span>
</div>
<div
style=
"margin-top: 8px;"
v-for=
"(city,index) in x.cities"
:key=
"index"
>
<IconMark
class=
"q-mr-md"
v-if=
"city==startCity || city==endCity"
></IconMark>
<span>
{{
city
}}
</span>
</div>
</div>
<div
class=
"day-item"
:class=
"
{'active':activeDayItem==-2}" @click="setCurrentDayItemHandle(-2)">
<span
class=
"day-title"
>
行程备注
</span>
</div>
</el-scrollbar>
</div>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
dateFormat
}
from
'@/utils/common'
;
import
{
ref
}
from
'vue'
;
const
emit
=
defineEmits
<
{
(
event
:
'update:value'
,
payload
:
number
):
void
}
>
()
const
days
=
ref
<
any
[]
>
()
const
startCity
=
'北京'
const
endCity
=
'北京'
const
cities
=
[[
'北京'
,
'巴黎'
],[
'巴黎'
],[
'巴黎'
],[
'巴黎'
,
'尼斯'
],[
'尼斯'
,
'威尼斯'
],[
'威尼斯'
],[
'威尼斯'
],[
'威尼斯'
,
'佛罗伦萨'
],[
'佛罗伦萨'
],[
'佛罗伦萨'
,
'罗马'
],[
'罗马'
],[
'罗马'
,
'伊斯坦布尔'
],[
'伊斯坦布尔'
,
'北京'
]]
const
activeDayItem
=
ref
(
-
1
)
const
startDate
=
new
Date
(
'2024/11/11'
)
days
.
value
=
cities
.
map
((
x
,
i
)
=>
{
const
newDate
=
new
Date
(
startDate
)
newDate
.
setDate
(
newDate
.
getDate
()
+
i
)
const
formatDate
=
dateFormat
(
newDate
,
'M月D日 ww'
)
return
{
day
:
`D
${
i
+
1
}
`
,
dateStr
:
formatDate
,
dayNum
:
i
,
cities
:
x
}
})
const
setCurrentDayItemHandle
=
(
i
:
number
)
=>
{
activeDayItem
.
value
=
i
emit
(
'update:value'
,
i
)
}
</
script
>
<
style
>
.proj-editor-left
{
width
:
184px
;
font-size
:
13px
;
}
.proj-editor-left
.day-item
{
padding
:
15px
12px
;
font-size
:
12px
;
position
:
relative
;
cursor
:
pointer
;
}
.proj-editor-left
.day-item.active
{
color
:
var
(
--el-color-primary
);
}
.proj-editor-left
.day-item
:hover
{
color
:
var
(
--el-color-primary
);
}
.proj-editor-left
.day-item.active
::before
{
display
:
block
;
content
:
' '
;
width
:
4px
;
height
:
16px
;
background-color
:
var
(
--el-color-primary
);
position
:
absolute
;
top
:
17px
;
left
:
0
;
}
.proj-editor-left
.day-item
.day-title
{
font-size
:
14px
;
font-weight
:
bold
;
}
</
style
>
\ No newline at end of file
src/views/project/components/Editor/EditorHeader.vue
0 → 100644
View file @
4d0d9ec2
<
template
>
<div
class=
"peditor-head-box row items-center"
>
<el-breadcrumb
:separator-icon=
"ArrowRight"
>
<el-breadcrumb-item
:to=
"
{ path: '/w' }">我参与的项目
</el-breadcrumb-item>
<el-breadcrumb-item>
2024/09/23-18:49 黄奎创建
</el-breadcrumb-item>
<el-breadcrumb-item>
行程编辑
</el-breadcrumb-item>
</el-breadcrumb>
<div
class=
"col"
></div>
<el-link
:underline=
"false"
>
<IconSettingOne></IconSettingOne>
<span
style=
"margin-left: 5px;"
class=
"text-small"
>
行程信息设置
</span>
</el-link>
<el-popover
placement=
"bottom"
trigger=
"click"
class=
"pa-none"
>
<template
#
reference
>
<el-link
:underline=
"false"
class=
"q-mx-lg"
>
<IconSwitch></IconSwitch>
<span
style=
"margin-left: 5px;"
class=
"text-small"
>
导入导出
</span>
</el-link>
</
template
>
<el-menu
mode=
"vertical"
class=
"min-menu none-border"
style=
"width: 100%;"
>
<el-menu-item
index=
"1"
>
导入行程路线
</el-menu-item>
<el-menu-item
index=
"2"
>
导出到行程库
</el-menu-item>
</el-menu>
</el-popover>
<el-button
color=
"#626aef"
style=
"height: 100%;border-radius: 0;"
>
返回
</el-button>
<div></div>
</div>
</template>
<
script
lang=
"ts"
setup
>
import
{
ArrowRight
}
from
'@element-plus/icons-vue'
</
script
>
<
style
>
.peditor-head-box
{
background-color
:
#000e1a
;
height
:
48px
;
padding-left
:
40px
;
}
.peditor-head-box
.el-breadcrumb__inner
{
color
:
#8c9398
;
}
.peditor-head-box
.el-breadcrumb__item
:last-child
.el-breadcrumb__inner
,
.peditor-head-box
.el-link
{
color
:
#D9DBDC
;
}
.peditor-head-box
.el-link
:hover
{
color
:
var
(
--el-link-hover-text-color
);
}
.none-border
{
border-right
:
none
;
}
.min-menu
.el-menu-item
{
height
:
40px
;
line-height
:
40px
;
}
</
style
>
\ No newline at end of file
src/views/project/components/Editor/TextEditor.vue
0 → 100644
View file @
4d0d9ec2
<
template
>
<el-drawer
v-model=
"editorVisible"
:show-close=
"false"
class=
"customer-drawer"
size=
"960px"
>
<template
#
header=
"
{ close, titleId, titleClass }">
<div
class=
"card-h2"
>
{{
title
}}
</div>
</
template
>
<div
class=
"full-height column"
>
<el-divider
class=
"q-ma-none"
/>
<div
class=
"draw-close"
@
click=
"closeHandle"
>
<IconCloseSmall></IconCloseSmall>
</div>
<div
class=
"col full-width row"
>
<div
class=
"col"
>
<Editor
/>
</div>
<el-divider
class=
"q-ma-none"
style=
"height: 100%;"
direction=
"vertical"
/>
<div
class=
"col q-pa-lg"
></div>
</div>
</div>
</el-drawer>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
watch
}
from
'vue'
import
Editor
from
'./Editor.vue'
const
props
=
withDefaults
(
defineProps
<
{
value
:
string
title
:
string
,
visible
:
boolean
}
>
(),
{
title
:
''
})
const
emit
=
defineEmits
<
{
(
event
:
'update:value'
,
payload
:
string
):
void
,
(
event
:
'update:visible'
,
payload
:
boolean
):
void
}
>
()
const
editorVisible
=
ref
(
props
.
visible
)
const
content
=
ref
(
props
.
value
)
const
closeHandle
=
()
=>
{
emit
(
'update:visible'
,
false
)
}
const
getContent
=
(
val
:
any
)
=>
{
console
.
log
(
val
)
}
watch
(()
=>
props
.
visible
,()
=>
{
editorVisible
.
value
=
props
.
visible
})
watch
(()
=>
props
.
value
,()
=>
{
content
.
value
=
props
.
value
})
</
script
>
\ No newline at end of file
src/views/project/components/Editor/TourItinerary.vue
0 → 100644
View file @
4d0d9ec2
<
template
>
<div
class=
"rounded q-pa-lg bg-white border-light col full-height"
>
<div
class=
"row"
>
<div
class=
"card-h2 col"
>
行程介绍
</div>
<el-button
color=
"#626aef"
style=
"border-radius: 3px;"
size=
"small"
@
click=
"()=>editorVisible=true"
>
编辑
</el-button>
</div>
<div
class=
"q-mt-lg"
>
<div
v-html=
"itinerary"
v-if=
"itinerary!=''"
></div>
<el-empty
description=
"请点击右上角“编辑”按钮进行添加 ~"
/>
</div>
</div>
<text-editor
v-model:value=
"itinerary"
v-model:visible=
"editorVisible"
title=
"编辑行程介绍"
></text-editor>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
;
import
TextEditor
from
'./TextEditor.vue'
;
const
itinerary
=
ref
(
''
)
const
editorVisible
=
ref
(
false
)
</
script
>
<
style
>
</
style
>
\ No newline at end of file
vue.config.js
View file @
4d0d9ec2
...
@@ -33,11 +33,17 @@ module.exports = {
...
@@ -33,11 +33,17 @@ module.exports = {
// cache: false,
// cache: false,
// fix: false,
// fix: false,
// }),
// }),
// AutoImport({
// resolvers: [ElementPlusResolver()],
// }),
// Components({
// resolvers: [ElementPlusResolver()],
// })
AutoImport
({
AutoImport
({
resolvers
:
[
ElementPlusResolver
()],
resolvers
:
[
ElementPlusResolver
(
{
importStyle
:
false
}
)],
}),
}),
Components
({
Components
({
resolvers
:
[
ElementPlusResolver
()],
resolvers
:
[
ElementPlusResolver
(
{
importStyle
:
false
}
)],
})
})
],
],
},
},
...
...
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