123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- <template>
- <view
- class="ui-tab"
- ref="tabRef"
- :id="'tab-' + vm.uid"
- :class="[
- props.ui,
- props.tpl,
- props.bg,
- props.align,
- { 'ui-tab-inline': props.inline },
- { 'ui-tab-scrolls': props.scroll },
- ]"
- >
- <block v-if="scroll">
- <view class="ui-tab-scroll-warp">
- <scroll-view
- scroll-x="true"
- class="ui-tab-scroll"
- :scroll-left="state.curValue > 1 ? state.tabNodeList[state.curValue - 1].left : 0"
- scroll-with-animation
- :style="{ width: `${state.content.width}px` }"
- >
- <view class="ss-flex ss-col-center">
- <su-tab-item
- v-for="(item, index) in props.tab"
- :data="item"
- :index="index"
- :key="index"
- @up="upitem"
- @tap.native="click(index, item)"
- ></su-tab-item>
- <view
- class="ui-tab-mark-warp"
- :class="[{ over: state.over }]"
- :style="[{ left: state.markLeft + 'px' }, { width: state.markWidth + 'px' }]"
- >
- <view
- class="ui-tab-mark"
- :class="[props.mark, { 'ui-btn': props.tpl == 'btn' || props.tpl == 'subtitle' }]"
- :style="[
- {
- background:
- props.tpl == 'btn' || props.tpl == 'subtitle' ? titleStyle.activeBg : 'none',
- },
- ]"
- ></view>
- </view>
- </view>
- </scroll-view>
- </view>
- </block>
- <block v-else>
- <su-tab-item
- v-for="(item, index) in props.tab"
- :data="item"
- :index="index"
- :key="index"
- @up="upitem"
- @tap.native="click(index, item)"
- ></su-tab-item>
- <view
- class="ui-tab-mark-warp"
- :class="[{ over: state.over }]"
- :style="[{ left: state.markLeft + 'px' }, { width: state.markWidth + 'px' }]"
- >
- <view
- class="ui-tab-mark"
- :class="[props.mark, { 'ui-btn': props.tpl == 'btn' || props.tpl == 'subtitle' }]"
- ></view>
- </view>
- </block>
- </view>
- </template>
- <script>
- export default {
- name: 'SuTab',
- };
- </script>
- <script setup>
- /**
- * 基础组件 - suTab
- */
- import {
- toRef,
- ref,
- reactive,
- unref,
- onMounted,
- nextTick,
- getCurrentInstance,
- provide,
- } from 'vue';
- const vm = getCurrentInstance();
- // 数据
- const state = reactive({
- curValue: 0,
- tabNodeList: [],
- scrollLeft: 0,
- markLeft: 0,
- markWidth: 0,
- content: {
- width: 100,
- },
- over: false,
- });
- const tabRef = ref(null);
- // 参数
- const props = defineProps({
- modelValue: {
- type: Number,
- default: 0,
- },
- ui: {
- type: String,
- default: '',
- },
- bg: {
- type: String,
- default: '',
- },
- tab: {
- type: Array,
- default() {
- return [];
- },
- },
- // line dot long,subtitle,trapezoid
- tpl: {
- type: String,
- default: 'line',
- },
- mark: {
- type: String,
- default: '',
- },
- align: {
- type: String,
- default: '',
- },
- curColor: {
- type: String,
- default: 'ui-TC',
- },
- defaultColor: {
- type: String,
- default: 'ui-TC',
- },
- scroll: {
- type: Boolean,
- default: false,
- },
- inline: {
- type: Boolean,
- default: false,
- },
- titleStyle: {
- type: Object,
- default: () => ({
- activeBg: '#DA2B10',
- activeColor: '#FEFEFE',
- color: '#D70000',
- }),
- },
- subtitleStyle: {
- type: Object,
- default: () => ({
- activeColor: '#333',
- color: '#C42222',
- }),
- },
- });
- const emits = defineEmits(['update:modelValue', 'change']);
- onMounted(() => {
- state.curValue = props.modelValue;
- setCurValue(props.modelValue);
- nextTick(() => {
- computedQuery();
- });
- uni.onWindowResize((res) => {
- computedQuery();
- });
- });
- const computedQuery = () => {
- uni.createSelectorQuery()
- .in(vm)
- .select('#tab-' + vm.uid)
- .boundingClientRect((data) => {
- if (data != null) {
- if (data.left == 0 && data.right == 0) {
- // setTimeout(() => {
- computedQuery();
- // }, 300);
- } else {
- state.content = data;
- setTimeout(() => {
- state.over = true;
- }, 300);
- }
- } else {
- console.log('tab-' + vm.uid + ' data error');
- }
- })
- .exec();
- };
- const setCurValue = (value) => {
- if (value == state.curValue) return;
- state.curValue = value;
- computedMark();
- };
- const click = (index, item) => {
- setCurValue(index);
- emits('update:modelValue', index);
- emits('change', {
- index: index,
- data: item,
- });
- };
- const upitem = (index, e) => {
- state.tabNodeList[index] = e;
- if (index == state.curValue) {
- computedMark();
- }
- };
- const computedMark = () => {
- if (state.tabNodeList.length == 0) return;
- let left = 0;
- let list = unref(state.tabNodeList);
- let cur = state.curValue;
- state.markLeft = list[cur].left - state.content.left;
- state.markWidth = list[cur].width;
- };
- const computedScroll = () => {
- if (state.curValue == 0 || state.curValue == state.tabNodeList.length - 1) {
- return false;
- }
- let i = 0;
- let left = 0;
- let list = state.tabNodeList;
- for (i in list) {
- if (i == state.curValue && i != 0) {
- left = left - list[i - 1].width;
- break;
- }
- left = left + list[i].width;
- }
- state.scrollLeft = left;
- };
- provide('suTabProvide', {
- props,
- curValue: toRef(state, 'curValue'),
- });
- </script>
- <style lang="scss">
- .ui-tab {
- position: relative;
- display: flex;
- height: 4em;
- align-items: center;
- &.ui-tab-scrolls {
- width: 100%;
- /* #ifdef MP-WEIXIN */
- padding-bottom: 10px;
- /* #endif */
- .ui-tab-scroll-warp {
- overflow: hidden;
- height: inherit;
- width: 100%;
- .ui-tab-scroll {
- position: relative;
- display: block;
- white-space: nowrap;
- overflow: auto;
- min-height: 4em;
- line-height: 4em;
- width: 100% !important;
- .ui-tab-mark-warp {
- display: flex;
- align-items: top;
- justify-content: center;
- .ui-tab-mark.ui-btn {
- /* #ifndef MP-WEIXIN */
- height: 2em;
- width: calc(100% - 0.6em);
- margin-top: 4px;
- /* #endif */
- /* #ifdef MP-WEIXIN */
- height: 2em;
- width: calc(100% - 0.6em);
- margin-top: 4px;
- /* #endif */
- }
- }
- }
- }
- }
- .ui-tab-mark-warp {
- color: inherit;
- position: absolute;
- top: 0;
- height: 100%;
- z-index: 0;
- &.over {
- transition: 0.3s;
- }
- .ui-tab-mark {
- color: var(--ui-BG-Main);
- height: 100%;
- }
- }
- &.line {
- .ui-tab-mark {
- border-bottom: 2px solid currentColor;
- }
- }
- &.topline {
- .ui-tab-mark {
- border-top: 2px solid currentColor;
- }
- }
- &.dot {
- .ui-tab-mark::after {
- content: '';
- width: 0.5em;
- height: 0.5em;
- background-color: currentColor;
- border-radius: 50%;
- display: block;
- position: absolute;
- bottom: 0.3em;
- left: 0;
- right: 0;
- margin: auto;
- }
- }
- &.long {
- .ui-tab-mark::after {
- content: '';
- width: 2em;
- height: 0.35em;
- background-color: currentColor;
- border-radius: 5em;
- display: block;
- position: absolute;
- bottom: 0.3em;
- left: 0;
- right: 0;
- margin: auto;
- }
- }
- &.trapezoid {
- .ui-tab-mark::after {
- content: '';
- width: calc(100% - 2em);
- height: 0.35em;
- background-color: currentColor;
- border-radius: 5em 5em 0 0;
- display: block;
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- margin: auto;
- }
- }
- &.btn {
- .ui-tab-mark-warp {
- display: flex;
- align-items: center;
- justify-content: center;
- .ui-tab-mark.ui-btn {
- height: calc(100% - 1.6em);
- width: calc(100% - 0.6em);
- }
- }
- &.sm .ui-tab-mark.ui-btn {
- height: calc(100% - 2px);
- width: calc(100% - 2px);
- border-radius: #{$radius - 2};
- }
- }
- &.subtitle {
- .ui-tab-mark-warp {
- display: flex;
- align-items: top;
- justify-content: center;
- padding-top: 0.6em;
- .ui-tab-mark.ui-btn {
- height: calc(100% - 2.8em);
- width: calc(100% - 0.6em);
- }
- }
- }
- &.ui-tab-inline {
- display: inline-flex;
- height: 3.5em;
- &.ui-tab-scrolls {
- .ui-tab-scroll {
- height: calc(3.5em + 17px);
- line-height: 3.5em;
- .ui-tab-mark-warp {
- height: 3.5em;
- }
- }
- }
- &.btn {
- .ui-tab-mark-warp {
- .ui-tab-mark.ui-btn {
- height: calc(100% - 10px);
- width: calc(100% - 10px);
- }
- }
- }
- }
- &.sm {
- height: 70rpx !important;
- &.ui-tab-inline {
- height: 70rpx;
- &.ui-tab-scrolls {
- .ui-tab-scroll {
- height: calc(70rpx + 17px);
- line-height: 70rpx;
- .ui-tab-mark-warp {
- height: 70rpx;
- }
- }
- }
- &.btn .ui-tab-mark.ui-btn {
- height: calc(100% - 2px);
- width: calc(100% - 2px);
- border-radius: #{$radius - 2};
- }
- }
- }
- }
- </style>
|