Files
qiming/qiming-mobile/subpackages/pages/chat-conversation-component/components/new-conversation-set/new-conversation-set.uvue

444 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="variables-box flex flex-col" v-if="variables?.length">
<!-- header -->
<view class="header">
<text class="title">{{ t("Mobile.Conversation.settingsTitle") }}</text>
<text class="iconfont icon-a-Chevrondown down-icon" :class="{'rotate-90': !isOpen}" @click="handleOpenClose" />
</view>
<!-- 内容区域 -->
<view :class="{'close-form': !isOpen, 'conversation-container': isOpen}">
<view class="form-box">
<view class="uni-form-item" v-for="item in variables" :key="item.key">
<!-- 名称 -->
<view class="form-item-title">
<text class="display-name">{{ item.displayName }}</text>
<text v-if="item.require" class="required">{{
t("Mobile.Common.requiredMark")
}}</text>
</view>
<!-- textarea -->
<textarea
v-if="item.inputType === InputTypeEnum.Paragraph"
:placeholder="item.description || t('Mobile.Common.pleaseInput')"
class="uni-input"
:class="{'disabled-style': disabled}"
:disabled="disabled"
:value="formSourceData[item.name]"
placeholder-class="uni-placeholder"
:maxlength="-1"
@input="(e) => handleChangeInput(e, item)" />
<!-- 级联下拉选择器(单选、多选) -->
<view
v-else-if="item.inputType === InputTypeEnum.Select || item.inputType === InputTypeEnum.MultipleSelect"
class="uni-input flex flex-row items-center content-between"
@click="handleSelect(item)"
>
<scroll-view
v-if="formSourceData[item.name]?.length"
class="flex-1 flex flex-row items-center"
direction="horizontal"
:scroll-with-animation="true"
:show-scrollbar="false"
scroll-x>
<!-- 循环渲染 -->
<template v-for="(info, index) in formSourceData[item.name]" :key="info">
<!-- 多选样式 -->
<view class="multiple-box" v-if="item.inputType === InputTypeEnum.MultipleSelect">
<text :class="{'disabled-style': disabled}">{{`${info?.label || info}`}}</text>
</view>
<!-- 单选样式 -->
<template v-else>
<text :class="{'disabled-style': disabled}">{{`${info?.label || info}`}}</text>
<text
:class="{'disabled-style': disabled}"
v-if="index !== formSourceData[item.name].length - 1"
>
{{ t("Mobile.Common.optionSeparator") }}
</text>
</template>
</template>
</scroll-view>
<text v-else class="uni-placeholder">{{
t("Mobile.Common.pleaseSelect")
}}</text>
<text class="iconfont icon-a-Chevrondown down-icon ml-10" />
</view>
<!-- 输入框 -->
<input
v-else
:disabled="disabled"
:value="formSourceData[item.name]"
:placeholder="item.description || t('Mobile.Common.pleaseInput')"
:type="item.inputType === InputTypeEnum.Number ? 'number' : 'text'"
class="uni-input"
:class="{'disabled-style': disabled}"
placeholder-class="uni-placeholder"
@input="(e) => handleChangeInput(e, item)"
/>
</view>
</view>
<view v-if="disabled" class="desc-box">
<text class="desc">{{
t("Mobile.Conversation.settingsLocked")
}}</text>
</view>
<!-- 单选级联选择器 -->
<l-cascader
v-if="visible"
:visible="visible"
v-model="cascaderValue"
:title="t('Mobile.Common.pleaseSelect')"
:options="options"
@close="handleCancelSelect"
@change="onChange" />
<!-- 多选级联选择器 -->
<l-cascader
v-if="visibleMultiple"
:visible="visibleMultiple"
v-model:multipleValue="cascaderMultipleValue"
:multiple="isMultiple"
:title="t('Mobile.Common.pleaseSelect')"
:options="options"
:confirmBtn="t('Mobile.Common.ok')"
@close="handleCancelSelect"
@finish="handleFinish" />
</view>
</view>
</template>
<script setup lang="uts">
import { BindConfigWithSub } from '@/types/interfaces/common';
import { InputTypeEnum } from '@/types/enums/agent';
import lCascader from '@/uni_modules/lime-cascader/components/l-cascader/l-cascader.uvue';
import { useI18n } from "@/utils/i18n";
const { t } = useI18n();
const props = defineProps({
variables: {
type: Array as PropType<BindConfigWithSub[]>,
default: () => [],
},
userFillVariables: {
type: Object,
default: () => {},
},
isFilled: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
});
// 是否打开表单
const isOpen = ref<boolean>(true);
// 是否显示单选级联选择器
const visible = ref<boolean>(false);
// 是否显示多选级联选择器
const visibleMultiple = ref<boolean>(false);
// 是否多选
const isMultiple = ref<boolean>(false);
// 级联选择器选项
const options = ref<any[]>([]);
// 已选择项的值(单选模式)
const cascaderValue = ref<string>('');
// 已选择项的值(多选模式)
const cascaderMultipleValue = ref<string[]>([]);
// 当前选择项的名称
const currentName = ref<string>('');
// 表单数据
const formData = reactive({});
// 已选择项的源数据
const formSourceData = reactive({});
const emit = defineEmits(['toggleWholeDisabled'])
defineExpose({
variableParams: formData
})
/**
* 监听智能体用户已填写的变量参数
*/
watch(() => props.userFillVariables, (newVal) => {
// 判断newVal是否为空对象
const isEmptyObject = newVal && typeof newVal === 'object' && Object.keys(newVal).length === 0
if (isEmptyObject) {
// 如果是空对象清空formSourceData的所有属性保持响应性
for (const key in formSourceData) {
delete formSourceData[key]
}
// 如果是空对象清空formData的所有属性保持响应性
for (const key in formData) {
delete formData[key]
}
} else if (newVal && typeof newVal === 'object') {
// 如果不是空对象更新formSourceData
Object.assign(formSourceData, newVal)
Object.assign(formData, newVal)
} else {
// 如果newVal为null或undefined也清空formSourceData
for (const key in formSourceData) {
delete formSourceData[key]
}
// 如果是空对象清空formData的所有属性保持响应性
for (const key in formData) {
delete formData[key]
}
}
}, { deep: true, immediate: true })
// 必填变量参数name列表
const requiredNameList = computed(() => {
return props.variables
?.filter((item: BindConfigWithSub) => !item.systemVariable && item.require)
?.map((item: BindConfigWithSub) => item.name) || []
})
// 监听必填参数列表和变量参数变化
watch([requiredNameList, formData], () => {
// 没有必填参数时,不禁用发送按钮
if (requiredNameList.value?.length > 0) {
let isSameName = true
for (const item of requiredNameList.value) {
if (!formData[item] || formData[item]?.length === 0) {
isSameName = false
break
}
}
// 未填写必填参数,禁用发送按钮
emit('toggleWholeDisabled', !isSameName)
}
}, { deep: true, immediate: true })
/**
* 级联选择器选择后
* @param selectedPaths 选中的路径数组 输出: [['440000', '440300', '440305'], ['440000', '440300', '440306']]
* @param selectedOptions 选中的源数据 // 输出:
* [
* { value: '440305', label: '南山区', ... },
* { value: '440306', label: '龙岗区', ... }
* ]
*/
const handleFinish = (selectedPaths: string[][], selectedOptions: UTSJSONObject[]) => {
// 输出: [['440000', '440300', '440305'], ['440000', '440300', '440306']]
formData[currentName.value] = selectedPaths || []
formSourceData[currentName.value] = selectedOptions || []
visibleMultiple.value = false
isMultiple.value = false
}
// 打开关闭表单
const handleOpenClose = () => {
isOpen.value = !isOpen.value
}
// 级联选择器选择后(单选模式)
const onChange = (_: string | string[], selectedOptions: any[]) => {
if (!isMultiple.value) {
visible.value = false
// 单选模式value 是 stringselectedOptions 是选中的选项数组
formData[currentName.value] = selectedOptions?.map(item => item.value) || []
formSourceData[currentName.value] = selectedOptions || []
}
}
// 点击后打开级联选择器
const handleSelect = (item) => {
// 如果禁用,则不打开级联选择器
if (props.disabled) {
return
}
// 当前选择项的名称
currentName.value = item.name
if (item.inputType === InputTypeEnum.MultipleSelect) {
visibleMultiple.value = true
} else {
// 显示级联选择器
visible.value = true
}
// 是否多选
isMultiple.value = item.inputType === InputTypeEnum.MultipleSelect
// 下拉选择项配置
options.value = item.selectConfig?.options || []
// 已选择项的源数据中获取当前项的值
const currentList = formSourceData[item.name] || []
if (isMultiple.value) {
// 多选模式:初始化多选值
if (currentList.length === 0) {
cascaderMultipleValue.value = []
} else {
cascaderMultipleValue.value = currentList.map((item: any) => item.value || '')
}
} else {
// 单选模式:初始化单选值
if (currentList.length === 0) {
cascaderValue.value = ''
} else {
const lastItem = currentList[currentList.length - 1]
cascaderValue.value = lastItem.value || ''
}
}
}
// 取消级联选择
const handleCancelSelect = () => {
// 当前选择项的名称
currentName.value = ''
// 隐藏级联选择器
visible.value = false
visibleMultiple.value = false
isMultiple.value = false
// 下拉选择项配置
options.value = [];
// 已选择项的值
cascaderValue.value = '';
cascaderMultipleValue.value = [];
}
// 输入框输入值
const handleChangeInput = (event: UniInputChangeEvent, item) => {
formData[item.name] = event.detail.value
formSourceData[item.name] = event.detail.value
}
</script>
<style lang="scss" scoped>
.variables-box {
max-height: 50%;
border-radius: 10rpx;
background-color: #fff;
border: 1rpx solid #e5e5e5;
box-sizing: border-box;
margin: 0 32rpx;
.header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
font-weight: bold;
padding: 18rpx 20rpx;
.title {
font-size: 28rpx;
color: #15171F;
}
}
.down-icon {
color: #95979c;
user-select: none;
font-size: 32rpx;
transition: transform 0.3s ease-in-out;
&.rotate-90 {
transform: rotate(-90deg);
}
}
.conversation-container {
flex: 1;
display: flex;
flex-direction: column;
padding-top: 20rpx;
border-top: 1rpx solid #e5e5e5;
box-sizing: border-box;
overflow: hidden;
}
.close-form {
transition: height 0.3s ease-in-out;
height: 0;
overflow: hidden;
}
.form-box {
flex: 1;
max-height: 400rpx;
overflow-y: auto;
padding: 0 20rpx;
.uni-form-item {
padding: 16rpx 0;
.form-item-title {
display: flex;
flex-direction: row;
align-items: center;
.display-name {
font-size: 28rpx;
color: #15171F;
}
.required {
margin-left: 4rpx;
color: #FF0000;
}
}
.uni-input {
width: 100%;
height: 72rpx;
line-height: 40rpx;
background-color: rgba(12,20,40,0.04);
border-radius: 8rpx;
font-size: 28rpx;
color: #15171F;
padding: 16rpx;
margin-top: 8rpx;
box-sizing: border-box;
}
// 禁用样式
.disabled-style {
background-color: #f5f5f5;
color: #999;
}
.uni-placeholder {
color: #6b6b75;
font-size: 28rpx;
}
.multiple-box {
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.06);
padding: 4rpx 12rpx;
margin-right: 16rpx;
border-radius: 8rpx;
.disabled-style {
background-color: transparent;
}
}
}
}
.desc-box {
display: flex;
justify-content: center;
height: 70rpx;
padding: 0 20rpx;
box-sizing: border-box;
.desc {
color: #e5e5e5;
font-size: 28rpx;
}
}
}
</style>