feat: 增加知识库新类型FAQ知识库
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
<script setup lang="ts">
|
||||
import type {IDomEditor} from '@wangeditor/editor';
|
||||
|
||||
import {onBeforeUnmount, ref, shallowRef, watch} from 'vue';
|
||||
|
||||
import {$t} from '@easyflow/locales';
|
||||
|
||||
import {ElButton, ElDialog, ElForm, ElFormItem, ElInput, ElMessage} from 'element-plus';
|
||||
import DOMPurify from 'dompurify';
|
||||
import {Editor, Toolbar} from '@wangeditor/editor-for-vue';
|
||||
import '@wangeditor/editor/dist/css/style.css';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
data: {
|
||||
type: Object as any,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['submit', 'update:modelValue']);
|
||||
|
||||
const editorRef = shallowRef<IDomEditor | null>(null);
|
||||
const form = ref<any>({
|
||||
id: '',
|
||||
collectionId: '',
|
||||
question: '',
|
||||
answerHtml: '',
|
||||
orderNo: 0,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(newData: any) => {
|
||||
form.value = {
|
||||
id: newData?.id || '',
|
||||
collectionId: newData?.collectionId || '',
|
||||
question: newData?.question || '',
|
||||
answerHtml: newData?.answerHtml || '',
|
||||
orderNo: newData?.orderNo ?? 0,
|
||||
};
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
const toolbarConfig = {
|
||||
excludeKeys: [
|
||||
'uploadImage',
|
||||
'insertImage',
|
||||
'group-image',
|
||||
'insertVideo',
|
||||
'group-video',
|
||||
'uploadVideo',
|
||||
'todo',
|
||||
'emotion',
|
||||
],
|
||||
};
|
||||
const editorConfig = {
|
||||
placeholder: $t('documentCollection.faq.answerPlaceholder'),
|
||||
};
|
||||
|
||||
const handleEditorCreated = (editor: IDomEditor) => {
|
||||
editorRef.value = editor;
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
emit('update:modelValue', false);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!form.value.question?.trim()) {
|
||||
ElMessage.error($t('documentCollection.faq.questionRequired'));
|
||||
return;
|
||||
}
|
||||
const sanitizedHtml = DOMPurify.sanitize(form.value.answerHtml || '');
|
||||
const pureText = sanitizedHtml.replace(/<[^>]*>/g, '').trim();
|
||||
if (!pureText) {
|
||||
ElMessage.error($t('documentCollection.faq.answerRequired'));
|
||||
return;
|
||||
}
|
||||
emit('submit', {
|
||||
...form.value,
|
||||
question: form.value.question.trim(),
|
||||
answerHtml: sanitizedHtml,
|
||||
orderNo: Number(form.value.orderNo) || 0,
|
||||
});
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value;
|
||||
if (editor) {
|
||||
editor.destroy();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElDialog
|
||||
class="faq-edit-dialog"
|
||||
:model-value="modelValue"
|
||||
:title="form.id ? $t('button.edit') : $t('button.add')"
|
||||
width="min(920px, 92vw)"
|
||||
:close-on-click-modal="false"
|
||||
@close="closeDialog"
|
||||
>
|
||||
<ElForm class="faq-form" label-position="top">
|
||||
<ElFormItem :label="$t('documentCollection.faq.question')">
|
||||
<ElInput
|
||||
v-model="form.question"
|
||||
:placeholder="$t('documentCollection.faq.questionPlaceholder')"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem :label="$t('documentCollection.faq.answer')">
|
||||
<div class="editor-wrapper">
|
||||
<Toolbar
|
||||
:editor="editorRef"
|
||||
:default-config="toolbarConfig"
|
||||
mode="default"
|
||||
/>
|
||||
<Editor
|
||||
v-model="form.answerHtml"
|
||||
:default-config="editorConfig"
|
||||
mode="default"
|
||||
@on-created="handleEditorCreated"
|
||||
/>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton class="footer-btn" @click="closeDialog">{{ $t('button.cancel') }}</ElButton>
|
||||
<ElButton class="footer-btn" type="primary" @click="handleSubmit">
|
||||
{{ $t('button.save') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.faq-form :deep(.el-form-item__label) {
|
||||
padding-bottom: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.editor-wrapper {
|
||||
width: 100%;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 10px;
|
||||
background: var(--el-fill-color-blank);
|
||||
overflow: hidden;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.editor-wrapper:focus-within {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
:deep(.w-e-toolbar) {
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
background: var(--el-fill-color-blank);
|
||||
}
|
||||
|
||||
:deep(.w-e-text-container) {
|
||||
min-height: 320px;
|
||||
background: var(--el-fill-color-blank);
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.footer-btn {
|
||||
min-width: 88px;
|
||||
}
|
||||
|
||||
:deep(.faq-edit-dialog .el-dialog) {
|
||||
border-radius: 14px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.faq-edit-dialog .el-dialog__header) {
|
||||
margin-right: 0;
|
||||
padding: 18px 22px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
:deep(.faq-edit-dialog .el-dialog__body) {
|
||||
padding: 18px 22px 16px;
|
||||
}
|
||||
|
||||
:deep(.faq-edit-dialog .el-dialog__footer) {
|
||||
padding: 12px 22px 18px;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user