vue 自定义 移动端筛选条件

1.创建组件

components/FilterBar/FilterBar.vue

<template><div class="filterbar" :style="{'top': top + 'px'}"><div class="container"><div class="row"><divclass="col":class="{'selected': index == selectedIndexMenu}"@click="handleShowDialog(barMenu, index)"v-for="(barMenu, index) in barMenus":key="index">{{barMenu.name}}<span :class="index == selectedIndexMenu ? barMenu.selectIcon : barMenu.defaultIcon"></span></div></div><filter-bar-pop:filterTop="top":show-dialog="isShow":hasTabHeader="hasTabHeader":menu="selectedMenu"@changeTab="handleChangeTab"@changeMainItem="handleChangeMainItem"@changeSelect="changeSelect"@closeDialog="handleCloseDialog"></filter-bar-pop></div></div>
</template><script>import FilterBarPop from './FilterBarPop'export default {props: {barMenus: {type: Array,required: true,validator: function (value) {//TODO:验证数据有效性return true;}},top: String},data() {return {isShow: false,hasTabHeader: false,selectedMenu: {},selectedIndexMenu: undefined}},methods: {handleShowDialog(menu, index) {this.isShow = true;this.selectedMenu = menu;this.selectedIndexMenu = index;if (menu.showTabHeader) {this.hasTabHeader = true;} else {this.hasTabHeader = false;}let _menu = JSON.parse(JSON.stringify(menu));_menu.tabs = {};this.$emit('showDialog', _menu);},handleChangeTab(tab) {this.$emit('changeTab', tab.index);},handleChangeMainItem(mainItem) {let _mainItem = JSON.parse(JSON.stringify(mainItem));this.$emit('changeMainItem', _mainItem);},handleCloseDialog() {this.isShow = false;this.selectedIndexMenu = -1;this.$emit('closeDialog');},changeSelect() {var selectData = [];this.barMenus.forEach(function (barMenu, index, arr) {let _selectBarData = {};// console.log("barMenu.name: " + barMenu.name);_selectBarData.name = barMenu.name;_selectBarData.value = barMenu.value;_selectBarData.tab = {};let tab = barMenu.tabs[barMenu.selectIndex];// console.log("tab.name: " + tab.name);_selectBarData.tab.name = tab.name;_selectBarData.tab.value = tab.value;let mainItem = tab.detailList[tab.selectIndex];_selectBarData.tab.mainItem = {}// console.log("mainItem.name: " + mainItem.name);_selectBarData.tab.mainItem.name = mainItem.name;_selectBarData.tab.mainItem.value = mainItem.vaule;let subItem = false;if (mainItem.list) {subItem = mainItem.list[mainItem.selectIndex];_selectBarData.tab.mainItem.subItem = {};// console.log("subItem.name: " + subItem.name);_selectBarData.tab.mainItem.subItem.name = subItem.name;_selectBarData.tab.mainItem.subItem.value = subItem.value;} else {_selectBarData.tab.mainItem.subItem = subItem;}selectData.push(_selectBarData);});this.$emit('changeSelect', selectData);}},components: {'filter-bar-pop': FilterBarPop}}
</script><style lang="scss">.filterbar {width: 100%;background: #fff;position: fixed;top: 0;left: 0;right: 0;.container {width: 100%;outline: 1px solid #DBDCDE;position: relative;.row {display: flex;display: -ms-flexbox;display: -moz-box;display: -webkit-box;display: -webkit-flex;flex-direction: row;-webkit-flex-direction: row;justify-content: space-around;-webkit-box-pack: space-around;-moz-box-pack: space-around;-ms-flex-pack: space-around;width: 90%;height: 40px;margin: 0 auto;line-height: 40px;.selected {color: orange;}.col {span {margin-left: 5px;vertical-align: middle;}}}}}
</style>

components/FilterBar/FilterBarPop.vue

<template><transition name="fade"><div class="filterbarpop-wrap" v-if="visible" :style="{'top': bgTop + 'px'}"><div class="filterbarpop-bg" @click="closeDialog" :style="{'top': bgTop + 'px'}"></div><div class="filterbarpop"><div class="tab-bar" v-show="hasTabHeader"><a href="javascript:;" :style="{'flex': column}" role="button" @click="clickTab(tab, index)" v-for="(tab, index) in menu.tabs":class="{'selected': selectIndexTab == index}"><span :class="tab.icon"></span>{{tab.name}}</a></div><div class="main"><div class="main-sidebar" :class="{'full-line': !items,'bg-style':items,'line-style':!items,}"><div v-if="menu.type !== 'filter'" class="item" @click="clickSidebar(sidemenu, index)" v-for="(sidemenu, index) in sideMenus.detailList":class="{'selected': currentSelectIndex == index}"><span :class="sidemenu.icon"></span>{{ sidemenu.name }}</div><div v-if="menu.type == 'filter'" v-for="(sm, _index) in menu.tabs"><div class="filter-name">{{sm.name}}</div><div class="filter-item"><span v-for="(sidemenu, index) in sm.detailList" class="item-operation" @click="clickFilterbar(sm, _index, index)" :class="{'multi-selected': sidemenu.selectIndex == index}">{{ sidemenu.name }}</span></div></div><div v-if="menu.type == 'filter'" class="filter-btns"><a href="javascript:;" role="button" @click="handleClean">取消</a><a href="javascript:;" role="button" @click="handleEnsure">确认</a></div></div><div class="main-list line-style" v-if="items"><span class="item" @click="clickItem(item, index)" v-for="(item, index) in items.list" :class="{'selected': currentSelectIndex == sideMenus.selectIndex && items.selectIndex == index}">{{item.name}}</span></div></div></div></div></transition>
</template><script>export default {props: {menu: {type: Object},showDialog: {type: Boolean,default: true},hasTabHeader: {type: Boolean,default: true},filterTop: {type: String}},data() {return {selectIndexTab: 0,currentSelectIndex: 0,sideMenus: {},items: {},column: '',visible: false,top: 1,bgTop: 0,range: {}}},mounted() {this.bgTop = document.querySelector('.filterbar').offsetHeight + this.filterTop / 1;},watch: {showDialog(v) {this.visible = v;if (v) {//初始化数据this.initData();}},menu(m) {//根据tabs数量计算列宽this.column = '0 0 ' + 100 / m.tabs.length + '%';//初始化数据this.initData();}},methods: {//初始化数据initData(tabIndex) {var tmpTabIndx = 0;tabIndex === undefined ? tmpTabIndx = this.menu.selectIndex : tmpTabIndx = tabIndex//判断tabindex的范围是否在数组内if (tmpTabIndx >= 0 && tmpTabIndx < this.menu.tabs.length) {this.selectIndexTab = tmpTabIndx;} else {this.selectIndexTab = 0;}//确认选中tab的一级列表this.sideMenus = this.menu.tabs[this.selectIndexTab];//如果当前选中tab是对应选中结果的tab// debugger;if (this.selectIndexTab == this.menu.selectIndex) {this.currentSelectIndex = this.sideMenus.selectIndex;}// else{//   this.sideMenus.selectIndex = -1;//   this.currentSelectIndex = -1;// }//判断是否包含二级列表,包含则赋值//如果一级列表的选中状态正确,则查询二级列表if (this.currentSelectIndex >= 0 && this.currentSelectIndex < this.sideMenus.detailList.length) {//判断是否有二级列表if (this.sideMenus.detailList[this.currentSelectIndex].list) {this.items = this.sideMenus.detailList[this.currentSelectIndex];} else {//不显示二级列表this.items = false;}} else { //如果一级列表选中状态不正确,按第一项的的数据判断//判断是否有二级列表if (this.sideMenus.detailList[0].list) {//显示空的二级列表this.items = [];} else {//不显示二级列表this.items = false;}}},//修改选项changeSelect(index) {//记录tabIndexthis.menu.selectIndex = this.selectIndexTab;//记录一级列表选项this.sideMenus.selectIndex = this.currentSelectIndex;if (this.items) {//确认二级列表选项this.items.selectIndex = index;//显示名称this.menu.name = this.items.list[this.items.selectIndex].name;this.menu.value = this.items.list[this.items.selectIndex].value;} else {//显示名称this.menu.name = this.sideMenus.detailList[this.sideMenus.selectIndex].name;this.menu.value = this.sideMenus.detailList[this.sideMenus.selectIndex].value;}this.$emit('changeSelect');this.closeDialog();},// 帅选修改选项changeRangeSelect() {this.menu.name = '筛选';for(var i in this.range){if(Object.keys(this.range[i].value).length == 0){delete this.range[i]}}this.menu.value = Object.keys(this.range).length > 0 ? this.range : '';this.$emit('changeSelect');this.closeDialog();},// 选择Tab菜单clickTab(tab, index) {if (index !== this.selectIndexTab) {//根据选中的tab初始化数据this.initData(index);this.$emit('changeTab', {tab,index})}},// 筛选方法clickFilterbar(v, I, i) {v.detailList[i].selectIndex = i;// debuggerif(!this.range[I]){this.range[I] = {name: v.name, value: {}};this.range[I].value[i] = v.detailList[i].value;} else {if(!this.range[I].value[i]){this.range[I].value[i] = v.detailList[i].value;} else {delete this.range[I].value[i];v.detailList[i].selectIndex = -1;}}},// 点击左侧列表clickSidebar(v, i) {if (this.currentSelectIndex !== i) {this.currentSelectIndex = i;//存在二级列表if (this.sideMenus.detailList[this.currentSelectIndex].list) {this.items = this.sideMenus.detailList[this.currentSelectIndex];} else {//只有一级列表,记录选项,退出this.changeSelect();}this.$emit('changeMainItem', {v,i});}},// 点击右侧列表clickItem(v, i) {//只有一级列表,记录选项,退出this.changeSelect(i);},// 关闭弹框closeDialog() {this.visible = false;this.$emit('closeDialog');},// 提交已选内容handleEnsure() {this.changeRangeSelect();this.$emit('changeMainItem', this.range);// this.closeDialog();},// 清除已选内容handleClean() {this.menu.tabs.map(item => {item.detailList.map(_item => {_item.selectIndex = -1;})});this.range = {};}}}/**TODOS:1. 需要一个属性去辨别帅选项2. 多选3. 添加多选框*/
</script><style lang="scss">.fade-enter-active,.fade-leave-active {transition: opacity .5s}.fade-enter,.fade-leave-active {opacity: 0}.filterbarpop-wrap {position: fixed;width: 100%;top: 0;bottom: 0;left: 0;overflow: hidden;max-height: 100%;.filterbarpop-bg {position: fixed;top: 0;bottom: 0;left: 0;width: 100%;background: rgba(0, 0, 0, .6);}.filterbarpop {position: absolute;width: 100%;border-top: 1px solid #ccc;.tab-bar {width: 100%;display: flex;display: -ms-flexbox;display: -moz-box;display: -webkit-box;display: -webkit-flex;flex-directives: row;-webkit-flex-direction: row;align-items: center;-webkit-align-items: center;-webkit-box-align: center;-moz-box-align: center;-ms-flex-align: center;height: 40px;.selected {border-bottom: 2px solid orange;box-sizing: border-box;}a {background: #fff;height: 100%;line-height: 40px;text-decoration: none;color: #323232;text-align: center;}}.main {display: flex;display: -webkit-flex;flex-direction: row;-webkit-flex-direction: row;height: 250px;background: #fff;.main-sidebar {flex: 0 0 50%;overflow: auto;width: 100%;}.full-line {flex: 0 0 100%;div {text-align: left; // text-indent: 1.5em;}}.item-operation {display: inline-block;padding: 10px 4px 10px 4px;border: 1px solid rgb(91, 149, 255);border-radius: 3px;height: 0;line-height: 1px;}.multi-selected {background: rgb(91, 149, 255);color: #fff !important;}.filter-item {border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;padding: 13px 0 5px 10px;span {margin-right: 8px;margin-bottom: 8px;}}.filter-name {padding: 10px 0 10px 10px;}.filter-btns {display: flex;display: -webkit-flex;flex-direction: row;-webkit-flex-direction: row;justify-content: space-around;-webkit-box-pack: space-around;-moz-box-pack: space-around;-ms-flex-pack: space-around;position: absolute;bottom: -40px;width: 100%;line-height: 40px;z-index: 100;background: #fff;a {display: block;width: 100%;text-align: center;text-decoration: none;color: #ccc;border-top: 1px solid #ccc;&:last-child {background: #39f;color: #fff;}}}.main-list {flex: 0 0 50%;overflow: auto;span:active {background: #f5f5f5;}}.line-style {.item {text-align: left;margin-left: 10px;padding-left: 15px;border-bottom: 1px solid #ccc;position: relative;&.selected {color: orange;border-color: orange;span {color: orange;}}.checkbox {position: absolute;right: 50px;top: 10px;}}}.bg-style {.item {background-color: #f5f5f5;&.selected {background-color: #FFF;}}}.item {display: inline-block;height: 40px;background: #fff;line-height: 40px;width: 100%;text-decoration: none;color: #444;span {font-size: 14px;color: #888;margin-right: 10px;vertical-align: middle;}&:active {color: #fff;}}}}}
</style>

2.页面调用

pages/FilterBarTest

<!-- 移动端筛选条件 测试页 -->
<template><div><!-- 标题栏 --><x-header title="移动端筛选条件 测试页"></x-header><!-- 内容部分 --><FilterBartop="40":barMenus="barMenus"@showDialog="handleShowDialog"@closeDialog="handleCloseDialog"@changeTab="handleChangeTab"@changeMainItem="handleChangeMainItem"@changeSelect="changeData"></FilterBar></div>
</template><script>import { XHeader } from 'vux'// 引入组件import FilterBar from '../../components/FilterBar/FilterBar.vue'// 引入假数据import barMenus from './data.js';export default {name: 'FilterBarTest',components: {XHeader,FilterBar,},data(){return {barMenus: barMenus}},methods: {handleShowDialog(v) {// console.log(v);},handleCloseDialog(v) {// console.log(v);},handleChangeTab(v) {// console.log(v);},handleChangeMainItem(v) {// console.log(v)},changeData(v) {console.log(v);}}}
</script><style lang="scss" scoped>//
</style>

data.js

export default [
{name: '附近',icon: '',value: 'area',showTabHeader: true,defaultIcon: '',selectIcon: '',selectIndex: 0,tabs: [{icon: '',name: '商圈',selectIndex: 0,detailList: [{name: '附近',icon: '',selectIndex: 0,list: [{name: '默认',value: 'all'}, {name: '500米',value: '500'}, {name: '1000米',value: '1000'}]},{name: '朝阳区',icon: '',selectIndex: 1,list: [{name: '全部',value: 'all'}, {name: '建国门',value: 'jianguomen'}, {name: '亚运村',value: 'yayuncun'}]},{name: '海淀区',icon: '',selectIndex: 2,list: [{name: '全部',value: 'all'}, {name: '中关村',value: 'zhongguancun'}, {name: '五道口',value: 'wudaokou'}]}]},{icon: '',name: '地铁沿线',selectIndex: 1,detailList: [{name: '1号线',icon: '',selectIndex: 0,list: [{name: '平果圆',value: 'pingguoyuan'}, {name: '古城',value: 'gucheng'}, {name: '八角游乐园',value: 'bajiaoyouleyuan'}]},{name: '2号线',icon: '',selectIndex: 1,list: [{name: '积水潭',value: 'jishuitan'}, {name: '鼓楼大街',value: 'guloudajie'}, {name: '安定门',value: 'andingmen'}]},{name: '4号线',icon: '',selectIndex: 2,list: [{name: '安和桥北',value: 'anheqiaobei'}, {name: '北宫门',value: 'beigongmen'}, {name: '西宛',value: 'xiwan'}]}]}]
},
{name: '菜系',icon: '',value: 'food',showTabHeader: false,defaultIcon: '',selectIcon: '',selectIndex: 0,tabs: [{icon: '',name: '',selectIndex: 0,detailList: [{name: '全部',icon: '',value: '全部',selectIndex: 0,list: [{name: "全部",value: 'all'}]},{name: '中餐馆',icon: '',value: '中餐馆',selectIndex: 1,list: [{name: '全部',value: 'all'}, {name: '火锅',value: 'hot pot'}, {name: '川菜',value: 'Sichuan cuisine'}]},{name: '西餐馆',icon: '',value: '西餐管',selectIndex: 2,list: [{name: '全部',value: 'all'}, {name: '披萨',value: 'pizza'}, {name: '牛排',value: 'steak'}]}]}]
},
{name: '排序',icon: '',value: 'compositor',showTabHeader: false,defaultIcon: '',selectIcon: '',selectIndex: 0,tabs: [{icon: '',name: '',selectIndex: 0,detailList: [{name: '只能排序',icon: '',value: '0',selectIndex: 0},{name: '离我最近',icon: '',value: '1',selectIndex: 1},{name: '评价最好',icon: '',value: '2',selectIndex: 2}]}]
},
{name: '筛选',icon: '',value: 'filter',type: 'filter',showTabHeader: false,defaultIcon: '',selectIcon: '',selectIndex: 0,tabs: [{icon: '',name: '价格',selectIndex: 0,detailList: [{name: '0-50',value: '0-50',selectIndex: -1},{name: '50-100',value: '50-100',selectIndex: -1},{name: '100-150',value: '100-150',selectIndex: -1},{name: '150-200',value: '150-200',selectIndex: -1},{name: '200-250',value: '200-250',selectIndex: -1},{name: '300-350',value: '300-350',selectIndex: -1}]},{icon: '',name: '入住类型',selectIndex: 1,detailList: [{name: '不限',value: 'all',selectIndex: -1}, {name: '全日房',value: 'daily',selectIndex: -1}, {name: '钟点房',value: 'time',selectIndex: -1},{name: '支持团购',value: 'group buy',selectIndex: -1}]}]
}]

3.效果图

  

.

转载于:https://www.cnblogs.com/crazycode2/p/8849448.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390680.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PSP

姓名&#xff1a;袁亚琴 日期&#xff1a;11月27日 教师&#xff1a;王建民 课程&#xff1a;PSP 项目计划日志&#xff1a; PSP Planning . Estimate Development . Analysis . Design Spec . Design Review . …

如何在Windows中打开和使用命令提示符

入门 (Getting started) Windows, MacOS and Linux have command line interfaces. Windows’ default command line is the command prompt. The command prompt allows users to use their computer without pointing and clicking with a mouse. Windows&#xff0c;MacOS和…

ACM-ICPC北京赛区2017网络同步赛H

http://hihocoder.com/contest/icpcbeijing2017/problem/8 预处理暴力枚举修改的点 #include <bits/stdc.h> using namespace std; const int maxn 159; const int inf 0x3f3f3f3f; int a[maxn][maxn]; int colsum[maxn][maxn]; int rowsum[maxn][maxn]; int dp[maxn];…

PPPOE拨号上网流程及密码窃取具体实现

楼主学生党一枚&#xff0c;最近研究netkeeper有些许心得。 关于netkeeper是调用windows的rasdial来进行上网的东西&#xff0c;网上已经有一大堆&#xff0c;我就不赘述了。 本文主要讲解rasdial的部分核心过程&#xff0c;以及我们可以利用它来干些什么。 netkeeper中rasdial…

leetcode 160. 相交链表(双指针)

给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果后&#…

android开发入门_Android开发入门

android开发入门Android is an open source, Linux-based mobile operating system. Android was developed by the Open Handset Alliance, which was lead by Google and featured contributions from many other companies.Android是基于Linux的开放源代码移动操作系统。 An…

新购阿里云服务器ECS创建之后无法ssh连接的问题处理

作者&#xff1a;13 GitHub&#xff1a;https://github.com/ZHENFENG13 版权声明&#xff1a;本文为原创文章&#xff0c;未经允许不得转载。 问题描述 由于原服务器将要到期&#xff0c;因此趁着阿里云搞促销活动重新购买了一台ECS服务器&#xff0c;但是在初始化并启动后却无…

数据下发非标准用户权限测试

与同事一起沟通了下MDM的Oracle权限部分: create user cx default tablespace cwbaseoe73 identified by Test6530 grant select,update,delete,insert on lcoe739999.lsbzdw to cx grant create table to cx alter user cx quota unlimited on cwbaseoe73 grant create sessio…

leetcode 474. 一和零(dp)

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的大小&#xff0c;该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素&#xff0c;集合 x 是集合 y 的 子集 。 示例 1&#xff1a; 输入&#xff1a;strs [“10”…

边缘计算 ai_在边缘探索AI!

边缘计算 ai介绍 (Introduction) What is Edge (or Fog) Computing?什么是边缘(或雾)计算&#xff1f; Gartner defines edge computing as: “a part of a distributed computing topology in which information processing is located close to the edge — where things a…

JavaScript中的全局变量介绍

Global variables are declared outside of a function for accessibility throughout the program, while local variables are stored within a function using var for use only within that function’s scope. If you declare a variable without using var, even if it’…

初识spring-boot

使用Spring或者SpringMVC的话依然有许多东西需要我们进行配置&#xff0c;这样不仅徒增工作量而且在跨平台部署时容易出问题。 使用Spring Boot可以让我们快速创建一个基于Spring的项目&#xff0c;而让这个Spring项目跑起来我们只需要很少的配置就可以了。Spring Boot主要有如…

leetcode 879. 盈利计划(dp)

这是我参与更文挑战的第9天 &#xff0c;活动详情查看更文挑战 题目 集团里有 n 名员工&#xff0c;他们可以完成各种各样的工作创造利润。 第 i 种工作会产生 profit[i] 的利润&#xff0c;它要求 group[i] 名成员共同参与。如果成员参与了其中一项工作&#xff0c;就不能…

区块链101:区块链的应用和用例是什么?

区块链技术是一场记录系统的革命。 比特币是历史上第一个永久的、分散的、全球性的、无信任的记录分类帐。自其发明以来&#xff0c;世界各地各行各业的企业家都开始明白这一发展的意义。 区块链技术的本质让人联想到疯狂&#xff0c;因为这个想法现在可以应用到任何值得信赖的…

java请求接口示例_用示例解释Java接口

java请求接口示例介面 (Interfaces) Interface in Java is a bit like the Class, but with a significant difference: an interface can only have method signatures, fields and default methods. Since Java 8, you can also create default methods. In the next block y…

如何建立搜索引擎_如何建立搜寻引擎

如何建立搜索引擎This article outlines one of the most important search algorithms used today and demonstrates how to implement it in Python in just a few lines of code.本文概述了当今使用的最重要的搜索算法之一&#xff0c;并演示了如何仅用几行代码就可以在Pyth…

用Docker自动构建纸壳CMS

纸壳CMS可以运行在Docker上&#xff0c;接下来看看如何自动构建纸壳CMS的Docker Image。我们希望的是在代码提交到GitHub以后&#xff0c;容器镜像服务可以自动构建Docker Image&#xff0c;构建好以后&#xff0c;就可以直接拿这个Docker Image来运行了。 Dockerfile 最重要的…

Linux学习笔记15—RPM包的安装OR源码包的安装

RPM安装命令1、 安装一个rpm包rpm –ivh 包名“-i” : 安装的意思“-v” : 可视化“-h” : 显示安装进度另外在安装一个rpm包时常用的附带参数有&#xff1a;--force : 强制安装&#xff0c;即使覆盖属于其他包的文件也要安装--nodeps : 当要安装的rpm包依赖其他包时&#xff0…

leetcode 518. 零钱兑换 II

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。 示例 1: 输入: amount 5, coins [1, 2, 5] 输出: 4 解释: 有四种方式可以凑成总金额: 55 5221 52111 511111 示例 2: 输入: amount 3, coins [2] 输出: 0 解…

软件测试中什么是正交实验法_软件工程中的正交性

软件测试中什么是正交实验法正交性 (Orthogonality) In software engineering, a system is considered orthogonal if changing one of its components changes the state of that component only. 在软件工程中&#xff0c;如果更改系统的组件之一仅更改该组件的状态&#xf…