123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- <template>
- <div class="knowledge-retrieval-container layout-padding">
- <el-card shadow="never" class="h100">
- <el-tabs v-model="state.queryParams.Attribution" @tab-change="handleClick">
- <el-tab-pane label="全部" name=" "></el-tab-pane>
- <el-tab-pane label="中心知识库" name="中心知识库"></el-tab-pane>
- <el-tab-pane label="部门知识库" name="部门知识库"></el-tab-pane>
- </el-tabs>
- <el-row :gutter="20" class="h100">
- <el-col :xs="6" :sm="6" :md="6" :lg="4" :xl="4" class="left-container">
- <div class="h100 pr10">
- <el-tabs v-model="state.activeName" stretch @tab-change="resetNode">
- <el-tab-pane label="部门" name="0">
- <el-input v-model="filterOrg" placeholder="请输入部门名称" class="input-with-select mt10 mb10" clearable> </el-input>
- </el-tab-pane>
- <el-tab-pane label="知识分类" name="1">
- <el-input v-model="filterType" placeholder="请输入知识分类名称" class="input-with-select mt10 mb10" clearable> </el-input>
- </el-tab-pane>
- <el-tab-pane label="热点" name="2">
- <el-input v-model="filterHot" placeholder="请输入热点名称" class="input-with-select mt10 mb10" clearable> </el-input>
- </el-tab-pane>
- </el-tabs>
- <div style="height: calc(100% - 100px);'">
- <el-scrollbar>
- <el-tree
- v-show="state.activeName === '0'"
- :data="state.orgData"
- highlight-current
- :expand-on-click-node="false"
- :props="{ children: 'children', label: 'name' }"
- @node-click="handleNodeClick"
- ref="orgRef"
- v-loading="state.loading"
- :filter-node-method="filterNode"
- node-key="id"
- :style="state.activeName === '0' ? 'min-width: 100%; display: inline-block' : 'display:none'"
- :default-expanded-keys="state.defaultExpandedOrgKeys"
- >
- </el-tree>
- <el-tree
- :data="state.knowledgeOptions"
- highlight-current
- :expand-on-click-node="false"
- :props="{ children: 'children', label: 'name' }"
- @node-click="handleNodeClick"
- ref="typeRef"
- v-loading="state.loading"
- :filter-node-method="filterNodeType"
- node-key="id"
- :style="state.activeName === '1' ? 'min-width: 100%; display: inline-block' : 'display:none'"
- :default-expanded-keys="state.defaultExpandedTypeKeys"
- >
- </el-tree>
- <el-tree
- filterable
- highlight-current
- placeholder="请选择热点分类"
- :props="HotspotProps"
- @node-click="handleNodeClick"
- lazy
- :load="load"
- node-key="id"
- ref="hotRef"
- :filter-node-method="filterNodeHot"
- :expand-on-click-node="false"
- :style="state.activeName === '2' ? 'min-width: 100%; display: inline-block' : 'display:none'"
- >
- <template #default="{ data }">
- <span :title="data.hotSpotName">
- {{ data.hotSpotName }}
- </span>
- </template>
- </el-tree>
- </el-scrollbar>
- </div>
- </div>
- </el-col>
- <el-col :xs="12" :sm="12" :md="14" :lg="14" :xl="14" class="center-container">
- <div class="input-box">
- <el-select v-model="state.queryParams.RetrievalType" placeholder="请选择" style="width: 120px" @change="queryList">
- <el-option label="全文" :value="0" />
- <el-option label="标题" :value="1" />
- <el-option label="知识内容" :value="2" />
- <el-option label="摘要" :value="3" />
- </el-select>
- <div class="input-with-button w100">
- <div class="flex">
- <el-autocomplete
- v-model="state.queryParams.Keyword"
- :fetch-suggestions="querySearchAsync"
- placeholder="关键词"
- @select="handleSelect"
- class="mr10 w100"
- clearable
- value-key="title"
- >
- <template #default="{ item }">
- <template v-if="state.queryParams.RetrievalType === 0">
- <div class="text-no-wrap">{{ item.title }}</div>
- <span class="text-no-wrap color-info">{{ item.summary }}</span>
- </template>
- <template v-if="state.queryParams.RetrievalType === 1">
- <div class="text-no-wrap">{{ item.title }}</div>
- </template>
- <template v-if="state.queryParams.RetrievalType === 2">
- <div class="text-no-wrap">{{ item.title }}</div>
- <span class="text-no-wrap color-info">{{ item.summary }}</span>
- </template>
- <template v-if="state.queryParams.RetrievalType === 3">
- <span class="text-no-wrap color-info">{{ item.summary }}</span>
- </template>
- </template>
- </el-autocomplete>
- <el-button type="primary" class="btn" :loading="state.loading" @click="queryList"
- ><SvgIcon name="ele-Search" class="mr5" />搜索</el-button
- >
- <el-button @click="resetQuery" class="default-button"> <SvgIcon name="ele-Refresh" class="mr5" />重置</el-button>
- </div>
- <div class="flex-center-align mt5">
- <div style="height: 32px; line-height: 32px">排序:</div>
- <el-radio-group v-model="state.queryParams.Sort" @change="queryList">
- <el-radio label="1">浏览量</el-radio>
- <el-radio label="2">评分</el-radio>
- <el-radio label="3">创建时间</el-radio>
- </el-radio-group>
- </div>
- </div>
- </div>
- <div v-loading="centerLoading" class="center-container-box" style="height: calc(100% - 180px)">
- <template v-if="state.retrievalList.length">
- <el-scrollbar>
- <div v-for="(v, i) in state.retrievalList" :key="i" class="retrieval-content-item" @click="onPreview(v)" title="查看详情">
- <h4 class="mb10 text-no-wrap">{{ v.title }}</h4>
- <div class="text-ellipsis2">{{ v.summary }}</div>
- <div class="flex-center-between mt10 color-info">
- <div>
- <span class="mr10">创建部门:{{ v.creatorOrgName }}</span>
- <span>创建时间:{{ formatDate(v.creationTime, 'YYYY-mm-dd HH:MM:SS') }}</span>
- </div>
- <div class="flex-center-align">
- <span class="flex-center-align"><SvgIcon name="ele-StarFilled" size="18px" class="mr3" />{{ v.score }}</span>
- <span class="flex-center-align ml10"><SvgIcon name="ele-ChatDotSquare" size="16px" class="mr3" />{{ v.commentNum }}</span>
- <span class="flex-center-align ml10"><SvgIcon name="ele-View" size="16px" class="mr3" />{{ v.pageView }}</span>
- </div>
- </div>
- </div>
- </el-scrollbar>
- </template>
- <template v-else>
- <Empty type="search" description="暂无结果" />
- </template>
- </div>
- <pagination
- :total="state.total"
- v-model:page="state.queryParams.PageIndex"
- v-model:limit="state.queryParams.PageSize"
- @pagination="queryList"
- />
- </el-col>
- <el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6" class="right-container">
- <p class="flex-center-between pt10">
- <span class="font16">常用知识前10</span>
- <el-button link type="primary" @click="querySearchNum"><SvgIcon name="ele-Refresh" class="mr4" /> 刷新</el-button>
- </p>
- <el-divider />
- <p class="flex-center-between">
- <span>排名</span>
- <span>搜索频率</span>
- </p>
- <div class="top10 mt10" style="height: calc(100% - 160px)" v-loading="rightLoading">
- <template v-if="topList.length">
- <el-scrollbar>
- <div class="flex-center-between top10-items" v-for="(item, index) in topList" :key="item.id">
- <p class="flex-center-align top10-items-title" @click="onPreview(item)">
- {{ index + 1 }}.<el-link type="primary" :underline="false">{{ item.title }}</el-link>
- </p>
- <span class="top10-items-num">{{ item.searchNum }}</span>
- </div>
- </el-scrollbar>
- </template>
- <template v-else>
- <Empty />
- </template>
- </div>
- </el-col>
- </el-row>
- </el-card>
- </div>
- </template>
- <script setup lang="ts" name="knowledgeRetrieval">
- import { onMounted, reactive, ref, watch } from 'vue';
- import { useRouter } from 'vue-router';
- import { ElMessage } from 'element-plus';
- import { auth } from '/@/utils/authFunction';
- import { knowledgeRetrieval, searchNumList,searchNumAdd } from '/@/api/knowledge/retrieval';
- import { hotSpotType } from '/@/api/business/order';
- import { getOrgList } from '/@/api/system/organize';
- import { treeList } from '/@/api/knowledge/type';
- import { formatDate } from '/@/utils/formatTime';
- import { throttle } from '/@/utils/tools';
- const state = reactive<any>({
- queryParams: {
- // 查询条件
- PageIndex: 1, // 当前页
- PageSize: 10, // 每页条数
- Attribution: ' ',
- Keyword: null, // 关键词
- RetrievalType: 0, // 检索类型
- Sort: '1',
- },
- activeName: '0',
- orgData: [], // 部门
- knowledgeOptions: [], // 知识类型
- total: 0, // 总条数
- loading: false, // 加载状态
- retrievalList: [], // 检索列表
- });
- const router = useRouter(); // 路由
- const topList = ref<EmptyArrayType>([]); // 常用知识前10
- const handleClick = () => {
- queryList();
- };
- // 热点分类远程搜索
- const HotspotProps = {
- label: 'hotSpotFullName',
- children: 'children',
- isLeaf: 'isLeaf',
- };
- // 热点分类懒加载
- const load = async (node: any, resolve: any) => {
- if (node.isLeaf) return resolve([]);
- const res: any = await hotSpotType({ id: node.data.id ? node.data.id : '' });
- resolve(res.result);
- };
- // 三个类型的搜索
- const filterOrg = ref('');
- const orgRef = ref<RefType>();
- watch(filterOrg, (val) => {
- orgRef.value!.filter(val);
- });
- const filterNode = (value: string, data: any) => {
- if (!value) return true;
- return data.name.includes(value);
- };
- const filterType = ref('');
- const typeRef = ref<RefType>();
- watch(filterType, (val) => {
- typeRef.value!.filter(val);
- });
- const filterNodeType = (value: string, data: any) => {
- if (!value) return true;
- return data.name.includes(value);
- };
- const filterHot = ref('');
- const hotRef = ref<RefType>();
- watch(filterHot, (val) => {
- hotRef.value!.filter(val);
- });
- const filterNodeHot = (value: string, data: any) => {
- if (!value) return true;
- return data.hotSpotName.includes(value);
- };
- // 获取所有组织结构 和基础数据
- const getOrgListApi = async () => {
- state.loading = true;
- try {
- const [orgRes, treeRes] = await Promise.all([getOrgList(), treeList({ IsEnable: true })]);
- state.orgData = orgRes.result ?? []; //部门
- state.knowledgeOptions = treeRes.result ?? []; // 知识类型
- state.loading = false;
- } catch (error) {
- state.loading = false;
- }
- };
- // 点击节点
- const handleNodeClick = (data: any) => {
- switch (state.activeName) {
- case '0':
- state.queryParams.CreateOrgId = data.id;
- break;
- case '1':
- state.queryParams.KnowledgeTypeId = data.id;
- break;
- case '2':
- state.queryParams.HotspotId = data.id;
- break;
- default:
- break;
- }
- queryList();
- };
- // 预览
- const onPreview = (row: any) => {
- router.push({
- name: 'knowledgePreview',
- params: {
- id: row.id,
- isAddPv: 'isAddPv',
- tagsViewName: '知识查看',
- },
- });
- };
- // 切换tab 查询列表
- const rightLoading = ref(false); // 右侧加载状态
- // 常用知识前10
- const querySearchNum = () => {
- rightLoading.value = true;
- searchNumList({ Keyword: state.queryParams.Keyword })
- .then((res: any) => {
- topList.value = res.result?.items ?? [];
- rightLoading.value = false;
- })
- .catch(() => {
- rightLoading.value = false;
- });
- };
- const queryTitleLight = (titleInfo: string) => {
- return titleInfo.replace(new RegExp(state.queryParams.Keyword, 'g'), `<span class="color-danger">${state.queryParams.Keyword}</span>`);
- };
- const centerLoading = ref(false); // 中间加载状态
- const querySearchAsync = (queryString: string, cb: (arg: any) => void) => {
- if (queryString) {
- centerLoading.value = true;
- knowledgeRetrieval({
- ...state.queryParams,
- Keyword: queryString,
- })
- .then((res: any) => {
- centerLoading.value = false;
- cb(res.result?.items ?? []);
- })
- .catch(() => {
- centerLoading.value = false;
- cb([]);
- });
- } else {
- cb([]);
- }
- };
- const handleSelect = (item: Record<string, any>) => {
- state.queryParams.Keyword = item.title;
- searchNumAdd({id: item.id});
- queryList();
- };
- const queryList = () => {
- if (!auth('knowledge:retrieval')) ElMessage.error('抱歉,您没有权限知识检索');
- else {
- centerLoading.value = true;
- knowledgeRetrieval(state.queryParams)
- .then((res: any) => {
- state.retrievalList = res.result?.items ?? [];
- state.total = res.result?.total ?? 0;
- centerLoading.value = false;
- })
- .catch(() => {
- centerLoading.value = false;
- state.retrievalList = [];
- state.total = 0;
- });
- }
- };
- /** 重置按钮操作 */
- const resetQuery = throttle(() => {
- state.queryParams.PageIndex = 1;
- state.queryParams.PageSize = 10;
- state.queryParams.Keyword = null;
- state.queryParams.RetrievalType = 0;
- state.queryParams.Sort = '1';
- state.queryParams.Attribution = ' ';
- state.activeName = '0';
- state.queryParams.CreateOrgId = null;
- state.queryParams.KnowledgeTypeId = null;
- state.queryParams.HotspotId = null;
- filterOrg.value = '';
- filterType.value = '';
- filterHot.value = '';
- typeRef.value?.setCurrentKey(null);
- orgRef.value?.setCurrentKey(null);
- hotRef.value?.setCurrentKey(null);
- queryList();
- }, 500);
- // 重置选中的节点
- const resetNode = () => {
- state.queryParams.CreateOrgId = null;
- state.queryParams.KnowledgeTypeId = null;
- state.queryParams.HotspotId = null;
- filterOrg.value = '';
- filterType.value = '';
- filterHot.value = '';
- typeRef.value?.setCurrentKey(null);
- orgRef.value?.setCurrentKey(null);
- hotRef.value?.setCurrentKey(null);
- queryList();
- };
- onMounted(() => {
- getOrgListApi();
- queryList();
- querySearchNum();
- });
- </script>
- <style scoped lang="scss">
- .knowledge-retrieval-container {
- .left-container {
- border-right: 1px solid var(--el-border-color);
- height: 100%;
- }
- .center-container {
- border-right: 1px solid var(--el-border-color);
- height: 100%;
- .input-box {
- display: flex;
- }
- .retrieval-content {
- &-item {
- border-bottom: var(--el-border);
- padding: 10px 15px;
- margin-bottom: 10px;
- cursor: pointer;
- &:last-child {
- margin-bottom: 0;
- border: none;
- }
- &:hover {
- color: var(--el-color-primary);
- }
- }
- }
- }
- .right-container {
- height: 100%;
- .top10 {
- &-items {
- margin-bottom: 20px;
- &:last-child {
- margin-bottom: 0;
- }
- &-title {
- flex: 1;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- &-num {
- display: inline-block;
- width: 50px;
- text-align: center;
- }
- }
- }
- }
- :deep(.el-tree-node__content) {
- height: 40px;
- }
- :deep(.el-card__body) {
- height: 100%;
- }
- }
- </style>
|