# MatchPrizeSet Prize Setting Component
Prize setting component that supports prize information configuration, image upload, quantity setting and other functions. Supports edit mode and view mode with flexible form field configuration, suitable for competition prize management scenarios. Suitable for multi-prize list display with support for dynamic adding and deleting prizes.
# Import
import PressMatchPrizeSet from 'press-next/press-match-prize-set/press-match-prize-set.vue';
# Code Examples
# Basic Usage
Basic usage demonstrates the component's core features, including prize information display, editing, and operations.
<template>
<PressMatchPrizeSet
v-model:data="prizeData"
:is-view-mode="false"
@change="handleChange"
@delete="handleDelete"
@add="handleAdd"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import type { PrizeSetData } from 'press-next/press-match-prize-set/press-match-prize-set.vue';
const prizeData = ref<PrizeSetData>({
title: 'Prize Configuration',
name: 'Game Skin',
description: 'Limited Skin Pack',
type: 'Physical Prize',
image: 'https://placehold.co/74x74',
quantity: 10,
quantityType: 'per_person',
});
const handleChange = (data: PrizeSetData) => {
console.log('Prize data changed:', data);
};
const handleDelete = () => {
console.log('Delete prize');
};
const handleAdd = () => {
console.log('Add prize');
};
</script>
# Custom Form Fields
Use the formFields property to customize form field configuration, supporting both input and select types.
<template>
<PressMatchPrizeSet
:data="prizeData"
:form-fields="customFormFields"
:is-view-mode="false"
@change="handleChange"
@field-select="handleFieldSelect"
/>
<!-- Field Selector -->
<PressActionSheet
v-model:show="showFieldSheet"
:actions="fieldActions"
:title="currentFieldLabel"
@select="handleFieldValueSelect"
/>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import type { FormFieldConfig } from 'press-next/press-match-prize-set/press-match-prize-set.vue';
const prizeData = ref({
title: 'First Prize',
name: 'Game Skin',
description: 'Limited Skin Pack',
image: 'https://placehold.co/74x74',
quantity: 5,
quantityType: 'total'
});
// Custom form field configuration
const customFormFields = ref<FormFieldConfig[]>([
{
key: 'name',
label: 'Prize Name',
placeholder: 'Enter prize name',
type: 'input',
},
{
key: 'description',
label: 'Prize Description',
placeholder: 'Enter prize description',
type: 'input',
},
{
key: 'type',
label: 'Prize Type',
type: 'select',
selectedValue: 'Physical Prize', // Selected value
defaultText: 'Please select',
},
{
key: 'level',
label: 'Prize Level',
type: 'select',
selectedValue: '',
defaultText: 'Select level',
},
]);
const showFieldSheet = ref(false);
const currentFieldKey = ref('');
const currentFieldLabel = ref('');
// Field selector options
const fieldOptionsMap: Record<string, { name: string; value: string }[]> = {
type: [
{ name: 'Physical Prize', value: 'Physical Prize' },
{ name: 'Virtual Prize', value: 'Virtual Prize' },
{ name: 'Cash Reward', value: 'Cash Reward' },
],
level: [
{ name: 'First Prize', value: 'First Prize' },
{ name: 'Second Prize', value: 'Second Prize' },
{ name: 'Third Prize', value: 'Third Prize' },
],
};
const fieldActions = computed(() => fieldOptionsMap[currentFieldKey.value] || []);
const handleChange = () => {
console.log('Prize data changed');
};
const handleFieldSelect = (fieldKey: string) => {
currentFieldKey.value = fieldKey;
const field = customFormFields.value.find(f => f.key === fieldKey);
if (field) {
currentFieldLabel.value = `Select ${field.label}`;
showFieldSheet.value = true;
}
};
const handleFieldValueSelect = (action: { value: string }) => {
const fieldIndex = customFormFields.value.findIndex(f => f.key === currentFieldKey.value);
if (fieldIndex !== -1) {
customFormFields.value[fieldIndex].selectedValue = action.value;
}
showFieldSheet.value = false;
};
</script>
# Multiple Prize List
Supports unified management of multiple prizes, showing title only in the first component and "Add Prize" button only in the last component.
<template>
<div class="prize-list">
<PressMatchPrizeSet
v-for="(prize, index) in prizeList"
:key="prize.id"
v-model:data="prizeList[index]"
:prize-title="index === 0 ? 'Prize Settings' : ''"
:show-title="index === 0"
:is-last-item="index === prizeList.length - 1"
:is-view-mode="false"
@change="handleChange(index, $event)"
@delete="handleDelete(index)"
@add="handleAdd"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import type { PrizeSetData } from 'press-next/press-match-prize-set/press-match-prize-set.vue';
const prizeList = ref<PrizeSetData[]>([
{
id: 1,
title: 'First Prize',
name: 'Limited Game Skin',
description: 'Season Limited Skin Pack',
type: 'Physical Prize',
image: 'https://placehold.co/74x74',
quantity: 5,
quantityType: 'total',
},
{
id: 2,
title: 'Second Prize',
name: 'Game Points',
description: '500 Points',
type: 'Virtual Prize',
image: 'https://placehold.co/74x74',
quantity: 10,
quantityType: 'total',
},
]);
const handleChange = (index: number, data: PrizeSetData) => {
console.log(`Prize ${index + 1} data changed:`, data);
};
const handleDelete = (index: number) => {
if (prizeList.value.length > 1) {
prizeList.value.splice(index, 1);
}
};
const handleAdd = () => {
const newId = Math.max(...prizeList.value.map(p => Number(p.id) || 0)) + 1;
const prizeNames = ['First Prize', 'Second Prize', 'Third Prize', 'Fourth Prize', 'Fifth Prize'];
const title = prizeNames[prizeList.value.length] || `Prize ${prizeList.value.length + 1}`;
prizeList.value.push({
id: newId,
title,
name: '',
description: '',
type: '',
image: 'https://placehold.co/74x74',
quantity: 1,
quantityType: 'per_person',
});
};
</script>
# View Mode
In view mode, all editing features are disabled, selector type fields are automatically hidden, and only read-only data content is displayed.
<template>
<PressMatchPrizeSet
:data="viewModeData"
:is-view-mode="true"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import type { PrizeSetData } from 'press-next/press-match-prize-set/press-match-prize-set.vue';
const viewModeData = ref<PrizeSetData>({
title: 'First Prize',
name: 'Limited Game Skin',
description: 'Season Limited Skin Pack + Exclusive Avatar Frame',
type: 'Physical Prize',
image: 'https://placehold.co/74x74',
quantity: 5,
quantityType: 'total',
});
</script>
# Complete Feature Example
<template>
<PressMatchPrizeSet
:data="prizeData"
:form-fields="customFormFields"
:is-view-mode="false"
custom-class="custom-prize-set"
@change="handleChange"
@delete="handleDelete"
@add="handleAdd"
@image-edit="handleImageEdit"
@field-select="handleFieldSelect"
@quantity-type-change="handleQuantityTypeChange"
/>
<!-- Field Selector -->
<PressActionSheet
v-model:show="showFieldSheet"
:actions="fieldActions"
:title="currentFieldLabel"
@select="handleFieldValueSelect"
/>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import type { FormFieldConfig } from 'press-next/press-match-prize-set/press-match-prize-set.vue';
const prizeData = ref({
id: '1',
title: 'First Prize',
name: 'Game Skin',
description: 'Limited Skin Pack',
image: 'https://placehold.co/74x74',
quantity: 5,
quantityType: 'total'
});
const customFormFields = ref<FormFieldConfig[]>([
{ key: 'name', label: 'Prize Name', type: 'input', placeholder: 'Enter prize name' },
{ key: 'description', label: 'Prize Description', type: 'input', placeholder: 'Enter description' },
{ key: 'type', label: 'Prize Type', type: 'select', selectedValue: 'Physical Prize' },
{ key: 'level', label: 'Prize Level', type: 'select', selectedValue: '' },
]);
const showFieldSheet = ref(false);
const currentFieldKey = ref('');
const currentFieldLabel = ref('');
const fieldOptionsMap: Record<string, { name: string; value: string }[]> = {
type: [
{ name: 'Physical Prize', value: 'Physical Prize' },
{ name: 'Virtual Prize', value: 'Virtual Prize' },
],
level: [
{ name: 'First Prize', value: 'First Prize' },
{ name: 'Second Prize', value: 'Second Prize' },
],
};
const fieldActions = computed(() => fieldOptionsMap[currentFieldKey.value] || []);
const handleChange = () => {
console.log('Prize data changed');
};
const handleDelete = () => {
console.log('Delete prize');
};
const handleAdd = () => {
console.log('Add prize');
};
const handleImageEdit = () => {
console.log('Edit prize image');
};
const handleFieldSelect = (fieldKey: string) => {
currentFieldKey.value = fieldKey;
const field = customFormFields.value.find(f => f.key === fieldKey);
if (field) {
currentFieldLabel.value = `Select ${field.label}`;
showFieldSheet.value = true;
}
};
const handleFieldValueSelect = (action: { value: string }) => {
const fieldIndex = customFormFields.value.findIndex(f => f.key === currentFieldKey.value);
if (fieldIndex !== -1) {
customFormFields.value[fieldIndex].selectedValue = action.value;
}
showFieldSheet.value = false;
};
const handleQuantityTypeChange = (type: 'per_person' | 'total') => {
console.log('Quantity type changed:', type);
};
</script>
# Custom Styling
<template>
<PressMatchPrizeSet
:data="prizeData"
custom-class="custom-prize-set"
/>
</template>
<style>
.custom-prize-set {
--pmps-card-bg: linear-gradient(135deg, #f0f8ff 0%, #e6f3ff 100%);
--pmps-title-text-color: #1890ff;
--pmps-image-size: 2rem;
}
</style>
# API
# Props
| Attribute | Description | Type | Default |
|---|---|---|---|
| data | Prize data | PrizeSetData | {} |
| prize-title | Prize title (displayed at top of component) | string | 'Prize Configuration' |
| show-title | Whether to show title | boolean | false |
| is-last-item | Whether it's the last item (controls "Add Prize" button display) | boolean | false |
| is-view-mode | Whether in view mode | boolean | false |
| custom-class | Custom style class name | string | '' |
| form-fields | Form field configuration | FormFieldConfig[] | Default config |
# Data Structure
interface PrizeSetData {
id?: string | number; // Unique prize identifier
title?: string; // Prize title (e.g., First Prize, Second Prize)
name: string; // Prize name
description: string; // Prize description
type?: string; // Prize type
image: string; // Prize image
quantity: number; // Quantity
quantityType?: 'per_person' | 'total'; // Quantity type
[key: string]: string | number | undefined; // Support dynamic fields
}
# FormFieldConfig Form Field Configuration
interface FormFieldConfig {
key: string; // Field key
label: string; // Field label
placeholder?: string; // Placeholder (input type only)
type?: 'input' | 'select'; // Field type
selectedValue?: string; // Selected value (select type only)
defaultText?: string; // Default text (select type only)
}
Default form field configuration:
[
{
key: 'name',
label: 'Prize Name',
placeholder: 'Enter prize name',
type: 'input',
},
{
key: 'description',
label: 'Prize',
placeholder: 'Enter prize description',
type: 'input',
},
{
key: 'type',
label: 'Prize Type',
type: 'select',
selectedValue: '',
defaultText: 'Please select',
},
]
# Events
| Event | Description | Parameters |
|---|---|---|
| change | Triggered when prize data changes | data: PrizeSetData - Complete data after change |
| update:data | v-model two-way binding event | data: PrizeSetData - Latest data |
| delete | Triggered when delete button is clicked | - |
| add | Triggered when add button is clicked | - |
| image-edit | Triggered when edit image is clicked | - |
| field-select | Triggered when selector field is clicked | fieldKey: string - Field key |
| quantity-type-change | Triggered when quantity type changes | type: 'per_person' | 'total' |
# Style Variables
The component provides the following CSS variables for custom styling.
| Name | Default Value | Description |
|---|---|---|
| --pmps-title-mb | $spacing-lg | Title bottom margin |
| --pmps-delete-mr | $spacing-sm | Delete button right margin |
| --pmps-content-padding-tb | $spacing-md | Content area vertical padding |
| --pmps-content-padding-lr | $spacing-lg | Content area horizontal padding |
| --pmps-editor-label-mr | $spacing-md | Editor label right margin |
| --pmps-form-gap | $spacing-sm | Form item gap |
| --pmps-editor-gap | $spacing-lg | Editor area gap |
| --pmps-editor-item-gap | .4rem | Editor item gap |
| --pmps-add-mt | $spacing-lg | Add button top margin |
| --pmps-add-gap | $spacing-sm | Add button internal gap |
| --pmps-editor-mt | $spacing-md | Editor top margin |
| --pmps-prize-set-mb | $spacing-lg | Component bottom margin |
| --pmps-editor-ctrls-icon-size | .4rem | Control icon size |
| --pmps-form-item-height | .9rem | Form item height |
| --pmps-form-label-width | 1.52rem | Form label width |
| --pmps-editor-item-height | .5rem | Editor item height |
| --pmps-image-size | 1.4rem | Image size |
| --pmps-image-edit-size | .4rem | Image edit icon size |
| --pmps-ctrls-icon-size | .4rem | Control button icon size |
| --pmps-tabs-item-width | 1rem | Tab item width |
| --pmps-tabs-item-height | .5rem | Tab item height |
| --pmps-card-bg | linear-gradient(...) | Card background gradient |
| --pmps-card-border-color | $color-surface-brand-light2 | Card border color |
| --pmps-title-text-color | $color-text-primary | Title text color |
| --pmps-primary-text-color | $color-text-primary | Primary text color |
| --pmps-secondary-text-color | $color-text-invert-default | Secondary text color |
| --pmps-image-edit-text-color | $color-text-invert-light | Image edit text color |
| --pmps-edit-overlay-bg | rgba(0, 0, 0, .41) | Edit overlay background |
| --pmps-tabs-border-color | $color-border-brand | Tab border color |
| --pmps-tabs-active-bg | $color-surface-brand | Tab active background |
| --pmps-tabs-text-color | $color-text-primary | Tab text color |
| --pmps-tabs-active-text-color | $color-text-invert-light | Tab active text color |
| --pmps-cell-border-color | $color-divider-light | Cell border color |
| --pmps-ctrls-text-color | $color-text-primary | Control button text color |
| --pmps-image-border-color | $color-divider-default | Image border color |
| --pmps-image-bg | $color-surface-secondary | Image background |
| --pmps-add-text-color | $color-text-primary | Add button text color |
| --pmps-add-icon-color | $color-text-primary | Add button icon color |
| --pmps-stepper-input-bg | rgba(#eceff2, .4) | Stepper input background |
| --pmps-title-font-size | $font-size-lg | Title font size |
| --pmps-label-font-size | $font-size-md | Label font size |
| --pmps-text-font-size | $font-size-md | Text font size |
| --pmps-icon-font-size | $font-size-md | IconPlus font size |
| --pmps-tabs-font-size | $font-size-sm | Tab font size |
| --pmps-add-font-size | $font-size-md | Add button font size |
| --pmps-image-border-radius | $border-radius-none | Image border radius |
| --pmps-content-border-radius | $border-radius-xs | Content area border radius |
# Notes
Data Binding:
- Component supports
v-model:datatwo-way binding - All field changes automatically trigger
changeandupdate:dataevents - External data changes are automatically synchronized to the component
- Component supports
View Mode:
- When
is-view-modeis set totrue, all editing functions are disabled - View mode automatically filters out fields with
type: 'select' - Input fields are displayed in read-only state
- When
Quantity Type:
per_person: Quantity per person can receivetotal: Total quantity share
Image Upload:
- Clicking the image edit button triggers the
image-editevent - Implement image upload logic and update the
data.imagefield yourself
- Clicking the image edit button triggers the
Form Field Configuration:
- Supports
inputandselectfield types selecttype fields should be used with thefield-selectevent- Use
selectedValueto implement selector value backfill - Support dynamic field extension via index signature
[key: string]
- Supports
Type Field:
- Component has built-in
typefield support - Configure prize type field as selector type via
formFields - Selector fields are automatically hidden in view mode
- Component has built-in
# FAQ
# How to customize form fields?
const customFormFields = ref<FormFieldConfig[]>([
{
key: 'name',
label: 'Prize Name',
placeholder: 'Enter prize name',
type: 'input',
},
{
key: 'level',
label: 'Prize Level',
type: 'select',
selectedValue: 'First Prize', // Set default selected value
defaultText: 'Select level',
},
]);
# How to handle selector fields?
const handleFieldSelect = (fieldKey: string) => {
// fieldKey is the key of the field, such as 'type', 'level'
// Display different selector options based on fieldKey
const field = customFormFields.value.find(f => f.key === fieldKey);
if (field) {
// Show corresponding selector (such as ActionSheet, Picker, etc.)
showFieldSheet.value = true;
}
};
// Selector callback
const handleFieldValueSelect = (value: string) => {
// Update the selected value of the corresponding field
const fieldIndex = customFormFields.value.findIndex(f => f.key === currentFieldKey.value);
if (fieldIndex !== -1) {
customFormFields.value[fieldIndex].selectedValue = value;
}
};
# How to customize card background?
.custom-prize-set {
--pmps-card-bg: linear-gradient(135deg, #f0f8ff 0%, #e6f3ff 100%);
}
# How to modify image size?
.custom-prize-set {
--pmps-image-size: 2rem;
}
# How to handle image upload?
const handleImageEdit = () => {
// Call image picker
uni.chooseImage({
count: 1,
success: (res) => {
// Upload image to server
uploadImage(res.tempFilePaths[0]).then((url) => {
prizeData.value.image = url;
});
}
});
};
# How to validate prize data?
const handleChange = () => {
if (!prizeData.value.name) {
uni.showToast({ title: 'Please enter prize name', icon: 'none' });
return;
}
if (!prizeData.value.description) {
uni.showToast({ title: 'Please enter prize description', icon: 'none' });
return;
}
if (prizeData.value.quantity < 1) {
uni.showToast({ title: 'Quantity cannot be less than 1', icon: 'none' });
return;
}
// Validation passed, save data
savePrizeData();
};