zheng 1 неделя назад
Родитель
Сommit
fae0bf3d11
2 измененных файлов с 293 добавлено и 0 удалено
  1. 4 0
      20.vue3/project1/src/App.vue
  2. 289 0
      20.vue3/project1/src/components/Demo9.vue

+ 4 - 0
20.vue3/project1/src/App.vue

@@ -3,6 +3,9 @@
     <h1>App</h1>
     <hr>
     <hr>
+    <Demo9></Demo9>
+    <hr>
+    <hr>
     <Demo8></Demo8>
     <hr>
     <hr>
@@ -37,6 +40,7 @@ import Demo5 from './components/Demo5.vue'
 import Demo6 from './components/Demo6.vue'
 import Demo7 from './components/Demo7.vue'
 import Demo8 from './components/Demo8.vue'
+import Demo9 from './components/Demo9.vue'
 </script>
 
 <style lang="scss" scoped>

+ 289 - 0
20.vue3/project1/src/components/Demo9.vue

@@ -0,0 +1,289 @@
+<template>
+  <div class="pet-box">
+    <h2>萌宠乐园 🐱🐶</h2>
+
+    <!-- 筛选区域 -->
+    <div class="search-bar">
+      <input
+        v-model="keyword"
+        placeholder="搜索宠物名称"
+        class="search-input"
+      />
+      <span>颜值分值:</span>
+      <input
+        v-model.number="minLevel"
+        type="number"
+        placeholder="最低分"
+        class="num-input"
+      />
+      -
+      <input
+        v-model.number="maxLevel"
+        type="number"
+        placeholder="最高分"
+        class="num-input"
+      />
+      <button @click="resetFilter" class="btn reset-btn">重置筛选</button>
+    </div>
+
+    <!-- 功能按钮 -->
+    <div class="btn-group">
+      <label class="check-label">
+        <input type="checkbox" v-model="isAllCheck" /> 全选萌宠
+      </label>
+      <button @click="reverseCheck" class="btn">反选</button>
+      <button @click="clearCheck" class="btn">清空选中</button>
+      <button @click="sortAsc" class="btn">分值升序</button>
+      <button @click="sortDesc" class="btn">分值降序</button>
+    </div>
+
+    <!-- 萌宠列表 + 动画卡片 -->
+    <div class="pet-list">
+      <!-- 过渡动画:列表切换自动生效 -->
+      <transition-group name="pet" tag="div" class="list-wrap">
+        <div
+          class="pet-card"
+          :class="{ active: pet.checked }"
+          v-for="pet in filterList"
+          :key="pet.id"
+        >
+          <input type="checkbox" v-model="pet.checked" class="card-check" />
+          <div class="pet-avatar">{{ pet.emoji }}</div>
+          <div class="pet-name">{{ pet.name }}</div>
+          <div class="pet-type">品类:{{ pet.type }}</div>
+          <div class="pet-level">颜值:{{ pet.level }} 分</div>
+        </div>
+      </transition-group>
+    </div>
+
+    <!-- 数据统计 -->
+    <div class="stat">
+      <p>当前萌宠总数:{{ totalNum }}</p>
+      <p>已挑选萌宠:{{ checkedNum }} 只</p>
+      <p>选中萌宠平均颜值:{{ avgLevel.toFixed(1) }} 分</p>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed } from 'vue'
+
+// 响应式数据
+const keyword = ref('')
+const minLevel = ref(0)
+const maxLevel = ref(100)
+
+// 萌宠数据源,搭配表情更有趣
+const petList = ref([
+  { id: 1, name: '橘猫', type: '猫咪', level: 92, emoji: '🐱', checked: false },
+  { id: 2, name: '柴犬', type: '狗狗', level: 88, emoji: '🐶', checked: false },
+  { id: 3, name: '小白兔', type: '萌宠', level: 95, emoji: '🐰', checked: false },
+  { id: 4, name: '小仓鼠', type: '萌宠', level: 80, emoji: '🐹', checked: false },
+  { id: 5, name: '布偶猫', type: '猫咪', level: 98, emoji: '😺', checked: false },
+  { id: 6, name: '柯基', type: '狗狗', level: 85, emoji: '🐕', checked: false },
+])
+
+// 1. 综合筛选:名称 + 颜值区间
+const filterList = computed(() => {
+  return petList.value.filter(pet => {
+    const nameMatch = pet.name.includes(keyword.value)
+    const levelMatch = pet.level >= minLevel.value && pet.level <= maxLevel.value
+    return nameMatch && levelMatch
+  })
+})
+
+// 2. 筛选后总数
+const totalNum = computed(() => filterList.value.length)
+
+// 3. 已选中数量
+const checkedNum = computed(() => filterList.value.filter(pet => pet.checked).length)
+
+// 4. 选中平均颜值
+const avgLevel = computed(() => {
+  const checkedArr = filterList.value.filter(pet => pet.checked)
+  if (checkedArr.length === 0) return 0
+  const total = checkedArr.reduce((sum, pet) => sum + pet.level, 0)
+  return total / checkedArr.length
+})
+
+// 5. 可写计算属性 - 全选
+const isAllCheck = computed({
+  get() {
+    return filterList.value.length > 0 && filterList.value.every(pet => pet.checked)
+  },
+  set(val: boolean) {
+    filterList.value.forEach(pet => pet.checked = val)
+  }
+})
+
+// 重置筛选
+const resetFilter = () => {
+  keyword.value = ''
+  minLevel.value = 0
+  maxLevel.value = 100
+}
+
+// 反选
+const reverseCheck = () => {
+  filterList.value.forEach(pet => pet.checked = !pet.checked)
+}
+
+// 清空选中
+const clearCheck = () => {
+  filterList.value.forEach(pet => pet.checked = false)
+}
+
+// 分值升序
+const sortAsc = () => {
+  petList.value.sort((a, b) => a.level - b.level)
+}
+
+// 分值降序
+const sortDesc = () => {
+  petList.value.sort((a, b) => b.level - a.level)
+}
+</script>
+
+<style scoped>
+.pet-box {
+  width: 90%;
+  margin: 20px auto;
+  font-family: "Microsoft Yahei", sans-serif;
+}
+h2 {
+  text-align: center;
+  color: #ff7875;
+}
+
+/* 筛选栏样式 */
+.search-bar {
+  text-align: center;
+  margin: 15px 0;
+}
+.search-input, .num-input {
+  padding: 6px 8px;
+  margin: 0 6px;
+  border: 1px solid #ccc;
+  border-radius: 6px;
+  outline: none;
+  transition: all 0.3s;
+}
+.search-input:focus, .num-input:focus {
+  border-color: #ff9494;
+  box-shadow: 0 0 4px #ffc8c8;
+}
+
+/* 按钮通用样式 + 动效 */
+.btn-group {
+  text-align: center;
+  margin: 10px 0;
+}
+.check-label {
+  margin-right: 10px;
+  cursor: pointer;
+}
+.btn {
+  padding: 6px 12px;
+  margin: 0 4px;
+  border: none;
+  border-radius: 6px;
+  background: #ff9494;
+  color: #fff;
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+.btn:hover {
+  background: #ff7875;
+  transform: translateY(-2px);
+  box-shadow: 0 3px 6px rgba(255, 120, 117, 0.4);
+}
+.btn:active {
+  transform: translateY(0);
+}
+.reset-btn {
+  background: #74b9ff;
+}
+.reset-btn:hover {
+  background: #0984e3;
+}
+
+/* 萌宠卡片布局 */
+.pet-list {
+  margin-top: 20px;
+}
+.list-wrap {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 20px;
+  justify-content: center;
+}
+
+/* 卡片基础样式 + hover 动画 */
+.pet-card {
+  width: 160px;
+  padding: 15px;
+  border-radius: 12px;
+  background: #fef5f5;
+  text-align: center;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+  transition: all 0.3s ease;
+  position: relative;
+}
+.pet-card:hover {
+  transform: translateY(-6px);
+  box-shadow: 0 8px 16px rgba(255, 120, 117, 0.25);
+}
+
+/* 选中状态动画:放大+变色 */
+.pet-card.active {
+  border: 2px solid #ff7875;
+  transform: scale(1.05);
+  background: #ffe8e8;
+}
+
+/* 宠物表情 */
+.pet-avatar {
+  font-size: 48px;
+  margin-bottom: 8px;
+}
+.pet-name {
+  font-size: 16px;
+  font-weight: bold;
+  color: #333;
+}
+.pet-type, .pet-level {
+  font-size: 14px;
+  color: #666;
+  margin: 4px 0;
+}
+.card-check {
+  position: absolute;
+  top: 8px;
+  left: 8px;
+  cursor: pointer;
+}
+
+/* Vue 列表过渡动画 入场/离场 */
+.pet-enter-active {
+  transition: all 0.4s ease;
+}
+.pet-leave-active {
+  transition: all 0.3s ease;
+}
+.pet-enter-from {
+  opacity: 0;
+  transform: scale(0.7) translateY(20px);
+}
+.pet-leave-to {
+  opacity: 0;
+  transform: scale(0.7) translateY(-20px);
+}
+
+/* 统计区域 */
+.stat {
+  margin-top: 30px;
+  text-align: center;
+  font-size: 15px;
+  color: #555;
+}
+</style>