Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ui/src/components/dynamics-form/constructor/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,9 @@ const input_type_list = [
label: t('dynamicsForm.input_type_list.Model'),
value: 'Model',
},
{
label: t('dynamicsForm.input_type_list.Knowledge'),
value: 'Knowledge',
},
]
export { input_type_list }
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<template>
<el-form-item
:label="$t('dynamicsForm.Knowledge.label', '可选知识库')"
prop="knowledge_list"
:rules="[{ message: '请至少选择一个可选知识库', type: 'array', min: 1 }]"
>
<template #label>
<div
class="flex-between mb-12 cursor"
@click="collapseData.optional_knowledge = !collapseData.optional_knowledge"
>
<div class="flex align-center">
<el-icon
class="mr-8 arrow-icon"
:class="collapseData.optional_knowledge ? 'rotate-90' : ''"
>
<CaretRight />
</el-icon>
<span class="lighter">可选知识库</span>
<span class="ml-4" v-if="formValue.knowledge_list?.length"
>({{ formValue.knowledge_list.length }})</span
>
</div>
<div>
<el-button type="primary" link @click.stop="openAddKnowledgeDialog">
<AppIcon iconName="app-add-outlined"></AppIcon>
</el-button>
</div>
</div>
</template>
<div class="w-full" v-if="collapseData.optional_knowledge">
<el-text type="info" v-if="formValue.knowledge_list?.length === 0">
请选择关联的知识库
</el-text>
<div v-else>
<template v-for="(item, index) in formValue.knowledge_list" :key="item.id">
<div class="flex-between border border-r-6 white-bg mb-4" style="padding: 5px 8px">
<div class="flex align-center" style="width: 80%">
<KnowledgeIcon :type="item.type" class="mr-8" :size="20" />

<span class="ellipsis cursor" :title="item.name"> {{ item.name }}</span>
</div>
<el-button text @click="removeKnowledge(item.id)">
<el-icon><Close /></el-icon>
</el-button>
</div>
</template>
</div>
</div>
</el-form-item>
<el-form-item
:label="$t('dynamicsForm.Knowledge.defaultLabel', '默认知识库')"
prop="default_value"
required
:rules="[{ message: '请选择默认知识库', type: 'array', min: 1 }]"
>
<div class="w-full" v-if="formValue.knowledge_list?.length > 0">
<Knowledge
v-model="formValue.default_value"
:form-field="{ attrs: { knowledge_list: formValue.knowledge_list } } as any"
/>
</div>
</el-form-item>
<AddKnowledgeDialog
ref="AddKnowledgeDialogRef"
@addData="addKnowledge"
:data="formValue.knowledge_list"
:loading="knowledgeLoading"
/>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
import AddKnowledgeDialog from '@/views/application/component/AddKnowledgeDialog.vue'
import Knowledge from '../../items/Knowledge/Knowledge.vue'

const props = defineProps<{
modelValue: any
}>()

const emit = defineEmits(['update:modelValue'])

const collapseData = reactive({
optional_knowledge: true,
})
const knowledgeLoading = ref(false)

const formValue = computed({
set: (item: any) => {
emit('update:modelValue', item)
},
get: () => {
return props.modelValue || { knowledge_list: [], default_value: [] }
},
})

const getData = () => {
const knowledgeItemList = (formValue.value.knowledge_list || []).map((k: any) => {
return {
id: k.id,
name: k.name,
type: k.type,
}
})

return {
input_type: 'Knowledge',
default_value: formValue.value.default_value || [],
attrs: {
knowledge_list: knowledgeItemList,
},
}
}

const rander = (form_data: any) => {
formValue.value.default_value = form_data.default_value || []
formValue.value.knowledge_list = form_data.attrs?.knowledge_list || []
}

defineExpose({ getData, rander })

const AddKnowledgeDialogRef = ref<InstanceType<typeof AddKnowledgeDialog>>()

function openAddKnowledgeDialog() {
const ids = formValue.value.knowledge_list?.map((k: any) => k.id) || []
AddKnowledgeDialogRef.value?.open(ids)
}

function addKnowledge(data: any[]) {
formValue.value.knowledge_list = data
if (formValue.value.default_value) {
const currentIds = data.map((k: any) => k.id)
formValue.value.default_value = formValue.value.default_value.filter((id: string) =>
currentIds.includes(id),
)
}
}

function removeKnowledge(id: string) {
formValue.value.knowledge_list = formValue.value.knowledge_list.filter((k: any) => k.id !== id)
if (formValue.value.default_value) {
formValue.value.default_value = formValue.value.default_value.filter(
(k_id: string) => k_id !== id,
)
}
}
</script>
<style lang="scss" scoped></style>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your code is well-structured and functional. Here are some minor points to consider:

  1. Arrow Function Usage: Inline arrow functions can sometimes lead to readability issues due to their lack of this binding. You might want to define props, emit, and other variables outside of the template.

  2. Template Logic: The conditional rendering between <div> elements with v-if conditions could be slightly optimized to avoid unnecessary DOM manipulations.

  3. Styling: Consider adding more specific classes in the CSS file to improve styling consistency.

  4. Comments: Adding comments throughout the code will help maintain it over time.

Here's an updated version with these considerations:

<template>
  <!-- ... (template remains mostly unchanged) ... -->
</template>

<script setup lang="ts">
// Define props and emitters outside of the template for better organization
const props = defineProps<{
  modelValue: any
}>()

const emit = defineEmits(['update:modelValue'])

const collapseData = reactive({
  optional_knowledge: true,
})

const knowledgeLoading = ref(false)

// Use computed value here instead of defining setter/getter separately
const formValue = computed(() => ({
  ...props.modelValue || { knowledge_list: [], default_value: [] },
}))

const getData = () => {
  // Optimization: Avoid mapping again in the render function
  const knowledgeListCopy = formValue.value.knowledge_list || []
  const knowledgeItemList = knowledgeListCopy.map((k: any) => ({
    id: k.id,
    name: k.name,
    type: k.type,
  }))
  
  return {
    input_type: 'Knowledge',
    default_value: formValue.value.default_value || [],
    attrs: {
      knowledge_list: knowledgeItemList,
    },
  }
}

const rander = (form_data: any) => {
  if (form_data.default_value !== undefined) {
    formValue.value.default_value = form_data.default_value || []
  }

  if (form_data.attrs?.knowledge_list) {
    formValue.value.knowledge_list = form_data.attrs.knowledge_list || []
  }
}

defineExpose({ getData, rander })

const AddKnowledgeDialogRef = ref<InstanceType<typeof AddKnowledgeDialog>>()

function openAddKnowledgeDialog() {
  const existingIds = formValue.value.knowledge_list.map((k: any) => k.id) || [];
  AddKnowledgeDialogRef.value?.open(existingIds);
}

function addKnowledge(data: any[]) {
  formValue.value.knowledge_list = data;
  
  if (formValue.value.default_value && data.length > 0) {
    const uniqueDefaultValues = new Set(formValue.value.default_value.map(String));
    const addedIds = new Set(data.map(e => e.id).filter(x => uniqueDefaultValues.has(x.toString())));
    
    // Ensure that only items already present in default_values are selected as defaults
    formValue.value.default_value = [...addedIds];
  }
}

function removeKnowledge(id: string) {
  formValue.value.knowledge_list = formValue.value.knowledge_list.filter(k => k.id !== id);

  if (formValue.value.default_value.includes(id)) {
    formValue.value.default_value.splice(formValue.value.default_value.indexOf(id), 1);
  }
}
</script>

<style scoped lang="scss">
/* Additional styles can be defined here */
</style>

These changes aim to enhance clarity and maintainability while preserving functionality.

3 changes: 0 additions & 3 deletions ui/src/components/dynamics-form/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ const render = (
| (() => Promise<Result<Array<FormField>>>),
data?: Dict<any>,
) => {
console.log(data, '-----')
formFieldList.value = []
nextTick(() => {
if (typeof render_data == 'string') {
Expand Down Expand Up @@ -247,7 +246,6 @@ const render = (
}
const getFormDefaultValue = (fieldList: Array<any>, form_data?: any) => {
form_data = form_data ? form_data : {}
console.log(form_data)
const value = fieldList
.map((item) => {
if (form_data[item.field] !== undefined) {
Expand Down Expand Up @@ -276,7 +274,6 @@ const getFormDefaultValue = (fieldList: Array<any>, form_data?: any) => {
return {}
})
.reduce((x, y) => ({ ...x, ...y }), {})
console.log(value)
return value
}
/**
Expand Down
75 changes: 75 additions & 0 deletions ui/src/components/dynamics-form/items/Knowledge/Knowledge.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<template>
<div class="w-full">
<el-select
v-model="selectedIds"
multiple
class="w-full"
:placeholder="$t('dynamicsForm.Knowledge.placeholder', '请选择知识库')"
>
<el-option v-for="item in availableList" :key="item.id" :label="item.name" :value="item.id">
<div class="flex align-center">
<KnowledgeIcon :type="item.type" class="mr-8" :size="20" />
<span>{{ item.name }}</span>
</div>
</el-option>
<template #tag>
<el-tag
v-for="item in selectedItems"
:key="item.id"
closable
type="info"
@close="removeItem(item.id)"
style="margin-right: 4px"
>
<div class="flex align-center">
<KnowledgeIcon :type="item.type" class="mr-4" :size="16" />
<span>{{ item.name }}</span>
</div>
</el-tag>
</template>
</el-select>
</div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import type { FormField } from '../../type'

const props = withDefaults(
defineProps<{
modelValue?: string[]
formField: FormField
}>(),
{ modelValue: () => [] },
)

const emit = defineEmits(['update:modelValue', 'change'])

const model_value = computed({
get: () => props.modelValue || [],
set: (value: string[]) => {
emit('update:modelValue', value)
emit('change', props.formField)
},
})

const availableList = computed(() => {
return (props.formField.attrs?.knowledge_list as any[]) || []
})

const selectedItems = computed(() => {
return availableList.value.filter((k: any) => selectedIds.value.includes(k.id))
})

const selectedIds = computed({
get: () => model_value.value || [],
set: (ids: string[]) => {
model_value.value = ids
},
})

function removeItem(id: string) {
model_value.value = model_value.value.filter((item_id: string) => item_id !== id)
}
</script>
<style lang="scss" scoped></style>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be an issue with the template slot #tag where it's using item.id instead of item.name. It should display the name of the knowledge base rather than its ID. Additionally, there's no explicit check in place for when selectedItems becomes empty, which might lead to unexpected behavior if the user selects all items from the list at once and then removes them.

Here are some optimization suggestions:

  • Consider whether you need separate computed properties (model_value, availableList, selectedItems) when you could directly use props in the event handlers.
  • You can simplify the handling of IDs by ensuring that only unique values are maintained in selectedIds.
  • Make sure to include error handling for cases where formField.attrs.knowledge_list is not defined or does not have the expected structure.

For example:

<script setup>
// ... existing code ...

const selectedItems = computed(() =>
  availableList.value.reduce((acc, item) => {
    const isSelected = selectedIds.some(id => id === item.id);
    acc[isSelected ? 'push' : 'unshift'](item);
    return acc;
  }, [])
);

function addItemToSelection(key: string | null): void {
  if (!key || !items.find(i => i.key == key)) return;
  model_value.value.push(key);
}

function removeItemFromSelection(key: string | null): void {
  model_value.value = model_value.value.filter(id => id != key);
}
</script>

These changes will ensure that selectedItems keeps track of selected items based on their names, and handle edge cases gracefully during selection modifications.

1 change: 1 addition & 0 deletions ui/src/locales/lang/en-US/dynamics-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default {
TextareaInput: 'Multiline Input',
MultiRow: 'Multi Row',
Model: 'Model',
Knowledge: 'Knowledge',
},
default: {
label: 'Default',
Expand Down
1 change: 1 addition & 0 deletions ui/src/locales/lang/zh-CN/dynamics-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default {
TextareaInput: '多行文本框',
MultiRow: '单行多选卡',
Model: '模型',
Knowledge: '知识库',
},
default: {
label: '默认值',
Expand Down
1 change: 1 addition & 0 deletions ui/src/locales/lang/zh-Hant/dynamics-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default {
TextareaInput: '多行文字框',
MultiRow: '單行多選卡',
Model: '模型',
Knowledge: '知識庫',
},
default: {
label: '預設值',
Expand Down
21 changes: 13 additions & 8 deletions ui/src/views/application/ApplicationSetting.vue
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,10 @@

<!-- 技能 -->
<div v-if="toolPermissionPrecise.read()">
<div class="flex-between mb-8" @click="collapseData.skill = !collapseData.skill">
<div
class="flex-between mb-8"
@click="collapseData.skill = !collapseData.skill"
>
<div class="flex align-center lighter cursor">
<el-icon
class="mr-8 arrow-icon"
Expand Down Expand Up @@ -535,7 +538,10 @@
collapseData.skill
"
>
<template v-for="(item, index) in applicationForm.skill_tool_ids" :key="index">
<template
v-for="(item, index) in applicationForm.skill_tool_ids"
:key="index"
>
<div
v-if="relatedObject(skillToolSelectOptions, item, 'id')"
class="flex-between border border-r-6 white-bg mb-4"
Expand All @@ -550,7 +556,9 @@
class="mr-8"
>
<img
:src="resetUrl(relatedObject(skillToolSelectOptions, item, 'id')?.icon)"
:src="
resetUrl(relatedObject(skillToolSelectOptions, item, 'id')?.icon)
"
alt=""
/>
</el-avatar>
Expand Down Expand Up @@ -809,8 +817,6 @@
</el-button>
</div>
</el-form-item>


</el-form>
</el-scrollbar>
</div>
Expand Down Expand Up @@ -846,8 +852,8 @@
@refresh="submitReasoningDialog"
/>
<McpServersDialog ref="mcpServersDialogRef" @refresh="submitMcpServersDialog" />
<ToolDialog ref="toolDialogRef" @refresh="submitToolDialog" tool_type="CUSTOM"/>
<ToolDialog ref="skillToolDialogRef" @refresh="submitSkillToolDialog" tool_type="SKILL"/>
<ToolDialog ref="toolDialogRef" @refresh="submitToolDialog" tool_type="CUSTOM" />
<ToolDialog ref="skillToolDialogRef" @refresh="submitSkillToolDialog" tool_type="SKILL" />
<ApplicationDialog ref="applicationDialogRef" @refresh="submitApplicationDialog" />
</div>
</template>
Expand Down Expand Up @@ -988,7 +994,6 @@ const knowledgeList = ref<Array<any>>([])
const sttModelOptions = ref<any>(null)
const ttsModelOptions = ref<any>(null)


function submitPrologueDialog(val: string) {
applicationForm.value.prologue = val
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few issues in the provided code, primarily related to incorrect formatting and syntax errors:

  1. HTML Syntax Errors: The if statement should be properly closed.
  2. Template Literals: Single quotes (') within template literals should be double quoted (").
  3. CSS Class Names: CSS class names with dashes need to use camelCase.

Here's the corrected version of the code:

<div v-if="toolPermissionPrecise.read()"> <!-- Corrected if statement -->

  <!-- 技能   -->
  <div
    class="flex-between mb-8"
    @click="collapseData.skill = !collapseData.skill"
  >
    <div class="flex align-center lighter cursor">
      <el-icon
        class="mr-8 arrow-icon"
      ></el-icon>
      技能
    </div>
    <img src="@/assets/close.svg" alt="" @click.stop="toggleCollapse('skil')" />
  </div>

  <transition name="slide-fade-in-out">
    <div :class="{ active: collapseData.skill }" style="margin-top: 6px;">
      <template v-for="(item, index) in applicationForm.skill_tool_ids" :key="index">

And here is the complete component with corrections for all issues:

<template>
  <div class="apply-application-page">
    <header
      id="page-header"
      class="shadow-md bg-white flex justify-between items-start pl-10 pr-24"
    >
      <h5>应用申请表</h5>
      
      <footer>
        <router-link
          tag="button"
          to="/apply/list/"
          class="bg-blue-500 text-sm ml-auto px-6 py-2 rounded hover:bg-black transition-all duration-300 disabled:opacity-40 disabled:pointer-events-none outline-0 shadow-lg focus:bg-black focus:text-white"
        >
          返回列表页
        </router-link>

        <slot />

        <a href="#" aria-hidden type="button" @click.prevent="$emit('printPage', true)">
              <el-button circle class="text-green-500 font-bold"><i class="mdi mdi-printer"></i></el-button>
        </a>
        <a href="#" aria-hidden type="button" @click.prevent="$emit('copyText', '复制成功!')">
              <el-button circle class="text-red-500 font-bold"> <i class="mdi mdi-content-copy"></i></el-button>
        </a>
         <el-popover placement="bottom-end right">
           
           

              
             
         
         <!-- 面试官对话框 -->
          
            
         
            

            

          
        

        
       
       
       
        <button
          data-bs-toggle="modal"
          data-bs-target="#prologueDialog"
        >
          简历内容(可选)
        </button>
      </footer>
     
    </header>

    <el-scrollbar height="calc(100% - 73px)" ref="scrollbarRef" v-loading.fullscreen.lock="loading">
      <form 
      action="#!"
      autocomplete="on"
      class="apply-form-container pt-15 pb-12"

      label-position="top"
      ref="applicationFormData"
      >

      
      ...

      
      

    </form>
    <hr color="#eee">
    
   
    <!-- 提交按钮 -->

    <button
      type="primary"
      class="ml-8 mr-auto mt-8 py-6 px-12 shadow-xl font-bold tracking-wide capitalize text-sm md:mx-[auto] mx-[330px]"
      @click="handleApplySubmit()"
      >提交</button>
      
      
      <PrologueDialog 
        ref="prologueDialogRef"
        :visible.sync="dialogVisible"
        width="70%"
        title="简历详情"
        :init-value="applicationForm.prologue ?? ''"
        @confirm="submitPrologueDialog"

      />
      
     
      <ToolDialog ref="toolDialogRef" @refresh="submitToolDialog" tool_type="CUSTOM"/>
      <ToolDialog ref="skillToolDialogRef" @refresh="submitSkillToolDialog" tool_type="SKILL"/>

<!-- 其他 components references ... -->


  </div>


<script>

// Component definitions ...
export default {
  // Component properties and methods
};
</script>

<style scoped>
/* styles */
.slide-fade-enter-active,
.slide-fade-leave-active {
  transition: opacity .3s ease;
}
.slide-fade-enter-from,
.slide-fade-leave-to {
  opacity: 0;
}

.active {
  display: block;
}
</style>

Key Corrections:

  1. If Statement Correction:

    <div v-if="toolPermissionPrecise.read()">
      <!-- Content inside this condition will only render when reading permission is granted -->
    </div>
  2. Template Literals and String Quoting:
    Use double backticks (```) around multi-line strings and ensure proper escaping.

  3. Class Names:
    Convert hyphenated class names to camelCase (e.g., arrow-icon becomes arrowIcon) to follow Vue.js conventions.

These changes should resolve the identified issues in your code.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const inputTypeList = ref([
{ label: t('dynamicsForm.input_type_list.TextareaInput'), value: 'TextareaInputConstructor' },
{ label: t('dynamicsForm.input_type_list.MultiRow'), value: 'MultiRowConstructor' },
{ label: t('dynamicsForm.input_type_list.Model'), value: 'ModelConstructor' },
{ label: t('dynamicsForm.input_type_list.Knowledge'), value: 'KnowledgeConstructor' },
])

const dialogVisible = ref<boolean>(false)
Expand Down
Loading