Skip to content

uniapp统一样式类名使用指南

一、 动态样式风格配置 (style)

应用主题通过在页面根元素上添加 style 计算属性实现,然后给对应的标签添加样式类名,即可应用动态的主题样式,目前支持n-cardn-btn-primaryn-btn-outline 等。

注: 必须在页面根元素上添加 style 才生效

使用方式

vue
<template>
    <view :style="appStyle"> 
        <!-- 页面内容 -->
        <view class="good-box n-card">
            <!-- 卡片内容 -->
             <view class="n-tag">标签1</view>
        </view>

        <view class="n-btn-primary">立即购买</view>
        <view class="n-btn-outline">加入购物车</view>
    </view>
</template>

<script setup>
import {nxhSDK} from "@/nxhsdk.module.min";

const store = nxhSDK.useSDKStore();
const appStyle = computed(() => store.state.appStyle);
</script>
<style lang="scss" scoped>
    .good-box {
        color: color(primary)
    }
</style>

1. 页面盒子样式 (n-page)

使用方式

html
<view class="n-page">
  <!-- 页面内容 -->
</view>

样式特点

  • 动态内边距:通过 --style-padding 变量控制,后台暂无配置处,现在默认是12px
  • 灰色背景:background-color: #F4F4F4

2. 卡片样式 (n-card)

使用方式

html
<view class="n-card">
  <!-- 卡片内容 -->
</view>

样式特点

  • 动态内边距:通过 --style-space 变量控制
  • 白色背景:background-color: #fff
  • 圆角边框:border-radius: 16rpx
  • 底部间距:margin-bottom: 16rpx
  • 标准字体大小:font-size: 28rpx

实际应用场景

在商品详情页中,n-card 被用于多个内容区块:

  • 商品基本信息区
  • 运费信息区
  • 服务信息区
  • 商品属性区
  • 售后信息区

3. 按钮样式 (n-btn-)

系统提供两种主要的按钮样式,可以根据应用主题自动适应颜色和圆角。

实心按钮 (n-btn-primary)

主要用于强调操作,如"立即购买"按钮。

html
<view class="n-btn-primary">立即购买</view>

样式特点

  • 动态圆角:通过 --style-radius 变量控制
  • 动态边框颜色:使用主题色 --style-color-primary
  • 动态背景颜色:使用主题色 --style-color-primary
  • 文字颜色:白色 #fff
  • 文字居中:text-align: center

空心按钮 (n-btn-outline)

次要操作按钮,如"加入购物车"按钮。

html
<view class="n-btn-outline">加入购物车</view>

样式特点

  • 动态圆角:与主按钮一致,通过 --style-radius 变量控制
  • 动态边框颜色:使用主题色 --style-color-primary
  • 动态文字颜色:使用主题色 --style-color-primary
  • 背景颜色:白色 #fff
  • 文字居中:text-align: center

4. 标签样式 (n-tag)

系统提供线框风格、填充风格两种样式,可以根据应用主题自动适应。

使用方式

html
<view class="n-tag">
  标签1
</view>

样式特点

4. 主题样式函数

系统提供了一系列 SCSS 函数和混合器,用于在样式中使用主题变量,保持样式的一致性和可维护性。

color() 函数

用于获取主题颜色变量,支持 primary 和 secondary 两种主色。

scss
// 使用方法
.my-element {
    color: color(primary);
    background-color: color(secondary);
}

// 编译后
.my-element {
    color: var(--style-color-primary);
    background-color: var(--style-color-secondary);
}

radius() 函数

用于获取主题圆角半径。

scss
// 使用方法
.my-element {
    border-radius: radius();
}

// 编译后
.my-element {
    border-radius: var(--style-radius);
}

space() 函数

用于获取主题间距变量。

scss
// 使用方法
.my-element {
    padding: space();
}

// 编译后
.my-element {
    padding: var(--style-space);
}

实际应用示例

scss
.price-tag {
    color: color(primary);
    border-radius: radius();
    padding: space();
}

.product-card {
    margin: space();
    background-color: #fff;
    border-radius: radius();
    
    .title {
        color: color(primary);
    }
}

二、 固定辅助样式类

系统还提供了一系列辅助样式类,方便布局和间距调整:

1. 内外边距类样式

  1. 边距尺寸变量
  • xs: 4px;
  • sm: 8px;
  • md: 12px;
  • lg: 16px;
  • xl: 20px;
  • xxl: 24px;
  1. 外边距(margin)样式
  • 左边距: .ml-xs, .ml-sm, .ml-md, .ml-lg, .ml-xl, .ml-xxl
  • 右边距: .mr-xs, .mr-sm, .mr-md, .mr-lg, .mr-xl, .mr-xxl
  • 上边距: .mt-xs, .mt-sm, .mt-md, .mt-lg, .mt-xl, .mt-xxl
  • 下边距: .mb-xs, .mb-sm, .mb-md, .mb-lg, .mb-xl, .mb-xxl
  • 水平边距: .mx-xs, .mx-sm, .mx-md, .mx-lg, .mx-xl, .mx-xxl
  • 垂直边距: .my-xs, .my-sm, .my-md, .my-lg, .my-xl, .my-xxl
  1. 内边距(padding)样式
  • 左内边距: .pl-xs, .pl-sm, .pl-md, .pl-lg, .pl-xl, .pl-xxl
  • 右内边距: .pr-xs, .pr-sm, .pr-md, .pr-lg, .pr-xl, .pr-xxl
  • 上内边距: .pt-xs, .pt-sm, .pt-md, .pt-lg, .pt-xl, .pt-xxl
  • 下内边距: .pb-xs, .pb-sm, .pb-md, .pb-lg, .pb-xl, .pb-xxl
  • 水平内边距: .px-xs, .px-sm, .px-md, .px-lg, .px-xl, .px-xxl
  • 垂直内边距: .py-xs, .py-sm, .py-md, .py-lg, .py-xl, .py-xxl

这些样式可以直接在模板中使用,例如:

html
<view class="ml-lg">左边距16px</view>
<view class="pt-md pb-md">上下内边距各12px</view>
<view class="mx-xl my-sm">水平边距20px,垂直边距8px</view>

2. 字体大小(font-size)样式

系统提供了一套统一的字体大小样式类,从最小的 .fs-xs 到最大的 .fs-xxl,方便在不同场景下使用合适的字号:

样式类名字体大小适用场景
.fs-xxl20px重要数据展示
.fs-xl18px次级标题、适用于价格、详情页商品名称
.fs-lg16px小标题、重要内容
.fs-md14px正文内容、按钮文字
.fs-sm12px适用于标注次级信息、辅助说明文字
.fs-xs10px用于角标、备注信息、小标签内文字

使用示例:

html
<view class="fs-xxl">页面大标题 (20px)</view>
<view class="fs-xl">次级标题 (18px)</view>
<view class="fs-lg">小标题 (16px)</view>
<view class="fs-md">正文内容 (14px)</view>
<view class="fs-sm">辅助说明 (12px)</view>
<view class="fs-xs">备注信息 (10px)</view>

通过使用这些预定义的字体大小类,可以保持整个应用的字体大小一致性,提升用户体验。

三、 代码示例(商品详情页)

示例代码如下:

vue
<template>
    <div class="detail-wrapper" :style="appStyle">
        <scroll-view scroll-y="true" class="wrapper">
            <swiper class="my-swipe" :indicator-dots="objGoods.album.length > 1" :autoplay="true" :interval="3000" circular>
                <swiper-item class="my-swipe-item" v-for="(image, index) in objGoods.album" :key="index">
                    <img :src="image.file_url" alt="" class="my-swipe-img" @click="methods.imagePreview(objGoods.album,index)"/>
                </swiper-item>
            </swiper>
            <view class="my-swipe" v-if="data.displayWay === 1">
                <view class="my-swipe-item">
                    <img :src="objGoods.mobile_thumb" alt="" class="my-swipe-img"/>
                </view>
            </view>
            <view class="card-list n-page">
                <view class="goods-base-info n-card">
                    <view class="goods-title">
                        <text class="title">{{ showName ? objGoods.goods_name : '' }}</text>
                    </view>
                    <view class="goods-price">
                        <n-price v-if="data.showPrice" :price="objGoods.goods_amount" :color="styleJson.color1"/>
                        <text class="original-price" v-if="data.showOriginPrice && objGoods.market_amount">原价:{{ objGoods.market_amount  }}元</text>
                    </view>
                </view>
                <view class="goods-block n-card" v-if="data.showFreight">
                    <view class="left-info">
                        <text class="label">运费</text>
                        <text class="value">¥{{ objGoods.delivery_amount }}</text>
                    </view>
                    <view class="right-info shippingfee" v-if="objGoods.attr && objGoods.attr.length && objGoods.attr[0].stock">剩余{{ objGoods.attr[0].stock }}件</view>
                </view>
                <view class="goods-block n-card" v-if="data.showService">
                    <view class="left-info">
                        <text class="label">服务</text>
                        <text class="value">快递发货 · 收货后结算</text>
                    </view>
                    <view class="right-info"><uni-icons type="right" size="16"></uni-icons></view>
                </view>
                <view class="goods-block multi-block n-card" v-if="data.showAttributes && objGoods.type?.length">
                    <view class="left-info" v-for="item in objGoods.type" :key="item.attr_value">
                        <text class="label" :class="{'label-multi':item.attr_name&&item.attr_name.length>=4}">{{ item.attr_name }}</text>
                        <text class="value">{{ item.attr_value }}</text>
                    </view>
                </view>
                <view class="goods-block n-card" v-if="data.showAfterSale">
                    <view class="left-info">
                        <text class="label">售后</text>
                        <text class="value">本商品由旗舰店销售,以订单...</text>
                    </view>
                    <view class="right-info"><uni-icons type="right" size="16"></uni-icons></view>
                </view>
            </view>
            <view class="goods-detail" v-if="data.showDetail">
                <view class="detail-content">
                    <rich-text :nodes="objGoods.context"></rich-text>
                </view>
            </view>
        </scroll-view>
        <view class="goods-btn-box">
            <view class="goods-btn-mini">
                <view class="btn-mini" @click="methods.showToast">
                    <image mode="widthFix" :src="`${cdnEnvUrl}images/user/car_v2.png`" class="icon-share"></image>
                    <text class="text">购物车</text>
                    <div class="num">1</div>
                </view>
            </view>
            <view class="goods-btn-large">
                <view class="btn-large n-btn-outline" @click="methods.showToast">加入购物车</view>
                <view class="btn-large n-btn-primary ml-sm" @click="methods.toBuy">立即购买</view>
            </view>
        </view>
    </div>
</template>
<script setup>
import { computed, reactive, ref } from 'vue'
import {nxhSDK, nxhSDKLib} from "@/nxhsdk.module.min";
import { onLoad } from "@dcloudio/uni-app";

const id = ref(''); // 商品id
const data = reactive({
    imgList: [],
    imageH: '100vw',
    goodsName: '',
    displayWay: 0,
    imgRatio: 0,
    showName: true,
    showPrice: true,
    showOriginPrice: true,
    showSoldOut: false,
    showPreSale: false,
    showFreight: true,
    showService: false,
    showAfterSale: false,
    showReviews: false,
    showStoreInfo: false,
    showOfflineStore: false,
    showDetail: true,
    showMoreGoods: false,
    content: '',
    padding: 10,
    gutter: 10,
    imageRatio: 1,
    listStyle: 0,
    showAttributes:true,
})
const cdnEnvUrl = ref(uni.cdnEnvUrl);
const methods = reactive({
    imagePreview: (imgList, index) => {
        let urls = imgList.map(item => item.file_url);
        uni.previewImage({
            urls,
            current: index
        });
    },
    showToast: () => {
        uni.showToast({
            title: '敬请期待',icon: 'none'
        })
    },
    toBuy: () => {
        uni.navigateTo({
            url: `/shop/order/confirm?id=${id.value}`
        })
    }
})
const store = nxhSDK.useSDKStore();
const appStyle = computed(() => store.state.appStyle);
const styleJson = computed(() => store.getters['getStyle']);
const { imageH, showName, showMoreGoods } = data
console.log(imageH,'imageH')

let objGoods = ref({})
const getGoodsDetail = async (id) => {
    try {
        uni.showLoading({
            title: '加载中',
            mask: true
        })
        const { result } = await nxhSDK.getData("shop_info_detail", { info_id: id})
        objGoods.value = {
            ...result,
            context: decodeURIComponent(result.context)
        };
        // 设置状态栏标题
        uni.setNavigationBarTitle({
            title: objGoods.value.goods_name || '商品详情'
        })
        console.warn('objGoods', objGoods.value);
    } catch(err) {
        console.error(err)
    } finally {
        uni.hideLoading()
    }
}
onLoad((query)=>{
    id.value = query.id
    nxhSDKLib.$init(async () => {
        getGoodsDetail(query.id)
    })
})

</script>
<style lang="scss" scoped>
.detail-wrapper{
    width: 100%;
    height: 100vh;
    .wrapper {
        position: relative;
        height: calc(100vh - 66px);
        background: #F4F4F4;
        max-width: 750px;
        padding-bottom: 66px;

        .my-swipe {
            // height: 417px;
            height: v-bind(imageH);

            .my-swipe-item {
                text-align: center;

                .my-swipe-img {
                    width: 100%;
                    height: 100%;
                    height: v-bind(imageH);
                    object-fit: contain;
                }
            }

            .custom-indicator {
                position: absolute;
                right: 5px;
                bottom: 5px;
                padding: 2px 5px;
                font-size: 12px;
                background: rgba(255,255,255,0.1);
                border-radius: 38px;
            }
        }

        .card-list {
            display: flex;
            flex-direction: column;
            padding: 10px;
            background-color: transparent;
            margin-bottom: 0;
        }

        .goods-base-info {
            display: flex;
            flex-direction: column;
            padding: 12px 0;
            background-color: #fff;
            border-radius: 8px;

            .goods-title {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 8px;

                .title {
                    font-size: 16px;
                    font-weight: 600;
                }

                .btn-share {
                    font-size: 12px;
                    padding: 2px 8px;
                    background-color: color(secondary);
                    color: color(primary);
                    border-radius: 10px 0 0 10px;
                }
            }

            .goods-price {
                display: flex;
                justify-content: space-between;
                align-items: baseline;

                .original-price {
                    text-decoration: line-through;
                    font-size: 14px;
                    color: #7C8FAC;
                }
            }
        }

        .goods-block {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 14px 16px;
            // margin-top: 8px;
            font-size: 14px;
            background-color: #fff;
            border-radius: 8px;

            .left-info {
                display: flex;
                .label {
                    display: inline-block;
                    width: 116rpx;
                    margin-right: 12px;
                    color: #969799;
                    text-align-last: justify;
                }
                .label-multi{
                    text-align-last:start;
                }

                .value {
                    color: #323233;
                }
            }

            .right-info {
                color: #969799;
            }
            .shippingfee{
                color: #7C8FAC;
            }

            &.multi-block {
                flex-direction: column;
                align-items: flex-start;

                .left-info:not(:last-of-type) {
                    margin-bottom: 8px;
                }
            }
        }

        .store-info-box {
            display: flex;
            padding: 14px 16px;
            margin-top: 8px;
            background-color: #fff;

            .store-logo {
                width: 56px;
                height: 56px;

                img {
                    width: 100%;
                    height: 100%;
                    object-fit: cover;
                }
            }

            .store-info {
                display: flex;
                align-items: flex-start;
                justify-content: space-between;
                flex: 1;
                margin-left: 16px;

                .store-name {
                    font-size: 16px;
                    font-weight: 600;
                }
                .store-enter {
                    padding: 4px 8px;
                    font-size: 13px;
                    text-align: center;
                    color: color(primary);
                    border: 1px solid color(primary);
                    border-radius: 4px;
                }
            }
        }

        .goods-detail {
            .detail-title {
                padding: 16px;
                font-size: 16px;
                font-weight: 600;
            }

            .detail-content {
                padding: 0;

                img {
                    width: 100% !important;
                }
            }
        }

        .goods-more {
            .more-title {
                padding: 16px 16px 0;
                font-size: 16px;
                font-weight: 600;
            }
        }


    }
    .goods-btn-box {
        // position: absolute;
        position: fixed;
        display: flex;
        align-items: center;
        justify-content: space-between;
        width: 100%;
        max-width: 750px;
        padding: 24rpx 20rpx 32rpx 30rpx;
        bottom: 0;
        background-color: #fff;
        box-sizing: border-box;
        z-index: 999;

        .goods-btn-mini {
            display: grid;
            grid-template-columns: 1fr;

            .btn-mini {
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                width: 42px;
                position: relative;
                .num{
                    position: absolute;
                    top: -20rpx;
                    right: 6rpx;
                    border-radius: 50%;
                    width: 28rpx;
                    height: 28rpx;
                    background-color: color(primary);
                    border: 2rpx solid #FFFFFF;
                    color: #FFFFFF;
                    font-size: 16rpx;
                    text-align: center;
                    line-height: 28rpx;
                }
                image{
                    width: 29rpx;
                    height: 26rpx;
                    margin-bottom: 8rpx;
                }

                .text {
                    color: rgba(42, 53, 71, 1);
                    font-size: 11px;
                }
            }
        }

        .goods-btn-large {
            display: flex;
            color: #fff;

            .btn-large {
                width: 286rpx;
                height: 76rpx;
                line-height: 76rpx;


            }
        }
    }
}
</style>