
需求描述
如果你开发商城类项目,必定会显示价格。如果想让价格显示具有突出且美观的效果,通常需要花费一些工夫调整样式。我在开发商城时,客户端和后台管理都需要展示价格,为了节省时间,我开发了一个价格组件,将价格显示的样式放到插件市场,导入后修改属性即可使用。

上面显示的价格样式,在之前的文章中讲过如何使用 CSS 完整显示价格尾数。下面我们就通过上面的需求,开发一款可以给 uniapp 项目使用的价格组件,介绍如何开发 DCloud 插件市场的插件。
一、新建 uni_modules 插件
- 在根目录下'uni_modules'单击右键,选择'新建 uni_modules 插件'。

- 为插件创建一个名称,前面最好加一个你的专属前缀,方便别人检索你的插件。然后,选择你开发的插件分类,本插件是通用组件,所以选择第一个。

- 插件目录结构如下:
- components 内的 xxm-price.vue 是你要写的组件
- changelog.md 是更新日志,提交时候会自动更新
- package.json 是插件的配置项,如插件说明及相关依赖等
- readme.md 是插件的使用说明,会在插件市场详情页展示的文档

二、开发插件
如上目录所示,核心代码是需要编写在 xxm-price.vue 文件中的。这是一个普通的 vue 文件,重点是需要接收父组件传递过来的属性,在当前组件中进行处理。
下面的代码是完整的实现逻辑,可以作为参考:
<script setup>
import { ref, computed } from 'vue';
const props = defineProps({
price: {
type: Number,
default: 999
},
symbol: {
type: String,
default: '¥'
},
size: {
type: Number,
default: 20
},
smallSize: {
type: Number,
default: undefined
},
color: {
type: String,
default: '#FF0F23'
},
origPrice: {
type: Number,
default: 0
},
origColor: {
type: String,
default: '#999'
},
origAlign: {
type: String,
default: 'right',
validator: (val) => ['left', 'right', 'top', 'bottom'].includes(val)
}
});
const comFontSize = computed(() => {
return props.smallSize ?? props.size * 0.618;
});
const flexDirection = computed(() => {
return ['left', 'right'].includes(props.origAlign) ? 'row' : 'column';
});
const alignItems = computed(() => {
return ['top', 'bottom'].includes(props.origAlign) ? 'flex-start' : 'flex-end';
});
const isOrigPriceFirst = computed(() => {
return ['top', 'left'].includes(props.origAlign);
});
function formatPrice(num, option = 0) {
if (typeof num !== 'number' || isNaN(num)) {
return '无效的数字,第一个参数必须是有效数字';
}
const baseValue = (num + Number.EPSILON) / 100;
if (option === 0) {
return Math.floor(baseValue);
} else if (option === 1) {
const fixedValue = Math.round(baseValue * 100) / 100;
const [, decimalPart = '00'] = fixedValue.toString().split('.');
return `.${decimalPart.padEnd(2, '0').slice(0, 2)}`;
} else {
return Math.round(baseValue * 100) / 100;
}
}
</script>
<template>
<view class="price-wrap" :style="{'--flex-direction': flexDirection,'--align-items': alignItems }">
<view class="price" :style="{ '--symbol': `'${symbol}'`, '--size': `${size}px`, '--color': `${color}`, '--com-size': `${comFontSize}px` }" :data-suffix="formatPrice(price, 1)">
{{ formatPrice(price, 0) }}
</view>
<view class="origPrice" v-if="origPrice > 0" :style="{ '--orig-color': origColor, '--com-size': `${comFontSize}px`, '--order': isOrigPriceFirst ? '-1' : 'initial' }">
{{ formatPrice(origPrice, -1) }}
</view>
</view>
</template>
<style lang="scss" scoped>
.price-wrap {
display: flex;
align-items: flex-start;
align-items: var(--align-items);
flex-direction: var(--flex-direction);
line-height: 1em;
gap: 5px;
.price {
color: var(--color);
font-size: var(--size);
font-weight: bold;
&::before {
font-size: var(--com-size);
content: var(--symbol);
}
&::after {
font-size: var(--com-size);
content: attr(data-suffix);
}
}
.origPrice {
text-decoration: line-through;
color: var(--orig-color);
font-size: var(--com-size);
order: var(--order);
}
}
</style>



