CzRger 2 月之前
父節點
當前提交
1ae32ca805

+ 2 - 2
src/components/czr-ui/CzrDialog.vue

@@ -35,7 +35,7 @@
       <div class="__czr-dialog-content">
         <slot/>
       </div>
-      <div class="__czr-dialog-foot" :style="`justify-content: ${footAlign};padding: ${footPadding};`" v-if="showSubmit || showClose || $slots.foot">
+      <div class="__czr-dialog-foot" :style="`justify-content: ${footAlign};`" v-if="showSubmit || showClose || $slots.foot">
         <slot name="foot" :close="CDClose" :submit="CDSubmit"/>
         <template v-if="footAlign === 'center'">
           <div v-if="showSubmit" class="__czr-dialog-foot_submit __hover" @click="CDSubmit">{{submitText}}</div>
@@ -76,7 +76,7 @@ const props = defineProps({
   showClose: {default: true},
   showSubmit: {default: true},
   footAlign: {default: 'center'},
-  footPadding: {default: '16px 26px'},
+  // footPadding: {default: '16px 26px'},
   height: {default: 'auto'},
   maxHeight: {default: 'unset'},
   minHeight: {default: 'unset'},

+ 1 - 0
src/components/czr-ui/CzrForm.vue

@@ -37,6 +37,7 @@ watch(() => props.formView, (n) => {
 })
 provide('form-view', _formView)
 provide('form-layout', props.layout)
+provide('form-label-width', props.labelWidth)
 const submit = () => {
   return new Promise((resolve, reject) => {
     const nodes: any = [];

+ 26 - 8
src/components/czr-ui/CzrFormColumn.vue

@@ -1,8 +1,9 @@
 <template>
   <el-col class="czr-form-column" :class="{
-    transparent: transparent
+    transparent: transparent,
+    'has-width': !!width
   }" :span="span" :offset="offset" ref="ref_czrFormColumn">
-    <el-form-item :label="label" :label-width="labelWidth" :class="{
+    <el-form-item :label="label" :label-width="labelWidthCpt" :class="{
       ['link_' + link + ($attrs.type ? '_' + $attrs.type : '')]: true,
       required: required !== false,
       'no-label': (labelWidth == 0 || labelWidth === '0px') && !isValue(label) && !$slots.label,
@@ -237,7 +238,7 @@ const props = defineProps({
   param: {},  // 绑定值
   label: {type: String, default: ''}, // 标题
   required: {default: false}, // 必填项
-  labelWidth: {type: [String, Number], default: 'auto'},  // 标题宽度
+  labelWidth: {type: [String, Number], default: ''},  // 标题宽度
   link: {type: String, default: 'input', validator(val: string) {
       return ['cascader', 'checkbox', 'date', 'datetime', 'input', 'radio', 'select', 'switch', 'dept', 'time', 'upload', 'number', 'input-number', 'tree-select', 'rich', 'area'].includes(val)
     }
@@ -249,7 +250,8 @@ const props = defineProps({
   unit: {default: '', type: String},  // 单位
   otherInfo: {}, // 其他信息
   layout: {},
-  transparent: {default: false}
+  transparent: {default: false},
+  width: {default: ''}
 })
 const attrs = (getCurrentInstance() as ComponentInternalInstance).attrs
 const state = reactive({
@@ -307,6 +309,16 @@ const handleValidate = (val: any = undefined, val2: any = undefined) => {
 }
 const formLayout = inject('form-layout', 'x')
 const handleEnterFunc = inject('handle-enter', () => {})
+const formLabelWidth = inject('form-label-width', '')
+const labelWidthCpt = computed(() => {
+  if (props.labelWidth) {
+    return props.labelWidth
+  }
+  if (formLabelWidth) {
+    return formLabelWidth
+  }
+  return 'auto'
+})
 const handleEnter = () => {
   handleEnterFunc?.()
 }
@@ -356,8 +368,11 @@ defineExpose({
 
 <style scoped lang="scss">
 .czr-form-column {
-  max-width: unset;
-  flex: unset;
+  &.has-width {
+    width: v-bind(width);
+    max-width: v-bind(width);
+    flex: unset;
+  }
   &.transparent {
     :deep(.el-form-item) {
       margin-bottom: 0;
@@ -368,7 +383,7 @@ defineExpose({
     }
   }
   :deep(.el-form-item) {
-    height: 2.25rem;
+    min-height: 2.25rem;
     &.link_time {
       margin-top: 1px;
     }
@@ -392,7 +407,7 @@ defineExpose({
       text-align: right;
       display: flex;
       align-items: center;
-      padding-left: 10px;
+      padding-left: 0.25rem;
       color: $textColor;
       font-weight: normal;
     }
@@ -421,6 +436,9 @@ defineExpose({
           -webkit-text-fill-color: $textColor;
         }
       }
+      .el-input__wrapper, .el-textarea__inner {
+        border-radius: 2px;
+      }
     }
   }
 }

+ 22 - 3
src/stores/modules/dictionary.ts

@@ -13,6 +13,11 @@ export const useDictionaryStore = defineStore('dictionary', {
 			waiting: false,
 			list: [],
 			map: new Map()
+		},
+		knowledgeGroups: {
+			waiting: false,
+			list: [],
+			map: new Map()
 		}
 	}),
 	getters: {
@@ -27,13 +32,27 @@ export const useDictionaryStore = defineStore('dictionary', {
 				setTimeout(() => {
 					const arr: any = []
 					for (let i = 0; i < 100; i++) {
-						arr.push({name: '标签' + i, id: i + '', total: i})
+						arr.push({label: '标签' + i, value: i + '', total: i})
 					}
 					this.knowledgeTags.list = arr
-					this.knowledgeTags.map = listToMap(arr, 'name', 'id')
+					this.knowledgeTags.map = listToMap(arr)
 					this.knowledgeTags.waiting = false
 				}, 1000)
 			}
-		}
+		},
+		initKnowledgeGroups() {
+			if (!this.knowledgeGroups.waiting) {
+				this.knowledgeGroups.waiting = true
+				setTimeout(() => {
+					const arr: any = []
+					for (let i = 0; i < 100; i++) {
+						arr.push({label: '分组' + i, value: i + '', total: i})
+					}
+					this.knowledgeGroups.list = arr
+					this.knowledgeGroups.map = listToMap(arr)
+					this.knowledgeGroups.waiting = false
+				}, 1000)
+			}
+		},
 	},
 })

+ 15 - 15
src/style/czr.scss

@@ -4,6 +4,7 @@
   --czr-gap: 0.63rem;
   --czr-border-color: #10182814;
   --czr-border: 1px solid #10182814;
+  --czr-dialog-bg: rgba(255,255,255,0.8);
 }
 
 .__disabled {
@@ -44,7 +45,6 @@
     }
   }
 }
-
 .__text-ellipsis-2 {
   display: -webkit-box;
   -webkit-box-orient: vertical;
@@ -193,7 +193,7 @@
         ._czr-dialog-head {
           width: 100%;
           height: 3.13rem;
-          background-color: rgba(255,255,255,0.8);
+          background-color: var(--czr-dialog-bg);
           display: flex;
           align-items: center;
           padding: 0 1rem;
@@ -231,6 +231,7 @@
             align-items: center;
             box-sizing: border-box;
             gap: 1rem;
+            margin-bottom: 1rem;
           }
           &.isFull {
             overflow-y: auto;
@@ -245,23 +246,22 @@
 }
 .__czr-title_1 {
   width: 100%;
-  height: 32px;
-  font-size: 14px;
-  font-family: PingFang SC-Regular, PingFang SC;
-  font-weight: 400;
-  color: #0062E9;
+  height: 2rem;
+  font-weight: bold;
+  font-size: 1rem;
+  color: var(--czr-main-color);
   display: flex;
   align-items: center;
   position: relative;
-  padding-left: 10px;
+  padding-left: 0.63rem;
   box-sizing: border-box;
   &:before {
     content: '';
     position: absolute;
     left: 0;
-    width: 2px;
-    height: 14px;
-    background-color: #0062E9;
+    width: 0.13rem;
+    height: 0.88rem;
+    background-color: var(--czr-main-color);
   }
 }
 
@@ -360,7 +360,7 @@
       .__czr-confirm-head {
         width: 100%;
         height: 3.13rem;
-        background-color: rgba(255,255,255,0.8);
+        background-color: var(--czr-dialog-bg);
         display: flex;
         align-items: center;
         padding: 0 1rem;
@@ -377,7 +377,7 @@
       .__czr-confirm-content {
         margin: 1rem 1.5rem 0;
         padding: 1rem;
-        background: rgba(255,255,255,0.8);
+        background-color: var(--czr-dialog-bg);
         border-radius: 0.5rem;
         display: flex;
         >div:nth-child(1) {
@@ -404,7 +404,7 @@
 
 .__czr-dialog-foot_submit {
   border: 1px solid var(--czr-main-color);
-  width: 5rem;
+  padding: 0 1.63rem;
   height: 2rem;
   background: var(--czr-main-color);
   border-radius: 0.25rem;
@@ -418,7 +418,7 @@
 }
 .__czr-dialog-foot_cancel {
   border: 1px solid var(--czr-main-color);
-  width: 5rem;
+  padding: 0 1.63rem;
   height: 2rem;
   background: #ffffff;
   border-radius: 0.25rem;

+ 5 - 0
src/style/manage.scss

@@ -21,4 +21,9 @@
       margin-bottom: 0;
     }
   }
+}
+.bm-form {
+  padding: 1rem;
+  background-color: var(--czr-dialog-bg);
+  border-radius: 0.5rem;
 }

+ 141 - 0
src/views/manage/knowledge/detail.vue

@@ -0,0 +1,141 @@
+<template>
+  <CzrDialog
+    :show="show"
+    :title="titleCpt"
+    @onClose="$emit('update:show', false)"
+    @onSubmit="onSubmit"
+    width="62.5rem"
+    height="auto"
+    max-height="36rem"
+    :loading="state.loading"
+    :show-submit="!isViewCpt"
+  >
+    <div class="bm-form">
+      <div class="__czr-title_1">基本信息</div>
+      <CzrForm ref="ref_form" label-width="6.1rem" :form-view="isViewCpt">
+        <CzrFormColumn
+          required
+          :span="24"
+          label="知识库名称"
+          v-model:param="state.form.name"
+        />
+        <CzrFormColumn
+          required
+          :span="24"
+          label="知识库描述"
+          v-model:param="state.form.remark"
+          type="textarea"
+          :rows="4"
+          placeholder="描述知识库的内容,详尽的描述将帮助AI能深入理解该知识库的内容,能更准确的检索到内容,提高该知识库的命中率。"
+        />
+        <CzrFormColumn
+          required
+          :span="24"
+          label="知识分组"
+          v-model:param="state.form.group"
+          link="select"
+          :options="DictionaryStore.knowledgeGroups.list"
+        />
+      </CzrForm>
+    </div>
+  </CzrDialog>
+</template>
+
+<script setup lang="ts">
+import {computed, getCurrentInstance, nextTick, reactive, ref, watch} from "vue";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {useDictionaryStore} from "@/stores";
+
+const DictionaryStore = useDictionaryStore();
+const emit = defineEmits(['update:show', 'refresh'])
+const {proxy} = getCurrentInstance()
+const props = defineProps({
+  show: {default: false},
+  transfer: <any>{}
+})
+const state: any = reactive({
+  loading: false,
+  form: {}
+})
+const ref_form = ref()
+const titleCpt = computed(() => {
+  let t = '知识库'
+  switch (props.transfer.mode) {
+    case 'add': t = '创建' + t
+      break
+    case 'edit': t = t + '编辑'
+      break
+    case 'view': t = '查看' + t
+      break
+  }
+  return t
+})
+const isViewCpt = computed(() => props.transfer?.mode === 'view')
+watch(() => props.show, (n) => {
+  if (n) {
+    initDictionary()
+    state.form = {}
+    if (props.transfer.mode !== 'add') {
+      initData()
+    }
+    nextTick(() => {
+      ref_form.value.reset()
+    })
+  }
+})
+const initDictionary = () => {
+}
+const initData = () => {
+}
+const onSubmit = () => {
+  ref_form.value.submit().then(() => {
+    ElMessageBox.confirm("是否提交?", "提示", {
+      confirmButtonText: "是",
+      cancelButtonText: "否",
+      type: "warning",
+    } as any).then(() => {
+      state.loading = true
+      if (props.transfer.mode == 'add') {
+        // addRuleLogisticsDataSubscribeInfo(Object.assign(state.form, {
+        //   subscribeDataType: 1  //   要传subscribeDataType这个值,"1"表示空运,"2"表示海运
+        // })).then(res => {
+        //   if (res.code == 200) {
+            ElMessage.success('新增成功!')
+            emit('update:show', false)
+            emit('refresh')
+            state.loading = false
+        //   } else {
+        //     ElMessage.error(res.msg)
+        //   }
+        // }).catch(() => {
+        //   state.loading = false
+        // })
+      } else if (props.transfer.mode == 'edit') {
+        // updateRuleLogisticsDataSubscribeInfo(Object.assign(state.form, {
+        //   subscribeDataType: 1  //   要传subscribeDataType这个值,"1"表示空运,"2"表示海运
+        // })).then(res => {
+        //   if (res.code == 200) {
+            ElMessage.success('修改成功!')
+            emit('update:show', false)
+            emit('refresh')
+            state.loading = false
+        //   } else {
+        //     ElMessage.error(res.msg)
+        //   }
+        // }).catch(() => {
+        //   state.loading = false
+        // })
+      }
+    }).catch(() => {})
+  }).catch((e) => {
+    ElMessage({
+      message: e[0].message,
+      grouping: true,
+      type: 'warning',
+    })
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 11 - 11
src/views/manage/knowledge/index.vue

@@ -11,31 +11,28 @@
         </div>
         <CzrForm class="bm-filter" label-width="" @handleEnter="onSearch">
           <CzrFormColumn
-            class="__czr-table-form-column w-[6.68rem]"
+            width="6.68rem"
+            class="__czr-table-form-column"
             :span="24"
             :label-width="0"
             v-model:param="state.query.form.group"
             link="select"
-            :options="[
-              {label: '分组一', value: 1},
-              {label: '分组三', value: 2},
-              {label: '分组二', value: 3},
-            ]"
+            :options="DictionaryStore.knowledgeGroups.list"
             placeholder="全部分组"
           />
           <CzrFormColumn
-            class="__czr-table-form-column w-[6.88rem]"
+            width="6.68rem"
+            class="__czr-table-form-column"
             :span="24"
             :label-width="0"
             v-model:param="state.query.form.tag"
             link="select"
             :options="DictionaryStore.knowledgeTags.list"
-            labelKey="name"
-            valueKey="id"
             placeholder="全部标签"
           />
           <CzrFormColumn
-            class="__czr-table-form-column w-[15.63rem]"
+            width="15.63rem"
+            class="__czr-table-form-column"
             :span="24"
             :label-width="0"
             v-model:param="state.text"
@@ -82,7 +79,7 @@
                 <div>|</div>
                 <div>关联应用:{{row.p6}}</div>
                 <el-tooltip content="编辑" effect="light" placement="top">
-                  <SvgIcon name="czr_edit" size="14" class="__hover ml-auto"/>
+                  <SvgIcon name="czr_edit" size="14" class="__hover ml-auto" @click="onEdit(row)"/>
                 </el-tooltip>
                 <el-tooltip content="删除" effect="light" placement="top">
                   <SvgIcon name="czr_del" size="16" class="__hover" @click="onDel(row)"/>
@@ -96,6 +93,7 @@
         </template>
       </CzrTableCard>
     </div>
+    <detailCom v-model:show="state.detail.show" :transfer="state.detail.transfer" @refresh="onSearch"/>
   </div>
 </template>
 
@@ -106,6 +104,7 @@ import {debounce} from "lodash";
 import {useDialogStore, useDictionaryStore} from "@/stores";
 import {ElMessage} from "element-plus";
 import tagsSelect from './tags-select.vue'
+import detailCom from './detail.vue'
 
 const DialogStore = useDialogStore();
 const DictionaryStore = useDictionaryStore();
@@ -228,6 +227,7 @@ const onChangeTag = (row, tags) => {
   // onSearch()
 }
 const initDictionary = () => {
+  DictionaryStore.initKnowledgeGroups()
 }
 onMounted(() => {
   initDictionary()

+ 8 - 8
src/views/manage/knowledge/tags-select.vue

@@ -30,7 +30,7 @@
           />
         </div>
         <div class="p-[0.3rem]">
-          <template v-if="state.text && !DictionaryStore.knowledgeTags.list.some(v => v.name.includes(state.text))">
+          <template v-if="state.text && !DictionaryStore.knowledgeTags.list.some(v => v.label.includes(state.text))">
             <div class="__hover-bg p-[0.5rem] flex items-center text-[0.88rem] text-[#354052] rounded" @click="onAdd">
               <SvgIcon name="czr_add" size="10" class="mr-2"/>
               创建“{{state.text}}”
@@ -40,9 +40,9 @@
             <template v-if="DictionaryStore.knowledgeTags.list.length > 0">
               <div class="max-h-[200px] overflow-y-auto">
                 <template v-for="item in DictionaryStore.knowledgeTags.list">
-                  <div class="__hover-bg p-[0.5rem] flex items-center text-[0.88rem] text-[#354052] rounded" @click="state.valueMap.has(item.id) ? state.valueMap.delete(item.id) : state.valueMap.set(item.id, item.id)">
-                    <div class="__check scale-[75%] mr-1" :class="{active: state.valueMap.has(item.id)}"/>
-                    <span class="flex-1" v-title>{{item.name}}</span>
+                  <div class="__hover-bg p-[0.5rem] flex items-center text-[0.88rem] text-[#354052] rounded" @click="state.valueMap.has(item.value) ? state.valueMap.delete(item.value) : state.valueMap.set(item.value, item.value)">
+                    <div class="__check scale-[75%] mr-1" :class="{active: state.valueMap.has(item.value)}"/>
+                    <span class="flex-1" v-title>{{item.label}}</span>
                   </div>
                 </template>
               </div>
@@ -74,7 +74,7 @@
     :show-submit="false"
     :show-close="false"
   >
-    <div class="flex flex flex-wrap gap-2 bg-[#ffffff] p-[1rem] rounded">
+    <div class="bm-form flex flex flex-wrap gap-2">
       <template v-for="item in DictionaryStore.knowledgeTags.list">
         <template v-if="item.__edit">
           <div class="w-[17rem] border border-[var(--czr-border-color)] rounded">
@@ -92,10 +92,10 @@
         <template v-else>
           <div class="max-w-[17rem] border border-[var(--czr-border-color)] px-[0.5rem] h-[2.25rem] rounded flex items-center gap-2">
             <span v-title>
-              {{item.name}}
+              {{item.label}}
             </span>
             <span class="opacity-75">{{item.total}}</span>
-            <SvgIcon name="czr_edit" size="12" class="__hover" @click="item.__value = item.name + '', item.__edit = true"/>
+            <SvgIcon name="czr_edit" size="12" class="__hover" @click="item.__value = item.label + '', item.__edit = true"/>
             <SvgIcon name="czr_del" size="14" class="__hover" @click="onDel(item)"/>
           </div>
         </template>
@@ -138,7 +138,7 @@ const onDel = (row) => {
   }
   if (row.total > 0) {
     DialogStore.confirm({
-      title: `删除标签“${row.name}”`,
+      title: `删除标签“${row.label}”`,
       content: `标签正在使用中,是否删除?`,
       onSubmit: () => {
         delHandle()