Просмотр исходного кода

feat:新增注销功能,标签管理静态页
1.注销功能完成
2.标签静态管理页面
3.新增知识库弹框
4.搜索逻辑完成

hanyang 1 год назад
Родитель
Сommit
2906b67e6a

+ 5 - 2
public/i18n/lang-en.json

@@ -36,8 +36,11 @@
     "usernameLength": "Username must be between 3 and 20 characters long",
     "passwordLength": "Password must be between 6 and 20 characters long",
     "emailFormat": "Incorrect email format",
-
-    "logout": "Exit"
+    "logout": "Exit",
+    "okBtn": "OK",
+    "cancelBtn": "Cancel",
+    "tip": "Prompt",
+    "okText": "Are you sure you want to log out?"
   },
   "management": {
     "knowledgeTitle": "Welcome back, GuanLi4",

+ 5 - 2
public/i18n/lang-zh.json

@@ -36,8 +36,11 @@
     "usernameLength": "用户名长度必须在3-20个字符之间",
     "passwordLength": "密码长度必须在6-20个字符之间",
     "emailFormat": "邮箱格式不正确",
-
-    "logout": "退出登录"
+    "logout": "退出登录",
+    "okBtn": "确定",
+    "cancelBtn": "取消",
+    "tip": "提示",
+    "okText": "确定退出登录?"
   },
   "management": {
     "knowledgeTitle": "欢迎回来,GuanLi4",

+ 3 - 2
src/apis/intelligent.js

@@ -4,10 +4,11 @@ import request from '../base/ajax';
  * 获取智能体列表
  * @param {number} current 当前页数
  * @param {number} pageSize 每页条数
+ * @param {string} keyword 搜索
  * */
-export function getDialogList(current, pageSize) {
+export function getDialogList(current, pageSize, keyword) {
   return request({
-    url: `/api/dialog/list?current=${current}&pageSize=${pageSize}`,
+    url: `/api/dialog/list?current=${current}&pageSize=${pageSize}&keyword=${keyword}`,
     method: 'get'
   });
 }

+ 3 - 2
src/apis/knowledge.js

@@ -4,10 +4,11 @@ import request from '../base/ajax';
  * 获取知识列表
  * @param {number} current 当前页数
  * @param {number} pageSize 每页条数
+ * @param {string} keyword 搜索
  * */
-export function getKnowledgeList(current, pageSize) {
+export function getKnowledgeList(current, pageSize, keyword) {
   return request({
-    url: `/api/knowledge/list?current=${current}&pageSize=${pageSize}`,
+    url: `/api/knowledge/list?current=${current}&pageSize=${pageSize}&keyword=${keyword}`,
     method: 'get'
   });
 }

+ 14 - 0
src/apis/model.js

@@ -0,0 +1,14 @@
+import request from '../base/ajax';
+
+/**
+ * 获取模型列表
+ * @param {number} current 当前页数
+ * @param {number} pageSize 每页条数
+ * @param {string} keyword 搜索
+ * */
+export function getModelList(current, pageSize, keyword) {
+  return request({
+    url: `/api/llm/list?current=${current}&pageSize=${pageSize}&keyword=${keyword}`,
+    method: 'get'
+  });
+}

+ 70 - 0
src/modules/intelligent/LabelSelected.vue

@@ -0,0 +1,70 @@
+<template>
+  <div class="label-select">
+    <a-input
+      v-model="searchText"
+      :placeholder="$t('management.search')"
+      style="width: 100%"
+      allow-clear
+      @input="handleSearch"
+    >
+      <template #prefix>
+        <icon-search />
+      </template>
+    </a-input>
+    <a-checkbox-group v-model="checkedList" direction="vertical">
+      <a-checkbox v-for="item in labelList" :key="item.id" :value="item.value">{{ item.name }}</a-checkbox>
+    </a-checkbox-group>
+    <div class="footer">
+      <div class="label-manage" @click="toManageTags"><icon-tags /> 管理标签</div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+
+const emit = defineEmits(['openManageTags']);
+
+const searchText = ref('');
+const checkedList = ref([]);
+const labelList = ref([
+  {
+    id: 1,
+    name: '标签1',
+    value: '1'
+  },
+  {
+    id: 2,
+    name: '标签2',
+    value: '2'
+  },
+  {
+    id: 3,
+    name: '标签3',
+    value: '3'
+  },
+  {
+    id: 4,
+    name: '标签4',
+    value: '4'
+  }
+]);
+
+const handleSearch = () => {};
+const toManageTags = () => {
+  emit('openManageTags');
+};
+</script>
+<style scoped lang="css">
+.footer {
+  display: flex;
+  border-top: 1px solid var(--color-neutral-4);
+}
+.footer .label-manage {
+  margin-top: 10px;
+  cursor: pointer;
+}
+.arco-input-wrapper {
+  background-color: var(--color-neutral-3);
+}
+</style>

+ 105 - 0
src/modules/intelligent/ManageTags.vue

@@ -0,0 +1,105 @@
+<template>
+  <a-modal
+    v-model:visible="show_model"
+    :esc-to-close="false"
+    :footer="false"
+    :maskClosable="false"
+    :align-center="false"
+    title="管理标签"
+    title-align="start"
+    top="15vh"
+    @before-cancel="handleCancel"
+  >
+    <a-space wrap>
+      <a-input
+        v-if="show_input"
+        ref="input_ref"
+        :style="{ width: '90px' }"
+        size="mini"
+        v-model.trim="input_val"
+        @keyup.enter="handleAdd"
+        @blur="handleAdd"
+      />
+      <a-tag
+        v-else
+        :style="{
+          width: '90px',
+          backgroundColor: 'var(--color-fill-2)',
+          border: '1px dashed var(--color-fill-3)',
+          cursor: 'pointer'
+        }"
+        @click="handleEdit"
+      >
+        <template #icon>
+          <icon-plus />
+        </template>
+        创建新标签
+      </a-tag>
+      <a-tag v-for="tag in tags" :key="tag.name">
+        {{ tag.name }}
+        <icon-delete @click="handleDelete(tag)" style="cursor: pointer" />
+      </a-tag>
+    </a-space>
+  </a-modal>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    default: false
+  }
+});
+const emit = defineEmits(['update:visible']);
+
+const tags = ref([
+  {
+    name: '标签1'
+  },
+  {
+    name: '标签2'
+  },
+  {
+    name: '标签3'
+  },
+  {
+    name: '标签4'
+  }
+]);
+const input_ref = ref(null);
+const show_input = ref(false);
+const input_val = ref('');
+const show_model = computed(() => props.visible);
+const handleDelete = (tag) => {
+  tags.value = tags.value.filter((t) => t.name !== tag.name);
+};
+const handleCancel = () => {
+  i;
+  emit('update:visible', false);
+};
+
+const handleEdit = () => {
+  show_input.value = true;
+
+  nextTick(() => {
+    if (input_ref.value) {
+      input_ref.value.focus();
+    }
+  });
+};
+
+const handleAdd = () => {
+  if (input_val.value) {
+    tags.value.push({ name: input_val.value });
+    input_val.value = '';
+  }
+  show_input.value = false;
+};
+</script>
+<style scoped lang="css">
+.arco-form-item-content-wrapper {
+  border-color: var(--color-neutral-4);
+}
+</style>

+ 36 - 10
src/modules/intelligent/MessageBody.vue

@@ -8,11 +8,20 @@
           <template #unchecked> {{ $t('management.offline') }} </template>
         </a-switch>
       </template>
-      <template #labels>
-        <div class="label-btn">
-          <icon-subscribe />
-          <div>{{ $t('management.addLabelBtn') }}</div>
-        </div>
+      <template #labelBtn>
+        <!-- <div class="label-btn"> -->
+        <a-dropdown ref="dropdown" v-model="show_down" class="label-btn" position="bl" :popup-max-height="false">
+          <a-button>
+            <icon-subscribe />
+            {{ $t('management.addLabelBtn') }}</a-button
+          >
+          <template #content>
+            <div class="label-add">
+              <LabelSelected @openManageTags="openManageTags"></LabelSelected>
+            </div>
+          </template>
+        </a-dropdown>
+        <!-- </div> -->
       </template>
       <template #bottom>
         <div class="bottom">
@@ -28,12 +37,15 @@
       <div v-if="scroll_bottom && pages.current >= total_pages">{{ $t('management.noMoreData') }}</div>
     </div>
   </div>
+  <ManageTags v-model:visible="visible"></ManageTags>
 </template>
 
 <script setup>
 import { ref, reactive, onMounted, onBeforeUnmount } from 'vue';
 import CardItem from '../../views/manage/CardItem.vue';
+import ManageTags from './ManageTags.vue';
 import { getImageIntelligent } from '../../views/manage/common';
+import LabelSelected from './LabelSelected.vue';
 import { getDialogList } from '../../apis/intelligent';
 import { bus_intelligentSearch } from '../../base/event-bus';
 
@@ -49,27 +61,30 @@ const moke_data = ref([
 ]);
 const pages = reactive({
   current: 1,
-  pageSize: 25
+  pageSize: 30
 });
 // 总页码
 const total_pages = ref(5);
 const body_container = ref(null);
 const scroll_bottom = ref(false);
 const search_text = ref('');
+const dropdown = ref(null);
+const visible = ref(false);
 
 const getDialog = async () => {
   try {
-    const res = await getDialogList(pages.current, pages.pageSize);
+    const res = await getDialogList(pages.current, pages.pageSize, search_text.value);
     const data = res.data.rows.map((item) => {
       return {
         ...item,
         time: new Date(item.update_time).toLocaleString('zh-CN'),
         desc: item.description,
         img_src: getImageIntelligent(item.icon),
-        user: item.user || 'admin',
+        user: item.user.userName || '-',
         online: item.status == 1
       };
     });
+    total_pages.value = Math.ceil(res.data.total / pages.pageSize);
     moke_data.value.push(...data);
   } catch (err) {
     console.log(err);
@@ -80,16 +95,22 @@ const scrollHandler = (e) => {
     scroll_bottom.value = true;
     // 调用函数加载更多数据
     if (pages.current < total_pages.value) {
-      pages.current += 1;
       getDialog();
+      pages.current += 1;
     } else {
     }
   }
 };
+const openManageTags = () => {
+  visible.value = true;
+};
 bus_intelligentSearch.on((text) => {
   search_text.value = text;
+  moke_data.value = [];
+  pages.current = 1;
   getDialog();
 });
+
 onMounted(() => {
   getDialog();
   body_container.value.addEventListener('scroll', scrollHandler);
@@ -116,10 +137,15 @@ onBeforeUnmount(() => {
   display: flex;
   gap: 10px;
   padding: 8px 16px;
-  background-color: var(--color-neutral-3);
+  background-color: var(--color-neutral-2);
+  color: var(--color-neutral-6);
   font-size: 12px;
   cursor: pointer;
 }
+.label-add {
+  width: 240px;
+  padding: 10px;
+}
 .bottom {
   display: flex;
   justify-content: space-between;

+ 52 - 0
src/modules/intelligent/ModalItem.vue

@@ -0,0 +1,52 @@
+<template>
+  <a-modal
+    v-model:visible="showModal"
+    title="添加智能体"
+    title-align="start"
+    :align-center="false"
+    top="15vh"
+    @cancel="handleCancel"
+    @before-ok="handleBeforeOk"
+  >
+    <a-form :model="form">
+      <a-form-item field="name" label="名称" validate-trigger="input" required>
+        <a-input v-model="form.name" placeholder="请输入名称" />
+      </a-form-item>
+    </a-form>
+  </a-modal>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    default: false
+  }
+});
+
+const emit = defineEmits(['update:visible']);
+
+const form = ref({
+  name: ''
+});
+const showModal = computed(() => props.visible);
+const handleBeforeOk = (done) => {
+  if (form.value.name.trim().length) {
+    // TODO:添加智能体
+    done();
+  } else {
+    return false;
+  }
+};
+const handleCancel = () => {
+  form.value.name = '';
+  emit('update:visible', false);
+};
+</script>
+<style scoped lang="css">
+.arco-form-item-content-wrapper {
+  border-color: var(--color-neutral-4);
+}
+</style>

+ 6 - 1
src/modules/intelligent/TopSetting.vue

@@ -18,14 +18,17 @@
       {{ $t('management.createIntelligent') }}
     </a-button>
   </a-space>
+  <ModalItem v-model:visible="visible"></ModalItem>
 </template>
 
 <script setup>
 import { ref } from 'vue';
+import ModalItem from './ModalItem.vue';
 import { funcDebounce } from '../../utils/utils';
 import { bus_intelligentSearch } from '../../base/event-bus';
 
 const searchText = ref('');
+const visible = ref(false);
 // 搜索
 const handleSearch = funcDebounce(
   () => {
@@ -35,7 +38,9 @@ const handleSearch = funcDebounce(
   false
 );
 // 创建智能体
-const handleCreate = () => {};
+const handleCreate = () => {
+  visible.value = true;
+};
 </script>
 <style scoped lang="scss"></style>
 <style>

+ 10 - 8
src/modules/knowledge/MessageBody.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="body-container" ref="body_container">
-    <CardItem v-for="item in moke_data" :key="item.name" :name="item.name" :img_src="item.img_src">
+    <CardItem v-for="item in moke_data" :key="item.id" :name="item.name" :img_src="item.img_src">
       <template #bottom>
         <div class="bottom">
           <div>{{ item.count }}{{ $t('management.document') }}</div>
@@ -42,15 +42,14 @@ const moke_data = ref([
 ]);
 const pages = reactive({
   current: 1,
-  pageSize: 25
+  pageSize: 30
 });
 // 总页码
-const total_pages = ref(5);
+const total_pages = ref(0);
 const body_container = ref(null);
 const scroll_bottom = ref(false);
 // 搜索文本
 const search_text = ref('');
-
 const handleSelect = ({ value }) => {
   if (value === 'edit') {
   } else {
@@ -59,17 +58,18 @@ const handleSelect = ({ value }) => {
 const getKnowledge = async () => {
   // TODO: 等待接口修改后,需要将searchText传到接口中
   try {
-    const res = await getKnowledgeList(pages.current, pages.pageSize);
+    const res = await getKnowledgeList(pages.current, pages.pageSize, search_text.value);
     if (res.code == 200) {
       const data = res.data.rows.map((item) => {
         return {
           ...item,
           time: new Date(item.update_time).toLocaleString('zh-CN'),
           img_src: getImageKnowLedge(item.avatar),
-          count: item.count ? item.count : 292
+          count: item.count ? item.count : 0
         };
       });
       moke_data.value.push(...data);
+      total_pages.value = Math.ceil(res.data.total / pages.pageSize);
     }
   } catch (err) {
     console.log(err, 'flag');
@@ -80,15 +80,16 @@ const scrollHandler = (e) => {
     scroll_bottom.value = true;
     // 调用函数加载更多数据
     if (pages.current < total_pages.value) {
-      pages.current += 1;
       getKnowledge();
-    } else {
+      pages.current += 1;
     }
   }
 };
 
 bus_knowledgeSearch.on((text) => {
   search_text.value = text;
+  moke_data.value = [];
+  pages.current = 1;
   getKnowledge();
 });
 
@@ -106,6 +107,7 @@ onBeforeUnmount(() => {
 .body-container {
   display: grid;
   grid-template-columns: repeat(6, 1fr);
+  align-content: flex-start;
   gap: 20px;
   overflow-y: scroll;
   height: calc(100% - 80px);

+ 52 - 0
src/modules/knowledge/ModalItem.vue

@@ -0,0 +1,52 @@
+<template>
+  <a-modal
+    v-model:visible="showModal"
+    title="添加知识库"
+    title-align="start"
+    :align-center="false"
+    top="15vh"
+    @cancel="handleCancel"
+    @before-ok="handleBeforeOk"
+  >
+    <a-form :model="form">
+      <a-form-item field="name" label="名称" validate-trigger="input" required>
+        <a-input v-model="form.name" placeholder="请输入名称" />
+      </a-form-item>
+    </a-form>
+  </a-modal>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    default: false
+  }
+});
+
+const emit = defineEmits(['update:visible']);
+
+const form = ref({
+  name: ''
+});
+const showModal = computed(() => props.visible);
+const handleBeforeOk = (done) => {
+  if (form.value.name.trim().length) {
+    // TODO:添加知识库
+    done();
+  } else {
+    return false;
+  }
+};
+const handleCancel = () => {
+  form.value.name = '';
+  emit('update:visible', false);
+};
+</script>
+<style scoped lang="css">
+.arco-form-item-content-wrapper {
+  border-color: var(--color-neutral-4);
+}
+</style>

+ 7 - 1
src/modules/knowledge/TopSetting.vue

@@ -18,14 +18,18 @@
       {{ $t('management.createKnowledgeBase') }}
     </a-button>
   </a-space>
+  <modalItem v-model:visible="visible" />
 </template>
 
 <script setup>
 import { ref } from 'vue';
+import modalItem from './modalItem.vue';
 import { funcDebounce } from '../../utils/utils';
 import { bus_knowledgeSearch } from '../../base/event-bus';
 
 const searchText = ref('');
+// 模态框控制
+const visible = ref(false);
 // 搜索
 const handleSearch = funcDebounce(
   () => {
@@ -35,7 +39,9 @@ const handleSearch = funcDebounce(
   false
 );
 // 创建知识库
-const handleCreate = () => {};
+const handleCreate = () => {
+  visible.value = true;
+};
 </script>
 <style scoped lang="scss"></style>
 <style>

+ 1 - 2
src/modules/login/FormItem.vue

@@ -150,8 +150,7 @@ const changeFormType = (e) => {
 .reg-link {
   color: rgb(var(--arcoblue-6));
 }
-</style>
-<style>
+
 .arco-form-item-label-col {
   padding: 0px;
 }

+ 6 - 6
src/views/common/header-btns/BtnCurrentUser.vue

@@ -8,20 +8,20 @@
       <a-doption value="logout"><icon-export />{{ $t('login.logout') }}</a-doption>
     </template>
   </a-dropdown>
+  <ModalLogout v-model:visible="visible"></ModalLogout>
 </template>
 
 <script setup>
-import { useRouter } from 'vue-router';
+import { ref } from 'vue';
+import ModalLogout from './ModalLogout.vue';
 import { useCurrentUserStore } from '../../../base/store/use-current-user';
 
-const router = useRouter();
-
 const currentUser = useCurrentUserStore();
 
-const handleSelect = (value) => {
-  console.log(value, '退出登录');
+const visible = ref(false);
 
-  router.push({ path: '/login', replace: true });
+const handleSelect = (value) => {
+  visible.value = true;
 };
 </script>
 

+ 44 - 0
src/views/common/header-btns/ModalLogout.vue

@@ -0,0 +1,44 @@
+<template>
+  <a-modal
+    :visible="showModal"
+    @ok="handleOk"
+    @cancel="handleCancel"
+    :okText="$t('login.okBtn')"
+    :cancelText="$t('login.cancelBtn')"
+    unmountOnClose
+  >
+    <template #title><icon-exclamation-circle-fill class="tips-icon" /> {{ $t('login.tip') }} </template>
+    <div>{{ $t('login.okText') }}</div>
+  </a-modal>
+</template>
+
+<script setup>
+import { computed } from 'vue';
+import { useRouter } from 'vue-router';
+import { kuky_Authorization } from '../../../base/storage';
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    default: false
+  }
+});
+const emit = defineEmits(['update:visible']);
+const router = useRouter();
+const showModal = computed(() => props.visible);
+
+const handleOk = () => {
+  kuky_Authorization.remove();
+  router.push({ path: '/login', replace: true });
+  emit('update:visible', false);
+};
+const handleCancel = () => {
+  emit('update:visible', false);
+};
+</script>
+<style scoped lang="css">
+.tips-icon {
+  font-size: 20px;
+  color: rgb(var(--orange-6));
+}
+</style>

+ 3 - 1
src/views/manage/CardItem.vue

@@ -10,7 +10,9 @@
     <div class="card-des">
       <slot name="description"></slot>
     </div>
-
+    <div class="card-label">
+      <slot name="labelBtn"></slot>
+    </div>
     <div class="card-bottom">
       <slot name="bottom"></slot>
     </div>