Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
P
poc-fe
Project
Project
Details
Activity
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
poc
poc-fe
Commits
863398a8
Commit
863398a8
authored
Apr 21, 2025
by
tyyin lan
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev'
parents
164941f0
6feba3da
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
516 additions
and
72 deletions
+516
-72
index.html
index.html
+1
-1
content-optimization-edit.vue
src/components/custom-editor/content-optimization-edit.vue
+315
-0
custom-editor.vue
src/components/custom-editor/custom-editor.vue
+65
-71
fetch-event-stream-source.ts
...mponents/custom-editor/utils/fetch-event-stream-source.ts
+83
-0
en.yaml
src/locales/langs/en.yaml
+13
-0
zh-cn.yaml
src/locales/langs/zh-cn.yaml
+13
-0
zh-hk.yaml
src/locales/langs/zh-hk.yaml
+13
-0
locales.d.ts
types/locales.d.ts
+13
-0
No files found.
index.html
View file @
863398a8
...
...
@@ -8,7 +8,7 @@
name=
"viewport"
content=
"width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link
rel=
"stylesheet"
href=
"//at.alicdn.com/t/c/font_4711453_
usul8q5bwqp
.css"
/>
<link
rel=
"stylesheet"
href=
"//at.alicdn.com/t/c/font_4711453_
lz4oxe9et7
.css"
/>
<link
rel=
"preload"
href=
"https://gsst-poe-sit.gz.bcebos.com/front/SourceHanSansCN-Medium.otf"
...
...
src/components/custom-editor/content-optimization-edit.vue
0 → 100644
View file @
863398a8
This diff is collapsed.
Click to expand it.
src/components/custom-editor/custom-editor.vue
View file @
863398a8
<
script
setup
lang=
"ts"
>
import
{
computed
,
nextTick
,
onMounted
,
ref
,
useTemplateRef
,
watch
}
from
'vue'
import
{
computed
,
nextTick
,
onMounted
,
ref
,
shallowRef
,
useTemplateRef
,
watch
}
from
'vue'
import
{
createEditorConfig
}
from
'./config/editor-config'
import
EditorToolbar
from
'./editor-toolbar.vue'
import
{
markdownTransformHtml
}
from
'@/utils/markdown-parse'
import
{
asBlob
}
from
'html-docx-js-typescript'
import
{
downloadFile
}
from
'@/utils/download-file'
import
{
useI18n
}
from
'vue-i18n'
import
{
fetchExportFile
}
from
'@/apis/file'
import
ContentOptimizationEdit
from
'./content-optimization-edit.vue'
import
{
useElementSize
}
from
'@vueuse/core'
import
type
{
Editor
}
from
'tinymce'
interface
Props
{
content
:
string
...
...
@@ -25,29 +27,23 @@ const emit = defineEmits<{
}
>
()
defineExpose
({
getWordCount
,
getContent
,
})
const
editorToolbarRef
=
useTemplateRef
<
InstanceType
<
typeof
EditorToolbar
>>
(
'editorToolbarRef'
)
const
{
t
}
=
useI18n
()
let
editor
:
any
=
{}
let
controller
:
AbortController
|
null
=
null
const
editorWrapperRef
=
useTemplateRef
(
'editorWrapperRef'
)
const
{
height
:
editorWrapperRefHeight
}
=
useElementSize
(
editorWrapperRef
)
const
editorConfig
=
createEditorConfig
()
const
editor
=
shallowRef
<
Editor
|
null
>
(
null
)
const
isShowEditor
=
ref
(
false
)
const
editorWrapperRef
=
ref
<
HTMLDivElement
|
null
>
(
null
)
const
modifyInputContainerStyle
=
ref
({
top
:
'0px'
,
})
const
articleModifyInputContent
=
ref
(
''
)
const
articleContentModifyContainerShow
=
ref
(
false
)
const
articleContentModifyState
=
ref
<
'selection'
|
'loading'
|
'generating'
|
'done'
>
(
'selection'
)
const
articleContentModifyResponseText
=
ref
(
''
)
const
articleContentModifyResponseTextResource
=
ref
(
''
)
const
articleContentModifyContainerClientY
=
ref
(
0
)
const
contentOptimizationEditContainerIsSetBottom
=
ref
(
false
)
const
contentOptimizationEditContainerLocation
=
ref
(
0
)
const
editorContent
=
computed
({
get
()
{
...
...
@@ -58,20 +54,17 @@ const editorContent = computed({
},
})
// const editorTitleText = ref('')
watch
(
articleContentModifyResponseTextResource
,
(
newVal
)
=>
{
if
(
newVal
)
articleContentModifyResponseText
.
value
=
markdownTransformHtml
(
editor
.
dom
?
editor
.
dom
.
decode
(
newVal
)
:
newVal
,
)
as
string
})
watch
(
()
=>
props
.
content
,
(
val
:
string
,
prevVal
:
string
)
=>
{
if
(
editor
&&
editor
.
getContent
&&
typeof
val
===
'string'
&&
val
!==
prevVal
&&
val
!==
editor
.
getContent
())
editor
.
setContent
(
val
)
if
(
editor
.
value
&&
editor
.
value
.
getContent
&&
typeof
val
===
'string'
&&
val
!==
prevVal
&&
val
!==
editor
.
value
.
getContent
()
)
editor
.
value
.
setContent
(
val
)
},
)
...
...
@@ -80,7 +73,7 @@ onMounted(() => {
...
editorConfig
,
init_instance_callback
:
(
editorInstance
)
=>
{
isShowEditor
.
value
=
true
editor
=
editorInstance
editor
.
value
=
editorInstance
nextTick
(()
=>
{
props
.
content
&&
editorInstance
.
setContent
(
props
.
content
)
...
...
@@ -111,19 +104,8 @@ onMounted(() => {
editorInstance
.
on
(
'mousedown'
,
(
e
:
MouseEvent
)
=>
{
if
(
e
.
button
!==
0
)
return
articleContentModifyContainerClientY
.
value
=
e
.
clientY
if
(
articleContentModifyContainerShow
.
value
)
{
if
(
controller
)
{
controller
.
abort
()
controller
=
null
}
articleContentModifyContainerShow
.
value
=
false
articleContentModifyResponseText
.
value
=
''
articleContentModifyResponseTextResource
.
value
=
''
articleContentModifyState
.
value
=
'selection'
articleModifyInputContent
.
value
=
''
}
editorInstance
.
selection
.
collapse
()
...
...
@@ -135,21 +117,35 @@ onMounted(() => {
const
selectionContent
=
editorInstance
.
selection
.
getContent
()
if
(
selectionContent
)
{
const
top
=
(
e
.
clientY
>
articleContentModifyContainerClientY
.
value
?
e
.
clientY
:
articleContentModifyContainerClientY
.
value
)
+
130
const
bodyHeight
=
document
.
body
.
offsetHeight
modifyInputContainerStyle
.
value
.
top
=
`
${
top
>
bodyHeight
-
400
?
bodyHeight
-
400
:
top
}
px`
const
rng
=
editorInstance
.
selection
.
getRng
()
const
rects
=
rng
.
getClientRects
()
const
lastRect
=
rects
[
rects
.
length
-
1
]
if
(
lastRect
)
{
/* 其中 46 是当前编辑器文档HTMl距离 外部挂载容器之间产生的高度 */
const
locationHeight
=
lastRect
.
bottom
+
46
+
10
if
(
editorWrapperRefHeight
.
value
-
260
<
locationHeight
)
{
contentOptimizationEditContainerIsSetBottom
.
value
=
true
contentOptimizationEditContainerLocation
.
value
=
editorWrapperRefHeight
.
value
-
(
rects
[
0
].
bottom
+
46
+
10
)
+
rects
[
0
].
height
+
16
}
else
{
contentOptimizationEditContainerIsSetBottom
.
value
=
false
contentOptimizationEditContainerLocation
.
value
=
locationHeight
}
}
articleContentModifyContainerShow
.
value
=
true
}
})
editorInstance
.
on
(
'keydown'
,
()
=>
{
if
(
articleContentModifyContainerShow
.
value
)
articleContentModifyContainerShow
.
value
=
false
editorInstance
.
on
(
'keyup'
,
()
=>
{
const
selectionContent
=
editorInstance
.
selection
.
getContent
()
if
(
!
selectionContent
&&
articleContentModifyContainerShow
.
value
)
{
articleContentModifyContainerShow
.
value
=
false
}
})
},
})
...
...
@@ -161,16 +157,16 @@ onMounted(() => {
// return !value.startsWith(' ') && !value.endsWith(' ')
// }
function
getWordCount
()
{
const
wordcount
=
window
.
tinymce
.
activeEditor
!
.
plugins
.
wordcount
//
function getWordCount() {
//
const wordcount = window.tinymce.activeEditor!.plugins.wordcount
return
wordcount
.
body
.
getWordCount
()
}
//
return wordcount.body.getWordCount()
//
}
function
getContent
()
{
if
(
!
isShowEditor
.
value
)
return
''
const
content
=
editor
.
getContent
()
const
content
=
editor
.
value
?.
getContent
()
||
''
const
parser
=
window
.
tinymce
.
html
.
DomParser
({
validate
:
false
})
parser
.
addNodeFilter
(
'mf-confirmation-box'
,
(
nodes
)
=>
{
...
...
@@ -185,11 +181,15 @@ function getContent() {
}
function
onDownloadFile
()
{
asBlob
(
getContent
()).
then
((
data
)
=>
{
downloadFile
(
data
as
Blob
).
then
(()
=>
{
const
content
=
getContent
()
fetchExportFile
<
string
>
(
'doc'
,
content
).
then
((
res
)
=>
{
if
(
res
.
code
!==
0
)
return
downloadFile
(
res
.
data
)
window
.
$message
.
success
(
t
(
'common_module.download_success'
))
})
})
}
</
script
>
...
...
@@ -203,25 +203,19 @@ function onDownloadFile() {
<EditorToolbar
ref=
"editorToolbarRef"
@
download-file=
"onDownloadFile"
/>
</div>
<!--
<div
class=
"doc-title px-4 py-2"
>
<NInput
v-model:value=
"editorTitleText"
type=
"textarea"
:allow-input=
"editorTitleTextVerify"
size=
"large"
class=
"font-semibold"
:autosize=
"
{ minRows: 1 }"
:maxlength="50"
show-count
placeholder="请输入标题..."
/>
</div>
-->
<div
class=
"h-[10px]"
></div>
<div
class=
"grow"
>
<textarea
class=
"tinymce-body"
/>
</div>
</div>
<ContentOptimizationEdit
v-model:is-show-modal=
"articleContentModifyContainerShow"
:editor=
"editor"
:is-set-bottom=
"contentOptimizationEditContainerIsSetBottom"
:location=
"contentOptimizationEditContainerLocation"
/>
</div>
<div
v-show=
"!isShowEditor"
class=
"flex h-full w-full items-center justify-center"
>
<n-spin
size=
"large"
/>
...
...
src/components/custom-editor/utils/fetch-event-stream-source.ts
0 → 100644
View file @
863398a8
import
{
BASE_URLS
}
from
'@/config/base-url'
import
{
useSystemLanguageStore
}
from
'@/store/modules/system-language'
import
{
useUserStore
}
from
'@/store/modules/user'
import
{
languageKeyTransform
}
from
'@/utils/language-key-transform'
import
{
fetchEventSource
}
from
'@microsoft/fetch-event-source'
export
interface
ResponseData
{
message
:
string
reasoningContent
:
string
function
:
{
name
:
string
}
}
interface
Options
{
onopen
?:
(
response
?:
Response
)
=>
Promise
<
void
>
onResponse
?:
(
data
:
ResponseData
)
=>
void
onend
?:
()
=>
void
onclose
?:
()
=>
void
onerror
?:
(
err
:
Error
)
=>
void
}
export
default
function
fetchEventStreamSource
(
url
:
string
,
payload
:
object
=
{},
options
:
Options
=
{
onopen
:
async
(
_response
)
=>
{},
onResponse
:
(
_data
:
ResponseData
)
=>
{},
onend
:
()
=>
{},
onclose
:
()
=>
{},
onerror
:
(
_err
:
Error
)
=>
{},
},
)
{
const
ENV
=
import
.
meta
.
env
.
VITE_APP_ENV
const
userStore
=
useUserStore
()
const
systemLanguageStore
=
useSystemLanguageStore
()
const
controller
=
new
AbortController
()
fetchEventSource
(
`
${
BASE_URLS
[
ENV
||
'DEV'
]}
/api/rest
${
url
}
`
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
'x-request-token'
:
userStore
.
token
,
'x-lang'
:
languageKeyTransform
(
systemLanguageStore
.
currentLanguageInfo
.
key
),
},
body
:
JSON
.
stringify
(
payload
),
signal
:
controller
?.
signal
,
onopen
:
async
(
response
)
=>
{
return
options
.
onopen
&&
options
.
onopen
(
response
)
},
onmessage
:
(
e
:
{
data
:
string
})
=>
{
if
(
e
.
data
===
'[DONE]'
)
{
options
.
onend
&&
options
.
onend
()
return
}
try
{
const
data
=
JSON
.
parse
(
e
.
data
)
if
(
data
.
code
===
0
||
data
.
code
===
'0'
)
{
data
&&
options
.
onResponse
&&
options
.
onResponse
(
data
)
}
else
{
options
.
onerror
&&
options
.
onerror
(
new
Error
(
data
.
message
))
controller
.
abort
()
options
.
onclose
&&
options
.
onclose
()
}
}
catch
(
error
)
{
options
.
onerror
&&
options
.
onerror
(
error
as
Error
)
}
},
onclose
:
()
=>
{
options
.
onclose
&&
options
.
onclose
()
},
onerror
:
(
err
)
=>
{
options
.
onerror
&&
options
.
onerror
(
err
)
throw
err
},
})
return
controller
}
src/locales/langs/en.yaml
View file @
863398a8
...
...
@@ -799,3 +799,16 @@ editor_module:
center_align
:
'
Center
align'
justify_right
:
'
Justify
right'
align_both_ends
:
'
Align
both
ends'
optimize_input_placeholder
:
'
Please
enter
the
instructions
for
optimizing
the
text'
retouching
:
'
Retouching'
expansion
:
'
Expansion'
abbreviation
:
'
Abbreviation'
adjust_the_tone
:
'
Adjust
the
tone'
colloquial
:
'
Colloquial'
academicization
:
'
Academicization'
humorous_and_vivid
:
'
Humorous
and
vivid'
serious_and_formal
:
'
Serious
and
formal'
concise_and_clear
:
'
Concise
and
clear'
elegant_literary_style
:
'
Elegant
literary
style'
src/locales/langs/zh-cn.yaml
View file @
863398a8
...
...
@@ -798,3 +798,16 @@ editor_module:
center_align
:
'
居中对齐'
justify_right
:
'
右对齐'
align_both_ends
:
'
两端对齐'
optimize_input_placeholder
:
'
请输入优化文本的指令'
retouching
:
'
润色'
expansion
:
'
扩写'
abbreviation
:
'
缩写'
adjust_the_tone
:
'
调整语气'
colloquial
:
'
口语化'
academicization
:
'
学术化'
humorous_and_vivid
:
'
幽默生动'
serious_and_formal
:
'
严肃正式'
concise_and_clear
:
'
简洁明了'
elegant_literary_style
:
'
文采优美'
src/locales/langs/zh-hk.yaml
View file @
863398a8
...
...
@@ -797,3 +797,16 @@ editor_module:
center_align
:
'
居中對齊'
justify_right
:
'
右對齊'
align_both_ends
:
'
兩端對齊'
optimize_input_placeholder
:
'
請輸入優化文本的指令'
retouching
:
'
潤色'
expansion
:
'
擴寫'
abbreviation
:
'
縮寫'
adjust_the_tone
:
'
調整語氣'
colloquial
:
'
口語化'
academicization
:
'
學術化'
humorous_and_vivid
:
'
幽默生動'
serious_and_formal
:
'
嚴肅正式'
concise_and_clear
:
'
簡潔明了'
elegant_literary_style
:
'
文采優美'
types/locales.d.ts
View file @
863398a8
...
...
@@ -820,6 +820,19 @@ declare namespace I18n {
center_align
:
string
justify_right
:
string
align_both_ends
:
string
optimize_input_placeholder
:
string
retouching
:
string
expansion
:
string
abbreviation
:
string
adjust_the_tone
:
string
colloquial
:
string
academicization
:
string
humorous_and_vivid
:
string
serious_and_formal
:
string
concise_and_clear
:
string
elegant_literary_style
:
string
}
}
}
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