| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- <template>
- <view
- :id="innerId"
- :class="[
- 'nana-flex-layout', {
- 'nana-flex-row': direction === 'row',
- 'nana-flex-column': direction === 'column'
- },
- innerClass,
- ]"
- :style="finalStyle"
- @mouseenter="handleMouseEnter"
- @mouseleave="handleMouseLeave"
- @mousedown="handleTouchStart"
- @mouseup="handleTouchEnd"
- @touchstart="handleTouchStart"
- @touchend="handleTouchEnd"
- @click="handleClick"
- >
- <slot></slot>
- </view>
- </template>
- <script setup lang="ts">
- /**
- * 组件说明:Flex组件,用于一些布局中快速写容器,是一系列盒子的基础组件。
- */
- import { computed, onMounted, ref } from 'vue';
- import { useTheme, type ThemePaddingMargin } from '../theme/ThemeDefine';
- import { configMargin, configPadding } from '../theme/ThemeTools';
- export type FlexDirection = "row"|"column"|'row-reverse'|'column-reverse';
- export type FlexJustifyType = 'flex-start' | 'flex-end' | 'center' |'space-between' |'space-around' |'space-evenly';
- export type FlexAlignType = "stretch"|'center'|'start'|'end'|'flex-start' | 'flex-end' | 'center';
- export type StateType = 'default' | 'active' | 'pressed';
- export interface FlexProps {
- innerId?: string,
- /**
- * 盒子定位
- */
- position?: "absolute" | "relative",
- /**
- * 弹性盒子方向
- */
- direction?: FlexDirection,
- /**
- * 子元素在主轴上的对齐方式
- */
- justify?: FlexJustifyType,
- /**
- * 子元素在交叉轴上的对齐方式
- */
- align?: FlexAlignType|"auto",
- /**
- * 当前元素在主轴上的对齐方式
- */
- alignSelf?: FlexAlignType|"auto",
- /**
- * 主轴与交叉轴是否居中
- */
- center?: boolean,
- /**
- * 弹性布局是否换行
- */
- wrap?: boolean,
- /**
- * 特殊样式
- */
- innerStyle?: object,
- /**
- * 特殊类名
- */
- innerClass?: string|string[]|object,
- /**
- * flex 参数
- */
- flex?: number|string,
- /**
- * flexBasis 参数
- */
- flexBasis?: number|string,
- /**
- * flexGrow 参数
- */
- flexGrow?: number,
- /**
- * flexShrink 参数
- */
- flexShrink?: number,
- /**
- * 内边距参数(支持数字或数组)
- */
- padding?: number|Array<number>|ThemePaddingMargin,
- /**
- * 外边距参数(支持数字或数组)
- */
- margin?: number|Array<number>|ThemePaddingMargin,
- /**
- * 位置参数
- */
- top?: number|string,
- right?: number|string,
- bottom?: number|string,
- left?: number|string,
- /**
- * 圆角
- */
- radius?: number|string,
- /**
- * 间距
- */
- gap?: number|string,
- /**
- * 是否可以点击
- */
- touchable?: boolean,
- /**
- * 背景颜色
- */
- backgroundColor?: string,
- /**
- * 按下时的颜色
- */
- pressedColor?: string,
- /**
- * 按下时的透明度(仅在 pressedColor 未设置时有效)
- */
- activeOpacity?: number,
- /**
- * 宽度
- */
- width?: number|string,
- /**
- * 高度
- */
- height?: number|string,
- overflow?: 'visible'|'hidden'|'scroll'|'auto'
- }
- const props = withDefaults(defineProps<FlexProps>(), {
- direction: "column",
- backgroundColor: '',
- activeOpacity: 0.7,
- touchable: false,
- });
- const themeContext = useTheme();
- const commonStyle = computed(() => {
- const obj : Record<string, any> = {
- flexDirection: props.direction,
- flexBasis: props.flexBasis,
- flexGrow: props.flexGrow,
- flexShrink: props.flexShrink,
- justifyContent: props.center ? (props.justify || 'center') : props.justify,
- alignItems: props.center ? (props.align || 'center') : props.align,
- position: props.position,
- alignSelf: props.alignSelf,
- flexWrap: props.wrap ? 'wrap' : 'nowrap',
- backgroundColor: themeContext.resolveThemeColor(props.backgroundColor),
- width: themeContext.resolveThemeSize(props.width),
- height: themeContext.resolveThemeSize(props.height),
- gap: themeContext.resolveThemeSize(props.gap),
- borderRadius: themeContext.resolveThemeSize(props.radius),
- overflow: props.overflow,
- ...(props.innerStyle ? props.innerStyle : {}),
- }
-
- //内边距样式
- configPadding(obj, themeContext.theme, props.padding as any);
- //外边距样式
- configMargin(obj, themeContext.theme, props.margin as any);
- if (obj.paddingVertical) {
- if (obj.paddingTop === undefined)
- obj.paddingTop = obj.paddingVertical;
- if (obj.paddingBottom === undefined)
- obj.paddingBottom = obj.paddingVertical;
- obj.paddingVertical = undefined;
- }
- if (obj.paddingHorizontal) {
- if (obj.paddingLeft === undefined)
- obj.paddingLeft = obj.paddingHorizontal;
- if (obj.paddingRight === undefined)
- obj.paddingRight = obj.paddingHorizontal;
- obj.paddingHorizontal = undefined;
- }
- if (obj.marginVertical) {
- obj.marginTop = obj.marginVertical;
- obj.marginBottom = obj.marginVertical;
- obj.marginVertical = undefined;
- }
- if (obj.marginHorizontal) {
- obj.marginLeft = obj.marginHorizontal;
- obj.marginRight = obj.marginHorizontal;
- obj.marginHorizontal = undefined;
- }
- //绝对距样式
- if (typeof props.left !== 'undefined')
- obj.left = themeContext.resolveThemeSize(props.left);
- if (typeof props.right !== 'undefined')
- obj.right = themeContext.resolveThemeSize(props.right);
- if (typeof props.top !== 'undefined')
- obj.top = themeContext.resolveThemeSize(props.top);
- if (typeof props.bottom !== 'undefined')
- obj.bottom = themeContext.resolveThemeSize(props.bottom);
- if (typeof props.flex !== 'undefined')
- obj.flex = props.flex;
- return obj
- });
- const finalStyle = computed(() => {
- const obj : Record<string, any> = {};
- if (props.pressedColor != undefined) {
- if (isPressed.value)
- obj.backgroundColor = themeContext.resolveThemeColor(props.pressedColor);
- } else if (props.activeOpacity != undefined)
- obj.opacity = isPressed.value ? props.activeOpacity : 1;
- const o = {
- ...commonStyle.value,
- ...obj
- }
- for (const key in o) {
- if (o[key] === undefined)
- delete o[key];
- }
- return o;
- })
- defineOptions({
- options: {
- styleIsolation: "shared",
- virtualHost: true
- }
- })
- const emit = defineEmits([ "click", "state" ]);
- const isPressed = ref(false)
- function handleTouchStart() {
- if (props.touchable) {
- isPressed.value = true; // 按下时改变状态
- emit('state', 'active');
- }
- }
- function handleMouseEnter() {
- if (props.touchable)
- emit('state', 'active')
- }
- function handleMouseLeave() {
- if (props.touchable)
- emit('state', 'default')
- }
- function handleClick(e: Event) {
- if (props.touchable)
- emit('click', e);
- }
- function handleTouchEnd() {
- if (props.touchable) {
- isPressed.value = false; // 释放时恢复状态
- emit('state', 'default');
- }
- }
- onMounted(() => {
- emit('state', 'default')
- })
- </script>
- <style>
- .nana-flex-layout {
- display: flex;
- }
- .nana-flex-row {
- flex-direction: row;
- }
- .nana-flex-column {
- flex-direction: column;
- }
- </style>
|