maxkb/ui/src/views/resource-management/paragraph/index.vue
wangdan-fit2cloud 1c96897d01 feat: shared
2025-06-24 22:12:26 +08:00

376 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<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"
>
<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__main mt-16"
v-loading="(paginationConfig.current_page === 1 && loading) || changeStateloading"
>
<div class="flex-between p-12-16 border-b">
<span>{{ paginationConfig.total }} {{ $t('views.paragraph.paragraph_count') }}</span>
<el-input
v-model="search"
:placeholder="$t('common.search')"
class="input-with-select"
style="width: 260px"
@change="searchHandle"
clearable
>
<template #prepend>
<el-select v-model="searchType" placeholder="Select" style="width: 80px">
<el-option :label="$t('common.title')" value="title" />
<el-option :label="$t('common.content')" value="content" />
</el-select>
</template>
</el-input>
</div>
<div class="flex">
<div class="paragraph-sidebar p-16 border-r">
<el-anchor
direction="vertical"
type="default"
:offset="130"
container=".paragraph-scollbar"
@click="handleClick"
>
<template v-for="(item, index) in paragraphDetail" :key="item.id">
<el-anchor-link :href="`#m${item.id}`" :title="item.title" v-if="item.title" />
</template>
</el-anchor>
</div>
<div class="w-full">
<el-empty v-if="paragraphDetail.length == 0" :description="$t('common.noData')" />
<div v-else>
<el-scrollbar class="paragraph-scollbar">
<div class="paragraph-detail">
<el-checkbox-group v-model="multipleSelection">
<InfiniteScroll
:size="paragraphDetail.length"
:total="paginationConfig.total"
:page_size="paginationConfig.page_size"
v-model:current_page="paginationConfig.current_page"
@load="getParagraphList"
:loading="loading"
>
<VueDraggable
ref="el"
v-model="paragraphDetail"
:disabled="isBatch === true"
handle=".handle"
:animation="150"
ghostClass="ghost"
>
<template v-for="(item, index) in paragraphDetail" :key="item.id">
<div :id="`m${item.id}`" style="display: flex; margin-bottom: 16px">
<!-- 批量操作 -->
<div class="paragraph-card flex" v-if="isBatch === true">
<el-checkbox :value="item.id" />
<ParagraphCard :data="item" class="mb-8 w-full" />
</div>
<!-- 非批量操作 -->
<div class="handle paragraph-card flex" :id="item.id" v-else>
<img
src="@/assets/sort.svg"
alt=""
height="15"
class="handle-img mr-8 mt-24 cursor"
/>
</div>
<ParagraphCard
:data="item"
class="mb-8 w-full"
@changeState="changeState"
@deleteParagraph="deleteParagraph"
/>
</div>
</template>
</VueDraggable>
</InfiniteScroll>
</el-checkbox-group>
</div>
</el-scrollbar>
</div>
</div>
</div>
<div class="mul-operation border-t w-full" v-if="isBatch === true">
<el-button :disabled="multipleSelection.length === 0" @click="openGenerateDialog()">
{{ $t('views.document.generateQuestion.title') }}
</el-button>
<el-button :disabled="multipleSelection.length === 0" @click="openSelectDocumentDialog()">
{{ $t('views.document.setting.migration') }}
</el-button>
<el-button :disabled="multipleSelection.length === 0" @click="deleteMulParagraph">
{{ $t('common.delete') }}
</el-button>
<span class="ml-8">
{{ $t('views.document.selected') }} {{ multipleSelection.length }}
{{ $t('views.document.items') }}
</span>
</div>
</el-card>
<ParagraphDialog ref="ParagraphDialogRef" :title="title" @refresh="refresh" />
<SelectDocumentDialog ref="SelectDocumentDialogRef" @refresh="refreshMigrateParagraph" />
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="refresh" :apiType="apiType" />
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import { cloneDeep } from 'lodash'
import paragraphApi from '@/api/resource-management/paragraph'
import ParagraphDialog from './component/ParagraphDialog.vue'
import ParagraphCard from './component/ParagraphCard.vue'
import SelectDocumentDialog from './component/SelectDocumentDialog.vue'
import GenerateRelatedDialog from '@/components/generate-related-dialog/index.vue'
import { VueDraggable } from 'vue-draggable-plus'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import { t } from '@/locales'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
const route = useRoute()
const {
params: { id, documentId },
query: { type },
} = route as any
const apiType = computed(() => {
return type as 'systemShare' | 'workspace' | 'systemManage'
})
const SelectDocumentDialogRef = ref()
const ParagraphDialogRef = ref()
const loading = ref(false)
const changeStateloading = ref(false)
const documentDetail = ref<any>({})
const paragraphDetail = ref<any[]>([])
const title = ref('')
const search = ref('')
const searchType = ref('title')
const handleClick = (e: MouseEvent, ele: any) => {
e.preventDefault()
document.querySelector(`${ele}`).scrollIntoView({ behavior: 'smooth', block: 'start' })
}
// 批量操作
const isBatch = ref(false)
const multipleSelection = ref<any[]>([])
const paginationConfig = reactive({
current_page: 1,
page_size: 30,
total: 0,
})
function deleteParagraph(id: string) {
const index = paragraphDetail.value.findIndex((v) => v.id === id)
paragraphDetail.value.splice(index, 1)
}
function changeState(id: string) {
const index = paragraphDetail.value.findIndex((v) => v.id === id)
paragraphDetail.value[index].is_active = !paragraphDetail.value[index].is_active
}
function refreshMigrateParagraph() {
paragraphDetail.value = paragraphDetail.value.filter(
(v) => !multipleSelection.value.includes(v.id),
)
multipleSelection.value = []
MsgSuccess(t('views.document.tip.migrationSuccess'))
}
function openSelectDocumentDialog(row?: any) {
if (row) {
multipleSelection.value = [row.id]
}
SelectDocumentDialogRef.value.open(multipleSelection.value)
}
function deleteMulParagraph() {
MsgConfirm(
`${t('views.document.delete.confirmTitle1')} ${multipleSelection.value.length} ${t('views.document.delete.confirmTitle2')}`,
t('views.paragraph.delete.confirmMessage'),
{
confirmButtonText: t('common.confirm'),
confirmButtonClass: 'danger',
},
)
.then(() => {
paragraphApi
.putMulParagraph(id, documentId, multipleSelection.value, changeStateloading)
.then(() => {
paragraphDetail.value = paragraphDetail.value.filter(
(v) => !multipleSelection.value.includes(v.id),
)
multipleSelection.value = []
MsgSuccess(t('views.document.delete.successMessage'))
})
})
.catch(() => {})
}
function batchSelectedHandle(bool: boolean) {
isBatch.value = bool
multipleSelection.value = []
}
function selectHandle(id: string) {
if (multipleSelection.value.includes(id)) {
multipleSelection.value.splice(multipleSelection.value.indexOf(id), 1)
} else {
multipleSelection.value.push(id)
}
}
function searchHandle() {
paginationConfig.current_page = 1
paragraphDetail.value = []
getParagraphList()
}
function addParagraph() {
title.value = t('views.paragraph.addParagraph')
ParagraphDialogRef.value.open()
}
function getDetail() {
loadSharedApi({ type: 'document', systemType: apiType.value })
.getDocumentDetail(id, documentId, loading)
.then((res) => {
documentDetail.value = res.data
})
}
function getParagraphList() {
paragraphApi
.getParagraph(
id,
documentId,
paginationConfig,
search.value && { [searchType.value]: search.value },
loading,
)
.then((res) => {
paragraphDetail.value = [...paragraphDetail.value, ...res.data.records]
paginationConfig.total = res.data.total
})
}
function refresh(data: any) {
if (data) {
const index = paragraphDetail.value.findIndex((v) => v.id === data.id)
paragraphDetail.value.splice(index, 1, data)
} else {
paginationConfig.current_page = 1
paragraphDetail.value = []
getParagraphList()
}
}
const GenerateRelatedDialogRef = ref()
function openGenerateDialog(row?: any) {
const arr: string[] = []
if (row) {
arr.push(row.id)
} else {
multipleSelection.value.map((v) => {
if (v) {
arr.push(v)
}
})
}
GenerateRelatedDialogRef.value.open(arr, 'paragraph')
}
function onEnd(event?: any) {
const { oldIndex, newIndex } = event
if (oldIndex === undefined || newIndex === undefined) return
const list = cloneDeep(paragraphDetail.value)
if (oldIndex === list.length - 1 || newIndex === list.length - 1) {
return
}
const newInstance = { ...list[oldIndex], type: list[newIndex].type, id: list[newIndex].id }
const oldInstance = { ...list[newIndex], type: list[oldIndex].type, id: list[oldIndex].id }
list[newIndex] = newInstance
list[oldIndex] = oldInstance
paragraphDetail.value = list
}
onMounted(() => {
getDetail()
getParagraphList()
})
</script>
<style lang="scss" scoped>
.paragraph {
position: relative;
.header-button {
position: absolute;
right: calc(var(--app-base-px) * 3);
top: calc(var(--app-base-px) + 4px);
}
.paragraph-sidebar {
width: 240px;
}
.paragraph-detail {
height: calc(100vh - 215px);
max-width: 1000px;
margin: 16px auto;
// .el-checkbox-group {
// display: flex;
// }
}
&__main {
position: relative;
box-sizing: border-box;
.mul-operation {
position: absolute;
bottom: 0;
left: 0;
padding: 16px 24px;
box-sizing: border-box;
background: #ffffff;
}
}
.paragraph-card {
&.handle {
.handle-img {
visibility: hidden;
}
&:hover {
.handle-img {
visibility: visible;
}
}
}
}
}
</style>