mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-28 23:32:48 +00:00
paragraph
This commit is contained in:
parent
7d898d870c
commit
1390ddb36e
|
|
@ -20,6 +20,7 @@
|
|||
"axios": "^1.8.4",
|
||||
"dingtalk-jsapi": "^3.1.0",
|
||||
"element-plus": "^2.9.10",
|
||||
"md-editor-v3": "^5.6.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^3.0.1",
|
||||
"use-element-plus-theme": "^0.0.5",
|
||||
|
|
|
|||
|
|
@ -3,9 +3,4 @@
|
|||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
<style lang="scss" scoped>
|
||||
.custom-slider {
|
||||
.el-input-number.is-without-controls .el-input__wrapper {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ import InfiniteScroll from './infinite-scroll/index.vue'
|
|||
import ModelSelect from './model-select/index.vue'
|
||||
import ReadWrite from './read-write/index.vue'
|
||||
import AutoTooltip from './auto-tooltip/index.vue'
|
||||
import MdEditor from './markdown/MdEditor.vue'
|
||||
import MdPreview from './markdown/MdPreview.vue'
|
||||
import MdEditorMagnify from './markdown/MdEditorMagnify.vue'
|
||||
export default {
|
||||
install(app: App) {
|
||||
app.component('LogoFull', LogoFull)
|
||||
|
|
@ -35,5 +38,8 @@ export default {
|
|||
app.component('ModelSelect', ModelSelect)
|
||||
app.component('ReadWrite', ReadWrite)
|
||||
app.component('AutoTooltip', AutoTooltip)
|
||||
app.component('MdPreview', MdPreview)
|
||||
app.component('MdEditor', MdEditor)
|
||||
app.component('MdEditorMagnify', MdEditorMagnify)
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
<template>
|
||||
<div class="charts-container">
|
||||
<div ref="chartsRef" :style="style" v-resize="changeChartSize"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, watch, onBeforeUnmount, ref } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
const tmp = ref()
|
||||
const props = defineProps<{ option: string }>()
|
||||
const chartsRef = ref()
|
||||
|
||||
const style = ref({
|
||||
height: '220px',
|
||||
width: '100%'
|
||||
})
|
||||
|
||||
function initChart() {
|
||||
if (chartsRef.value) {
|
||||
let myChart = echarts?.getInstanceByDom(chartsRef.value)
|
||||
if (myChart === null || myChart === undefined) {
|
||||
myChart = echarts.init(chartsRef.value)
|
||||
}
|
||||
const option = JSON.parse(props.option)
|
||||
if (option.actionType === 'EVAL') {
|
||||
myChart.setOption(evalParseOption(option), true)
|
||||
} else {
|
||||
myChart.setOption(jsonParseOption(option), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
function jsonParseOption(option: any) {
|
||||
if (option.style) {
|
||||
style.value = option.style
|
||||
}
|
||||
|
||||
if (option.option) {
|
||||
// 渲染数据
|
||||
return option.option
|
||||
}
|
||||
return option
|
||||
}
|
||||
function evalParseOption(option_json: any) {
|
||||
if (option_json.style) {
|
||||
style.value = option_json.style
|
||||
}
|
||||
let option = {}
|
||||
echarts
|
||||
tmp.value = echarts
|
||||
eval(option_json.option)
|
||||
return option
|
||||
}
|
||||
|
||||
function changeChartSize() {
|
||||
echarts?.getInstanceByDom(chartsRef.value)?.resize()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.option,
|
||||
(val) => {
|
||||
if (val) {
|
||||
nextTick(() => {
|
||||
initChart()
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initChart()
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
echarts.getInstanceByDom(chartsRef.value)?.dispose()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.charts-container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.charts-container::-webkit-scrollbar-track-piece {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border-left: 1px solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.charts-container::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.charts-container::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
|
||||
background-clip: padding-box;
|
||||
|
||||
-webkit-border-radius: 5px;
|
||||
|
||||
-moz-border-radius: 5px;
|
||||
|
||||
border-radius: 5px;
|
||||
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.charts-container::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
|
||||
-webkit-border-radius: 5px;
|
||||
|
||||
-moz-border-radius: 5px;
|
||||
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<template>
|
||||
<div>
|
||||
<DynamicsForm
|
||||
:disabled="is_submit || disabled"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
ref="dynamicsFormRef"
|
||||
:render_data="form_field_list"
|
||||
label-suffix=":"
|
||||
v-model="form_data"
|
||||
:model="form_data"
|
||||
></DynamicsForm>
|
||||
<el-button
|
||||
:type="is_submit ? 'info' : 'primary'"
|
||||
:disabled="is_submit || disabled"
|
||||
@click="submit"
|
||||
>{{$t('common.submit')}}</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
form_setting: string
|
||||
disabled?: boolean
|
||||
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
||||
child_node?: any
|
||||
chat_record_id?: string
|
||||
runtime_node_id?: string
|
||||
}>(),
|
||||
{
|
||||
disabled: false
|
||||
}
|
||||
)
|
||||
const form_setting_data = computed(() => {
|
||||
if (props.form_setting) {
|
||||
return JSON.parse(props.form_setting)
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
const _submit = ref<boolean>(false)
|
||||
/**
|
||||
* 表单字段列表
|
||||
*/
|
||||
const form_field_list = computed(() => {
|
||||
if (form_setting_data.value.form_field_list) {
|
||||
return form_setting_data.value.form_field_list
|
||||
}
|
||||
return []
|
||||
})
|
||||
const is_submit = computed(() => {
|
||||
if (_submit.value) {
|
||||
return true
|
||||
}
|
||||
if (form_setting_data.value.is_submit) {
|
||||
return form_setting_data.value.is_submit
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
const _form_data = ref<any>({})
|
||||
const form_data = computed({
|
||||
get: () => {
|
||||
if (form_setting_data.value.is_submit) {
|
||||
return form_setting_data.value.form_data
|
||||
} else {
|
||||
return _form_data.value
|
||||
}
|
||||
},
|
||||
set: (v) => {
|
||||
_form_data.value = v
|
||||
}
|
||||
})
|
||||
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
|
||||
const submit = () => {
|
||||
dynamicsFormRef.value?.validate().then(() => {
|
||||
_submit.value = true
|
||||
if (props.sendMessage) {
|
||||
props.sendMessage('', 'old', {
|
||||
child_node: props.child_node,
|
||||
runtime_node_id: props.runtime_node_id,
|
||||
chat_record_id: props.chat_record_id,
|
||||
node_data: form_data.value
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<div ref="htmlRef" :innerHTML="source"></div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
const htmlRef = ref<HTMLElement>()
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
source?: string
|
||||
script_exec?: boolean
|
||||
}>(),
|
||||
{
|
||||
source: '',
|
||||
script_exec: true
|
||||
}
|
||||
)
|
||||
onMounted(() => {
|
||||
if (htmlRef.value && props.script_exec) {
|
||||
const range = document.createRange()
|
||||
range.selectNode(htmlRef.value)
|
||||
const scripts = htmlRef.value.getElementsByTagName('script')
|
||||
if (scripts) {
|
||||
var documentFragment = range.createContextualFragment(
|
||||
[...scripts]
|
||||
.map((item: HTMLElement) => {
|
||||
htmlRef.value?.removeChild(item)
|
||||
return item.outerHTML
|
||||
})
|
||||
.join('\n')
|
||||
)
|
||||
htmlRef.value.appendChild(documentFragment)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<MdEditor :language="language" noIconfont noPrettier v-bind="$attrs">
|
||||
<template #defFooters>
|
||||
<slot name="defFooters"> </slot>
|
||||
</template>
|
||||
</MdEditor>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { MdEditor, config } from 'md-editor-v3'
|
||||
import { getBrowserLang } from '@/locales/index'
|
||||
import './assets/markdown-iconfont.js'
|
||||
// 引入公共库中的语言配置
|
||||
import ZH_TW from '@vavt/cm-extension/dist/locale/zh-TW'
|
||||
|
||||
defineOptions({ name: 'MdEditor' })
|
||||
const language = computed(() => localStorage.getItem('MaxKB-locale') || getBrowserLang() || '')
|
||||
config({
|
||||
editorConfig: {
|
||||
languageUserDefined: {
|
||||
'zh-Hant': ZH_TW
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<MdEditor
|
||||
v-bind="$attrs"
|
||||
v-model="data"
|
||||
:preview="false"
|
||||
:toolbars="[]"
|
||||
class="magnify-md-editor"
|
||||
:footers="footers"
|
||||
>
|
||||
<template #defFooters>
|
||||
<el-button text type="info" @click="openDialog">
|
||||
<AppIcon class="color-secondary" iconName="app-magnify" style="font-size: 16px"></AppIcon>
|
||||
</el-button>
|
||||
</template>
|
||||
</MdEditor>
|
||||
<!-- 回复内容弹出层 -->
|
||||
<el-dialog v-model="dialogVisible" :title="title" append-to-body align-center>
|
||||
<MdEditor v-model="cloneContent" :preview="false" :toolbars="[]" :footers="[]"></MdEditor>
|
||||
<template #footer>
|
||||
<div class="dialog-footer mt-24">
|
||||
<el-button type="primary" @click="submitDialog"> {{ $t('common.confirm') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
defineOptions({ name: 'MdEditorMagnify' })
|
||||
const props = defineProps<{
|
||||
title: String
|
||||
modelValue: any
|
||||
}>()
|
||||
const emit = defineEmits(['update:modelValue', 'submitDialog'])
|
||||
const data = computed({
|
||||
set: (value) => {
|
||||
emit('update:modelValue', value)
|
||||
},
|
||||
get: () => {
|
||||
return props.modelValue
|
||||
}
|
||||
})
|
||||
const dialogVisible = ref(false)
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
emit('submitDialog', cloneContent.value)
|
||||
}
|
||||
})
|
||||
|
||||
const cloneContent = ref('')
|
||||
const footers: any = [null, '=', 0]
|
||||
function openDialog() {
|
||||
cloneContent.value = props.modelValue
|
||||
dialogVisible.value = true
|
||||
}
|
||||
function submitDialog() {
|
||||
emit('submitDialog', cloneContent.value)
|
||||
dialogVisible.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.magnify-md-editor {
|
||||
:deep(.md-editor-footer) {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<MdPreview :language="language" noIconfont noPrettier :codeFoldable="false" v-bind="$attrs" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { MdPreview, config } from 'md-editor-v3'
|
||||
import { getBrowserLang } from '@/locales/index'
|
||||
import useStore from '@/stores'
|
||||
// 引入公共库中的语言配置
|
||||
import ZH_TW from '@vavt/cm-extension/dist/locale/zh-TW'
|
||||
|
||||
defineOptions({ name: 'MdPreview' })
|
||||
const { user } = useStore()
|
||||
const language = computed(() => user.getLanguage() || getBrowserLang() || '')
|
||||
config({
|
||||
editorConfig: {
|
||||
languageUserDefined: {
|
||||
'zh-Hant': ZH_TW
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
<template>
|
||||
<div>
|
||||
<!-- 推理过程组件 -->
|
||||
<ReasoningRander :content="reasoning_content" v-if="reasoning_content?.trim()" />
|
||||
<template v-for="(item, index) in md_view_list" :key="index">
|
||||
<div
|
||||
v-if="item.type === 'question'"
|
||||
@click="sendMessage ? sendMessage(item.content, 'new') : (content: string) => {}"
|
||||
class="problem-button mt-4 mb-4 flex"
|
||||
:class="sendMessage ? 'cursor' : 'disabled'"
|
||||
>
|
||||
<el-icon class="mr-8" style="margin-top: 2px;">
|
||||
<EditPen />
|
||||
</el-icon>
|
||||
{{ item.content }}
|
||||
</div>
|
||||
<HtmlRander v-else-if="item.type === 'html_rander'" :source="item.content"></HtmlRander>
|
||||
<EchartsRander
|
||||
v-else-if="item.type === 'echarts_rander'"
|
||||
:option="item.content"
|
||||
></EchartsRander>
|
||||
<FormRander
|
||||
:chat_record_id="chat_record_id"
|
||||
:runtime_node_id="runtime_node_id"
|
||||
:child_node="child_node"
|
||||
:disabled="disabled"
|
||||
:send-message="sendMessage"
|
||||
v-else-if="item.type === 'form_rander'"
|
||||
:form_setting="item.content"
|
||||
></FormRander>
|
||||
<MdPreview
|
||||
v-else
|
||||
ref="editorRef"
|
||||
editorId="preview-only"
|
||||
:modelValue="item.content"
|
||||
:key="index"
|
||||
class="maxkb-md"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { config } from 'md-editor-v3'
|
||||
import HtmlRander from './HtmlRander.vue'
|
||||
import EchartsRander from './EchartsRander.vue'
|
||||
import FormRander from './FormRander.vue'
|
||||
import ReasoningRander from './ReasoningRander.vue'
|
||||
config({
|
||||
markdownItConfig(md) {
|
||||
md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
||||
tokens[idx].attrSet('style', 'display:inline-block;min-height:33px;padding:0;margin:0')
|
||||
if (tokens[idx].content) {
|
||||
tokens[idx].attrSet('title', tokens[idx].content)
|
||||
}
|
||||
tokens[idx].attrSet(
|
||||
'onerror',
|
||||
'this.src="/ui/assets/load_error.png";this.onerror=null;this.height="33px"'
|
||||
)
|
||||
return md.renderer.renderToken(tokens, idx, options)
|
||||
}
|
||||
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
|
||||
tokens[idx].attrSet('target', '_blank')
|
||||
return md.renderer.renderToken(tokens, idx, options)
|
||||
}
|
||||
document.appendChild
|
||||
}
|
||||
})
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
source?: string
|
||||
reasoning_content?: string
|
||||
inner_suffix?: boolean
|
||||
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
||||
child_node?: any
|
||||
chat_record_id?: string
|
||||
runtime_node_id?: string
|
||||
disabled?: boolean
|
||||
}>(),
|
||||
{
|
||||
source: '',
|
||||
disabled: false
|
||||
}
|
||||
)
|
||||
const editorRef = ref()
|
||||
const md_view_list = computed(() => {
|
||||
const temp_source = props.source
|
||||
return split_form_rander(
|
||||
split_echarts_rander(split_html_rander(split_quick_question([temp_source])))
|
||||
)
|
||||
})
|
||||
|
||||
const split_quick_question = (result: Array<string>) => {
|
||||
return result
|
||||
.map((item) => split_quick_question_(item))
|
||||
.reduce((x: any, y: any) => {
|
||||
return [...x, ...y]
|
||||
}, [])
|
||||
}
|
||||
const split_quick_question_ = (source: string) => {
|
||||
const temp_md_quick_question_list = source.match(/<quick_question>[\d\D]*?<\/quick_question>/g)
|
||||
const md_quick_question_list = temp_md_quick_question_list
|
||||
? temp_md_quick_question_list.filter((i) => i)
|
||||
: []
|
||||
const split_quick_question_value = source
|
||||
.split(/<quick_question>[\d\D]*?<\/quick_question>/g)
|
||||
.filter((item) => item !== undefined)
|
||||
.filter((item) => !md_quick_question_list?.includes(item))
|
||||
const result = Array.from(
|
||||
{ length: md_quick_question_list.length + split_quick_question_value.length },
|
||||
(v, i) => i
|
||||
).map((index) => {
|
||||
if (index % 2 == 0) {
|
||||
return { type: 'md', content: split_quick_question_value[Math.floor(index / 2)] }
|
||||
} else {
|
||||
return {
|
||||
type: 'question',
|
||||
content: md_quick_question_list[Math.floor(index / 2)]
|
||||
.replace('<quick_question>', '')
|
||||
.replace('</quick_question>', '')
|
||||
}
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
const split_html_rander = (result: Array<any>) => {
|
||||
return result
|
||||
.map((item) => split_html_rander_(item.content, item.type))
|
||||
.reduce((x: any, y: any) => {
|
||||
return [...x, ...y]
|
||||
}, [])
|
||||
}
|
||||
|
||||
const split_html_rander_ = (source: string, type: string) => {
|
||||
const temp_md_quick_question_list = source.match(/<html_rander>[\d\D]*?<\/html_rander>/g)
|
||||
const md_quick_question_list = temp_md_quick_question_list
|
||||
? temp_md_quick_question_list.filter((i) => i)
|
||||
: []
|
||||
const split_quick_question_value = source
|
||||
.split(/<html_rander>[\d\D]*?<\/html_rander>/g)
|
||||
.filter((item) => item !== undefined)
|
||||
.filter((item) => !md_quick_question_list?.includes(item))
|
||||
const result = Array.from(
|
||||
{ length: md_quick_question_list.length + split_quick_question_value.length },
|
||||
(v, i) => i
|
||||
).map((index) => {
|
||||
if (index % 2 == 0) {
|
||||
return { type: type, content: split_quick_question_value[Math.floor(index / 2)] }
|
||||
} else {
|
||||
return {
|
||||
type: 'html_rander',
|
||||
content: md_quick_question_list[Math.floor(index / 2)]
|
||||
.replace('<html_rander>', '')
|
||||
.replace('</html_rander>', '')
|
||||
}
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
const split_echarts_rander = (result: Array<any>) => {
|
||||
return result
|
||||
.map((item) => split_echarts_rander_(item.content, item.type))
|
||||
.reduce((x: any, y: any) => {
|
||||
return [...x, ...y]
|
||||
}, [])
|
||||
}
|
||||
|
||||
const split_echarts_rander_ = (source: string, type: string) => {
|
||||
const temp_md_quick_question_list = source.match(/<echarts_rander>[\d\D]*?<\/echarts_rander>/g)
|
||||
const md_quick_question_list = temp_md_quick_question_list
|
||||
? temp_md_quick_question_list.filter((i) => i)
|
||||
: []
|
||||
const split_quick_question_value = source
|
||||
.split(/<echarts_rander>[\d\D]*?<\/echarts_rander>/g)
|
||||
.filter((item) => item !== undefined)
|
||||
.filter((item) => !md_quick_question_list?.includes(item))
|
||||
const result = Array.from(
|
||||
{ length: md_quick_question_list.length + split_quick_question_value.length },
|
||||
(v, i) => i
|
||||
).map((index) => {
|
||||
if (index % 2 == 0) {
|
||||
return { type: type, content: split_quick_question_value[Math.floor(index / 2)] }
|
||||
} else {
|
||||
return {
|
||||
type: 'echarts_rander',
|
||||
content: md_quick_question_list[Math.floor(index / 2)]
|
||||
.replace('<echarts_rander>', '')
|
||||
.replace('</echarts_rander>', '')
|
||||
}
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
const split_form_rander = (result: Array<any>) => {
|
||||
return result
|
||||
.map((item) => split_form_rander_(item.content, item.type))
|
||||
.reduce((x: any, y: any) => {
|
||||
return [...x, ...y]
|
||||
}, [])
|
||||
}
|
||||
|
||||
const split_form_rander_ = (source: string, type: string) => {
|
||||
const temp_md_quick_question_list = source.match(/<form_rander>[\d\D]*?<\/form_rander>/g)
|
||||
const md_quick_question_list = temp_md_quick_question_list
|
||||
? temp_md_quick_question_list.filter((i) => i)
|
||||
: []
|
||||
const split_quick_question_value = source
|
||||
.split(/<form_rander>[\d\D]*?<\/form_rander>/g)
|
||||
.filter((item) => item !== undefined)
|
||||
.filter((item) => !md_quick_question_list?.includes(item))
|
||||
const result = Array.from(
|
||||
{ length: md_quick_question_list.length + split_quick_question_value.length },
|
||||
(v, i) => i
|
||||
).map((index) => {
|
||||
if (index % 2 == 0) {
|
||||
return { type: type, content: split_quick_question_value[Math.floor(index / 2)] }
|
||||
} else {
|
||||
return {
|
||||
type: 'form_rander',
|
||||
content: md_quick_question_list[Math.floor(index / 2)]
|
||||
.replace('<form_rander>', '')
|
||||
.replace('</form_rander>', '')
|
||||
}
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.problem-button {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: var(--app-layout-bg-color);
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
color: var(--el-text-color-regular);
|
||||
word-break: break-all;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
&:hover {
|
||||
background: var(--app-layout-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-icon) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<template>
|
||||
<div class="reasoning">
|
||||
<el-button text @click="showThink = !showThink" class="reasoning-button">
|
||||
{{ $t('views.applicationWorkflow.nodes.aiChatNode.think') }}
|
||||
<el-icon class="ml-4" :class="showThink ? 'rotate-180' : ''"><ArrowDownBold /> </el-icon>
|
||||
</el-button>
|
||||
<el-collapse-transition>
|
||||
<div class="border-l mt-8" v-show="showThink">
|
||||
<MdPreview
|
||||
ref="editorRef"
|
||||
editorId="preview-only"
|
||||
:modelValue="content"
|
||||
class="reasoning-md"
|
||||
/>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const props = defineProps<{ content?: string }>()
|
||||
const showThink = ref<boolean>(true)
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.reasoning {
|
||||
.reasoning-button {
|
||||
font-size: 14px;
|
||||
color: var(--app-text-color-secondary) !important;
|
||||
}
|
||||
.reasoning-md {
|
||||
padding-left: 8px;
|
||||
--md-color: var(--app-text-color-secondary) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -80,4 +80,5 @@ export default {
|
|||
uploadImagePrompt: 'Please upload an image',
|
||||
},
|
||||
info: 'Base Information',
|
||||
otherSetting: 'Other Settings',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,4 +84,5 @@ export default {
|
|||
uploadImagePrompt: '请上传一张图片',
|
||||
},
|
||||
info: '基本信息',
|
||||
otherSetting: '其他设置',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,9 @@ import application from './application'
|
|||
import problem from './problem'
|
||||
import applicationOverview from './application-overview'
|
||||
import applicationWorkflow from './application-workflow'
|
||||
import paragraph from './paragraph'
|
||||
// import notFound from './404'
|
||||
|
||||
// import paragraph from './paragraph'
|
||||
|
||||
// import log from './log'
|
||||
|
||||
// import operateLog from './operate-log'
|
||||
|
|
@ -30,8 +29,8 @@ export default {
|
|||
problem,
|
||||
applicationOverview,
|
||||
applicationWorkflow,
|
||||
paragraph,
|
||||
// notFound,
|
||||
// paragraph,
|
||||
|
||||
// log,
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ export default {
|
|||
label: '选择器',
|
||||
placeholder: '默认为 body,可输入 .classname/#idname/tagname',
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
ResultSuccess: {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
export default {
|
||||
title: '段落',
|
||||
paragraph_count: '段落',
|
||||
editParagraph: '编辑分段',
|
||||
addParagraph: '添加分段',
|
||||
paragraphDetail: '分段详情',
|
||||
character_count: '个字符',
|
||||
setting: {
|
||||
batchSelected: '批量选择',
|
||||
cancelSelected: '取消选择'
|
||||
},
|
||||
delete: {
|
||||
confirmTitle: '是否删除段落:',
|
||||
confirmMessage: '删除后无法恢复,请谨慎操作。'
|
||||
},
|
||||
relatedProblem: {
|
||||
title: '关联问题',
|
||||
placeholder: '请选择问题'
|
||||
},
|
||||
form: {
|
||||
paragraphTitle: {
|
||||
label: '分段标题',
|
||||
placeholder: '请输入分段标题'
|
||||
},
|
||||
content: {
|
||||
label: '分段内容',
|
||||
placeholder: '请输入分段内容',
|
||||
requiredMessage1: '请输入分段内容',
|
||||
requiredMessage2: '内容最多不超过 100000 个字'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -80,4 +80,5 @@ export default {
|
|||
uploadImagePrompt: '請上傳一張圖片',
|
||||
},
|
||||
info: '使用者資訊',
|
||||
otherSetting: '其他設定',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,40 @@ import router from '@/router'
|
|||
import i18n from '@/locales'
|
||||
import Components from '@/components'
|
||||
import directives from '@/directives'
|
||||
|
||||
import { config } from 'md-editor-v3'
|
||||
import screenfull from 'screenfull'
|
||||
|
||||
import katex from 'katex'
|
||||
import 'katex/dist/katex.min.css'
|
||||
|
||||
import Cropper from 'cropperjs'
|
||||
import 'cropperjs/dist/cropper.css'
|
||||
|
||||
import mermaid from 'mermaid'
|
||||
|
||||
import highlight from 'highlight.js'
|
||||
import 'highlight.js/styles/atom-one-dark.css'
|
||||
|
||||
config({
|
||||
editorExtensions: {
|
||||
highlight: {
|
||||
instance: highlight
|
||||
},
|
||||
screenfull: {
|
||||
instance: screenfull
|
||||
},
|
||||
katex: {
|
||||
instance: katex
|
||||
},
|
||||
cropper: {
|
||||
instance: Cropper
|
||||
},
|
||||
mermaid: {
|
||||
instance: mermaid
|
||||
}
|
||||
}
|
||||
})
|
||||
const app = createApp(App)
|
||||
app.use(createPinia())
|
||||
for (const [key, component] of Object.entries(ElementPlusIcons)) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const ParagraphRouter = {
|
|||
children: [
|
||||
{
|
||||
path: '/paragraph/:id/:documentId',
|
||||
name: 'Paragraph1',
|
||||
name: 'ParagraphIndex',
|
||||
meta: { activeMenu: '/knowledge' },
|
||||
component: () => import('@/views/paragraph/index.vue'),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -93,3 +93,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-slider {
|
||||
.el-input-number.is-without-controls .el-input__wrapper {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,13 @@
|
|||
// card
|
||||
.el-card {
|
||||
--el-card-padding: calc(var(--app-base-px) * 2);
|
||||
--el-card-border-radius: 8px;
|
||||
--el-card-border-radius: 6px;
|
||||
box-shadow: 0px 2px 4px 0px rgba(31, 35, 41, 0.12) !important;
|
||||
border: none;
|
||||
&.is-never-shadow {
|
||||
border: 1px solid var(--el-card-border-color);
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// tree
|
||||
|
|
|
|||
|
|
@ -4,3 +4,5 @@
|
|||
@use './app.scss';
|
||||
@use './component.scss';
|
||||
@import 'nprogress/nprogress.css';
|
||||
@import 'md-editor-v3/lib/style.css';
|
||||
@import './md-editor.scss';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
.md-editor {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.md-editor-preview {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: inherit;
|
||||
word-break: break-word;
|
||||
table {
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.md-editor-admonition {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
img {
|
||||
border: 0 !important;
|
||||
max-width: 360px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.md-editor-preview {
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.md-editor-preview-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.md-editor-footer {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.ͼ1 .cm-placeholder {
|
||||
color: var(--app-input-color-placeholder);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
|
@ -502,12 +502,6 @@
|
|||
</el-table-column>
|
||||
</app-table>
|
||||
</div>
|
||||
|
||||
<ImportDocumentDialog ref="ImportDocumentDialogRef" :title="title" @refresh="refresh" />
|
||||
<SyncWebDialog ref="SyncWebDialogRef" @refresh="refresh" />
|
||||
<!-- 选择知识库 -->
|
||||
<SelectDatasetDialog ref="SelectDatasetDialogRef" @refresh="refreshMigrate" />
|
||||
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="getList" />
|
||||
</div>
|
||||
</el-card>
|
||||
<div class="mul-operation w-full flex" v-if="multipleSelection.length !== 0">
|
||||
|
|
@ -525,7 +519,14 @@
|
|||
{{ $t('common.clear') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<EmbeddingContentDialog ref="embeddingContentDialogRef"></EmbeddingContentDialog>
|
||||
|
||||
<ImportDocumentDialog ref="ImportDocumentDialogRef" :title="title" @refresh="refresh" />
|
||||
<SyncWebDialog ref="SyncWebDialogRef" @refresh="refresh" />
|
||||
<!-- 选择知识库 -->
|
||||
<SelectDatasetDialog ref="SelectDatasetDialogRef" @refresh="refreshMigrate" />
|
||||
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="getList" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
|
@ -768,7 +769,7 @@ function rowClickHandle(row: any, column: any) {
|
|||
return
|
||||
}
|
||||
|
||||
router.push({ path: `/knowledge/${id}/${row.id}` })
|
||||
router.push({ path: `/paragraph/${id}/${row.id}` })
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -930,7 +931,7 @@ function getList(bool?: boolean) {
|
|||
folder_id: folderId,
|
||||
}
|
||||
documentApi
|
||||
.getDocument( id as string, paginationConfig.value, param, bool ? undefined : loading)
|
||||
.getDocument(id as string, paginationConfig.value, param, bool ? undefined : loading)
|
||||
.then((res) => {
|
||||
documentData.value = res.data.records
|
||||
paginationConfig.value.total = res.data.total
|
||||
|
|
@ -938,7 +939,7 @@ function getList(bool?: boolean) {
|
|||
}
|
||||
|
||||
function getDetail() {
|
||||
knowledge.asyncGetDatasetDetail( id, loading).then((res: any) => {
|
||||
knowledge.asyncGetDatasetDetail(id, loading).then((res: any) => {
|
||||
datasetDetail.value = res.data
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('common.info') }}
|
||||
</h4>
|
||||
<BaseForm ref="BaseFormRef" :data="detail"/>
|
||||
<BaseForm ref="BaseFormRef" :data="detail" />
|
||||
|
||||
<el-form
|
||||
ref="webFormRef"
|
||||
|
|
@ -18,23 +18,33 @@
|
|||
require-asterisk-position="right"
|
||||
>
|
||||
<el-form-item :label="$t('views.knowledge.knowledgeType.label')" required>
|
||||
<el-card shadow="never" class="mb-8" style="width: 50%" v-if="detail.type === 0">
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-8 w-full"
|
||||
style="line-height: 22px"
|
||||
v-if="detail.type === 0"
|
||||
>
|
||||
<div class="flex align-center">
|
||||
<el-avatar class="mr-8 avatar-blue" shape="square" :size="32">
|
||||
<img src="@/assets/knowledge/icon_document.svg" style="width: 58%" alt=""/>
|
||||
<img src="@/assets/knowledge/icon_document.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<div>
|
||||
<div>{{ $t('views.knowledge.knowledgeType.generalKnowledge') }}</div>
|
||||
<el-text type="info"
|
||||
>{{ $t('views.knowledge.knowledgeType.generalInfo') }}
|
||||
>{{ $t('views.knowledge.knowledgeType.generalInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="mb-8" style="width: 50%" v-if="detail?.type === 1">
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-8 w-full"
|
||||
style="line-height: 22px"
|
||||
v-if="detail?.type === 1"
|
||||
>
|
||||
<div class="flex align-center">
|
||||
<el-avatar class="mr-8 avatar-purple" shape="square" :size="32">
|
||||
<img src="@/assets/knowledge/icon_web.svg" style="width: 58%" alt=""/>
|
||||
<img src="@/assets/knowledge/icon_web.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<div>
|
||||
<div>{{ $t('views.knowledge.knowledgeType.webKnowledge') }}</div>
|
||||
|
|
@ -44,17 +54,22 @@
|
|||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="mb-8" style="width: 50%" v-if="detail?.type === 2">
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-8 w-full"
|
||||
style="line-height: 22px"
|
||||
v-if="detail?.type === 2"
|
||||
>
|
||||
<div class="flex align-center">
|
||||
<el-avatar shape="square" :size="32" style="background: none">
|
||||
<img src="@/assets/knowledge/logo_lark.svg" style="width: 100%" alt=""/>
|
||||
<img src="@/assets/knowledge/logo_lark.svg" style="width: 100%" alt="" />
|
||||
</el-avatar>
|
||||
<div>
|
||||
<p>
|
||||
<el-text>{{ $t('views.knowledge.knowledgeType.larkKnowledge') }}</el-text>
|
||||
</p>
|
||||
<el-text type="info"
|
||||
>{{ $t('views.knowledge.knowledgeType.larkInfo') }}
|
||||
>{{ $t('views.knowledge.knowledgeType.larkInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -107,48 +122,23 @@
|
|||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div v-if="application_id_list.length > 0">
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('views.dataset.relatedApplications') }}
|
||||
</h4>
|
||||
<el-row :gutter="12">
|
||||
<el-col
|
||||
:span="12"
|
||||
v-for="(item, index) in application_list.filter((obj: any) =>
|
||||
application_id_list.some((v: any) => v === obj?.id),
|
||||
)"
|
||||
:key="index"
|
||||
class="mb-16"
|
||||
>
|
||||
<el-card shadow="never">
|
||||
<div class="flex-between">
|
||||
<div class="flex align-center">
|
||||
<el-avatar
|
||||
v-if="isAppIcon(item?.icon)"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
class="mr-12"
|
||||
>
|
||||
<img :src="item?.icon" alt=""/>
|
||||
</el-avatar>
|
||||
<el-avatar
|
||||
v-else-if="item?.name"
|
||||
:name="item?.name"
|
||||
pinyinColor
|
||||
shape="square"
|
||||
:size="32"
|
||||
class="mr-12"
|
||||
/>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div v-if="detail.type === 0">
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('common.otherSetting') }}
|
||||
</h4>
|
||||
</div>
|
||||
<el-form-item :label="$t('上传的每个文档最大限制')">
|
||||
<el-slider
|
||||
v-model="form.max_paragraph_char_number"
|
||||
show-input
|
||||
:show-input-controls="false"
|
||||
:min="500"
|
||||
:max="100000"
|
||||
class="custom-slider"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="text-right">
|
||||
<el-button @click="submit" type="primary"> {{ $t('common.save') }}</el-button>
|
||||
</div>
|
||||
|
|
@ -159,22 +149,22 @@
|
|||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {ref, onMounted, reactive} from 'vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import BaseForm from '@/views/knowledge/component/BaseForm.vue'
|
||||
import KnowledgeApi from '@/api/knowledge/knowledge'
|
||||
import type {ApplicationFormType} from '@/api/type/application'
|
||||
import {MsgSuccess, MsgConfirm} from '@/utils/message'
|
||||
import {isAppIcon} from '@/utils/common'
|
||||
import type { ApplicationFormType } from '@/api/type/application'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import { isAppIcon } from '@/utils/common'
|
||||
import useStore from '@/stores'
|
||||
import {t} from '@/locales'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: {id},
|
||||
params: { id },
|
||||
} = route as any
|
||||
|
||||
const {knowledge} = useStore()
|
||||
const { knowledge } = useStore()
|
||||
const webFormRef = ref()
|
||||
const BaseFormRef = ref()
|
||||
const loading = ref(false)
|
||||
|
|
@ -229,14 +219,14 @@ async function submit() {
|
|||
const obj =
|
||||
detail.value.type === '1' || detail.value.type === '2'
|
||||
? {
|
||||
application_id_list: application_id_list.value,
|
||||
meta: form.value,
|
||||
...BaseFormRef.value.form,
|
||||
}
|
||||
application_id_list: application_id_list.value,
|
||||
meta: form.value,
|
||||
...BaseFormRef.value.form,
|
||||
}
|
||||
: {
|
||||
application_id_list: application_id_list.value,
|
||||
...BaseFormRef.value.form,
|
||||
}
|
||||
application_id_list: application_id_list.value,
|
||||
...BaseFormRef.value.form,
|
||||
}
|
||||
|
||||
if (cloneModelId.value !== BaseFormRef.value.form.embedding_mode_id) {
|
||||
MsgConfirm(t('common.tip'), t('views.knowledge.tip.updateModeMessage'), {
|
||||
|
|
@ -257,8 +247,7 @@ async function submit() {
|
|||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
})
|
||||
.catch(() => {})
|
||||
} else {
|
||||
if (detail.value.type === 2) {
|
||||
KnowledgeApi.putLarkDataset(id, obj, loading).then((res) => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<el-card shadow="hover" class="paragraph-box" @mouseenter="cardEnter()" @mouseleave="cardLeave()">
|
||||
<div class="card-header">
|
||||
<h2>{{ 1111 }}</h2>
|
||||
</div>
|
||||
<MdPreview
|
||||
ref="editorRef"
|
||||
editorId="preview-only"
|
||||
:modelValue="form.content"
|
||||
class="maxkb-md"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, useSlots } from 'vue'
|
||||
import { t } from '@/locales'
|
||||
defineOptions({ name: 'CardBox' })
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
title?: string
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
description?: string
|
||||
/**
|
||||
* 是否展示icon
|
||||
*/
|
||||
showIcon?: boolean
|
||||
}>(),
|
||||
{ title: t('common.title'), description: '', showIcon: true, border: true },
|
||||
)
|
||||
|
||||
const show = ref(false)
|
||||
// card上面存在dropdown菜单
|
||||
const subHovered = ref(false)
|
||||
function cardEnter() {
|
||||
show.value = true
|
||||
subHovered.value = false
|
||||
}
|
||||
|
||||
function cardLeave() {
|
||||
show.value = subHovered.value
|
||||
}
|
||||
|
||||
function subHoveredEnter() {
|
||||
subHovered.value = true
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.card-box {
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
min-height: var(--card-min-height);
|
||||
min-width: var(--card-min-width);
|
||||
.card-header {
|
||||
margin-top: -5px;
|
||||
}
|
||||
.description {
|
||||
line-height: 22px;
|
||||
font-weight: 400;
|
||||
min-height: 70px;
|
||||
.content {
|
||||
display: -webkit-box;
|
||||
height: var(--app-card-box-description-height, 40px);
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
left: 0;
|
||||
min-height: 30px;
|
||||
font-weight: 400;
|
||||
padding: 0 16px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,39 +1,34 @@
|
|||
<template>
|
||||
<LayoutContainer back-to="-1" class="document-detail">
|
||||
<template #header>
|
||||
<div style="width: 78%">
|
||||
<h3 style="display: inline-block">{{ documentDetail?.name }}</h3>
|
||||
<el-text type="info" v-if="documentDetail?.type === '1'"
|
||||
>({{ $t('views.document.form.source_url.label') }}:<el-link
|
||||
:href="documentDetail?.meta?.source_url"
|
||||
target="_blank"
|
||||
>
|
||||
<span class="break-all">{{ documentDetail?.meta?.source_url }} </span></el-link
|
||||
>)
|
||||
</el-text>
|
||||
</div>
|
||||
<div class="header-button">
|
||||
<el-button @click="batchSelectedHandle(true)" v-if="isBatch === false">
|
||||
{{ $t('views.paragraph.setting.batchSelected') }}
|
||||
</el-button>
|
||||
<el-button @click="batchSelectedHandle(false)" v-if="isBatch === true">
|
||||
{{ $t('views.paragraph.setting.cancelSelected') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="addParagraph"
|
||||
type="primary"
|
||||
:disabled="loading"
|
||||
v-if="isBatch === false"
|
||||
<div class="paragraph p-12-24">
|
||||
<div class="flex align-center" style="width: 78%">
|
||||
<back-button to="-1" style="margin-left: -4px"></back-button>
|
||||
<h3 style="display: inline-block">{{ documentDetail?.name }}</h3>
|
||||
<el-text type="info" v-if="documentDetail?.type === '1'"
|
||||
>({{ $t('views.document.form.source_url.label') }}:<el-link
|
||||
:href="documentDetail?.meta?.source_url"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t('views.paragraph.addParagraph') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
class="document-detail__main p-16"
|
||||
<span class="break-all">{{ documentDetail?.meta?.source_url }} </span></el-link
|
||||
>)
|
||||
</el-text>
|
||||
</div>
|
||||
<div class="header-button">
|
||||
<el-button @click="batchSelectedHandle(true)" v-if="isBatch === false">
|
||||
{{ $t('views.paragraph.setting.batchSelected') }}
|
||||
</el-button>
|
||||
<el-button @click="batchSelectedHandle(false)" v-if="isBatch === true">
|
||||
{{ $t('views.paragraph.setting.cancelSelected') }}
|
||||
</el-button>
|
||||
<el-button @click="addParagraph" type="primary" :disabled="loading" v-if="isBatch === false">
|
||||
{{ $t('views.paragraph.addParagraph') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-card
|
||||
style="--el-card-padding: 0"
|
||||
class="paragraph-detail__main mt-16"
|
||||
v-loading="(paginationConfig.current_page === 1 && loading) || changeStateloading"
|
||||
>
|
||||
<div class="flex-between p-8">
|
||||
<div class="flex-between p-12-16 border-b">
|
||||
<span>{{ paginationConfig.total }} {{ $t('views.paragraph.paragraph_count') }}</span>
|
||||
<el-input
|
||||
v-model="search"
|
||||
|
|
@ -51,111 +46,23 @@
|
|||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-scrollbar>
|
||||
<div class="document-detail-height">
|
||||
<el-empty v-if="paragraphDetail.length == 0" :description="$t('common.noData')" />
|
||||
<el-empty v-if="paragraphDetail.length == 0" :description="$t('common.noData')" />
|
||||
<div v-else>
|
||||
<el-scrollbar>
|
||||
<div class="paragraph-detail-height">
|
||||
<InfiniteScroll
|
||||
:size="paragraphDetail.length"
|
||||
:total="paginationConfig.total"
|
||||
:page_size="paginationConfig.page_size"
|
||||
v-model:current_page="paginationConfig.current_page"
|
||||
@load="getParagraphList"
|
||||
:loading="loading"
|
||||
>
|
||||
|
||||
<InfiniteScroll
|
||||
v-else
|
||||
:size="paragraphDetail.length"
|
||||
:total="paginationConfig.total"
|
||||
:page_size="paginationConfig.page_size"
|
||||
v-model:current_page="paginationConfig.current_page"
|
||||
@load="getParagraphList"
|
||||
:loading="loading"
|
||||
>
|
||||
<el-row>
|
||||
<el-col
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="8"
|
||||
:lg="6"
|
||||
:xl="6"
|
||||
v-for="(item, index) in paragraphDetail"
|
||||
:key="index"
|
||||
class="p-8"
|
||||
>
|
||||
<!-- 批量操作card -->
|
||||
<CardBox
|
||||
v-if="isBatch === true"
|
||||
shadow="hover"
|
||||
:title="item.title || '-'"
|
||||
:description="item.content"
|
||||
class="document-card cursor"
|
||||
:class="multipleSelection.includes(item.id) ? 'selected' : ''"
|
||||
:showIcon="false"
|
||||
@click="selectHandle(item.id)"
|
||||
>
|
||||
<div class="active-button" @click.stop></div>
|
||||
|
||||
<template #footer>
|
||||
<div class="footer-content flex-between">
|
||||
<span>
|
||||
{{ numberFormat(item?.content.length) || 0 }}
|
||||
{{ $t('views.paragraph.character_count') }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</CardBox>
|
||||
<!-- 非批量操作card -->
|
||||
<CardBox
|
||||
v-else
|
||||
shadow="hover"
|
||||
:title="item.title || '-'"
|
||||
:description="item.content"
|
||||
class="document-card cursor"
|
||||
:class="item.is_active ? '' : 'disabled'"
|
||||
:showIcon="false"
|
||||
@click="editParagraph(item)"
|
||||
>
|
||||
<div class="active-button" @click.stop>
|
||||
<el-switch
|
||||
:loading="loading"
|
||||
v-model="item.is_active"
|
||||
:before-change="() => changeState(item)"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="footer-content flex-between">
|
||||
<span>
|
||||
{{ numberFormat(item?.content.length) || 0 }}
|
||||
{{ $t('views.paragraph.character_count') }}
|
||||
</span>
|
||||
|
||||
<span @click.stop>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button text>
|
||||
<el-icon><MoreFilled /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openGenerateDialog(item)">
|
||||
<el-icon><Connection /></el-icon>
|
||||
{{
|
||||
$t('views.document.generateQuestion.title')
|
||||
}}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="openSelectDocumentDialog(item)">
|
||||
<AppIcon iconName="app-migrate"></AppIcon>
|
||||
{{ $t('views.document.setting.migration') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item icon="Delete" @click.stop="deleteParagraph(item)">{{
|
||||
$t('common.delete')
|
||||
}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</CardBox>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<div class="mul-operation border-t w-full" v-if="isBatch === true">
|
||||
<el-button :disabled="multipleSelection.length === 0" @click="openGenerateDialog()">
|
||||
|
|
@ -173,11 +80,11 @@
|
|||
{{ $t('views.document.items') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<ParagraphDialog ref="ParagraphDialogRef" :title="title" @refresh="refresh" />
|
||||
<SelectDocumentDialog ref="SelectDocumentDialogRef" @refresh="refreshMigrateParagraph" />
|
||||
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="refresh" />
|
||||
</LayoutContainer>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted, computed } from 'vue'
|
||||
|
|
@ -194,7 +101,7 @@ import { t } from '@/locales'
|
|||
const { paragraph } = useStore()
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id, documentId }
|
||||
params: { id, documentId },
|
||||
} = route as any
|
||||
|
||||
const SelectDocumentDialogRef = ref()
|
||||
|
|
@ -214,12 +121,12 @@ const multipleSelection = ref<any[]>([])
|
|||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 30,
|
||||
total: 0
|
||||
total: 0,
|
||||
})
|
||||
|
||||
function refreshMigrateParagraph() {
|
||||
paragraphDetail.value = paragraphDetail.value.filter(
|
||||
(v) => !multipleSelection.value.includes(v.id)
|
||||
(v) => !multipleSelection.value.includes(v.id),
|
||||
)
|
||||
multipleSelection.value = []
|
||||
MsgSuccess(t('views.document.tip.migrationSuccess'))
|
||||
|
|
@ -237,15 +144,15 @@ function deleteMulParagraph() {
|
|||
t('views.paragraph.delete.confirmMessage'),
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger'
|
||||
}
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
paragraphApi
|
||||
.delMulParagraph(id, documentId, multipleSelection.value, changeStateloading)
|
||||
.then(() => {
|
||||
paragraphDetail.value = paragraphDetail.value.filter(
|
||||
(v) => !multipleSelection.value.includes(v.id)
|
||||
(v) => !multipleSelection.value.includes(v.id),
|
||||
)
|
||||
multipleSelection.value = []
|
||||
MsgSuccess(t('views.document.delete.successMessage'))
|
||||
|
|
@ -275,7 +182,7 @@ function searchHandle() {
|
|||
|
||||
function changeState(row: any) {
|
||||
const obj = {
|
||||
is_active: !row.is_active
|
||||
is_active: !row.is_active,
|
||||
}
|
||||
paragraph
|
||||
.asyncPutParagraph(id, documentId, row.id, obj, changeStateloading)
|
||||
|
|
@ -295,8 +202,8 @@ function deleteParagraph(row: any) {
|
|||
t('views.paragraph.delete.confirmMessage'),
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger'
|
||||
}
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
paragraph.asyncDelParagraph(id, documentId, row.id, loading).then(() => {
|
||||
|
|
@ -337,7 +244,7 @@ function getParagraphList() {
|
|||
documentId,
|
||||
paginationConfig,
|
||||
search.value && { [searchType.value]: search.value },
|
||||
loading
|
||||
loading,
|
||||
)
|
||||
.then((res) => {
|
||||
paragraphDetail.value = [...paragraphDetail.value, ...res.data.records]
|
||||
|
|
@ -378,16 +285,18 @@ onMounted(() => {
|
|||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.document-detail {
|
||||
.paragraph {
|
||||
position: relative;
|
||||
.header-button {
|
||||
position: absolute;
|
||||
right: calc(var(--app-base-px) * 3);
|
||||
top: calc(var(--app-base-px) + 4px);
|
||||
}
|
||||
|
||||
.document-detail-height {
|
||||
.paragraph-detail-height {
|
||||
height: calc(var(--app-main-height) - 75px);
|
||||
}
|
||||
.document-card {
|
||||
.paragraph-card {
|
||||
height: 210px;
|
||||
background: var(--app-layout-bg-color);
|
||||
border: 1px solid var(--app-layout-bg-color);
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@
|
|||
</el-icon>
|
||||
{{ $t('common.edit') }}
|
||||
</el-dropdown-item>
|
||||
<!-- <el-dropdown-item
|
||||
<el-dropdown-item
|
||||
:disabled="!canEdit(item)"
|
||||
v-if="!item.template_id"
|
||||
@click.stop="copytool(item)"
|
||||
|
|
@ -172,7 +172,7 @@
|
|||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
{{ $t('common.delete') }}
|
||||
</el-dropdown-item> -->
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
|
|
|||
Loading…
Reference in New Issue