feat: 段落模块

This commit is contained in:
wangdan-fit2cloud 2023-11-13 19:02:13 +08:00
parent 76dd9d2192
commit e452a942be
12 changed files with 341 additions and 65 deletions

View File

@ -184,6 +184,43 @@ const getParagraph: (dataset_id: string, document_id: string) => Promise<Result<
return get(`${prefix}/${dataset_id}/document/${document_id}/paragraph`) return get(`${prefix}/${dataset_id}/document/${document_id}/paragraph`)
} }
/**
*
* @param dataset_id, document_id, document_id
*/
const delParagraph: (
dataset_id: string,
document_id: string,
paragraph_id: string
) => Promise<Result<boolean>> = (dataset_id, document_id, paragraph_id) => {
return del(`${prefix}/${dataset_id}/document/${document_id}/paragraph/${paragraph_id}`)
}
/**
*
* @param
* dataset_id, document_id, paragraph_id
* {
"content": "string",
"title": "string",
"is_active": true,
"problem_list": [
{
"id": "string",
"content": "string"
}
]
}
*/
const putParagraph: (
dataset_id: string,
document_id: string,
paragraph_id: string,
data: any
) => Promise<Result<any>> = (dataset_id, document_id, paragraph_id, data: any) => {
return put(`${prefix}/${dataset_id}/document/${document_id}/paragraph/${paragraph_id}`, data)
}
export default { export default {
getDateset, getDateset,
getAllDateset, getAllDateset,
@ -197,5 +234,7 @@ export default {
putDocument, putDocument,
delDocument, delDocument,
getDocumentDetail, getDocumentDetail,
getParagraph getParagraph,
delParagraph,
putParagraph
} }

View File

@ -67,9 +67,13 @@ function cardLeave() {
.card-footer { .card-footer {
position: absolute; position: absolute;
bottom: 8px; bottom: 8px;
left: 0;
min-height: 30px; min-height: 30px;
color: var(--app-text-color-secondary); color: var(--app-text-color-secondary);
font-weight: 400; font-weight: 400;
padding: 0 16px;
width: 100%;
box-sizing: border-box;
} }
} }
</style> </style>

View File

@ -19,7 +19,7 @@ body {
height: 100%; height: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
color: var(--app-text-color-primary); color: var(--app-text-color);
} }
#app { #app {
@ -135,7 +135,7 @@ h4 {
margin-right: calc(var(--app-base-px) + 4px); margin-right: calc(var(--app-base-px) + 4px);
} }
.mr-16 { .mr-16 {
margin-right: calc(var(--app-base-px) * 3); margin-right: calc(var(--app-base-px) * 2);
} }
.p-8 { .p-8 {
@ -212,6 +212,7 @@ h4 {
// 内容部分 自适应高度 // 内容部分 自适应高度
.main-calc-height { .main-calc-height {
height: var(--app-main-height); height: var(--app-main-height);
box-sizing: border-box;
} }
// 标题前带竖线样式 // 标题前带竖线样式

View File

@ -3,7 +3,7 @@
--el-color-primary-light-9: rgba(51, 112, 255, 0.1); --el-color-primary-light-9: rgba(51, 112, 255, 0.1);
--el-menu-item-height: 45px; --el-menu-item-height: 45px;
--el-box-shadow-light: 0px 2px 4px 0px rgba(31, 35, 41, 0.12); --el-box-shadow-light: 0px 2px 4px 0px rgba(31, 35, 41, 0.12);
--el-border-color: rgba(31, 35, 41, 0.15); --el-border-color: #DEE0E3;
} }
.el-button { .el-button {
@ -13,7 +13,7 @@
font-size: 16px; font-size: 16px;
max-height: 24px; max-height: 24px;
&:not(.is-disabled):hover { &:not(.is-disabled):hover {
background: var(--app-text-color-primary-light-1); background: var(--app-text-color-light-1);
} }
} }
} }
@ -48,7 +48,7 @@
.el-message-box__content { .el-message-box__content {
padding: 24px 0; padding: 24px 0;
color: var(--app-text-color-primary); color: var(--app-text-color);
font-weight: 400; font-weight: 400;
} }
.el-message-box__btns { .el-message-box__btns {
@ -62,6 +62,7 @@
button.danger { button.danger {
background: var(--el-color-danger); background: var(--el-color-danger);
border: var(--el-color-danger); border: var(--el-color-danger);
color: #ffffff;
} }
} }
.el-message-box__headerbtn { .el-message-box__headerbtn {
@ -92,7 +93,7 @@
--el-card-padding: calc(var(--app-base-px) * 2); --el-card-padding: calc(var(--app-base-px) * 2);
} }
.el-dropdown { .el-dropdown {
color: var(--app-text-color-primary); color: var(--app-text-color);
} }
.el-tag { .el-tag {
@ -100,7 +101,7 @@
} }
.el-table { .el-table {
--el-table-header-bg-color: var(--app-layout-bg-color); --el-table-header-bg-color: var(--app-layout-bg-color);
--el-table-text-color: var(--app-text-color-primary); --el-table-text-color: var(--app-text-color);
font-weight: 400; font-weight: 400;
thead { thead {
color: var(--app-text-color-secondary); color: var(--app-text-color-secondary);
@ -165,3 +166,14 @@
.el-slider__input { .el-slider__input {
width: 60px; width: 60px;
} }
.input-with-select {
.el-input__wrapper {
// border: 1px solid var(--el-border-color);
// box-shadow: none!important;
}
.el-input-group__prepend {
background-color: var(--el-fill-color-blank);
}
}

View File

@ -1,9 +1,10 @@
:root { :root {
--app-base-px: 8px; --app-base-px: 8px;
--app-layout-bg-color: #f5f6f7; --app-layout-bg-color: #f5f6f7;
--app-text-color-primary: #1f2329; --app-text-color: #1f2329;
--app-text-color-primary-light-1: rgba(31, 35, 41, 0.1); --app-text-color-light-1: rgba(31, 35, 41, 0.1);
--app-text-color-secondary: #646a73; --app-text-color-secondary: #646a73;
--app-text-color-disable: #bbbfc4;
--app-view-padding: 24px; --app-view-padding: 24px;
--app-view-bg-color: #ffffff; --app-view-bg-color: #ffffff;
--app-border-color-dark: #bbbfc4; --app-border-color-dark: #bbbfc4;

View File

@ -31,7 +31,7 @@ const router = useRouter()
} }
.message-container { .message-container {
color: var(--app-text-color-primary); color: var(--app-text-color);
.title { .title {
font-size: 50px; font-size: 50px;

View File

@ -167,7 +167,6 @@ function submit() {
width: 100%; width: 100%;
margin: 0 auto; margin: 0 auto;
overflow: hidden; overflow: hidden;
box-sizing: border-box;
} }
&__footer { &__footer {
padding: 16px 24px; padding: 16px 24px;

View File

@ -1,61 +1,79 @@
<template> <template>
<LayoutContainer :header="documentDetail?.name" back-to="-1" class="dataset-detail"> <LayoutContainer :header="documentDetail?.name" back-to="-1" class="document-detail">
<template #header> <template #header>
<!-- <el-steps :active="active" finish-status="success" align-center class="create-dataset__steps"> <div class="document-detail__header">
<el-step v-for="(item, index) in steps" :key="index"> <el-button @click="addParagraph" type="primary" :disabled="loading"> 添加分段 </el-button>
<template #icon> </div>
<div class="app-step flex align-center">
<div class="el-step__icon is-text">
<div class="el-step__icon-inner">
<el-icon v-if="active == index + 1" style="margin-top: 1px"><Select /></el-icon>
<span v-else> {{ index + 1 }}</span>
</div>
</div>
<span class="ml-4">{{ item.name }}</span>
</div>
</template>
</el-step>
</el-steps> -->
</template> </template>
<div class="dataset-detail__main main-calc-height p-24"> <div class="document-detail__main p-16">
<el-row :gutter="15"> <div class="flex-between p-8">
<el-col <span>{{ paragraphDetail.length }} 段落</span>
:xs="24" <el-input
:sm="12" v-model="search"
:md="8" placeholder="搜索"
:lg="6" class="input-with-select"
:xl="4" style="width: 260px"
v-for="(item, index) in paragraphDetail"
:key="index"
class="mt-8"
> >
<CardBox <template #prepend>
shadow="hover" <el-select v-model="searchType" placeholder="Select" style="width: 80px">
:title="item.title" <el-option label="标题" value="title" />
:description="item.content" <el-option label="内容" value="content" />
class="cursor" </el-select>
:showIcon="false" </template>
> </el-input>
<!-- <template #footer> </div>
<div class="footer-content"> <el-scrollbar>
<span class="bold">{{ item?.document_count || 0 }}</span> <div class="document-detail-height" v-loading="loading">
文档<el-divider direction="vertical" /> <el-row>
<span class="bold">{{ numberFormat(item?.char_length) || 0 }}</span> <el-col
字符<el-divider direction="vertical" /> :xs="24"
<span class="bold">{{ item?.char_length || 0 }}</span> :sm="12"
关联应用 :md="8"
</div> :lg="6"
</template> --> :xl="4"
</CardBox> v-for="(item, index) in paragraphDetail"
</el-col> :key="index"
</el-row> class="p-8"
>
<CardBox
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">
<el-switch v-model="item.is_active" @change="changeState($event, item)" />
</div>
<template #footer>
<div class="footer-content flex-between">
<span> {{ numberFormat(item?.content.length) || 0 }} 字符 </span>
<el-tooltip effect="dark" content="删除" placement="top">
<el-button text @click.stop="deleteParagraph(item)" class="delete-button">
<el-icon><Delete /></el-icon>
</el-button>
</el-tooltip>
</div>
</template>
</CardBox>
</el-col>
</el-row>
</div>
</el-scrollbar>
</div> </div>
<ParagraphDialog ref="ParagraphDialogRef" :title="title" />
</LayoutContainer> </LayoutContainer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed } from 'vue' import { ref, onMounted, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import datasetApi from '@/api/dataset' import datasetApi from '@/api/dataset'
import ParagraphDialog from './component/ParagraphDialog.vue'
import { numberFormat } from '@/utils/utils'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
@ -63,9 +81,57 @@ const {
params: { datasetId, documentId } params: { datasetId, documentId }
} = route as any } = route as any
const ParagraphDialogRef = ref()
const loading = ref(false) const loading = ref(false)
const documentDetail = ref<any>({}) const documentDetail = ref<any>({})
const paragraphDetail = ref<any[]>([]) const paragraphDetail = ref<any[]>([])
const title = ref('')
const search = ref('')
const searchType = ref('title')
function changeState(bool: Boolean, row: any) {
const obj = {
is_active: bool
}
loading.value = true
datasetApi
.putParagraph(datasetId, documentId, row.id, obj)
.then((res) => {
loading.value = false
})
.catch(() => {
loading.value = false
})
}
function deleteParagraph(row: any) {
MsgConfirm(`是否删除段落:${row.title} ?`, `删除后无法恢复,请谨慎操作。`, {
confirmButtonText: '删除',
confirmButtonClass: 'danger'
})
.then(() => {
loading.value = true
datasetApi
.delParagraph(datasetId, documentId, row.id)
.then(() => {
MsgSuccess('删除成功')
getParagraphDetail()
})
.catch(() => {
loading.value = false
})
})
.catch(() => {})
}
function addParagraph() {
title.value = '添加分段'
ParagraphDialogRef.value.open()
}
function editParagraph(row: any) {
title.value = '分段详情'
ParagraphDialogRef.value.open(row)
}
function getDetail() { function getDetail() {
loading.value = true loading.value = true
@ -99,9 +165,42 @@ onMounted(() => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.dataset-detail { .document-detail {
&__main{ &__header {
box-sizing: border-box; position: absolute;
right: calc(var(--app-base-px) * 3);
}
.document-detail-height {
height: calc(var(--app-main-height) - 75px);
}
.document-card {
height: 210px;
background: var(--app-layout-bg-color);
border: 1px solid var(--app-layout-bg-color);
&:hover {
background: #ffffff;
border: 1px solid var(--el-border-color);
}
&.disabled {
background: var(--app-layout-bg-color);
border: 1px solid var(--app-layout-bg-color);
:deep(.description) {
color: var(--app-border-color-dark);
}
:deep(.title) {
color: var(--app-border-color-dark);
}
}
:deep(.description) {
-webkit-line-clamp: 5 !important;
height: 110px;
}
.active-button {
position: absolute;
right: 16px;
top: 16px;
}
} }
} }
</style> </style>

View File

@ -0,0 +1,121 @@
<template>
<el-dialog :title="title" v-model="dialogVisible" width="800" class="paragraph-dialog">
<el-row>
<el-col :span="16" class="p-24">
<div class="flex-between mb-16">
<div class="bold title align-center">分段内容</div>
<el-button text>
<el-icon><EditPen /></el-icon>
</el-button>
</div>
<el-form
ref="segmentFormRef"
:model="segmentForm"
label-position="top"
:rules="rules"
@submit.prevent
>
<el-form-item label="分段标题">
<el-input v-model="segmentForm.title" placeholder="请输入分段标题"> </el-input>
</el-form-item>
<el-form-item label="分段内容" prop="content">
<el-input
v-model="segmentForm.content"
placeholder="请输入分段内容"
maxlength="500"
show-word-limit
:rows="8"
type="textarea"
>
</el-input>
</el-form-item>
</el-form>
<div class="text-right">
<el-button @click.prevent="dialogVisible = false"> 取消 </el-button>
<el-button type="primary" @click="submitHandle(segmentFormRef)"> 保存 </el-button>
</div>
</el-col>
<el-col :span="8" class="border-l p-24">
<p class="bold title mb-16">
关联问题 <el-divider direction="vertical" />
<el-button text>
<el-icon><Plus /></el-icon>
</el-button>
</p>
</el-col>
</el-row>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { cloneDeep } from 'lodash'
const props = defineProps({
title: String
})
const emit = defineEmits(['updateContent'])
const segmentFormRef = ref<FormInstance>()
const dialogVisible = ref<boolean>(false)
const segmentForm = ref({
title: '',
content: ''
})
const rules = ref<FormRules>({
content: [{ required: true, message: '请输入分段内容', trigger: 'blur' }]
})
const isEdit = ref(false)
watch(dialogVisible, (bool) => {
if (!bool) {
segmentForm.value = {
title: '',
content: ''
}
isEdit.value = false
}
})
const open = (data: any) => {
if (data) {
segmentForm.value.title = data.title
segmentForm.value.content = data.content
isEdit.value = true
}
dialogVisible.value = true
}
const submitHandle = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
emit('updateContent', segmentForm.value)
dialogVisible.value = false
} else {
console.log('error submit!')
}
})
}
defineExpose({ open })
</script>
<style lang="scss" scope>
.paragraph-dialog {
.el-dialog__header {
padding-bottom: 16px;
}
.el-dialog__body {
border-top: 1px solid var(--el-border-color);
padding: 0 !important;
}
.title {
color: var(--app-text-color);
}
}
</style>

View File

@ -110,7 +110,7 @@ onMounted(() => {})
<style scoped lang="scss"> <style scoped lang="scss">
.segment-tabs { .segment-tabs {
:deep(.el-tabs__item) { :deep(.el-tabs__item) {
background: var(--app-text-color-primary-light-1); background: var(--app-text-color-light-1);
margin: 4px; margin: 4px;
border-radius: 4px; border-radius: 4px;
padding: 5px 10px 5px 8px !important; padding: 5px 10px 5px 8px !important;

View File

@ -135,7 +135,7 @@ onMounted(() => {
} }
.footer-content { .footer-content {
.bold { .bold {
color: var(--app-text-color-primary); color: var(--app-text-color);
} }
} }
} }

View File

@ -158,7 +158,7 @@ defineExpose({
height: 100%; height: 100%;
padding: calc(var(--app-base-px) * 2); padding: calc(var(--app-base-px) * 2);
line-height: 22px; line-height: 22px;
color: var(--app-text-color-primary); color: var(--app-text-color);
} }
:deep(.el-radio__label) { :deep(.el-radio__label) {
padding-left: 32px; padding-left: 32px;