Commit d28e0366 authored by tyyin lan's avatar tyyin lan

chore: 编辑器优化输入框

parent 28db76d1
import { request } from '@/utils/request'
export function fetchExportFile<T>(type: 'doc', content: string) {
return request.post<T>('/contentReportRest/report.json', { reportType: type, content })
}
<script setup lang="ts">
import { ref } from 'vue'
interface Props {
containerTop: number
}
defineProps<Props>()
const questionContent = ref()
</script>
<template>
<div class="absolute w-full px-[16px]" :style="{ top: `${containerTop || 0}px` }">
<div class="rounded-[6px] bg-white py-[6px]">
<n-input
v-model:value="questionContent"
size="large"
type="textarea"
placeholder="请输入优化文本的指令"
:autosize="{ minRows: 1, maxRows: 3 }"
/>
</div>
</div>
</template>
...@@ -3,9 +3,10 @@ import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue' ...@@ -3,9 +3,10 @@ import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue'
import { createEditorConfig } from './config/editor-config' import { createEditorConfig } from './config/editor-config'
import EditorToolbar from './editor-toolbar.vue' import EditorToolbar from './editor-toolbar.vue'
import { markdownTransformHtml } from '@/utils/markdown-parse' import { markdownTransformHtml } from '@/utils/markdown-parse'
import { asBlob } from 'html-docx-js-typescript'
import { downloadFile } from '@/utils/download-file' import { downloadFile } from '@/utils/download-file'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { fetchExportFile } from '@/apis/file'
import ContentOptimizationEdit from './content-optimization-edit.vue'
interface Props { interface Props {
content: string content: string
...@@ -25,7 +26,6 @@ const emit = defineEmits<{ ...@@ -25,7 +26,6 @@ const emit = defineEmits<{
}>() }>()
defineExpose({ defineExpose({
getWordCount,
getContent, getContent,
}) })
...@@ -49,6 +49,8 @@ const articleContentModifyResponseText = ref('') ...@@ -49,6 +49,8 @@ const articleContentModifyResponseText = ref('')
const articleContentModifyResponseTextResource = ref('') const articleContentModifyResponseTextResource = ref('')
const articleContentModifyContainerClientY = ref(0) const articleContentModifyContainerClientY = ref(0)
const contentOptimizationEditContainerTop = ref(300)
const editorContent = computed({ const editorContent = computed({
get() { get() {
return props.content return props.content
...@@ -135,6 +137,19 @@ onMounted(() => { ...@@ -135,6 +137,19 @@ onMounted(() => {
const selectionContent = editorInstance.selection.getContent() const selectionContent = editorInstance.selection.getContent()
if (selectionContent) { if (selectionContent) {
const rng = editorInstance.selection.getRng()
const rects = rng.getClientRects()
const lastRect = rects[rects.length - 1]
// const locationEl = rng.endContainer.parentElement
if (lastRect) {
/* 其中 46 是当前编辑器文档HTMl距离 外部挂载容器之间产生的高度 */
contentOptimizationEditContainerTop.value = lastRect.bottom + 46 + 10
// contentOptimizationEditContainerTop.value = locationEl.offsetTop + locationEl.offsetHeight
}
const top = const top =
(e.clientY > articleContentModifyContainerClientY.value (e.clientY > articleContentModifyContainerClientY.value
? e.clientY ? e.clientY
...@@ -161,11 +176,11 @@ onMounted(() => { ...@@ -161,11 +176,11 @@ onMounted(() => {
// return !value.startsWith(' ') && !value.endsWith(' ') // return !value.startsWith(' ') && !value.endsWith(' ')
// } // }
function getWordCount() { // function getWordCount() {
const wordcount = window.tinymce.activeEditor!.plugins.wordcount // const wordcount = window.tinymce.activeEditor!.plugins.wordcount
return wordcount.body.getWordCount() // return wordcount.body.getWordCount()
} // }
function getContent() { function getContent() {
if (!isShowEditor.value) return '' if (!isShowEditor.value) return ''
...@@ -185,11 +200,15 @@ function getContent() { ...@@ -185,11 +200,15 @@ function getContent() {
} }
function onDownloadFile() { function onDownloadFile() {
asBlob(getContent()).then((data) => { const content = getContent()
downloadFile(data as Blob).then(() => {
fetchExportFile<string>('doc', content).then((res) => {
if (res.code !== 0) return
downloadFile(res.data)
window.$message.success(t('common_module.download_success')) window.$message.success(t('common_module.download_success'))
}) })
})
} }
</script> </script>
...@@ -203,25 +222,14 @@ function onDownloadFile() { ...@@ -203,25 +222,14 @@ function onDownloadFile() {
<EditorToolbar ref="editorToolbarRef" @download-file="onDownloadFile" /> <EditorToolbar ref="editorToolbarRef" @download-file="onDownloadFile" />
</div> </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="h-[10px]"></div>
<div class="grow"> <div class="grow">
<textarea class="tinymce-body" /> <textarea class="tinymce-body" />
</div> </div>
</div> </div>
<ContentOptimizationEdit :container-top="contentOptimizationEditContainerTop" />
</div> </div>
<div v-show="!isShowEditor" class="flex h-full w-full items-center justify-center"> <div v-show="!isShowEditor" class="flex h-full w-full items-center justify-center">
<n-spin size="large" /> <n-spin size="large" />
......
...@@ -3,17 +3,22 @@ import CustomEditor from '@/components/custom-editor/custom-editor.vue' ...@@ -3,17 +3,22 @@ import CustomEditor from '@/components/custom-editor/custom-editor.vue'
const contentEdit = defineModel<string>('contentEdit', { required: true }) const contentEdit = defineModel<string>('contentEdit', { required: true })
const isShowEditorDrawerDraft = true
const contentEditDraft = `<h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h1>标题</h1><h4>济南的冬天,是一幅独特的画卷。它不同于北方的严寒,也不同于南方的温润。这里的冬天,有着独特的魅力和风情。济南的冬日,天空湛蓝,阳光明媚,尽管寒风凛冽,但总能带给人一份宁静和温馨。济南的泉水在冬天依然潺潺流淌,为这座城市增添了一份生机和活力。济南的冬日,不仅是季节的更迭,更是一种生活的体验,一种对大自然的敬畏和感慨。</h4>`
const isShowEditorDrawer = defineModel<boolean>('isShowEditorDrawer', { required: true }) const isShowEditorDrawer = defineModel<boolean>('isShowEditorDrawer', { required: true })
console.log(isShowEditorDrawer)
function onDrawerAfterLeave() { function onDrawerAfterLeave() {
contentEdit.value = '' contentEdit.value = ''
} }
</script> </script>
<template> <template>
<n-drawer v-model:show="isShowEditorDrawer" :width="800" placement="right" :on-after-leave="onDrawerAfterLeave"> <n-drawer v-model:show="isShowEditorDrawerDraft" :width="800" placement="right" :on-after-leave="onDrawerAfterLeave">
<n-drawer-content> <n-drawer-content>
<CustomEditor :content="contentEdit" /> <CustomEditor :content="contentEditDraft" />
</n-drawer-content> </n-drawer-content>
</n-drawer> </n-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { Computer, PreviewOpen, AllApplication, SettingOne, Api } from '@icon-park/vue-next' import {
Computer,
PreviewOpen,
AllApplication,
SettingOne,
Api,
Wechat,
SourceCode,
Permissions,
} from '@icon-park/vue-next'
import useTableScrollY from '@/composables/useTableScrollY' import useTableScrollY from '@/composables/useTableScrollY'
import { copyToClip } from '@/utils/copy' import { copyToClip } from '@/utils/copy'
import { formatDateTime } from '@/utils/date-formatter' import { formatDateTime } from '@/utils/date-formatter'
...@@ -16,6 +25,8 @@ import { ...@@ -16,6 +25,8 @@ import {
import { defaultPersonalAppConfigState, usePersonalAppConfigStore } from '@/store/modules/personal-app-config' import { defaultPersonalAppConfigState, usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
import { ApplicationMallInfo } from '../../../personal-app/types' import { ApplicationMallInfo } from '../../../personal-app/types'
import ApiCallDrawer from './components/api-call-drawer.vue' import ApiCallDrawer from './components/api-call-drawer.vue'
import WechatPublicNumberModal from './components/wechat-public-number-modal.vue'
import WebsiteEmbeddingModal from './components/website-embedding-modal.vue'
const { t } = useI18n() const { t } = useI18n()
...@@ -36,6 +47,8 @@ const applicationMallInfo = ref<ApplicationMallInfo>({ ...@@ -36,6 +47,8 @@ const applicationMallInfo = ref<ApplicationMallInfo>({
launchTime: '', launchTime: '',
}) })
const isShowAPICallDrawer = ref(false) const isShowAPICallDrawer = ref(false)
const isShowWebsiteEmbeddingModal = ref(false)
const isShowWechatPublicNumberModal = ref(false)
watch( watch(
() => saleApplicationsInfo.value, () => saleApplicationsInfo.value,
...@@ -346,6 +359,126 @@ function handleAPIConfiguration() { ...@@ -346,6 +359,126 @@ function handleAPIConfiguration() {
</div> </div>
</td> </td>
</tr> </tr>
<tr>
<td class="border-[1px] border-[#efeff5]">
<div class="flex items-center justify-start p-[12px]">
<Wechat theme="filled" size="24" fill="#000dff" :stroke-width="3" />
<div class="ml-[12px]">
<div class="flex">
<span class="flex items-center gap-[4px]">
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.wechat_public_account',
)
}}
</span>
</div>
<div class="text-[#84868c]">
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.wechat_public_account_docs',
)
}}
</div>
</div>
</div>
</td>
<td class="border-[1px] border-[#efeff5] p-[12px]">
<div class="flex flex-col items-start justify-center">
<div class="rounded-[4px] bg-[#BBB] px-[14px] py-[3px] text-[#fff]">
{{
t('personal_space_module.agent_module.agent_setting_module.agent_publish_module.not_configured_btn')
}}
</div>
</div>
<div class="flex flex-col items-start justify-center">
<div class="mb-[4px] rounded-[4px] bg-[#34a853] px-[14px] py-[3px] text-[#fff]">
{{ t('common_module.be_authorized') }}
</div>
<div class="text-[#84868c]">
{{
applicationMallInfo?.launchTime
? formatDateTime(applicationMallInfo.launchTime) + t('common_module.publish')
: ''
}}
</div>
</div>
</td>
<td class="border-[1px] border-[#efeff5] p-[12px]">
<div class="flex select-none">
<button
class="flex cursor-pointer items-center justify-start rounded-[5px] border-[1px] border-[#000DFF] bg-[#f7f7f9] px-[12px] py-[4px] text-[14px] text-[#000DFF] hover:opacity-80"
>
<SettingOne theme="outline" size="16" fill="#000dff" :stroke-width="3" class="mr-[3px]" />
<span>
{{ t('common_module.config') }}
</span>
</button>
<button
class="flex cursor-pointer items-center justify-start rounded-[5px] border-[1px] border-[#000DFF] bg-[#f7f7f9] px-[12px] py-[4px] text-[14px] text-[#000DFF] hover:opacity-80"
@click="isShowWechatPublicNumberModal = true"
>
<Permissions theme="outline" size="16" fill="#000dff" :stroke-width="3" class="mr-[3px]" />
<span>
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.authorization_information',
)
}}
</span>
</button>
</div>
</td>
</tr>
<tr>
<td class="border-[1px] border-[#efeff5]">
<div class="flex items-center justify-start p-[12px]">
<SourceCode theme="filled" size="24" fill="#000dff" :stroke-width="3" />
<div class="ml-[12px]">
<div class="flex">
<span class="flex items-center gap-[4px]">
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.website_embedding',
)
}}
</span>
</div>
<div class="text-[#84868c]">
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.website_embedding_docs',
)
}}
</div>
</div>
</div>
</td>
<td class="border-[1px] border-[#efeff5] p-[12px]">
<span>---</span>
</td>
<td class="border-[1px] border-[#efeff5] p-[12px]">
<div class="flex select-none">
<div
class="flex cursor-pointer items-center justify-start rounded-[5px] border-[1px] border-[#000DFF] bg-[#f7f7f9] px-[12px] py-[4px] text-[14px] text-[#000DFF] hover:opacity-80"
@click="isShowWebsiteEmbeddingModal = true"
>
<SettingOne theme="outline" size="16" fill="#000dff" :stroke-width="3" class="mr-[3px]" />
<span>
{{ t('common_module.config') }}
</span>
</div>
</div>
</td>
</tr>
</tbody> </tbody>
</table> </table>
...@@ -358,5 +491,8 @@ function handleAPIConfiguration() { ...@@ -358,5 +491,8 @@ function handleAPIConfiguration() {
@update="(newValue: string) => (applicationMallInfo.isSale = newValue)" @update="(newValue: string) => (applicationMallInfo.isSale = newValue)"
@trigger-get-application-mall-info="handleGetApplicationMallInfo" @trigger-get-application-mall-info="handleGetApplicationMallInfo"
/> />
<WechatPublicNumberModal v-model:show-modal="isShowWechatPublicNumberModal" />
<WebsiteEmbeddingModal v-model:show-modal="isShowWebsiteEmbeddingModal" />
</div> </div>
</template> </template>
<script setup lang="ts">
import { readonly, ref, toValue, watch } from 'vue'
import { useI18n } from 'vue-i18n'
const showModal = defineModel<boolean>('showModal', { default: false })
const { t } = useI18n()
const embedCodeEum = readonly({
fullScreen: `\`\`\`html\n <!-- ${t('personal_space_module.agent_module.agent_setting_module.agent_publish_module.website_embedding_modal.code_location_docs')} -->\n <script src="https://gsst-poe-sit.gz.bcebos.com/v1/package/embed-web-sdk.min.js"><\u002fscript>\n <script>\n \tnew window.EmbedWebSDK({ appId: 'cc0647c66dfc4c9f80ec839f66f8d433' })\n <\u002fscript> \n\`\`\``,
popupBubble: `\`\`\`html\n <!-- ${t('personal_space_module.agent_module.agent_setting_module.agent_publish_module.website_embedding_modal.code_location_docs')} -->\n <script src="https://gsst-poe-sit.gz.bcebos.com/v1/package/embed-web-sdk.min.js"><\u002fscript>\n <script>\n \tnew window.PopupBubbleWebSDK({ appId: 'cc0647c66dfc4c9f80ec839f66f8d433' })\n <\u002fscript> \n\`\`\``,
})
const currentShowMethod = ref(0)
const codeContent = ref('')
watch(
currentShowMethod,
(newValue) => {
switch (newValue) {
case 0:
codeContent.value = toValue(embedCodeEum.fullScreen)
break
case 1:
codeContent.value = toValue(embedCodeEum.popupBubble)
break
}
},
{ immediate: true },
)
function handleCurrentShowMethodSwitch(index: number) {
currentShowMethod.value = index
}
function onModalAfterLeave() {
currentShowMethod.value = 0
}
</script>
<template>
<n-modal
v-model:show="showModal"
style="width: 800px"
preset="card"
:title="
t('personal_space_module.agent_module.agent_setting_module.agent_publish_module.website_embedding_modal.title')
"
size="huge"
:bordered="false"
header-style="padding-bottom: 15px;"
:on-after-leave="onModalAfterLeave"
>
<div>
<ul class="grid select-none grid-cols-2 gap-[18px]">
<li
class="cursor-pointer rounded-[10px] border border-[#999] p-[21px] transition"
:class="{ '!border-[#000DFF]': currentShowMethod === 0 }"
@click="handleCurrentShowMethodSwitch(0)"
>
<h4 class="text-center text-[16px] font-medium">
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.website_embedding_modal.whole_plane',
)
}}
</h4>
<p class="mt-[16px] text-[14px] leading-[18px] text-[#999]">
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.website_embedding_modal.whole_plane_docs',
)
}}
</p>
<div class="mt-[18px] w-full">
<div
class="h-[160px] bg-[url('@/assets/images/agent-publish/website-embedding-modal/full-screen.png')] bg-center bg-no-repeat"
></div>
</div>
</li>
<li
class="cursor-pointer rounded-[10px] border border-[#999] p-[21px] transition"
:class="{ '!border-[#000DFF]': currentShowMethod === 1 }"
@click="handleCurrentShowMethodSwitch(1)"
>
<h4 class="text-center text-[16px] font-medium">
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.website_embedding_modal.popup_bubble',
)
}}
</h4>
<p class="mt-[16px] text-[14px] leading-[18px] text-[#999]">
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.website_embedding_modal.popup_bubble_docs',
)
}}
</p>
<div class="mt-[18px] w-full">
<div
class="h-[160px] bg-[url('@/assets/images/agent-publish/website-embedding-modal/full-screen.png')] bg-center bg-no-repeat"
></div>
</div>
</li>
</ul>
<div class="mt-[19px]">
<MarkdownRender :raw-text-content="codeContent" color="#999" />
</div>
</div>
</n-modal>
</template>
<script setup lang="ts">
import { Help } from '@icon-park/vue-next'
import { useI18n } from 'vue-i18n'
const showModal = defineModel<boolean>('showModal', { default: false })
const { t } = useI18n()
function handlePositiveClick() {}
</script>
<template>
<n-modal
v-model:show="showModal"
style="width: 800px"
preset="card"
:title="
t('personal_space_module.agent_module.agent_setting_module.agent_publish_module.wechat_public_number_modal.title')
"
size="huge"
:bordered="false"
header-style="padding-bottom: 15px;"
>
<div>
<p class="text-[14px] leading-[21px] text-[#999]">
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.wechat_public_number_modal.docs',
)
}}
</p>
<ul class="mt-[30px]">
<li class="flex items-center">
<span class="flex items-center text-[14px] font-medium"
>{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.wechat_public_number_modal.public_number_authentication',
)
}}
<n-popover trigger="hover" :width="400">
<template #trigger>
<Help class="ml-[5px]" theme="outline" size="16" fill="#999" :stroke-width="3" />
</template>
<span>
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.wechat_public_number_modal.public_number_authentication_docs',
)
}}
</span>
</n-popover>
</span>
<span class="ml-[30px]"><n-radio disabled :label="t('common_module.not_certified_yet')" /></span>
</li>
<li class="mt-[30px]">
<div>
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.wechat_public_number_modal.public_number_name',
)
}}
</div>
<div class="mt-[19px]">
<n-input value="公众号名称" type="text" disabled placeholder="-" />
</div>
</li>
<li class="mt-[30px]">
<div>
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.wechat_public_number_modal.public_number_picture',
)
}}
</div>
<div class="mt-[19px]">
<n-image width="100" src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg" />
</div>
</li>
</ul>
</div>
<template #footer>
<div class="mt-[30px] text-center">
<n-popconfirm :width="500" @positive-click="handlePositiveClick">
<template #trigger>
<n-button class="!bg-[#FFF2F2]" color="#F25744" ghost round>{{
t('common_module.cancel_authorization')
}}</n-button>
</template>
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_publish_module.wechat_public_number_modal.cancel_authorization_docs',
)
}}
</n-popconfirm>
</div>
</template>
</n-modal>
</template>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment