Skip to content

使用v-model实现Tabs组件

新增item相关路由

给StartPage页面的开始记账和添加按钮添加路由,详细内容具体见链接

制作MainLayout组件

设置navbar和中间内容的布局组件,详细内容具体见链接

创建记一笔的页面

创建记一笔的页面,详细内容具体见链接

实现Tabs组件

父子组件之间通信一般通过props$emit来实现,默认情况下v-model在组件都是使用modelValue作为prop,并以update:modelValue作为对应的事件。但Vue3现在可以通过v-model指定一个参数来更改这些名字,具体详细资料可见Vue3官网v-model的参数。如下所示,可以通过props实现通信,也可以通过v-model指定参数来实现。

tsx
import { defineComponent, PropType } from 'vue'
import s from './Tabs.module.scss'
export const Tabs = defineComponent({
  props: {
    selected: {
      type: String as PropType<string>
    },
    onUpdateSelected: {
      type: Function as PropType<(name: string) => void>,
      required: false
    }
  },
  setup: (props, context) => {
    return () => {
      const tabs = context.slots.default?.()
      if (!tabs) return () => null
      // 如果tabs默认值没有的话则是undefined,则返回null
      for (let i = 0; i < tabs.length; i++) {
        if (tabs[i].type !== Tab) {
        // 判断Tabs里面的子集是否为Tab组件
          return new Error('<Tabs> only accepts <Tab> as children')
        }
      }
      return <div class={s.tabs}>
        <ol class={s.tabs_nav}>
          {
            tabs.map(item =>
              <li class={item.props?.name === props.selected ? s.selected : ''}
                // props 
                // onClick={()=> props.onUpdateSelected?.(item.props?.name)}
                // v-model emit 传递 update:selected 事件
                onClick={()=> context.emit('update:selected', item.props?.name)}
                >
                {item.props?.name}
              </li>
                    )
          }
        </ol>
        <div>
          {tabs.find(item => item.props?.name === props.selected)}
        </div>
      </div>
    }
  }
})

export const Tab = defineComponent({
  props: {
    name: {
      type: String as PropType<string>
    }
  },
  setup: (props, context) => {
    return () => (
      <div>{context.slots.default?.()}</div>
    )
  }
})

然后在ItemCreate.tsx组件中使用。

tsx
import { defineComponent, PropType, ref } from 'vue'
import { MainLayout } from '../../layouts/MainLayout'
import { Icon } from '../../shared/Icon'
import { Tab, Tabs } from '../../shared/Tabs'
import s from './ItemCreate.module.scss'
export const itemCreate = defineComponent({
  props: {},
  setup: (props, context) => {
    const refKind = ref('支出')
    return () => (
      <MainLayout>{{
        title: () => '记一笔',
        icon: () => <Icon name='left' class={s.navIcon}/>,
        default: () => <>
          {/* props */}
          {/* <Tabs selected={refKind.value} onUpdateSelected={name => refKind.value = name }> */}
          {/* v-model */}
          <Tabs v-model:selected={ refKind.value }>
            <Tab name="支出">
              icon 支出列表
            </Tab>
            <Tab name="收入">
              icon 收入列表
            </Tab>
          </Tabs>
        </>
      }}</MainLayout>
    )
  }
})

创建Tabs,详细内容具体见链接