index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <template>
  2. <div class="case-retrieval-container layout-padding">
  3. <el-card shadow="never" class="h100">
  4. <splitpanes class="h100" Vertical>
  5. <pane min-size="16" max-size="25" size="16" class="left-container">
  6. <el-scrollbar ref="scrollBarRef">
  7. <el-skeleton :loading="state.typeLoading" animated :rows="10">
  8. <template #default>
  9. <v-tree-search
  10. :data="state.typeOptions"
  11. titleField="name"
  12. keyField="id"
  13. selectable
  14. searchPlaceholder="预案分类名称"
  15. @select="selectType"
  16. @unselect="unSelectType"
  17. ref="treeSearchRef"
  18. >
  19. <template #node="{ node }">
  20. <span>{{ node.name }}</span>
  21. </template>
  22. </v-tree-search>
  23. </template>
  24. </el-skeleton>
  25. </el-scrollbar>
  26. </pane>
  27. <pane class="center-container">
  28. <div class="input-box">
  29. <el-select v-model="state.queryParams.RetrievalType" placeholder="请选择" class="width120" @change="handleQuery">
  30. <el-option label="标题" value="0" />
  31. <el-option label="关键词" value="1" />
  32. <el-option label="摘要" value="2" />
  33. </el-select>
  34. <div class="input-with-button w100">
  35. <div class="flex">
  36. <el-input v-model="state.queryParams.text" placeholder="关键词" clearable class="mr10 w100" @keyup.enter="handleQuery"> </el-input>
  37. <el-button type="primary" class="btn" :loading="state.loading" @click="handleQuery"
  38. ><SvgIcon name="ele-Search" class="mr5" />搜索</el-button
  39. >
  40. <el-button @click="resetQuery" class="default-button"> <SvgIcon name="ele-Refresh" class="mr5" />重置</el-button>
  41. </div>
  42. </div>
  43. </div>
  44. <div style="display: flex; margin: 10px 15px 0">
  45. <div style="height: 32px; line-height: 32px">排序:</div>
  46. <el-radio-group v-model="state.queryParams.SortField" @change="handleQuery" style="align-items: normal">
  47. <el-radio value="pageView">浏览量</el-radio>
  48. <el-radio value="score">评分</el-radio>
  49. <el-radio value="creationTime">创建时间</el-radio>
  50. </el-radio-group>
  51. </div>
  52. <div v-loading="centerLoading" class="center-container-box" style="height: calc(100% - 110px)">
  53. <template v-if="state.retrievalList.length">
  54. <el-scrollbar>
  55. <div v-for="(v, i) in state.retrievalList" :key="i" class="retrieval-content-item" @click="onPreview(v)" title="查看详情">
  56. <h4 class="mb10 text-no-wrap retrieval-content-item-title">{{ v.title }}</h4>
  57. <!-- <div class="text-ellipsis2">{{ v.summary }}</div>-->
  58. <div class="flex-center-between mt10 color-info">
  59. <div>
  60. <span class="mr10">创建部门:{{ v.creatorOrgName }}</span>
  61. <span>创建时间:{{ formatDate(v.creationTime, 'YYYY-mm-dd HH:MM:SS') }}</span>
  62. </div>
  63. <div class="flex-center-align">
  64. <span class="flex-center-align"><SvgIcon name="ele-StarFilled" size="18px" class="mr3" />{{ v.score }}</span>
  65. <!-- <span class="flex-center-align ml10"><SvgIcon name="ele-ChatDotSquare" size="16px" class="mr3" />{{ v.commentNum }}</span>-->
  66. <span class="flex-center-align ml10"><SvgIcon name="ele-View" size="16px" class="mr3" />{{ v.pageView }}</span>
  67. </div>
  68. </div>
  69. </div>
  70. </el-scrollbar>
  71. </template>
  72. <el-empty v-else description="暂无结果" />
  73. <pagination
  74. @pagination="queryList"
  75. :total="state.total"
  76. v-model:current-page="state.queryParams.PageIndex"
  77. v-model:page-size="state.queryParams.PageSize"
  78. :disabled="centerLoading"
  79. />
  80. </div>
  81. </pane>
  82. <pane min-size="20" max-size="30" size="20" class="right-container">
  83. <p class="flex-center-between pt10">
  84. <span class="font16">常用预案前10</span>
  85. <el-button link type="primary" @click="querySearchNum"><SvgIcon name="ele-Refresh" class="mr4" /> 刷新</el-button>
  86. </p>
  87. <el-divider />
  88. <p class="flex-center-between">
  89. <span>排名</span>
  90. <span>搜索频率</span>
  91. </p>
  92. <el-skeleton :loading="rightLoading" animated :rows="10">
  93. <template #default>
  94. <div class="top10 mt10" style="height: calc(100% - 90px)">
  95. <template v-if="topList.length">
  96. <el-scrollbar>
  97. <div class="flex-center-between top10-items" v-for="(item, index) in topList" :key="item.id">
  98. <p class="flex-center-align top10-items-title" @click="onPreview(item)">
  99. {{ index + 1 }}.
  100. <el-text class="text-no-wrap color-primary cursor-pointer" @click="onPreview(item)">
  101. <TextTooltip :content="item.title" placement="top" effect="dark" />
  102. </el-text>
  103. </p>
  104. <span class="top10-items-num">{{ item.searchNum }}</span>
  105. </div>
  106. </el-scrollbar>
  107. </template>
  108. <el-empty v-else />
  109. </div>
  110. </template>
  111. </el-skeleton>
  112. </pane>
  113. </splitpanes>
  114. </el-card>
  115. </div>
  116. </template>
  117. <script setup lang="ts" name="caseRetrieval">
  118. import { onMounted, reactive, ref, defineAsyncComponent } from 'vue';
  119. import { useRouter } from 'vue-router';
  120. import { formatDate } from '@/utils/formatTime';
  121. import { throttle } from '@/utils/tools';
  122. import { Splitpanes, Pane } from 'splitpanes';
  123. import 'splitpanes/dist/splitpanes.css';
  124. import { VTreeSearch } from '@wsfe/vue-tree';
  125. import { caseTreeList } from '@/api/case/type';
  126. import { caseRetrieval, caseRetrievalTop10 } from '@/api/case/retrieval';
  127. const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
  128. const TextTooltip = defineAsyncComponent(() => import('@/components/TextTooltip/index.vue'));
  129. const router = useRouter(); // 路由
  130. const state = reactive<any>({
  131. queryParams: {
  132. // 查询条件
  133. PageIndex: 1, // 当前页
  134. PageSize: 10, // 每页条数
  135. Attribution: '中心案例库',
  136. text: null, // 关键词
  137. RetrievalType: '0', // 检索类型 默认全文
  138. OrderByType: 1, // 排序类型 降序
  139. SortField: 'pageView',
  140. },
  141. typeOptions: [], // 预案类型
  142. total: 0, // 总条数
  143. loading: false, // 加载状态
  144. retrievalList: [], // 检索列表
  145. typeLoading: false, // 预案类型loading
  146. });
  147. const topList = ref<EmptyArrayType>([]); // 常用预案前10
  148. // 获取分类
  149. const getType = async () => {
  150. state.typeLoading = true;
  151. try {
  152. const { result } = await caseTreeList({ IsEnable: true, Attribution: state.queryParams.Attribution });
  153. state.typeOptions = result ?? [];
  154. state.typeLoading = false;
  155. } catch (error) {
  156. state.typeLoading = false;
  157. }
  158. };
  159. // 选择分类
  160. const selectType = (data: any) => {
  161. state.queryParams.CaseTypeID = data.id;
  162. handleQuery();
  163. };
  164. // 取消选择
  165. const unSelectType = () => {
  166. state.queryParams.CaseTypeID = null;
  167. handleQuery();
  168. };
  169. // 预览
  170. const onPreview = (row: any) => {
  171. router.push({
  172. name: 'casePreview',
  173. params: {
  174. id: row.id,
  175. isAddPv: 'isAddPv',
  176. tagsViewName: row.title,
  177. },
  178. });
  179. };
  180. // 切换tab 查询列表
  181. const rightLoading = ref(false); // 右侧加载状态
  182. // 常用预案前10
  183. const querySearchNum = () => {
  184. rightLoading.value = true;
  185. caseRetrievalTop10({ Keyword: state.queryParams.Keyword })
  186. .then((res: any) => {
  187. topList.value = res.result?.items ?? [];
  188. rightLoading.value = false;
  189. })
  190. .catch(() => {
  191. rightLoading.value = false;
  192. });
  193. };
  194. const centerLoading = ref(false); // 中间加载状态
  195. /** 搜索按钮操作 */
  196. const handleQuery = () => {
  197. state.queryParams.PageIndex = 1;
  198. switch (state.queryParams.RetrievalType) {
  199. case '0': // 标题
  200. state.queryParams.Title = state.queryParams.text;
  201. state.queryParams.Abstract = null;
  202. state.queryParams.Keyword = null;
  203. break;
  204. case '1': // 关键词
  205. state.queryParams.Keyword = state.queryParams.text;
  206. state.queryParams.Abstract = null;
  207. state.queryParams.Title = null;
  208. break;
  209. case '2': // 摘要
  210. state.queryParams.Abstract = state.queryParams.text;
  211. state.queryParams.Title = null;
  212. state.queryParams.Keyword = null;
  213. break;
  214. }
  215. queryList();
  216. };
  217. const queryList = () => {
  218. centerLoading.value = true;
  219. caseRetrieval(state.queryParams)
  220. .then((res: any) => {
  221. state.retrievalList = res.result?.items ?? [];
  222. state.total = res.result?.total ?? 0;
  223. centerLoading.value = false;
  224. })
  225. .catch(() => {
  226. centerLoading.value = false;
  227. state.retrievalList = [];
  228. state.total = 0;
  229. });
  230. };
  231. /** 重置按钮操作 */
  232. const treeSearchRef = ref<RefType>();
  233. const resetQuery = throttle(() => {
  234. state.queryParams.PageIndex = 1;
  235. state.queryParams.PageSize = 10;
  236. state.queryParams.text = null;
  237. state.queryParams.RetrievalType = '0';
  238. state.queryParams.SortField = 'pageView';
  239. state.queryParams.CaseTypeID = null;
  240. state.queryParams.Abstract = null;
  241. state.queryParams.Title = null;
  242. state.queryParams.Keyword = null;
  243. treeSearchRef.value.setSelected(state.queryParams.CaseTypeID, false); // 清空选择
  244. treeSearchRef.value.clearKeyword(); // 清空搜索关键词
  245. treeSearchRef.value.search(); // 搜索
  246. queryList();
  247. setTimeout(() => {
  248. treeSearchRef.value.setExpandAll(false); // 默认全部收起
  249. }, 300);
  250. }, 500);
  251. onMounted(() => {
  252. getType();
  253. queryList();
  254. querySearchNum();
  255. });
  256. </script>
  257. <style scoped lang="scss">
  258. .case-retrieval-container {
  259. .left-container {
  260. height: 100%;
  261. }
  262. .center-container {
  263. height: 100%;
  264. display: flex;
  265. flex-direction: column;
  266. .input-box {
  267. display: flex;
  268. }
  269. .retrieval-content {
  270. &-item {
  271. border-bottom: var(--el-border);
  272. padding: 10px 15px;
  273. margin-bottom: 10px;
  274. cursor: pointer;
  275. &:last-child {
  276. margin-bottom: 0;
  277. border: none;
  278. }
  279. &:hover {
  280. color: var(--el-color-primary);
  281. }
  282. }
  283. }
  284. }
  285. .right-container {
  286. height: 100%;
  287. .top10 {
  288. &-items {
  289. margin-bottom: 20px;
  290. &:last-child {
  291. margin-bottom: 0;
  292. }
  293. &-title {
  294. flex: 1;
  295. overflow: hidden;
  296. text-overflow: ellipsis;
  297. white-space: nowrap;
  298. }
  299. &-num {
  300. display: inline-block;
  301. width: 50px;
  302. text-align: center;
  303. }
  304. }
  305. }
  306. }
  307. :deep(.el-tree-node__content) {
  308. height: 32px;
  309. }
  310. :deep(.el-card__body) {
  311. height: 100%;
  312. }
  313. .keyword-box {
  314. display: flex;
  315. flex: 1;
  316. flex-wrap: wrap;
  317. line-height: 18px;
  318. .keyword-item {
  319. margin-right: 5px;
  320. cursor: pointer;
  321. color: var(--el-color-info);
  322. font-size: var(--el-font-size-extra-small);
  323. &:last-child {
  324. margin-right: 0;
  325. }
  326. &:hover {
  327. color: var(--el-color-primary);
  328. }
  329. }
  330. }
  331. }
  332. </style>