Skip to content

示例

汇总一些常用的完整组件示例。

基础表单组件

使用 queryRef 展示不同查询参数类型的完整 Vue 组件:

vue
<script setup lang="ts">
import { serializers, queryRef } from 'vue-qs';

const name = queryRef('name', { defaultValue: '', parse: String });

const itemCount = queryRef('itemCount', { defaultValue: 0, codec: serializers.numberCodec });

const isPublished = queryRef('isPublished', {
  defaultValue: false,
  codec: serializers.booleanCodec,
});

const tags = queryRef<string[]>('tags', {
  defaultValue: [],
  codec: serializers.createArrayCodec(serializers.stringCodec),
});
</script>

<template>
  <div class="form-container">
    <h2>基础表单示例</h2>

    <div class="field">
      <label>姓名:</label>
      <input v-model="name" placeholder="您的姓名" />
    </div>

    <div class="field">
      <label>项目数量:</label>
      <input v-model="itemCount" type="number" placeholder="项目数量" />
    </div>

    <div class="field">
      <label>
        <input v-model="isPublished" type="checkbox" />
        已发布
      </label>
    </div>

    <div class="field">
      <label>标签:</label>
      <select v-model="tags" multiple>
        <option v-for="tag in ['vue', 'javascript', 'frontend', 'backend']" :key="tag" :value="tag">
          {{ tag }}
        </option>
      </select>
    </div>

    <div class="output">
      <h3>当前状态:</h3>
      <pre>{{ JSON.stringify({ name, itemCount, isPublished, tags }, null, 2) }}</pre>
    </div>
  </div>
</template>

<style scoped>
.form-container {
  max-width: 500px;
  margin: 0 auto;
  padding: 20px;
}

.field {
  margin-bottom: 15px;
}

.field label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

.field input[type='text'],
.field input[type='number'],
.field select {
  width: 100%;
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.field select[multiple] {
  height: 100px;
}

.output {
  margin-top: 20px;
  padding: 15px;
  background-color: #f5f5f5;
  border-radius: 4px;
}

.output pre {
  margin: 0;
  white-space: pre-wrap;
}
</style>

混合使用 queryRef 和 queryReactive 组件

同时展示 queryRefqueryReactive 的组件:

vue
<script setup lang="ts">
import { computed } from 'vue';
import { serializers, queryRef, queryReactive } from 'vue-qs';

// 使用 queryRef 进行类别选择
const category = queryRef<'books' | 'music' | 'games'>('category', {
  defaultValue: 'books',
  codec: serializers.createEnumCodec(['books', 'music', 'games'] as const),
});

// 使用 queryReactive 管理分组的过滤器状态
const filters = queryReactive({
  search: { defaultValue: '' },
  page: { defaultValue: 1, codec: serializers.numberCodec },
  minPrice: { defaultValue: 0, codec: serializers.numberCodec },
  maxPrice: { defaultValue: 1000, codec: serializers.numberCodec },
  inStock: { defaultValue: false, codec: serializers.booleanCodec },
});

// 组合两种状态的计算属性
const searchSummary = computed(() => ({
  category: category.value,
  totalFilters: Object.values(filters).filter(Boolean).length,
  ...filters,
}));

// 重置所有过滤器的方法
const resetFilters = () => {
  filters.search = '';
  filters.page = 1;
  filters.minPrice = 0;
  filters.maxPrice = 1000;
  filters.inStock = false;
};

// 应用快速过滤器的方法
const applyQuickFilter = (type: 'cheap' | 'expensive' | 'available') => {
  switch (type) {
    case 'cheap':
      filters.minPrice = 0;
      filters.maxPrice = 50;
      filters.inStock = true;
      break;
    case 'expensive':
      filters.minPrice = 500;
      filters.maxPrice = 1000;
      break;
    case 'available':
      filters.inStock = true;
      filters.page = 1;
      break;
  }
};
</script>

<template>
  <div class="search-container">
    <h2>产品搜索</h2>

    <!-- 使用 queryRef 的类别选择 -->
    <div class="section">
      <h3>类别 (queryRef)</h3>
      <div class="category-buttons">
        <button
          v-for="cat in ['books', 'music', 'games']"
          :key="cat"
          :class="{ active: category === cat }"
          @click="category = cat"
        >
          {{ { books: '图书', music: '音乐', games: '游戏' }[cat] }}
        </button>
      </div>
    </div>

    <!-- 使用 queryReactive 的过滤器 -->
    <div class="section">
      <h3>过滤器 (queryReactive)</h3>

      <div class="field">
        <label>搜索:</label>
        <input v-model="filters.search" placeholder="搜索产品..." />
      </div>

      <div class="field">
        <label>页码:</label>
        <input v-model="filters.page" type="number" min="1" />
      </div>

      <div class="field-group">
        <div class="field">
          <label>最低价格:</label>
          <input v-model="filters.minPrice" type="number" min="0" />
        </div>

        <div class="field">
          <label>最高价格:</label>
          <input v-model="filters.maxPrice" type="number" min="0" />
        </div>
      </div>

      <div class="field">
        <label>
          <input v-model="filters.inStock" type="checkbox" />
          仅显示有库存商品
        </label>
      </div>
    </div>

    <!-- 快速操作 -->
    <div class="section">
      <h3>快速过滤器</h3>
      <div class="quick-buttons">
        <button @click="applyQuickFilter('cheap')">便宜商品</button>
        <button @click="applyQuickFilter('expensive')">昂贵商品</button>
        <button @click="applyQuickFilter('available')">有库存商品</button>
        <button @click="resetFilters()" class="reset">重置全部</button>
      </div>
    </div>

    <!-- 状态显示 -->
    <div class="output">
      <h3>当前搜索状态:</h3>
      <pre>{{ JSON.stringify(searchSummary, null, 2) }}</pre>
    </div>
  </div>
</template>

<style scoped>
.search-container {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
}

.section {
  margin-bottom: 25px;
  padding: 15px;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
}

.section h3 {
  margin-top: 0;
  color: #333;
}

.category-buttons,
.quick-buttons {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.category-buttons button,
.quick-buttons button {
  padding: 8px 16px;
  border: 1px solid #ccc;
  border-radius: 4px;
  background: white;
  cursor: pointer;
  transition: all 0.2s;
}

.category-buttons button:hover,
.quick-buttons button:hover {
  background-color: #f0f0f0;
}

.category-buttons button.active {
  background-color: #007bff;
  color: white;
  border-color: #007bff;
}

.quick-buttons button.reset {
  background-color: #dc3545;
  color: white;
  border-color: #dc3545;
}

.quick-buttons button.reset:hover {
  background-color: #c82333;
}

.field {
  margin-bottom: 15px;
}

.field-group {
  display: flex;
  gap: 15px;
}

.field-group .field {
  flex: 1;
}

.field label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

.field input[type='text'],
.field input[type='number'] {
  width: 100%;
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.output {
  margin-top: 20px;
  padding: 15px;
  background-color: #f8f9fa;
  border-radius: 4px;
}

.output pre {
  margin: 0;
  white-space: pre-wrap;
  font-size: 14px;
}
</style>

Released under the MIT License.