# MatchPrizeSet 奖品设置组件
奖品设置组件,支持奖品信息配置、图片上传、数量设置等功能。支持编辑模式和查看模式,提供灵活的表单字段配置,可用于比赛奖品管理场景。适用于多奖品列表展示,支持动态添加、删除奖品。
# 引入
import PressMatchPrizeSet from 'press-next/press-match-prize-set/press-match-prize-set.vue';
# 代码演示
# 基础用法
基础用法展示了组件的基本功能,包括奖品信息展示、编辑和操作。
<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: '奖项配置',
name: '王者荣耀皮肤',
description: '限定皮肤礼包',
type: '实物奖品',
image: 'https://placehold.co/74x74',
quantity: 10,
quantityType: 'per_person',
});
const handleChange = (data: PrizeSetData) => {
console.log('奖品数据变更:', data);
};
const handleDelete = () => {
console.log('删除奖品');
};
const handleAdd = () => {
console.log('添加奖品');
};
</script>
# 自定义表单字段
通过 formFields 属性可以自定义表单字段的配置,支持输入框和选择器两种类型。
<template>
<PressMatchPrizeSet
:data="prizeData"
:form-fields="customFormFields"
:is-view-mode="false"
@change="handleChange"
@field-select="handleFieldSelect"
/>
<!-- 字段选择器 -->
<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: '一等奖',
name: '游戏皮肤',
description: '限定皮肤礼包',
image: 'https://placehold.co/74x74',
quantity: 5,
quantityType: 'total'
});
// 自定义表单字段配置
const customFormFields = ref<FormFieldConfig[]>([
{
key: 'name',
label: '奖项名称',
placeholder: '请输入奖项名称',
type: 'input',
},
{
key: 'description',
label: '奖品名称',
placeholder: '请输入奖品名称',
type: 'input',
},
{
key: 'type',
label: '奖品类型',
type: 'select',
selectedValue: '实物奖品', // 回选值
defaultText: '请选择',
},
{
key: 'level',
label: '奖品等级',
type: 'select',
selectedValue: '',
defaultText: '请选择等级',
},
]);
const showFieldSheet = ref(false);
const currentFieldKey = ref('');
const currentFieldLabel = ref('');
// 字段选择器选项
const fieldOptionsMap: Record<string, { name: string; value: string }[]> = {
type: [
{ name: '实物奖品', value: '实物奖品' },
{ name: '虚拟奖品', value: '虚拟奖品' },
{ name: '现金红包', value: '现金红包' },
],
level: [
{ name: '一等奖', value: '一等奖' },
{ name: '二等奖', value: '二等奖' },
{ name: '三等奖', value: '三等奖' },
],
};
const fieldActions = computed(() => fieldOptionsMap[currentFieldKey.value] || []);
const handleChange = () => {
console.log('奖品数据变更');
};
const handleFieldSelect = (fieldKey: string) => {
currentFieldKey.value = fieldKey;
const field = customFormFields.value.find(f => f.key === fieldKey);
if (field) {
currentFieldLabel.value = `选择${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>
# 多奖品列表
支持多个奖品的统一管理,只在第一个组件显示标题,只在最后一个组件显示"添加奖品"按钮。
<template>
<div class="prize-list">
<PressMatchPrizeSet
v-for="(prize, index) in prizeList"
:key="prize.id"
v-model:data="prizeList[index]"
:prize-title="index === 0 ? '奖品设置' : ''"
: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: '一等奖',
name: '王者荣耀限定皮肤',
description: '赛季限定皮肤礼包',
type: '实物奖品',
image: 'https://placehold.co/74x74',
quantity: 5,
quantityType: 'total',
},
{
id: 2,
title: '二等奖',
name: '游戏点券',
description: '500点券',
type: '虚拟奖品',
image: 'https://placehold.co/74x74',
quantity: 10,
quantityType: 'total',
},
]);
const handleChange = (index: number, data: PrizeSetData) => {
console.log(`奖品 ${index + 1} 数据变更:`, 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 = ['一等奖', '二等奖', '三等奖', '四等奖', '五等奖'];
const title = prizeNames[prizeList.value.length] || `奖项${prizeList.value.length + 1}`;
prizeList.value.push({
id: newId,
title,
name: '',
description: '',
type: '',
image: 'https://placehold.co/74x74',
quantity: 1,
quantityType: 'per_person',
});
};
</script>
# 查看模式
查看模式下,所有编辑功能被禁用,选择器类型字段自动隐藏,仅显示只读的数据内容。
<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: '一等奖',
name: '王者荣耀限定皮肤',
description: '赛季限定皮肤礼包 + 专属头像框',
type: '实物奖品',
image: 'https://placehold.co/74x74',
quantity: 5,
quantityType: 'total',
});
</script>
# 完整功能示例
<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"
/>
<!-- 字段选择器 -->
<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: '一等奖',
name: '游戏皮肤',
description: '限定皮肤礼包',
image: 'https://placehold.co/74x74',
quantity: 5,
quantityType: 'total'
});
const customFormFields = ref<FormFieldConfig[]>([
{ key: 'name', label: '奖项名称', type: 'input', placeholder: '请输入奖项名称' },
{ key: 'description', label: '奖品名称', type: 'input', placeholder: '请输入奖品名称' },
{ key: 'type', label: '奖品类型', type: 'select', selectedValue: '实物奖品' },
{ key: 'level', label: '奖品等级', type: 'select', selectedValue: '' },
]);
const showFieldSheet = ref(false);
const currentFieldKey = ref('');
const currentFieldLabel = ref('');
const fieldOptionsMap: Record<string, { name: string; value: string }[]> = {
type: [
{ name: '实物奖品', value: '实物奖品' },
{ name: '虚拟奖品', value: '虚拟奖品' },
],
level: [
{ name: '一等奖', value: '一等奖' },
{ name: '二等奖', value: '二等奖' },
],
};
const fieldActions = computed(() => fieldOptionsMap[currentFieldKey.value] || []);
const handleChange = () => {
console.log('奖品数据变更');
};
const handleDelete = () => {
console.log('删除奖品');
};
const handleAdd = () => {
console.log('添加奖品');
};
const handleImageEdit = () => {
console.log('编辑奖品图片');
};
const handleFieldSelect = (fieldKey: string) => {
currentFieldKey.value = fieldKey;
const field = customFormFields.value.find(f => f.key === fieldKey);
if (field) {
currentFieldLabel.value = `选择${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('数量类型变更:', type);
};
</script>
# 自定义样式
<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
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| data | 奖品数据 | PrizeSetData | {} |
| prize-title | 奖品标题(显示在组件顶部) | string | '奖品配置' |
| show-title | 是否显示标题 | boolean | false |
| is-last-item | 是否为最后一项(控制"添加奖品"按钮显示) | boolean | false |
| is-view-mode | 是否为查看模式 | boolean | false |
| custom-class | 自定义样式类名 | string | '' |
| form-fields | 表单字段配置 | FormFieldConfig[] | 默认配置 |
# Data 数据结构
interface PrizeSetData {
id?: string | number; // 奖品唯一标识
title?: string; // 奖项标题(如:一等奖、二等奖)
name: string; // 奖项名称
description: string; // 奖品描述
type?: string; // 奖品类型
image: string; // 奖品图片
quantity: number; // 数量
quantityType?: 'per_person' | 'total'; // 数量类型
[key: string]: string | number | undefined; // 支持动态扩展字段
}
# FormFieldConfig 表单字段配置
interface FormFieldConfig {
key: string; // 字段键名
label: string; // 字段标签
placeholder?: string; // 占位符(仅input类型)
type?: 'input' | 'select'; // 字段类型
selectedValue?: string; // 选择器回选值(仅select类型)
defaultText?: string; // 选择器默认文本(仅select类型)
}
默认表单字段配置:
[
{
key: 'name',
label: '奖项名称',
placeholder: '请输入奖项名称',
type: 'input',
},
{
key: 'description',
label: '奖品',
placeholder: '请输入奖品名称',
type: 'input',
},
{
key: 'type',
label: '奖品类型',
type: 'select',
selectedValue: '',
defaultText: '请选择',
},
]
# Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| change | 奖品数据变更时触发 | data: PrizeSetData - 变更后的完整数据 |
| update:data | v-model 双向绑定事件 | data: PrizeSetData - 最新数据 |
| delete | 点击删除按钮时触发 | - |
| add | 点击添加按钮时触发 | - |
| image-edit | 点击编辑图片时触发 | - |
| field-select | 点击选择器字段时触发 | fieldKey: string - 字段键名 |
| quantity-type-change | 数量类型变更时触发 | type: 'per_person' | 'total' |
# 样式变量
组件提供了下列 CSS 变量,可用于自定义样式。
| 名称 | 默认值 | 说明 |
|---|---|---|
| --pmps-title-mb | $spacing-lg | 标题下边距 |
| --pmps-delete-mr | $spacing-sm | 删除按钮右边距 |
| --pmps-content-padding-tb | $spacing-md | 内容区域上下内边距 |
| --pmps-content-padding-lr | $spacing-lg | 内容区域左右内边距 |
| --pmps-editor-label-mr | $spacing-md | 编辑器标签右边距 |
| --pmps-form-gap | $spacing-sm | 表单项间距 |
| --pmps-editor-gap | $spacing-lg | 编辑器区域间距 |
| --pmps-editor-item-gap | .4rem | 编辑器项间距 |
| --pmps-add-mt | $spacing-lg | 添加按钮上边距 |
| --pmps-add-gap | $spacing-sm | 添加按钮内部间距 |
| --pmps-editor-mt | $spacing-md | 编辑器上边距 |
| --pmps-prize-set-mb | $spacing-lg | 组件下边距 |
| --pmps-editor-ctrls-icon-size | .4rem | 操作图标尺寸 |
| --pmps-form-item-height | .9rem | 表单项高度 |
| --pmps-form-label-width | 1.52rem | 表单标签宽度 |
| --pmps-editor-item-height | .5rem | 编辑器项高度 |
| --pmps-image-size | 1.4rem | 图片尺寸 |
| --pmps-image-edit-size | .4rem | 图片编辑图标尺寸 |
| --pmps-tabs-item-width | 1rem | 标签页项宽度 |
| --pmps-tabs-item-height | .5rem | 标签页项高度 |
| --pmps-placeholder-width | 3.6rem | 占位符宽度 |
| --pmps-card-bg | linear-gradient(...) | 卡片背景渐变 |
| --pmps-card-border-color | $color-surface-brand-light2 | 卡片边框颜色 |
| --pmps-title-text-color | $color-text-primary | 标题文字颜色 |
| --pmps-primary-text-color | $color-text-primary | 主要文字颜色 |
| --pmps-secondary-text-color | $color-text-invert-default | 次要文字颜色 |
| --pmps-placeholder-text-color | $color-text-primary | 占位符文字颜色 |
| --pmps-add-text-color | $color-text-primary | 添加按钮文字颜色 |
| --pmps-add-icon-color | $color-text-primary | 添加按钮图标颜色 |
| --pmps-image-edit-text-color | $color-text-invert-light | 图片编辑文字颜色 |
| --pmps-edit-overlay-bg | $color-surface-dark | 编辑遮罩背景 |
| --pmps-cell-border-color | $color-divider-light | 单元格边框颜色 |
| --pmps-image-bg | $color-surface-secondary | 图片背景 |
| --pmps-image-border-color | $color-divider-default | 图片边框颜色 |
| --pmps-tabs-border-color | $color-border-brand | 标签页边框颜色 |
| --pmps-tabs-active-bg | $color-surface-brand | 标签页激活背景 |
| --pmps-tabs-text-color | $color-text-primary | 标签页文字颜色 |
| --pmps-tabs-active-text-color | $color-text-invert-light | 标签页激活文字颜色 |
| --pmps-cell-border-color | $color-divider-light | 单元格边框颜色 |
| --pmps-ctrls-text-color | $color-icon-default | 控制按钮文字颜色 |
| --pmps-image-bg | $color-surface-secondary | 图片背景 |
| --pmps-image-border-color | $color-divider-default | 图片边框颜色 |
| --pmps-add-text-color | $color-text-primary | 添加按钮文字颜色 |
| --pmps-add-icon-color | $color-text-primary | 添加按钮图标颜色 |
| --pmps-stepper-input-bg | rgba(#eceff2, .4) | 步进器输入框背景 |
| --pmps-input-text-color | $color-text-primary | 输入框文字颜色 |
| --pmps-input-placeholder-color | $color-text-invert-default | 输入框占位符颜色 |
| --pmps-title-font-size | $font-size-lg | 标题字体大小 |
| --pmps-label-font-size | $font-size-md | 标签字体大小 |
| --pmps-text-font-size | $font-size-md | 文本字体大小 |
| --pmps-placeholder-font-size | $font-size-md | 占位符字体大小 |
| --pmps-add-font-size | $font-size-md | 添加按钮字体大小 |
| --pmps-input-font-size | $font-size-md | 输入框字体大小 |
| --pmps-icon-font-size | $font-size-md | 图标字体大小 |
| --pmps-tabs-font-size | $font-size-sm | 标签页字体大小 |
| --pmps-content-border-radius | $border-radius-xs | 内容区域圆角 |
| --pmps-image-border-radius | $border-radius-none | 图片圆角 |
# 注意事项
数据绑定:
- 组件支持
v-model:data双向绑定 - 所有字段变更会自动触发
change和update:data事件 - 外部数据变化会自动同步到组件内部
- 组件支持
查看模式:
- 设置
is-view-mode为true时,所有编辑功能将被禁用 - 查看模式下会自动过滤掉
type: 'select'的字段 - 输入框字段显示为只读状态
- 设置
数量类型:
per_person:每人可获得的数量total:总共的数量份额
图片上传:
- 点击图片编辑按钮会触发
image-edit事件 - 需自行实现图片上传逻辑和更新
data.image字段
- 点击图片编辑按钮会触发
表单字段配置:
- 支持
input和select两种字段类型 select类型字段需配合field-select事件使用- 通过
selectedValue实现选择器回选功能 - 可通过索引签名
[key: string]支持动态扩展字段
- 支持
类型字段:
- 组件已内置
type字段支持 - 可通过
formFields配置选择器类型的奖品类型字段 - 查看模式下选择器字段会自动隐藏
- 组件已内置
# 常见问题
# 如何自定义表单字段?
const customFormFields = ref<FormFieldConfig[]>([
{
key: 'name',
label: '奖项名称',
placeholder: '请输入奖项名称',
type: 'input',
},
{
key: 'level',
label: '奖品等级',
type: 'select',
selectedValue: '一等奖', // 设置默认回选值
defaultText: '请选择等级',
},
]);
# 如何处理选择器字段?
const handleFieldSelect = (fieldKey: string) => {
// fieldKey 为字段的 key 值,如 'type', 'level'
// 可根据 fieldKey 显示不同的选择器选项
const field = customFormFields.value.find(f => f.key === fieldKey);
if (field) {
// 显示对应的选择器(如 ActionSheet、Picker 等)
showFieldSheet.value = true;
}
};
// 选择器回调
const handleFieldValueSelect = (value: string) => {
// 更新对应字段的回选值
const fieldIndex = customFormFields.value.findIndex(f => f.key === currentFieldKey.value);
if (fieldIndex !== -1) {
customFormFields.value[fieldIndex].selectedValue = value;
}
};
# 如何自定义卡片背景?
.custom-prize-set {
--pmps-card-bg: linear-gradient(135deg, #f0f8ff 0%, #e6f3ff 100%);
}
# 如何修改图片尺寸?
.custom-prize-set {
--pmps-image-size: 2rem;
}
# 如何处理图片上传?
const handleImageEdit = () => {
// 调用图片选择器
uni.chooseImage({
count: 1,
success: (res) => {
// 上传图片到服务器
uploadImage(res.tempFilePaths[0]).then((url) => {
prizeData.value.image = url;
});
}
});
};
# 如何验证奖品数据?
const handleChange = () => {
if (!prizeData.value.name) {
uni.showToast({ title: '请输入奖项名称', icon: 'none' });
return;
}
if (!prizeData.value.description) {
uni.showToast({ title: '请输入奖品名称', icon: 'none' });
return;
}
if (prizeData.value.quantity < 1) {
uni.showToast({ title: '数量不能小于1', icon: 'none' });
return;
}
// 验证通过,保存数据
savePrizeData();
};