Vue3中a-tree插槽自定义树形新增组件、扩展a-input实现a-input-number功能

组件代码

<template>
	<a-spin :spinning="loading">
		<div style="min-height: 220px;">
			<a-tree
				v-model:checkedKeys="checkedKeys"
				v-model:expandedKeys="expandedKeys"
				v-model:selectedKeys="selectedKeys"
				:fieldNames="{
				children:'children', title:'name', key:'code'
			}"
				:tree-data="dataSource"
				checkable
			>
				<template #title="{parentCode,collectionDate, name,pictureUrl,quantity, unit, code, time}">
					<div v-if="parentCode === '0'">
						{{ name }}
					</div>
					<div v-else style="display: flex; align-items: center; gap: 12px;">
						<img :src="pictureUrl" alt="" style="height: 80px; width: 80px" />
						<span style="width: 260px;">{{ name }}</span>
						<a-input :min="0" :value="quantity" size="small" style="height: 25px; width: 66px;"
										 @change="(e) => handleQuantityChange(e?.target?.value, code)" />
						<div
							class="arrow-container"
						>
							<CaretUpOutlined class="up"  @click="handleQuantityChange(quantity + 1, code)"/>
							<CaretDownOutlined class="down" @click="handleQuantityChange(quantity - 1, code)"/>
						</div>
						<span>{{ unit }}</span>
						<a-date-picker
							:getPopupContainer="(triggerNode) => triggerNode.parentNode"
							:show-time="false"
							format="YYYY-MM-DD"
							placeholder="请选择领取日期"
							style="width: 160px"
							@change="(date, dateString) => handleCollectionDate(date, dateString, code)"
						/>
					</div>
				</template>
			</a-tree>
		</div>
	</a-spin>
</template>

<script setup>
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { RESPONSE_STATUS } from '@/utils/constants'
import mitt from '@/utils/mitt'
import { useStore } from 'vuex'
import { isEmpty } from 'lodash'
import { doDownload } from '@/utils/common'
import { typeList } from '@/api/biz/common'
import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons-vue'

const store = useStore()
const route = useRoute()
const router = useRouter()

const emits = defineEmits('updateCheckedSource')
const expandedKeys = ref([])
const selectedKeys = ref([])
const checkedKeys = ref([])
const props = defineProps({
	tableCols: {
		type: Array
	},
	formState: {
		type: Object
	},
	modelInputVal: {
		type: String,
		default: ''
	}
})

const handleCollectionDate = (date, dateString, code) => {
	handleDateChange(dateString, code)
}

const handleDateChange = (newVal, nodeCode) => {
	const updateNode = (nodes) => {
		return nodes.map(node => {
			if (node.code === nodeCode) {
				return { ...node, collectionDate: newVal }
			}
			if (node.children) {
				return { ...node, children: updateNode(node.children) }
			}
			return node
		})
	}
	dataSource.value = updateNode(dataSource.value)
}

const handleQuantityChange = (newVal, nodeCode) => {
	console.log('output-> {newVal, nodeCode}::: 💡💡', { newVal, nodeCode })
	const updateNode = (nodes) => {
		return nodes.map(node => {
			if (node.code === nodeCode) {
				checkedKeys.value = [...checkedKeys.value, nodeCode]
				return { ...node, quantity: parseInt(newVal) }
			}
			if (node.children) {
				return { ...node, children: updateNode(node.children) }
			}
			return node
		})
	}
	dataSource.value = updateNode(dataSource.value)
}


const onSelectChange = (selectedRowKeys, selectedRows) => {
	// console.log('output-> selectedRows::: ', selectedRows)
	// if (isEmpty(selectedRowKeys)) {
	// 	store.dispatch('setIsMainSelected', false)
	// } else {
	// 	store.dispatch('setIsMainSelected', true)
	// }
	// let curPageIds = state.dataSource.map(item => item.id)
	// let unSelectedIds = curPageIds.filter(id => !selectedRowKeys.includes(id))
	// state.selectedRowKeys = selectedRowKeys
	// state.allSelectedRowKeys = Array.from(new Set([...state.allSelectedRowKeys, ...selectedRowKeys]))
	// state.allSelectedRowKeys = state.allSelectedRowKeys.filter(key => !unSelectedIds.includes(key))
	// mitt.emit('activeBtn', { len: state.selectedRowKeys.length })
	// mitt.emit('emitImportData', state.importDataPayload)
}

const loading = ref(false)
const dataSource = ref([])

function filterNodesByCheckedKeysDeep(dataSource, checkedKeys) {
	const result = []

	function traverse(nodes) {
		for (const node of nodes) {
			if (checkedKeys.includes(node.code)) {
				result.push(node)
			}
			if (node.children && node.children.length > 0) {
				traverse(node.children)
			}
		}
	}

	traverse(dataSource)
	return result
}

watch([() => checkedKeys.value, () => dataSource.value], (newValue, oldValue) => {
	const filteredNodes = filterNodesByCheckedKeysDeep(dataSource.value, checkedKeys.value)
	console.log('output-> filteredNodes', filteredNodes)
	emits('updateCheckedSource', filteredNodes)
}, { deep: true })
const selectedRowKeys = ref([])
const loadTableData = async (field, orderBy, isCache = false) => {
	loading.value = true
	console.log('output-> props.modelInputVal.value::: ', props.modelInputVal)
	console.log('output-> route.query?.sfzh::: ', route.query?.sfzh)
	const res = await typeList({ keyword: props.modelInputVal, sfzh: route.query?.sfzh })
	if (RESPONSE_STATUS.SUCCESS === res.status) {
		loading.value = false
		dataSource.value = res.data?.data || []
		checkedKeys.value = getCheckedCodes(dataSource.value)
		console.log('output-> dataSource.value::: ', dataSource.value)
		console.log('output-> checkedKeys.value::: ', checkedKeys.value)
	}
}
const getCheckedCodes = (nodes) => {
	let result = []
	const traverse = (nodeList) => {
		for (const node of nodeList) {
			if (node.checked === 1) {
				result.push(node.code)
			}
			if (node.children && node.children.length > 0) {
				traverse(node.children)
			}
		}
	}

	traverse(nodes)
	return result
}

const onQuery = () => {
	console.log('output-> 查询 emitQueryForm')
}

const refreshModal = () => {
	loadTableData('', '', true)
}
onMounted(() => {
	loadTableData('', '', false)
	mitt.on('emitQueryForm', onQuery)
	mitt.on('emitResetForm', () => {
		console.log('output-> 重置')
	})
	mitt.on('refresh-add-modal', refreshModal)
})

onUnmounted(() => {
	mitt.off('emitQueryForm', onQuery)
	mitt.off('emitResetForm')
	mitt.off('emitDeliveryCheck')
	mitt.off('refresh-add-modal', refreshModal)
})


const onVisibleChange = () => {
}

const onDownLoad = ({ attachmentUrl, attachmentName, extName }) => {
	if (isEmpty(attachmentUrl)) {
		return
	}
	let param = {
		name: attachmentName.split('.')[0],
		extName: extName,
		address: attachmentUrl
	}
	doDownload(param)
}

</script>

<style lang="scss" scoped>
:deep(.ant-table-thead) {
	display: none;
}

.score-info {
	overflow-x: scroll;
}

.scheme-sign-box {
	width: 100%;
	display: flex;
	justify-content: flex-start;
}

.month-info {
	padding: 16px 52px;
}

.tb-pagination {
	margin-top: 10px;
	text-align: right;
}

.table-switch {
	display: flex;
	align-items: center;
	justify-content: flex-start;

	span {
		margin-left: 8px;
	}
}

:deep(.ant-pagination-total-text) {
	float: left;
}

.single-text {
	overflow: hidden; //超出的文本隐藏
	text-overflow: ellipsis; //用省略号显示
	white-space: nowrap; //不换行(文字不允许换行,单行文本)
	text-decoration: underline; // 添加下划线
	text-decoration-color: dodgerblue; // 设置下划线颜色为蓝色
	text-decoration-thickness: 2px; // 设置下划线宽度为2px
}

.arrow-container {
	cursor: pointer;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1px;
}
.up, .down {
	font-size: 12px;
}
.up:hover :deep(svg) {
	color: dodgerblue;
}
.down:hover :deep(svg) {
	color: dodgerblue;
}
</style>



posted @ 2025-06-10 11:33  Felix_Openmind  阅读(235)  评论(0)    收藏  举报
*{cursor: url(https://files-cdn.cnblogs.com/files/morango/fish-cursor.ico),auto;}