Files
lingji-work-fe/src/components/xt-chat/conversations/index.vue

174 lines
4.6 KiB
Vue
Raw Normal View History

<script lang="tsx">
import { ref, defineComponent, computed } from 'vue';
import { Input, Dropdown, Menu } from 'ant-design-vue';
import type { MenuProps } from 'ant-design-vue';
import type { VNode } from 'vue';
import SvgIcon from '@/components/svg-icon';
import TextoverTips from '@/components/text-over-tips';
// 定义对话项类型
interface ConversationItem {
key: string;
label: string | VNode;
icon?: string | VNode;
disabled?: boolean;
[key: string]: any;
}
const DEFAULT_MENU_CONFIG = [
{
label: '置顶',
key: 'pin',
icon: () => <SvgIcon name="svg-pushpin" size={14} class="color-#737478 hover:color-#6D4CFE" />,
},
{
label: '重命名',
key: 'rename',
icon: <icon-edit size={14} class="color-#737478" />,
},
{
label: '删除',
key: 'delete',
icon: <icon-delete size={14} class="color-#F64B31" />,
status: 'danger',
},
] as ConversationItem[];
export default defineComponent({
name: 'Conversations',
props: {
dataSource: {
type: Array as () => ConversationItem[],
default: () => [],
},
activeKey: {
type: String,
default: '',
},
defaultActiveKey: {
type: String,
default: '',
},
menu: {
type: Array as () => ConversationItem[],
default: () => DEFAULT_MENU_CONFIG,
},
},
emits: ['activeChange', 'menuClick', 'update:dataSource', 'update:modelValue', 'rename'],
setup(props, { emit, expose }) {
const activeKey = ref(props.activeKey || props.defaultActiveKey || '');
const localDataSource = ref<ConversationItem[]>([]);
const inputRef = ref(null);
const menuConfigs = ref<ConversationItem[]>(props.menu ?? DEFAULT_MENU_CONFIG);
// 处理选中变更
const handleActiveChange = (value: string) => {
activeKey.value = value;
emit('update:modelValue', value);
emit('activeChange', value);
};
const onMenuItemClick = ({ menuInfo, item }) => {
const { key } = menuInfo;
emit('menuClick', menuInfo);
switch (key) {
case 'rename':
item.editing = true;
nextTick(() => {
inputRef.value.focus();
});
break;
}
};
const changeItems = () => {
emit('update:dataSource', localDataSource.value);
};
watch(
() => props.dataSource,
(newItems) => {
if (newItems) {
localDataSource.value = cloneDeep(newItems);
}
},
{ deep: true, immediate: true },
);
const renderItems = () => {
return localDataSource.value.map((item, index) => (
<div
class={`group flex justify-between cursor-pointer items-center p-8px h-40px rounded-8px hover:bg-#F2F3F5 ${
activeKey.value === item.key ? 'bg-#F2F3F5' : ''
}`}
onClick={() => handleActiveChange(item.key)}
>
{item.editing ? (
<Input
ref={inputRef}
v-model:value={item.label}
onBlur={() => {
item.editing = false;
changeItems();
emit('rename', item);
}}
onPressEnter={() => {
item.editing = false;
}}
class="flex-1"
/>
) : (
<TextoverTips context={item.label} class="flex-1" placement="bottom" />
)}
<Dropdown
class="p-0"
overlayClassName="xt-conversations-dropdown"
placement="bottomRight"
v-slots={{
overlay: () => (
<Menu onClick={(menuInfo: MenuProps) => onMenuItemClick({ menuInfo, item })}>
{menuConfigs.value.map((menuItem) => (
<Menu.Item key={menuItem.key} icon={menuItem.icon} class={`${menuItem.status || ''}`}>
{menuItem.label}
</Menu.Item>
))}
</Menu>
),
}}
>
<icon-more size={16} class="color-#737478 cursor-pointer ml-8px opacity-0 group-hover:opacity-100" />
</Dropdown>
</div>
));
};
return () => <div className="xt-conversations-container">{renderItems()}</div>;
},
});
</script>
<style scoped lang="scss">
@import './style.scss';
</style>
<style lang="scss">
.xt-conversations-dropdown {
.ant-dropdown-menu {
padding: 4px 0;
.ant-dropdown-menu-item {
padding: 0 12px;
min-width: 124px;
height: 36px;
display: flex;
align-items: center;
&:hover {
background: var(--BG-200, #f2f3f5);
}
&.danger {
color: var(--RED-600, #f64b31);
}
}
}
}
</style>