Files
mini-yu/uni_modules/tdesign-uniapp/components/action-sheet/action-sheet.vue
lingxiao865 c5af079d8c first commit
2026-02-10 08:05:03 +08:00

351 lines
10 KiB
Vue

<template>
<view
:id="classPrefix"
:style="tools._style([rootCustomStyle])"
:class="tools.cls(classPrefix, [dataAlign, dataTheme, ['no-description', !description]]) + ' ' + tClass"
>
<t-popup
:visible="dataVisible"
placement="bottom"
:using-custom-navbar="dataUsingCustomNavbar"
:custom-navbar-height="customNavbarHeight"
:show-overlay="dataShowOverlay"
:z-index="(dataPopupProps && dataPopupProps.zIndex) || defaultPopUpzIndex"
:overlay-props="(dataPopupProps && dataPopupProps.overlayProps) || defaultPopUpProps"
@visible-change="onPopupVisibleChange"
>
<view
:class="classPrefix + '__content ' + tClassContent"
tabindex="0"
>
<view
v-if="dataDescription"
tabindex="0"
:class="classPrefix + '__description'"
>
{{ dataDescription }}
</view>
<block v-if="gridThemeItems.length">
<block v-if="gridThemeItems.length === 1">
<t-grid
align="center"
:t-class="classPrefix + '__grid'"
:column="dataCount / 2"
:class="classPrefix + '__single-wrap'"
>
<t-grid-item
v-for="(item, index) in gridThemeItems[0]"
:key="index"
:t-class="classPrefix + '__grid-item ' + classPrefix + '__square'"
:data-index="index"
:icon="{ name: item.icon, color: item.color }"
:text="item.label || ''"
:description="item.description || ''"
:image="item.image || ''"
:style="'--td-grid-item-text-color: ' + item.color"
@click="onSelect($event, { index })"
/>
</t-grid>
</block>
<block v-else-if="gridThemeItems.length > 1">
<view :class="classPrefix + '__swiper-wrap'">
<swiper
:style="heightStyle"
:autoplay="false"
:current="currentSwiperIndex"
@change="onSwiperChange"
>
<swiper-item
v-for="(item, index) in gridThemeItems"
:key="index"
>
<t-grid
align="center"
:t-class="classPrefix + '__grid ' + classPrefix + '__grid--swiper'"
:column="dataCount / 2"
:custom-style="gridStyle"
>
<t-grid-item
v-for="(item, index1) in item"
:key="index1"
:t-class="classPrefix + '__grid-item'"
:class="classPrefix + '__square'"
:data-index="index"
:icon="{ name: item.icon, color: item.color }"
:text="item.label || ''"
:description="item.description || ''"
:image="item.image || ''"
:style="'--td-grid-item-text-color: ' + item.color"
@click="onSelect($event, { index })"
/>
</t-grid>
</swiper-item>
</swiper>
<view :class="classPrefix + '__nav'">
<view :class="classPrefix + '__dots'">
<view
v-for="(item, index) in gridThemeItems.length"
:key="index"
:class="classPrefix + '__dots-item ' + (index === currentSwiperIndex ? prefix + '-is-active' : '')"
/>
</view>
</view>
</view>
</block>
</block>
<view
v-else-if="dataItems && dataItems.length"
:class="classPrefix + '__list'"
>
<block
v-for="(item, index) in dataItems"
:key="index"
>
<view
:data-index="index"
:style="item.color ? 'color: ' + item.color : ''"
:class="tools.cls(classPrefix + '__list-item', [['disabled', item.disabled]])"
:aria-role="ariaRole || 'button'"
:aria-label="item.label || item"
tabindex="0"
@click="() => onSelect(item, { index })"
>
<view :class="classPrefix + '__list-item-content'">
<t-icon
v-if="item.icon"
:name="item.icon"
:t-class="classPrefix + '__list-item-icon'"
:custom-style="iconCustomStyle"
/>
<view :class="classPrefix + '__list-item-text'">
{{ item.label || item }}
</view>
<t-icon
v-if="item.suffixIcon"
:name="item.suffixIcon"
:t-class="classPrefix + '__list-item-icon ' + classPrefix + '__list-item-icon--suffix'"
style="margin-left: auto;"
:custom-style="suffixIconCustomStyle"
/>
</view>
<view
v-if="item.description"
:class="classPrefix + '__list-item-desc'"
>
{{ item.description }}
</view>
</view>
</block>
</view>
</view>
<slot />
<view
v-if="dataShowCancel"
:class="classPrefix + '__footer'"
>
<view :class="classPrefix + '__gap-' + dataTheme" />
<view
:class="classPrefix + '__cancel ' + tClassCancel"
:hover-class="classPrefix + '__cancel--hover'"
hover-stay-time="70"
aria-role="button"
@click="onCancel"
>
{{ dataCancelText || '取消' }}
</view>
</view>
</t-popup>
</view>
</template>
<script>
import TIcon from '../icon/icon';
import TPopup from '../popup/popup';
import TGrid from '../grid/grid';
import TGridItem from '../grid-item/grid-item';
import { chunk } from '../common/utils';
import { uniComponent } from '../common/src/index';
import { prefix } from '../common/config';
import { actionSheetTheme } from './show';
import props from './props';
import useCustomNavbar from '../mixins/using-custom-navbar';
import tools from '../common/utils.wxs';
import { getFunctionalMixin } from '../common/functional/mixin';
const name = `${prefix}-action-sheet`;
export default uniComponent({
name,
options: {
styleIsolation: 'shared',
},
controlledProps: [{
key: 'visible',
event: 'visible-change',
}],
externalClasses: [
`${prefix}-class`,
`${prefix}-class-content`,
`${prefix}-class-cancel`,
],
mixins: [getFunctionalMixin(props), useCustomNavbar],
components: {
TIcon,
TPopup,
TGrid,
TGridItem,
},
props: {
...props,
},
emits: [
'visible-change',
'update:visible',
],
data() {
return {
prefix,
classPrefix: name,
gridThemeItems: [],
currentSwiperIndex: 0,
defaultPopUpProps: {},
defaultPopUpzIndex: 11500,
tools,
heightStyle: 'height: 456rpx;',
};
},
computed: {
rootCustomStyle() {
return tools._style([
this.customStyle,
this.dataTheme === 'grid' ? 'padding-bottom: 16rpx' : '',
]);
},
iconCustomStyle() {
return 'margin-right: 8px;';
},
suffixIconCustomStyle() {
return 'margin-right: 8px;margin-left: auto;';
},
gridStyle() {
return `${this.heightStyle}padding-bottom: 48rpx;`;
},
},
watch: {
dataVisible: {
handler(e) {
if (e) {
this.init();
}
},
immediate: true,
},
dataItems: {
handler() {
if (this.dataVisible) {
this.init();
}
},
immediate: true,
},
},
methods: {
init() {
this.memoInitialData();
this.splitGridThemeActions();
},
memoInitialData() {
this.initialData = {
};
},
splitGridThemeActions() {
if (this.dataTheme !== actionSheetTheme.Grid) return;
this.gridThemeItems = chunk(this.dataItems, this.dataCount);
},
/** 指令调用显示 */
show(options) {
const defaultOptions = [
'align',
'cancelText',
'count',
'description',
'items',
'popupProps',
'showCancel',
'showOverlay',
'theme',
'usingCustomNavbar',
].reduce((acc, key) => ({
...acc,
[key]: props[key].default,
}));
this.setData({
...defaultOptions,
...options,
visible: true,
});
this.splitGridThemeActions();
this.autoClose = true;
this._trigger('visible-change', { visible: true });
},
/** 指令调用隐藏 */
close() {
this.$emit('close', { trigger: 'command' });
this._trigger('visible-change', { visible: false });
},
/** 默认点击遮罩关闭 */
onPopupVisibleChange(detail) {
if (!detail.visible) {
this.$emit('close', { trigger: 'overlay' });
this._trigger('visible-change', { visible: false });
}
if (this.autoClose) {
this.dataVisible = false;
this.autoClose = false;
}
},
onSwiperChange(e) {
const { current } = e.detail;
this.currentSwiperIndex = current;
},
onSelect(event, { index }) {
const { currentSwiperIndex, dataItems, gridThemeItems, dataCount, dataTheme } = this;
const isSwiperMode = dataTheme === actionSheetTheme.Grid;
const item = isSwiperMode ? gridThemeItems[currentSwiperIndex][index] : dataItems[index];
const realIndex = isSwiperMode ? index + currentSwiperIndex * dataCount : index;
if (item) {
this.$emit('selected', { selected: item, index: realIndex });
if (!item.disabled) {
this.$emit('close', { trigger: 'select' });
this._trigger('visible-change', { visible: false });
}
}
},
onCancel() {
this.$emit('cancel');
if (this.autoClose) {
this.dataVisible = false;
this.autoClose = false;
}
},
},
});
</script>
<style scoped>
@import './action-sheet.css';
</style>