基础插件修改
This commit is contained in:
		
							parent
							
								
									d2f735d2f6
								
							
						
					
					
						commit
						28e34c191d
					
				@ -0,0 +1,79 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style :styleSpacing="styleSpacing" :styleColor="styleColor" :index="index">
 | 
			
		||||
		<view class="banner">
 | 
			
		||||
			<cl-banner
 | 
			
		||||
				:indicatorDots="indicatorDots"
 | 
			
		||||
				:list="list"
 | 
			
		||||
				:type="mode"
 | 
			
		||||
				:height="height"
 | 
			
		||||
				:autoplay="autoplay"
 | 
			
		||||
				@select="toPath"
 | 
			
		||||
			>
 | 
			
		||||
				<template #item="{ item }">
 | 
			
		||||
					<cl-skeleton height="100%" :loading="!item.pic" :loading-style="style">
 | 
			
		||||
						<image :style="style" mode="fill" :src="item.pic" />
 | 
			
		||||
					</cl-skeleton>
 | 
			
		||||
				</template>
 | 
			
		||||
			</cl-banner>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup name="fix-banner">
 | 
			
		||||
import { computed, type PropType } from "vue";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
import type { Form } from "../../types/form";
 | 
			
		||||
 | 
			
		||||
const emits = defineEmits(["jump"]);
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	mode: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "",
 | 
			
		||||
	},
 | 
			
		||||
	height: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 300,
 | 
			
		||||
	},
 | 
			
		||||
	autoplay: {
 | 
			
		||||
		type: Boolean,
 | 
			
		||||
		default: true,
 | 
			
		||||
	},
 | 
			
		||||
	indicatorDots: Boolean,
 | 
			
		||||
	list: {
 | 
			
		||||
		type: Array as PropType<Form.Banner[]>,
 | 
			
		||||
		default: function () {
 | 
			
		||||
			return [];
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function toPath(index: number) {
 | 
			
		||||
	const link = props.list[index].link;
 | 
			
		||||
	emits("jump", link);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const style = computed(() => {
 | 
			
		||||
	const { borderTopLR, borderBottomLR } = props.styleSpacing;
 | 
			
		||||
	return {
 | 
			
		||||
		borderRadius: `${borderTopLR}rpx ${borderTopLR}rpx ${borderBottomLR}rpx ${borderBottomLR}rpx`,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.banner {
 | 
			
		||||
	margin: 0 auto;
 | 
			
		||||
	width: 100%;
 | 
			
		||||
 | 
			
		||||
	image {
 | 
			
		||||
		height: 100%;
 | 
			
		||||
		width: 100%;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,135 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<view
 | 
			
		||||
		:style="{
 | 
			
		||||
			paddingTop: seatHeight + 'px',
 | 
			
		||||
		}"
 | 
			
		||||
		class="layer"
 | 
			
		||||
	>
 | 
			
		||||
		<view
 | 
			
		||||
			:style="{
 | 
			
		||||
				...basePosition,
 | 
			
		||||
				width: '100%',
 | 
			
		||||
			}"
 | 
			
		||||
		>
 | 
			
		||||
			<view :style="{ position: 'absolute', ...baseBackground }"></view>
 | 
			
		||||
			<view class="fix-base-style" :style="baseStyle" :class="[`fix-base-style-${index}`]">
 | 
			
		||||
				<slot></slot>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</view>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup name="fix-base-style">
 | 
			
		||||
import { computed, watch, ref, nextTick, getCurrentInstance } from "vue";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
 | 
			
		||||
const { proxy }: any = getCurrentInstance();
 | 
			
		||||
const { statusBarHeight = 0 } = uni.getSystemInfoSync();
 | 
			
		||||
 | 
			
		||||
// 定义属性,简化默认对象的写法
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	...baseProps,
 | 
			
		||||
	// 背景跟随子元素的间距圆角
 | 
			
		||||
	flowInner: {
 | 
			
		||||
		type: Boolean,
 | 
			
		||||
		default: true,
 | 
			
		||||
	},
 | 
			
		||||
	statusBar: {
 | 
			
		||||
		type: Boolean,
 | 
			
		||||
		default: false,
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 计算 baseStyle,使用解构优化
 | 
			
		||||
const baseStyle = computed(() => {
 | 
			
		||||
	const { marginTop, marginBottom, marginLR, padding, borderTopLR, borderBottomLR } =
 | 
			
		||||
		props.styleSpacing;
 | 
			
		||||
 | 
			
		||||
	const { color } = props.styleColor;
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		margin: `${marginTop}rpx ${marginLR}rpx ${marginBottom}rpx ${marginLR}rpx`,
 | 
			
		||||
		color: color,
 | 
			
		||||
		padding: `${padding}rpx`,
 | 
			
		||||
		borderRadius: `${borderTopLR}rpx ${borderTopLR}rpx ${borderBottomLR}rpx ${borderBottomLR}rpx`,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
const baseBackground = computed(() => {
 | 
			
		||||
	const { backgroundColor, opacity } = props.styleColor;
 | 
			
		||||
	const { marginTop, marginBottom, marginLR, borderTopLR, borderBottomLR } = props.styleSpacing;
 | 
			
		||||
	let flow: any = {
 | 
			
		||||
		left: 0,
 | 
			
		||||
		top: 0,
 | 
			
		||||
		right: 0,
 | 
			
		||||
		bottom: 0,
 | 
			
		||||
	};
 | 
			
		||||
	if (props.flowInner) {
 | 
			
		||||
		flow = {
 | 
			
		||||
			left: `${marginLR}rpx`,
 | 
			
		||||
			top: `${marginTop}rpx`,
 | 
			
		||||
			right: `${marginLR}rpx`,
 | 
			
		||||
			bottom: `${marginBottom}rpx`,
 | 
			
		||||
			borderRadius: `${borderTopLR}rpx ${borderTopLR}rpx ${borderBottomLR}rpx ${borderBottomLR}rpx`,
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
	return {
 | 
			
		||||
		backgroundColor: backgroundColor,
 | 
			
		||||
		opacity: opacity,
 | 
			
		||||
		zIndex: -1,
 | 
			
		||||
		...flow,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const basePosition = computed(() => {
 | 
			
		||||
	const { top, mode, zIndex } = props.position;
 | 
			
		||||
	let topPos = top;
 | 
			
		||||
	if (props.statusBar) topPos = topPos + statusBarHeight;
 | 
			
		||||
	return {
 | 
			
		||||
		top: `${topPos}px`,
 | 
			
		||||
		position: mode,
 | 
			
		||||
		zIndex: zIndex,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
const seatHeight = ref(0);
 | 
			
		||||
watch(
 | 
			
		||||
	() => props.position,
 | 
			
		||||
	async (position) => {
 | 
			
		||||
		if (position.isSeat && position.mode === "fixed") {
 | 
			
		||||
			refreshSeat();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		deep: true,
 | 
			
		||||
		immediate: true,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
function refreshSeat() {
 | 
			
		||||
	nextTick(() => {
 | 
			
		||||
		uni.createSelectorQuery()
 | 
			
		||||
			// #ifndef MP-ALIPAY
 | 
			
		||||
			.in(proxy)
 | 
			
		||||
			// #endif
 | 
			
		||||
			.select(`.fix-base-style-${props.index}`)
 | 
			
		||||
			.boundingClientRect((res: any) => {
 | 
			
		||||
				seatHeight.value = res.height;
 | 
			
		||||
			})
 | 
			
		||||
			.exec();
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.layer {
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	position: relative;
 | 
			
		||||
	.fix-base-style {
 | 
			
		||||
		height: auto;
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										221
									
								
								uni_modules/cool-fixtures/components/fix-coupon/fix-coupon.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								uni_modules/cool-fixtures/components/fix-coupon/fix-coupon.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,221 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style
 | 
			
		||||
		:styleSpacing="styleSpacing"
 | 
			
		||||
		:styleColor="styleColor"
 | 
			
		||||
		:position="position"
 | 
			
		||||
		:index="index"
 | 
			
		||||
	>
 | 
			
		||||
		<div class="fix-coupon" v-if="item.id" :style="couponStyle">
 | 
			
		||||
			<div class="coupon-activity" @tap="toGet">
 | 
			
		||||
				<div class="a">
 | 
			
		||||
					<div class="text">
 | 
			
		||||
						<span class="name">{{ item?.title }}</span>
 | 
			
		||||
						<span class="desc">{{ item?.description }}</span>
 | 
			
		||||
					</div>
 | 
			
		||||
 | 
			
		||||
					<span class="tag" v-if="item">点击领券</span>
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
				<div class="b">
 | 
			
		||||
					<template v-if="item">
 | 
			
		||||
						<span class="amount">{{ item?.amount || 0 }}</span>
 | 
			
		||||
						<span class="doc">
 | 
			
		||||
							{{ doc }}
 | 
			
		||||
						</span>
 | 
			
		||||
					</template>
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
				<div class="c"></div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-coupon" setup>
 | 
			
		||||
import { computed, type PropType } from "vue";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
import { useCool } from "/@/cool";
 | 
			
		||||
import { useUi } from "/$/cool-ui";
 | 
			
		||||
const { service } = useCool();
 | 
			
		||||
const ui = useUi();
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	item: {
 | 
			
		||||
		type: Object as PropType<Eps.MarketCouponInfoEntity>,
 | 
			
		||||
		default: () => {
 | 
			
		||||
			return {
 | 
			
		||||
				id: 0,
 | 
			
		||||
				title: "",
 | 
			
		||||
				description: "",
 | 
			
		||||
				amount: 0,
 | 
			
		||||
				type: 0,
 | 
			
		||||
				condition: "",
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	color: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "#2b2e3d",
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 领取
 | 
			
		||||
function toGet() {
 | 
			
		||||
	service.market.coupon.user
 | 
			
		||||
		.receive({
 | 
			
		||||
			couponId: props.item?.id,
 | 
			
		||||
		})
 | 
			
		||||
		.then(() => {
 | 
			
		||||
			ui.showToast("领取成功");
 | 
			
		||||
		})
 | 
			
		||||
		.catch((err) => {
 | 
			
		||||
			ui.showToast(err.message);
 | 
			
		||||
		});
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * 将十六进制颜色转换为 RGB
 | 
			
		||||
 * @param  hex - 十六进制颜色代码 (例如: #2b2e3d)
 | 
			
		||||
 * @returns - RGB 颜色字符串 (例如: 43, 46, 61)
 | 
			
		||||
 */
 | 
			
		||||
function hexToRgb(hex: string) {
 | 
			
		||||
	// 移除前导 #
 | 
			
		||||
	hex = hex.replace(/^#/, "");
 | 
			
		||||
 | 
			
		||||
	// 处理 3 位和 6 位十六进制颜色
 | 
			
		||||
	if (hex.length === 4) {
 | 
			
		||||
		hex = hex
 | 
			
		||||
			.split("")
 | 
			
		||||
			.map((char: any, index: number) => (index > 0 ? char + char : char))
 | 
			
		||||
			.join("");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const bigint = parseInt(hex, 16);
 | 
			
		||||
	return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255].join(", ");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 使用说明
 | 
			
		||||
const doc = computed(() => {
 | 
			
		||||
	const { type, condition } = props.item;
 | 
			
		||||
	switch (type) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			return `满${condition?.fullAmount}可用`;
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const couponStyle = computed(() => {
 | 
			
		||||
	const { color } = props;
 | 
			
		||||
	const rgbaColor = hexToRgb(color);
 | 
			
		||||
	// 返回样式对象
 | 
			
		||||
	return {
 | 
			
		||||
		"--coupon-color": rgbaColor,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-coupon {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.coupon-activity {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	position: relative;
 | 
			
		||||
	height: 160rpx;
 | 
			
		||||
	letter-spacing: 1rpx;
 | 
			
		||||
 | 
			
		||||
	.a {
 | 
			
		||||
		background: linear-gradient(
 | 
			
		||||
			140deg,
 | 
			
		||||
			rgba(var(--coupon-color), 0.75),
 | 
			
		||||
			rgba(var(--coupon-color), 1)
 | 
			
		||||
		);
 | 
			
		||||
		height: 140rpx;
 | 
			
		||||
		width: calc(100% - 250rpx);
 | 
			
		||||
		border-radius: 12rpx;
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		left: 24rpx;
 | 
			
		||||
		bottom: 1rpx;
 | 
			
		||||
		z-index: 2;
 | 
			
		||||
		color: #fff;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: space-between;
 | 
			
		||||
		padding: 0 24rpx;
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
 | 
			
		||||
		.name {
 | 
			
		||||
			display: block;
 | 
			
		||||
			font-size: 28rpx;
 | 
			
		||||
			font-weight: 500;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.desc {
 | 
			
		||||
			font-size: 20rpx;
 | 
			
		||||
			color: #ccc;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.tag {
 | 
			
		||||
			padding: 2rpx 10rpx;
 | 
			
		||||
			border-radius: 4rpx;
 | 
			
		||||
			background-color: #eb10ab;
 | 
			
		||||
			color: #fff;
 | 
			
		||||
			font-size: 20rpx;
 | 
			
		||||
			margin-right: 16rpx;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.b {
 | 
			
		||||
		height: 160rpx;
 | 
			
		||||
		width: 220rpx;
 | 
			
		||||
		background-color: #e2e2e2;
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		right: 24rpx;
 | 
			
		||||
		bottom: 0;
 | 
			
		||||
		border-radius: 12rpx;
 | 
			
		||||
		z-index: 3;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		flex-direction: column;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
 | 
			
		||||
		.amount {
 | 
			
		||||
			font-size: 60rpx;
 | 
			
		||||
			font-weight: bold;
 | 
			
		||||
			line-height: 1;
 | 
			
		||||
			color: rgba(var(--coupon-color), 1);
 | 
			
		||||
 | 
			
		||||
			&::after {
 | 
			
		||||
				content: "元";
 | 
			
		||||
				font-size: 48rpx;
 | 
			
		||||
				position: relative;
 | 
			
		||||
				top: -4rpx;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.doc {
 | 
			
		||||
			font-size: 22rpx;
 | 
			
		||||
			color: #666;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.c {
 | 
			
		||||
		content: "";
 | 
			
		||||
		display: block;
 | 
			
		||||
		height: 40rpx;
 | 
			
		||||
		width: 40rpx;
 | 
			
		||||
		background-color: #868686;
 | 
			
		||||
		border-radius: 40rpx;
 | 
			
		||||
		z-index: 1;
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		right: 216rpx;
 | 
			
		||||
		top: 2rpx;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										31
									
								
								uni_modules/cool-fixtures/components/fix-empty/fix-empty.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								uni_modules/cool-fixtures/components/fix-empty/fix-empty.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style
 | 
			
		||||
		:styleSpacing="styleSpacing"
 | 
			
		||||
		:styleColor="styleColor"
 | 
			
		||||
		:position="position"
 | 
			
		||||
		:index="index"
 | 
			
		||||
	>
 | 
			
		||||
		<view class="fix-empty" :style="{ height: height + 'rpx' }"></view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-empty" setup>
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	height: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 20,
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-empty {
 | 
			
		||||
	width: 750rpx;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,236 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style :styleSpacing="styleSpacing" :styleColor="styleColor" :position="position" :index="index">
 | 
			
		||||
		<view class="fix-goods-list">
 | 
			
		||||
			<template v-if="list.length">
 | 
			
		||||
				<view class="inner" :class="[`is-${data.mode}`, data.isShadow ? 'is-shadow-pb' : '']">
 | 
			
		||||
					<view class="grid-item" v-for="(item, index) in list" :key="index" :class="[data.isShadow ? 'is-shadow' : '']"
 | 
			
		||||
						@click="toDetail(item)">
 | 
			
		||||
						<image mode="widthFix" class="icon" :src="item.mainPic" />
 | 
			
		||||
						<view class="content">
 | 
			
		||||
							<view class="title">{{ item.title }}</view>
 | 
			
		||||
							<view class="price-sold">
 | 
			
		||||
								<cl-text type="price" :color="styleColor.color" :size="34" :value="item.price" bold />
 | 
			
		||||
								<cl-text color="info" :size="22" :value="`${item.sold || 0}件已售`" />
 | 
			
		||||
							</view>
 | 
			
		||||
						</view>
 | 
			
		||||
					</view>
 | 
			
		||||
				</view>
 | 
			
		||||
			</template>
 | 
			
		||||
			<template v-else>
 | 
			
		||||
				<cl-empty :fixed="false" text="暂无商品~~"></cl-empty>
 | 
			
		||||
			</template>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-goods-list" setup>
 | 
			
		||||
	import { ref, watch, computed, type PropType } from "vue";
 | 
			
		||||
	import type { Form } from "../../types/form";
 | 
			
		||||
	import { baseProps } from "../../hooks";
 | 
			
		||||
	import { useCool, useStore } from "/@/cool";
 | 
			
		||||
	const { service, router } = useCool();
 | 
			
		||||
	const { user } = useStore();
 | 
			
		||||
 | 
			
		||||
	const props = defineProps({
 | 
			
		||||
		data: {
 | 
			
		||||
			type: Object as PropType<Form.Goods>,
 | 
			
		||||
			default: () => {
 | 
			
		||||
				({
 | 
			
		||||
					mode: "mode-1",
 | 
			
		||||
					source: "source-1",
 | 
			
		||||
					attribute: 0,
 | 
			
		||||
					num: 99,
 | 
			
		||||
					gap: 0,
 | 
			
		||||
					isShadow: false,
 | 
			
		||||
					isVoucher: false,
 | 
			
		||||
					type: [],
 | 
			
		||||
					list: [],
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		index: {
 | 
			
		||||
			type: Number,
 | 
			
		||||
			default: 0,
 | 
			
		||||
		},
 | 
			
		||||
		...baseProps,
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const list = ref<Array<Eps.GoodsInfoEntity>>([]);
 | 
			
		||||
	// 创建计算属性来返回我们关心的特定属性
 | 
			
		||||
	const watchedData = computed(() => ({
 | 
			
		||||
		source: props.data.source,
 | 
			
		||||
		attribute: props.data.attribute,
 | 
			
		||||
		num: props.data.num,
 | 
			
		||||
		ids: props.data.list.map((e) => e.id),
 | 
			
		||||
		typeIds: props.data.type.map((e) => e.id),
 | 
			
		||||
	}));
 | 
			
		||||
	watch(
 | 
			
		||||
		() => watchedData.value,
 | 
			
		||||
		async (newValue) => {
 | 
			
		||||
			let goods = [];
 | 
			
		||||
			try {
 | 
			
		||||
				goods = await service.goods.info.getGoodsFromFixture({
 | 
			
		||||
					source: newValue.source,
 | 
			
		||||
					attribute: newValue.attribute,
 | 
			
		||||
					num: newValue.num,
 | 
			
		||||
					ids: newValue.ids,
 | 
			
		||||
					typeIds: newValue.typeIds,
 | 
			
		||||
				});
 | 
			
		||||
			} catch (error) {
 | 
			
		||||
				console.log(error);
 | 
			
		||||
			}
 | 
			
		||||
			list.value = goods;
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			deep: true,
 | 
			
		||||
			immediate: true,
 | 
			
		||||
		},
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	async function toDetail(item : Eps.GoodsInfoEntity) {
 | 
			
		||||
		if (item.openJump && item.jumpInfo.AppID) {
 | 
			
		||||
			try {
 | 
			
		||||
				// @ts-ignore
 | 
			
		||||
				await wx.openEmbeddedMiniProgram({
 | 
			
		||||
					appId: item.jumpInfo.AppID,
 | 
			
		||||
					path: item.jumpInfo.page,
 | 
			
		||||
					allowFullScreen: true,
 | 
			
		||||
				});
 | 
			
		||||
			} catch {
 | 
			
		||||
				// @ts-ignore
 | 
			
		||||
				await wx.navigateToMiniProgram({
 | 
			
		||||
					appId: item.jumpInfo.AppID,
 | 
			
		||||
					path: item.jumpInfo.page,
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			router.push({
 | 
			
		||||
				path: "/pages/goods/detail",
 | 
			
		||||
				query: {
 | 
			
		||||
					id: item.id,
 | 
			
		||||
				},
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-goods-list {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
 | 
			
		||||
	.is-shadow-pb {
 | 
			
		||||
		padding-bottom: 20rpx;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.inner {
 | 
			
		||||
		.grid-item {
 | 
			
		||||
			background-color: #fff;
 | 
			
		||||
			border-radius: 12rpx;
 | 
			
		||||
			box-sizing: border-box;
 | 
			
		||||
			overflow: hidden;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.is-shadow {
 | 
			
		||||
			box-shadow:
 | 
			
		||||
				0 3px 5px rgba(0, 0, 0, 0.2),
 | 
			
		||||
				0 6px 10px rgba(0, 0, 0, 0.1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.icon {
 | 
			
		||||
			width: 100%;
 | 
			
		||||
			max-width: 100%;
 | 
			
		||||
			vertical-align: middle;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.content {
 | 
			
		||||
			max-width: 100%;
 | 
			
		||||
			padding: 20rpx;
 | 
			
		||||
			box-sizing: border-box;
 | 
			
		||||
			overflow: hidden;
 | 
			
		||||
 | 
			
		||||
			.title {
 | 
			
		||||
				box-sizing: border-box;
 | 
			
		||||
				white-space: nowrap;
 | 
			
		||||
				/* 禁止换行 */
 | 
			
		||||
				overflow: hidden;
 | 
			
		||||
				/* 隐藏溢出的内容 */
 | 
			
		||||
				text-overflow: ellipsis;
 | 
			
		||||
				/* 超出部分显示省略号 */
 | 
			
		||||
				max-width: 100%;
 | 
			
		||||
				/* 确保元素最大宽度不超过容器 */
 | 
			
		||||
				font-size: 28rpx;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			.price-sold {
 | 
			
		||||
				margin-top: 16rpx;
 | 
			
		||||
				font-size: 28rpx;
 | 
			
		||||
				display: flex;
 | 
			
		||||
				justify-content: space-between;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			.voucher {
 | 
			
		||||
				font-size: 28rpx;
 | 
			
		||||
				margin-top: 16rpx;
 | 
			
		||||
 | 
			
		||||
				.tips {
 | 
			
		||||
					color: #e6a23c;
 | 
			
		||||
					font-size: 24rpx;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				.di {
 | 
			
		||||
					margin-left: 20rpx;
 | 
			
		||||
					color: #e6a23c;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.is-mode-1 {
 | 
			
		||||
		display: grid;
 | 
			
		||||
		grid-template-columns: 1fr 1fr;
 | 
			
		||||
		gap: 20rpx;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.is-mode-2 {
 | 
			
		||||
		display: grid;
 | 
			
		||||
		grid-template-columns: 1fr 1fr 1fr;
 | 
			
		||||
		gap: 20rpx;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.is-mode-3 {
 | 
			
		||||
		display: grid;
 | 
			
		||||
		grid-template-columns: 1fr;
 | 
			
		||||
		gap: 20rpx;
 | 
			
		||||
 | 
			
		||||
		.grid-item {
 | 
			
		||||
			background-color: #fff;
 | 
			
		||||
			border-top-right-radius: 12rpx;
 | 
			
		||||
			border-top-left-radius: 12rpx;
 | 
			
		||||
			border-bottom-right-radius: 12rpx;
 | 
			
		||||
			border-bottom-left-radius: 12rpx;
 | 
			
		||||
			overflow: hidden;
 | 
			
		||||
			display: grid;
 | 
			
		||||
			grid-template-columns: 200rpx 1fr;
 | 
			
		||||
			gap: 40rpx;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.is-mode-4 {
 | 
			
		||||
		display: grid;
 | 
			
		||||
		grid-template-columns: 1fr;
 | 
			
		||||
		gap: 20rpx;
 | 
			
		||||
 | 
			
		||||
		.grid-item {
 | 
			
		||||
			display: flex;
 | 
			
		||||
			flex-direction: column;
 | 
			
		||||
			background-color: #fff;
 | 
			
		||||
			border-top-right-radius: 12rpx;
 | 
			
		||||
			border-top-left-radius: 12rpx;
 | 
			
		||||
			border-bottom-right-radius: 12rpx;
 | 
			
		||||
			border-bottom-left-radius: 12rpx;
 | 
			
		||||
			overflow: hidden;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,112 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style :styleSpacing="styleSpacing" :styleColor="styleColor" :position="position">
 | 
			
		||||
		<view class="fix-hot-image" :class="[`fix-hot-image-${index}`]">
 | 
			
		||||
			<image
 | 
			
		||||
				v-if="data.pic"
 | 
			
		||||
				mode="widthFix"
 | 
			
		||||
				:src="data.pic"
 | 
			
		||||
				class="img hot-image"
 | 
			
		||||
				@load="loadImage"
 | 
			
		||||
				@click="toPath"
 | 
			
		||||
			/>
 | 
			
		||||
			<view
 | 
			
		||||
				v-for="(item, index) in hot"
 | 
			
		||||
				:key="index"
 | 
			
		||||
				class="hot-item"
 | 
			
		||||
				:style="{
 | 
			
		||||
					width: `${item.relativeW}px`,
 | 
			
		||||
					height: `${item.relativeH}px`,
 | 
			
		||||
					top: `${item.relativeY}px`,
 | 
			
		||||
					left: `${item.relativeX}px`,
 | 
			
		||||
					zIndex: item.index,
 | 
			
		||||
				}"
 | 
			
		||||
				@click="hotJump(item)"
 | 
			
		||||
			>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-hot-image" setup>
 | 
			
		||||
import { type PropType, ref, nextTick, getCurrentInstance } from "vue";
 | 
			
		||||
import type { Form } from "../../types/form";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
const { proxy }: any = getCurrentInstance();
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	data: {
 | 
			
		||||
		type: Object as PropType<Form.HotImage>,
 | 
			
		||||
		default: () => {
 | 
			
		||||
			return {
 | 
			
		||||
				pic: "",
 | 
			
		||||
				link: {},
 | 
			
		||||
				width: 0,
 | 
			
		||||
				height: 0,
 | 
			
		||||
				attr: [],
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
const emits = defineEmits(["jump"]);
 | 
			
		||||
 | 
			
		||||
const hot = ref<Form.Hot[]>(props.data.attr);
 | 
			
		||||
 | 
			
		||||
function refreshHot() {
 | 
			
		||||
	nextTick(() => {
 | 
			
		||||
		uni.createSelectorQuery()
 | 
			
		||||
			// #ifndef MP-ALIPAY
 | 
			
		||||
			.in(proxy)
 | 
			
		||||
			// #endif
 | 
			
		||||
			.select(`.fix-hot-image-${props.index}`)
 | 
			
		||||
			.boundingClientRect((res: any) => {
 | 
			
		||||
				const { width, height } = res;
 | 
			
		||||
 | 
			
		||||
				// 调整等比例缩放位置 大小
 | 
			
		||||
				const scaleX = width / props.data.width;
 | 
			
		||||
				const scaleY = height / props.data.height;
 | 
			
		||||
				// 调整子元素属性
 | 
			
		||||
				hot.value = props.data.attr.map((element) => {
 | 
			
		||||
					element.relativeX = Math.floor(element.x * scaleX);
 | 
			
		||||
					element.relativeY = Math.floor(element.y * scaleY);
 | 
			
		||||
					element.relativeW = Math.floor(element.w * scaleX);
 | 
			
		||||
					element.relativeH = Math.floor(element.h * scaleY);
 | 
			
		||||
					return element;
 | 
			
		||||
				});
 | 
			
		||||
			})
 | 
			
		||||
			.exec();
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function loadImage(event: any) {
 | 
			
		||||
	refreshHot();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function hotJump(item: Form.Hot) {
 | 
			
		||||
	emits("jump", item.link);
 | 
			
		||||
}
 | 
			
		||||
function toPath() {
 | 
			
		||||
	const link = props.data.link;
 | 
			
		||||
	emits("jump", link);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-hot-image {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
	position: relative;
 | 
			
		||||
	.img {
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		max-width: 100%;
 | 
			
		||||
		vertical-align: middle;
 | 
			
		||||
	}
 | 
			
		||||
	.hot-item {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										175
									
								
								uni_modules/cool-fixtures/components/fix-index/fix-index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								uni_modules/cool-fixtures/components/fix-index/fix-index.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,175 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<view class="parent">
 | 
			
		||||
		<view class="item" v-for="(item, index) in list" :key="index">
 | 
			
		||||
			<block v-if="item.component.name == 'fix-top-bar'">
 | 
			
		||||
				<fix-top-bar
 | 
			
		||||
					v-bind="item.component.props"
 | 
			
		||||
					:statusBar="statusBar"
 | 
			
		||||
					@jump="jump"
 | 
			
		||||
				></fix-top-bar>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-positioning'">
 | 
			
		||||
				<fix-positioning
 | 
			
		||||
					v-bind="item.component.props"
 | 
			
		||||
					:index="index"
 | 
			
		||||
					@jump="jump"
 | 
			
		||||
				></fix-positioning>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-search'">
 | 
			
		||||
				<fix-search
 | 
			
		||||
					v-bind="item.component.props"
 | 
			
		||||
					:statusBar="statusBar"
 | 
			
		||||
					:index="index"
 | 
			
		||||
					@jump="jump"
 | 
			
		||||
				></fix-search>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-banner'">
 | 
			
		||||
				<fix-banner v-bind="item.component.props" :index="index" @jump="jump"></fix-banner>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-menus'">
 | 
			
		||||
				<fix-menus v-bind="item.component.props" :index="index" @jump="jump"></fix-menus>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-goods-list'">
 | 
			
		||||
				<fix-goods-list v-bind="item.component.props" :index="index"></fix-goods-list>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-picture'">
 | 
			
		||||
				<fix-picture
 | 
			
		||||
					v-bind="item.component.props"
 | 
			
		||||
					:index="index"
 | 
			
		||||
					@jump="jump"
 | 
			
		||||
				></fix-picture>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-hot-image'">
 | 
			
		||||
				<fix-hot-image
 | 
			
		||||
					v-bind="item.component.props"
 | 
			
		||||
					:index="index"
 | 
			
		||||
					@jump="jump"
 | 
			
		||||
				></fix-hot-image>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-coupon'">
 | 
			
		||||
				<fix-coupon v-bind="item.component.props" :index="index"></fix-coupon>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-title'">
 | 
			
		||||
				<fix-title v-bind="item.component.props" :index="index" @jump="jump"></fix-title>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-empty'">
 | 
			
		||||
				<fix-empty v-bind="item.component.props" :index="index"></fix-empty>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-line'">
 | 
			
		||||
				<fix-line v-bind="item.component.props" :index="index"></fix-line>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-rich-text'">
 | 
			
		||||
				<fix-rich-text v-bind="item.component.props" :index="index"></fix-rich-text>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-list-menu'">
 | 
			
		||||
				<fix-list-menu
 | 
			
		||||
					v-bind="item.component.props"
 | 
			
		||||
					:index="index"
 | 
			
		||||
					@jump="jump"
 | 
			
		||||
				></fix-list-menu>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-rubik-cube'">
 | 
			
		||||
				<fix-rubik-cube
 | 
			
		||||
					v-bind="item.component.props"
 | 
			
		||||
					:index="index"
 | 
			
		||||
					@jump="jump"
 | 
			
		||||
				></fix-rubik-cube>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-video'">
 | 
			
		||||
				<fix-video v-bind="item.component.props" :index="index"></fix-video>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-suspension'">
 | 
			
		||||
				<fix-suspension v-bind="item.component.props" @jump="jump"></fix-suspension>
 | 
			
		||||
			</block>
 | 
			
		||||
			<block v-if="item.component.name == 'fix-wechat'">
 | 
			
		||||
				<fix-wechat v-bind="item.component.props"></fix-wechat>
 | 
			
		||||
			</block>
 | 
			
		||||
		</view>
 | 
			
		||||
	</view>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup name="fix-index">
 | 
			
		||||
import { type PropType } from "vue";
 | 
			
		||||
import { useCool } from "/@/cool";
 | 
			
		||||
const { router } = useCool();
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	list: {
 | 
			
		||||
		type: Array as PropType<{ component: { name: string; props: Record<string, any> } }[]>,
 | 
			
		||||
		default: () => [],
 | 
			
		||||
	},
 | 
			
		||||
	statusBar: Boolean,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 将字符串 id=1&t=2 转成对象
 | 
			
		||||
function strToObj(str: string): Record<string, string> {
 | 
			
		||||
	if (!str) return {};
 | 
			
		||||
	return str.split("&").reduce((acc: { [key: string]: string }, pair) => {
 | 
			
		||||
		const [key, value] = pair.split("=");
 | 
			
		||||
		acc[key] = value;
 | 
			
		||||
		return acc;
 | 
			
		||||
	}, {});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 统一的跳转函数
 | 
			
		||||
function jump(link: { name: string; page: string; type: string; appid: string }) {
 | 
			
		||||
	let [path, param = ""] = link.page.split("?");
 | 
			
		||||
	const query = strToObj(param);
 | 
			
		||||
	switch (link.type) {
 | 
			
		||||
		case "goodsType":
 | 
			
		||||
			// 分类
 | 
			
		||||
			router.push({
 | 
			
		||||
				path,
 | 
			
		||||
				query,
 | 
			
		||||
			});
 | 
			
		||||
			break;
 | 
			
		||||
		case "goodsDetails":
 | 
			
		||||
			// 商品
 | 
			
		||||
			router.push({
 | 
			
		||||
				path,
 | 
			
		||||
				query,
 | 
			
		||||
			});
 | 
			
		||||
			break;
 | 
			
		||||
		case "index":
 | 
			
		||||
			// 平台
 | 
			
		||||
			router.push(path);
 | 
			
		||||
			break;
 | 
			
		||||
		case "web":
 | 
			
		||||
			// 网页
 | 
			
		||||
			router.push({
 | 
			
		||||
				path: "/pages/index/web",
 | 
			
		||||
				query: {
 | 
			
		||||
					url: link.page,
 | 
			
		||||
					title: link.name,
 | 
			
		||||
				},
 | 
			
		||||
			});
 | 
			
		||||
			break;
 | 
			
		||||
		case "applet":
 | 
			
		||||
			// 小程序
 | 
			
		||||
			try {
 | 
			
		||||
				// @ts-ignore
 | 
			
		||||
				wx.openEmbeddedMiniProgram({
 | 
			
		||||
					appId: link.appid,
 | 
			
		||||
					path: link.page,
 | 
			
		||||
					allowFullScreen: true,
 | 
			
		||||
				});
 | 
			
		||||
			} catch {
 | 
			
		||||
				// @ts-ignore
 | 
			
		||||
				wx.navigateToMiniProgram({
 | 
			
		||||
					appId: link.appid,
 | 
			
		||||
					path: link.page,
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case "fixtures":
 | 
			
		||||
			// 自定义
 | 
			
		||||
			router.push({
 | 
			
		||||
				path,
 | 
			
		||||
				query,
 | 
			
		||||
			});
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										44
									
								
								uni_modules/cool-fixtures/components/fix-line/fix-line.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								uni_modules/cool-fixtures/components/fix-line/fix-line.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style :styleSpacing="styleSpacing" :styleColor="styleColor" :index="index">
 | 
			
		||||
		<view class="fix-line">
 | 
			
		||||
			<view class="inner" :style="innerStyle"></view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-line" setup>
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	mode: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "solid",
 | 
			
		||||
	},
 | 
			
		||||
	color: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "#f5f6fa",
 | 
			
		||||
	},
 | 
			
		||||
	height: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 4,
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
const innerStyle = computed(() => {
 | 
			
		||||
	return {
 | 
			
		||||
		borderBottom: `${props.height}rpx ${props.mode} ${props.color}`,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-line {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,133 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style
 | 
			
		||||
		:styleSpacing="styleSpacing"
 | 
			
		||||
		:styleColor="styleColor"
 | 
			
		||||
		:position="position"
 | 
			
		||||
		:index="index"
 | 
			
		||||
	>
 | 
			
		||||
		<view class="fix-list-menu">
 | 
			
		||||
			<view class="inner" v-if="list.length">
 | 
			
		||||
				<view
 | 
			
		||||
					class="item"
 | 
			
		||||
					:class="[`${isBorder ? 'is-border' : ''}`]"
 | 
			
		||||
					v-for="(item, index) in list"
 | 
			
		||||
					:key="index"
 | 
			
		||||
					@click="toPath(item.link)"
 | 
			
		||||
				>
 | 
			
		||||
					<view class="left">
 | 
			
		||||
						<image v-if="item.icon" :src="item.icon" class="icon" />
 | 
			
		||||
						<view class="text" :style="{ color: item.color }">
 | 
			
		||||
							{{ item.text }}
 | 
			
		||||
						</view>
 | 
			
		||||
					</view>
 | 
			
		||||
					<view class="right">
 | 
			
		||||
						<text class="text">{{ item.text2 }}</text>
 | 
			
		||||
						<cl-icon color="#a8abb2" name="arrow-right"></cl-icon>
 | 
			
		||||
					</view>
 | 
			
		||||
				</view>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-list-menu" setup>
 | 
			
		||||
import { type PropType } from "vue";
 | 
			
		||||
import type { Form } from "../../types/form";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	list: {
 | 
			
		||||
		type: Array as PropType<Form.Title[]>,
 | 
			
		||||
		default: () => {
 | 
			
		||||
			return [
 | 
			
		||||
				{
 | 
			
		||||
					text: "",
 | 
			
		||||
					text2: "",
 | 
			
		||||
					color: "",
 | 
			
		||||
					icon: "",
 | 
			
		||||
					link: {
 | 
			
		||||
						name: "",
 | 
			
		||||
						type: "",
 | 
			
		||||
						appid: "",
 | 
			
		||||
						page: "",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			];
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	isBorder: {
 | 
			
		||||
		type: Boolean,
 | 
			
		||||
		default: false,
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
const emits = defineEmits(["jump"]);
 | 
			
		||||
 | 
			
		||||
function toPath(link: Form.Link) {
 | 
			
		||||
	emits("jump", link);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-list-menu {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
	.inner {
 | 
			
		||||
		.is-border {
 | 
			
		||||
			border-bottom: 1px solid #e4e7ed;
 | 
			
		||||
		}
 | 
			
		||||
		.is-border:last-child {
 | 
			
		||||
			border-bottom: none;
 | 
			
		||||
		}
 | 
			
		||||
		.item {
 | 
			
		||||
			height: 80rpx;
 | 
			
		||||
			padding: 12rpx;
 | 
			
		||||
			box-sizing: border-box;
 | 
			
		||||
			display: flex;
 | 
			
		||||
			justify-content: space-between;
 | 
			
		||||
			align-items: center;
 | 
			
		||||
			.left {
 | 
			
		||||
				flex: 1;
 | 
			
		||||
				height: 100%;
 | 
			
		||||
				display: flex;
 | 
			
		||||
				justify-content: flex-start;
 | 
			
		||||
				align-items: center;
 | 
			
		||||
				margin-right: 40rpx;
 | 
			
		||||
				.icon {
 | 
			
		||||
					margin-right: 12rpx;
 | 
			
		||||
					width: 50rpx;
 | 
			
		||||
					height: 50rpx;
 | 
			
		||||
				}
 | 
			
		||||
				.text {
 | 
			
		||||
					width: 440rpx;
 | 
			
		||||
					height: 60rpx;
 | 
			
		||||
					line-height: 60rpx;
 | 
			
		||||
					font-size: 32rpx;
 | 
			
		||||
					text-overflow: ellipsis;
 | 
			
		||||
					white-space: nowrap;
 | 
			
		||||
					overflow: hidden;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			.right {
 | 
			
		||||
				width: 160rpx;
 | 
			
		||||
				height: 100%;
 | 
			
		||||
				display: flex;
 | 
			
		||||
				justify-content: flex-end;
 | 
			
		||||
				align-items: center;
 | 
			
		||||
				.text {
 | 
			
		||||
					color: #a8abb2;
 | 
			
		||||
					font-size: 24rpx;
 | 
			
		||||
					margin-right: 8rpx;
 | 
			
		||||
					text-overflow: ellipsis;
 | 
			
		||||
					white-space: nowrap;
 | 
			
		||||
					overflow: hidden;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										208
									
								
								uni_modules/cool-fixtures/components/fix-menus/fix-menus.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								uni_modules/cool-fixtures/components/fix-menus/fix-menus.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,208 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style
 | 
			
		||||
		:styleSpacing="styleSpacing"
 | 
			
		||||
		:styleColor="styleColor"
 | 
			
		||||
		:position="position"
 | 
			
		||||
		:index="index"
 | 
			
		||||
	>
 | 
			
		||||
		<view class="fix-menus">
 | 
			
		||||
			<swiper
 | 
			
		||||
				class="inner"
 | 
			
		||||
				:style="{
 | 
			
		||||
					height: height,
 | 
			
		||||
				}"
 | 
			
		||||
				:circular="false"
 | 
			
		||||
				:indicator-dots="false"
 | 
			
		||||
				@change="changeCurrent"
 | 
			
		||||
			>
 | 
			
		||||
				<swiper-item v-for="(item, index) in tabs" :key="index">
 | 
			
		||||
					<view
 | 
			
		||||
						class="row"
 | 
			
		||||
						:class="[`is-${style.rowNum}`]"
 | 
			
		||||
						v-for="(row, r) in item"
 | 
			
		||||
						:key="r"
 | 
			
		||||
					>
 | 
			
		||||
						<view
 | 
			
		||||
							class="item"
 | 
			
		||||
							v-for="(col, c) in row"
 | 
			
		||||
							:key="c"
 | 
			
		||||
							@click="toPath(col.link)"
 | 
			
		||||
						>
 | 
			
		||||
							<view
 | 
			
		||||
								:class="[`is-${style.shape}`, `is-${col.mode}`]"
 | 
			
		||||
								:style="{ backgroundColor: col.backgroundColor }"
 | 
			
		||||
								class="icon-box"
 | 
			
		||||
							>
 | 
			
		||||
								<cl-image :src="col.icon" mode="aspectFill" class="icon">
 | 
			
		||||
								</cl-image>
 | 
			
		||||
							</view>
 | 
			
		||||
							<span
 | 
			
		||||
								v-if="col.mode == 'mode-1'"
 | 
			
		||||
								class="text"
 | 
			
		||||
								:style="{ color: col.color }"
 | 
			
		||||
								>{{ col.text }}</span
 | 
			
		||||
							>
 | 
			
		||||
						</view>
 | 
			
		||||
					</view>
 | 
			
		||||
				</swiper-item>
 | 
			
		||||
			</swiper>
 | 
			
		||||
			<!-- 自定义指示器 -->
 | 
			
		||||
			<view class="custom-indicator">
 | 
			
		||||
				<view
 | 
			
		||||
					v-for="(item, index) in tabs"
 | 
			
		||||
					:key="index"
 | 
			
		||||
					class="custom-indicator-dot"
 | 
			
		||||
					:class="{ active: current === index }"
 | 
			
		||||
				></view>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup name="fix-menus">
 | 
			
		||||
import { computed, ref, type PropType } from "vue";
 | 
			
		||||
import type { Form } from "../../types/form";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
 | 
			
		||||
const emits = defineEmits(["jump"]);
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	style: {
 | 
			
		||||
		type: Object,
 | 
			
		||||
		default: () => {
 | 
			
		||||
			return {
 | 
			
		||||
				shape: "round",
 | 
			
		||||
				pageNum: 1,
 | 
			
		||||
				rowNum: 3,
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	list: {
 | 
			
		||||
		type: Array as PropType<Form.Menu[]>,
 | 
			
		||||
		default: () => [],
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
const current = ref(0);
 | 
			
		||||
function changeCurrent(event: any) {
 | 
			
		||||
	current.value = event.detail.current;
 | 
			
		||||
}
 | 
			
		||||
const height = computed(() => {
 | 
			
		||||
	return 180 * props.style.pageNum + "rpx";
 | 
			
		||||
});
 | 
			
		||||
const tabs = computed(() => {
 | 
			
		||||
	return paginateArray(props.list, props.style.pageNum, props.style.rowNum);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 解决 每页1行 每行3个 的问题
 | 
			
		||||
function paginateArray(data: Form.Menu[], pageNum: number, rowNum: number) {
 | 
			
		||||
	const result: any[] = [];
 | 
			
		||||
	const totalItems = data.length;
 | 
			
		||||
	let pageIndex = 0;
 | 
			
		||||
 | 
			
		||||
	while (pageIndex < totalItems) {
 | 
			
		||||
		const page: any[] = [];
 | 
			
		||||
		for (let i = 0; i < pageNum && pageIndex < totalItems; i++) {
 | 
			
		||||
			const row: any[] = [];
 | 
			
		||||
			for (let j = 0; j < rowNum && pageIndex < totalItems; j++) {
 | 
			
		||||
				row.push(data[pageIndex]);
 | 
			
		||||
				pageIndex++;
 | 
			
		||||
			}
 | 
			
		||||
			page.push(row);
 | 
			
		||||
		}
 | 
			
		||||
		result.push(page);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toPath(link: Form.Link) {
 | 
			
		||||
	emits("jump", link);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-menus {
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	padding-bottom: 20rpx;
 | 
			
		||||
	.inner {
 | 
			
		||||
		height: 100%;
 | 
			
		||||
		padding: 10rpx;
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
		.row {
 | 
			
		||||
			margin-bottom: 20rpx;
 | 
			
		||||
			.item {
 | 
			
		||||
				display: flex;
 | 
			
		||||
				flex-direction: column;
 | 
			
		||||
				justify-content: center;
 | 
			
		||||
				align-items: center;
 | 
			
		||||
				height: 160rpx;
 | 
			
		||||
				box-sizing: border-box;
 | 
			
		||||
 | 
			
		||||
				.is-round {
 | 
			
		||||
					border-radius: 40rpx;
 | 
			
		||||
				}
 | 
			
		||||
				.is-mode-2 {
 | 
			
		||||
					width: 120rpx !important;
 | 
			
		||||
					height: 120rpx !important;
 | 
			
		||||
				}
 | 
			
		||||
				.icon-box {
 | 
			
		||||
					width: 80rpx;
 | 
			
		||||
					height: 80rpx;
 | 
			
		||||
					overflow: hidden;
 | 
			
		||||
					.icon {
 | 
			
		||||
						width: 100%;
 | 
			
		||||
						height: 100%;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				.text {
 | 
			
		||||
					font-size: 26rpx;
 | 
			
		||||
					margin-top: 12rpx;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		.row:last-child {
 | 
			
		||||
			margin-bottom: 0;
 | 
			
		||||
		}
 | 
			
		||||
		.is-3 {
 | 
			
		||||
			display: grid;
 | 
			
		||||
			grid-template-columns: repeat(3, 1fr); /* 每行3列,每列宽度相等 */
 | 
			
		||||
			gap: 20rpx; /* 间距 */
 | 
			
		||||
		}
 | 
			
		||||
		.is-4 {
 | 
			
		||||
			display: grid;
 | 
			
		||||
			grid-template-columns: repeat(4, 1fr); /* 每行3列,每列宽度相等 */
 | 
			
		||||
			gap: 20rpx; /* 间距 */
 | 
			
		||||
		}
 | 
			
		||||
		.is-5 {
 | 
			
		||||
			display: grid;
 | 
			
		||||
			grid-template-columns: repeat(5, 1fr); /* 每行3列,每列宽度相等 */
 | 
			
		||||
			gap: 20rpx; /* 间距 */
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
.custom-indicator {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	justify-content: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.custom-indicator-dot {
 | 
			
		||||
	width: 12rpx;
 | 
			
		||||
	height: 12rpx;
 | 
			
		||||
	background-color: #686580;
 | 
			
		||||
	border-radius: 50%; /* 默认圆形 */
 | 
			
		||||
	margin: 0 10rpx;
 | 
			
		||||
	transition: all 0.3s ease; /* 过渡效果 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.custom-indicator-dot.active {
 | 
			
		||||
	width: 40rpx; /* 激活状态变成长方形 */
 | 
			
		||||
	height: 12rpx;
 | 
			
		||||
	background: var(--btn-color) !important;
 | 
			
		||||
	border-radius: 20rpx; /* 调整边角的圆度 */
 | 
			
		||||
	transition: all 0.5s ease; /* 激活状态有更慢的动画过渡 */
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,65 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style
 | 
			
		||||
		:styleSpacing="styleSpacing"
 | 
			
		||||
		:styleColor="styleColor"
 | 
			
		||||
		:position="position"
 | 
			
		||||
		:index="index"
 | 
			
		||||
	>
 | 
			
		||||
		<view class="fix-picture">
 | 
			
		||||
			<view class="inner" @click="toPath">
 | 
			
		||||
				<template v-if="data.pic">
 | 
			
		||||
					<image mode="widthFix" :src="data.pic" class="img" />
 | 
			
		||||
				</template>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-picture" setup>
 | 
			
		||||
import { type PropType } from "vue";
 | 
			
		||||
import type { Form } from "../../types/form";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	data: {
 | 
			
		||||
		type: Object as PropType<Form.Picture>,
 | 
			
		||||
		default: () => {
 | 
			
		||||
			return {
 | 
			
		||||
				pic: "",
 | 
			
		||||
				link: {},
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
const emits = defineEmits(["jump"]);
 | 
			
		||||
 | 
			
		||||
function toPath() {
 | 
			
		||||
	const link = props.data.link;
 | 
			
		||||
	emits("jump", link);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-picture {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
 | 
			
		||||
	.inner {
 | 
			
		||||
		height: 100%;
 | 
			
		||||
		flex: 1;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		.img {
 | 
			
		||||
			width: 100%;
 | 
			
		||||
			max-width: 100%;
 | 
			
		||||
			vertical-align: middle;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,378 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style :styleSpacing="styleSpacing" :styleColor="styleColor" :index="index">
 | 
			
		||||
		<view class="fix-positioning">
 | 
			
		||||
			<view :class="[`is-${mode}`]" class="header">
 | 
			
		||||
				<view class="item">
 | 
			
		||||
					<view v-if="mode == 'mode-1'">
 | 
			
		||||
						<view>{{ locationData.name }},{{ locationData.time }}</view>
 | 
			
		||||
						<view class="mt-10 text-size-m row-center" @click="moreStore">
 | 
			
		||||
							<cl-icon name="location"></cl-icon>
 | 
			
		||||
							<text class="mx-6">{{ locationData.store }}</text>
 | 
			
		||||
							<text class="text-grey">{{ locationData.distance }}</text>
 | 
			
		||||
							<text class="mx-6">更多门店</text>
 | 
			
		||||
							<cl-icon name="arrow-bottom"></cl-icon>
 | 
			
		||||
						</view>
 | 
			
		||||
						<view></view>
 | 
			
		||||
					</view>
 | 
			
		||||
					<view v-if="mode == 'mode-2'" @click="moreStore">
 | 
			
		||||
						<view class="row-center">
 | 
			
		||||
							<cl-icon name="location"></cl-icon>
 | 
			
		||||
							<text class="mx-6">{{ locationData.store }}</text>
 | 
			
		||||
							<cl-icon name="arrow-right"></cl-icon>
 | 
			
		||||
						</view>
 | 
			
		||||
						<view class="mt-10 text-size-m">
 | 
			
		||||
							<text class="text-grey mx-6">{{ locationData.distance }}</text>
 | 
			
		||||
						</view>
 | 
			
		||||
					</view>
 | 
			
		||||
					<view v-if="mode == 'mode-3'" @click="moreStore">
 | 
			
		||||
						<view class="row-center">
 | 
			
		||||
							<cl-icon name="location"></cl-icon>
 | 
			
		||||
							<text class="mx-6">{{ locationData.address }}</text>
 | 
			
		||||
						</view>
 | 
			
		||||
						<view class="mt-10 text-size-m">
 | 
			
		||||
							<text class="text-grey">由</text>
 | 
			
		||||
							<text class="text-grey mx-6">{{ locationData.store }}</text>
 | 
			
		||||
							<text class="text-grey">提供</text>
 | 
			
		||||
						</view>
 | 
			
		||||
					</view>
 | 
			
		||||
				</view>
 | 
			
		||||
				<view class="is-bg" :style="headerBackground"></view>
 | 
			
		||||
			</view>
 | 
			
		||||
			<view class="banner" v-if="banner.open">
 | 
			
		||||
				<swiper
 | 
			
		||||
					:class="[{ 'is-float': float.open }]"
 | 
			
		||||
					:style="{ height: `${props.banner.height}rpx` }"
 | 
			
		||||
				>
 | 
			
		||||
					<swiper-item v-for="(item, index) in banner.list" :key="index">
 | 
			
		||||
						<image
 | 
			
		||||
							:src="item.pic"
 | 
			
		||||
							:class="[{ 'is-float': float.open }]"
 | 
			
		||||
							class="banner-item"
 | 
			
		||||
							:style="{ height: `${props.banner.height}rpx` }"
 | 
			
		||||
						/>
 | 
			
		||||
					</swiper-item>
 | 
			
		||||
				</swiper>
 | 
			
		||||
				<view class="float" v-if="float.open">
 | 
			
		||||
					<view class="left" :style="leftStyle" @click="toPath(float.left.link)">
 | 
			
		||||
						<template v-if="float.left.mode === 'mode-1'">
 | 
			
		||||
							<image class="icon" mode="widthFix" :src="float.left.icon"></image>
 | 
			
		||||
							<view class="mt-20 text">{{ float.left.text }}</view>
 | 
			
		||||
						</template>
 | 
			
		||||
					</view>
 | 
			
		||||
					<view class="right" :style="rightStyle" @click="toPath(float.right.link)">
 | 
			
		||||
						<template v-if="float.right.mode === 'mode-1'">
 | 
			
		||||
							<image class="icon" mode="widthFix" :src="float.right.icon"></image>
 | 
			
		||||
							<view class="mt-20 text">{{ float.right.text }}</view>
 | 
			
		||||
						</template>
 | 
			
		||||
					</view>
 | 
			
		||||
				</view>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-positioning" setup>
 | 
			
		||||
import { computed, watch, ref, type PropType } from "vue";
 | 
			
		||||
import type { Form } from "../../types/form";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
import { useCool, useStore } from "/@/cool";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	mode: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "",
 | 
			
		||||
	},
 | 
			
		||||
	banner: {
 | 
			
		||||
		type: Object as PropType<{ open: boolean; height: number; list: Form.Banner[] }>,
 | 
			
		||||
		default: () => {
 | 
			
		||||
			return {
 | 
			
		||||
				open: false,
 | 
			
		||||
				height: 750,
 | 
			
		||||
				list: [],
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	float: {
 | 
			
		||||
		type: Object,
 | 
			
		||||
		default: () => {
 | 
			
		||||
			return {
 | 
			
		||||
				open: false,
 | 
			
		||||
				left: {
 | 
			
		||||
					text: "",
 | 
			
		||||
					mode: "mode-1",
 | 
			
		||||
					icon: "",
 | 
			
		||||
					color: "#000",
 | 
			
		||||
					backgroundColor: "#FFFFFF",
 | 
			
		||||
					link: {
 | 
			
		||||
						name: "",
 | 
			
		||||
						type: "",
 | 
			
		||||
						appid: "",
 | 
			
		||||
						page: "",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				right: {
 | 
			
		||||
					text: "",
 | 
			
		||||
					mode: "mode-1",
 | 
			
		||||
					icon: "",
 | 
			
		||||
					color: "#000",
 | 
			
		||||
					backgroundColor: "#FFFFFF",
 | 
			
		||||
					link: {
 | 
			
		||||
						name: "",
 | 
			
		||||
						appid: "",
 | 
			
		||||
						type: "",
 | 
			
		||||
						page: "",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
const { service, router } = useCool();
 | 
			
		||||
const { user } = useStore();
 | 
			
		||||
const emits = defineEmits(["jump"]);
 | 
			
		||||
 | 
			
		||||
const headerBackground = computed(() => {
 | 
			
		||||
	return {
 | 
			
		||||
		opacity: props.styleColor.opacity,
 | 
			
		||||
		backgroundColor: props.styleColor.backgroundColor,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const leftStyle = computed(() => {
 | 
			
		||||
	let url = "";
 | 
			
		||||
	if (props.float.left.mode === "mode-2") {
 | 
			
		||||
		url = props.float.left.icon;
 | 
			
		||||
	}
 | 
			
		||||
	return {
 | 
			
		||||
		backgroundColor: props.float.left.backgroundColor,
 | 
			
		||||
		color: props.float.left.color,
 | 
			
		||||
		backgroundImage: `url('${url}')`,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const rightStyle = computed(() => {
 | 
			
		||||
	let url = "";
 | 
			
		||||
	if (props.float.right.mode === "mode-2") {
 | 
			
		||||
		url = props.float.right.icon;
 | 
			
		||||
	}
 | 
			
		||||
	return {
 | 
			
		||||
		backgroundColor: props.float.right.backgroundColor,
 | 
			
		||||
		color: props.float.right.color,
 | 
			
		||||
		backgroundImage: `url('${url}')`,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
const locationData = ref({
 | 
			
		||||
	name: "张大仙",
 | 
			
		||||
	time: "早上好",
 | 
			
		||||
	store: "南村番禺店",
 | 
			
		||||
	distance: "距离你 0.7km",
 | 
			
		||||
	address: "广州番禺南村镇(建奇大厦5号)...",
 | 
			
		||||
});
 | 
			
		||||
watch(
 | 
			
		||||
	() => props.mode,
 | 
			
		||||
	async (newValue) => {
 | 
			
		||||
		locationData.value.time = getTimePeriod();
 | 
			
		||||
		getStore();
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		deep: true,
 | 
			
		||||
		immediate: true,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
function distanceInMeters(distanceInKilometers: number) {
 | 
			
		||||
	let dis = "0m";
 | 
			
		||||
	if (distanceInKilometers < 1) {
 | 
			
		||||
		// 如果距离小于1公里,将其转换为米(1公里 = 1000米)
 | 
			
		||||
		dis = Math.floor(distanceInKilometers * 1000) + "m";
 | 
			
		||||
	} else {
 | 
			
		||||
		dis = distanceInKilometers + "km";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "距离你 " + dis;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getTimePeriod() {
 | 
			
		||||
	const now = new Date();
 | 
			
		||||
	const hours = now.getHours();
 | 
			
		||||
 | 
			
		||||
	if (hours >= 5 && hours < 12) {
 | 
			
		||||
		return "早上好!"; // 5:00 - 11:59
 | 
			
		||||
	} else if (hours >= 12 && hours < 14) {
 | 
			
		||||
		return "中午好!"; // 12:00 - 13:59
 | 
			
		||||
	} else if (hours >= 14 && hours < 18) {
 | 
			
		||||
		return "下午好!"; // 14:00 - 17:59
 | 
			
		||||
	} else if (hours >= 18 && hours < 24) {
 | 
			
		||||
		return "晚上好!"; // 18:00 - 23:59
 | 
			
		||||
	} else {
 | 
			
		||||
		return "深夜好!"; // 00:00 - 4:59
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getStore() {
 | 
			
		||||
	uni.getLocation({
 | 
			
		||||
		type: "wgs84",
 | 
			
		||||
		success(res) {
 | 
			
		||||
			service.store.sample
 | 
			
		||||
				.vicinity({
 | 
			
		||||
					...res,
 | 
			
		||||
					size: 1,
 | 
			
		||||
					page: 1,
 | 
			
		||||
				})
 | 
			
		||||
				.then((res: any) => {
 | 
			
		||||
					if (res.list.length) {
 | 
			
		||||
						const { intact, sampleName, distance } = res.list[0];
 | 
			
		||||
						locationData.value.distance = distanceInMeters(distance);
 | 
			
		||||
						locationData.value.store = sampleName;
 | 
			
		||||
						locationData.value.address = intact;
 | 
			
		||||
						locationData.value.name = user.info?.nickName || "游客";
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
		},
 | 
			
		||||
		fail(result) {
 | 
			
		||||
			// 第一次拒绝后,下次点击提示打开设置授权弹窗
 | 
			
		||||
			uni.showModal({
 | 
			
		||||
				title: "定位获取失败",
 | 
			
		||||
				content: "附近门店查找失败,是否去设置授权打开?",
 | 
			
		||||
				success(modal: any) {
 | 
			
		||||
					if (modal.confirm) {
 | 
			
		||||
						uni.openSetting({
 | 
			
		||||
							success: (res) => {},
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
function moreStore() {
 | 
			
		||||
	router.push("/pages/partnership/sample/vicinity");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toPath(link: Form.Link) {
 | 
			
		||||
	emits("jump", link);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-positioning {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	flex-direction: column;
 | 
			
		||||
	align-items: center;
 | 
			
		||||
	justify-content: space-between;
 | 
			
		||||
	min-height: 140rpx;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
	position: relative;
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	.header {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		top: 0;
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		padding: 20rpx;
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
		min-height: 140rpx;
 | 
			
		||||
 | 
			
		||||
		.is-bg {
 | 
			
		||||
			position: absolute;
 | 
			
		||||
			top: 0;
 | 
			
		||||
			left: 0;
 | 
			
		||||
			bottom: 0;
 | 
			
		||||
			right: 0;
 | 
			
		||||
			z-index: 1;
 | 
			
		||||
		}
 | 
			
		||||
		.item {
 | 
			
		||||
			position: absolute;
 | 
			
		||||
			top: 0;
 | 
			
		||||
			left: 0;
 | 
			
		||||
			bottom: 0;
 | 
			
		||||
			right: 0;
 | 
			
		||||
			z-index: 2;
 | 
			
		||||
			padding: 20rpx;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	.banner {
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		height: 100%;
 | 
			
		||||
		position: relative;
 | 
			
		||||
		.banner-item {
 | 
			
		||||
			width: 750rpx;
 | 
			
		||||
			max-width: 100%;
 | 
			
		||||
			min-height: 750rpx;
 | 
			
		||||
			height: 100%;
 | 
			
		||||
		}
 | 
			
		||||
		.float {
 | 
			
		||||
			display: flex;
 | 
			
		||||
			justify-content: center;
 | 
			
		||||
			position: absolute;
 | 
			
		||||
			bottom: 20rpx;
 | 
			
		||||
			left: 20rpx;
 | 
			
		||||
			right: 20rpx;
 | 
			
		||||
 | 
			
		||||
			.left {
 | 
			
		||||
				margin-right: 30rpx;
 | 
			
		||||
			}
 | 
			
		||||
			.right {
 | 
			
		||||
				margin-left: 30rpx;
 | 
			
		||||
			}
 | 
			
		||||
			.left,
 | 
			
		||||
			.right {
 | 
			
		||||
				width: 240rpx;
 | 
			
		||||
				height: 280rpx;
 | 
			
		||||
				border-radius: 12rpx;
 | 
			
		||||
				background-size: 100% 100%;
 | 
			
		||||
				display: flex;
 | 
			
		||||
				flex-direction: column;
 | 
			
		||||
				justify-content: center;
 | 
			
		||||
				align-items: center;
 | 
			
		||||
				.text {
 | 
			
		||||
					font-size: 32rpx;
 | 
			
		||||
					font-family: PingFang SC;
 | 
			
		||||
					font-weight: bold;
 | 
			
		||||
				}
 | 
			
		||||
				.icon {
 | 
			
		||||
					width: 140rpx;
 | 
			
		||||
					height: 140rpx;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.is-mode-4 {
 | 
			
		||||
		display: none;
 | 
			
		||||
	}
 | 
			
		||||
	.row-center {
 | 
			
		||||
		display: flex;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
	}
 | 
			
		||||
	.flex-1 {
 | 
			
		||||
		flex: 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.is-float {
 | 
			
		||||
		min-height: 750rpx;
 | 
			
		||||
		:deep(.uni-swiper-wrapper) {
 | 
			
		||||
			overflow: inherit !important;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	.mt-20 {
 | 
			
		||||
		margin-top: 20rpx;
 | 
			
		||||
	}
 | 
			
		||||
	.mt-10 {
 | 
			
		||||
		margin-top: 10rpx;
 | 
			
		||||
	}
 | 
			
		||||
	.text-size-m {
 | 
			
		||||
		font-size: 24rpx;
 | 
			
		||||
	}
 | 
			
		||||
	.mx-6 {
 | 
			
		||||
		margin: 0 6rpx;
 | 
			
		||||
	}
 | 
			
		||||
	.text-grey {
 | 
			
		||||
		color: #9e9e9e;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,35 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style :styleSpacing="styleSpacing" :styleColor="styleColor" :index="index">
 | 
			
		||||
		<view class="fix-rich-text">
 | 
			
		||||
			<mp-html :content="data"></mp-html>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-rich-text" setup>
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	data: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "",
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-rich-text {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
	:deep(img) {
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		max-width: 100%;
 | 
			
		||||
		height: auto; /* 保持图像的原始纵横比 */
 | 
			
		||||
		display: block; /* 消除图像下方的默认间隙(如果是内联元素的话)*/
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,147 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style
 | 
			
		||||
		:styleSpacing="styleSpacing"
 | 
			
		||||
		:styleColor="styleColor"
 | 
			
		||||
		:position="position"
 | 
			
		||||
		:index="index"
 | 
			
		||||
	>
 | 
			
		||||
		<div class="fix-rubik-cube">
 | 
			
		||||
			<div
 | 
			
		||||
				class="box"
 | 
			
		||||
				:class="[`is-${data.mode}`]"
 | 
			
		||||
				:style="{
 | 
			
		||||
					gap: `${data.gap}rpx`,
 | 
			
		||||
					'--gap': `${data.gap}rpx`,
 | 
			
		||||
				}"
 | 
			
		||||
			>
 | 
			
		||||
				<div
 | 
			
		||||
					class="item"
 | 
			
		||||
					v-for="(item, index) in data.list"
 | 
			
		||||
					:key="index"
 | 
			
		||||
					:class="[`is-${data.mode}-item-${index}`]"
 | 
			
		||||
					@click="toPath(item.link)"
 | 
			
		||||
				>
 | 
			
		||||
					<image class="icon" :src="item.icon" mode="widthFix"></image>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-rubik-cube" setup>
 | 
			
		||||
import { type PropType } from "vue";
 | 
			
		||||
import type { Form } from "../../types/form";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	data: {
 | 
			
		||||
		type: Object as PropType<Form.RubikCube>,
 | 
			
		||||
		default: () => {
 | 
			
		||||
			return {
 | 
			
		||||
				mode: "mode-1",
 | 
			
		||||
				gap: 0,
 | 
			
		||||
				list: [
 | 
			
		||||
					{
 | 
			
		||||
						icon: "",
 | 
			
		||||
						tips: "宽度375px",
 | 
			
		||||
						link: {
 | 
			
		||||
							name: "",
 | 
			
		||||
							type: "",
 | 
			
		||||
							appid: "",
 | 
			
		||||
							page: "",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						icon: "",
 | 
			
		||||
						tips: "宽度375px",
 | 
			
		||||
						link: {
 | 
			
		||||
							name: "",
 | 
			
		||||
							type: "",
 | 
			
		||||
							appid: "",
 | 
			
		||||
							page: "",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				],
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
const emits = defineEmits(["jump"]);
 | 
			
		||||
 | 
			
		||||
function toPath(link: Form.Link) {
 | 
			
		||||
	emits("jump", link);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.box {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	.item {
 | 
			
		||||
		display: flex;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
		.icon {
 | 
			
		||||
			width: 100%;
 | 
			
		||||
			max-width: 100%;
 | 
			
		||||
			border: 0;
 | 
			
		||||
			vertical-align: middle;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.is-mode-1 {
 | 
			
		||||
	display: grid;
 | 
			
		||||
	grid-template-columns: repeat(2, 1fr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.is-mode-2 {
 | 
			
		||||
	display: grid;
 | 
			
		||||
	grid-template-columns: repeat(3, 1fr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.is-mode-3 {
 | 
			
		||||
	display: grid;
 | 
			
		||||
	grid-template-columns: 1fr 1fr;
 | 
			
		||||
	grid-template-rows: 1fr 1fr;
 | 
			
		||||
	.item {
 | 
			
		||||
		&.is-mode-3-item-0 {
 | 
			
		||||
			grid-column: 1 / 2;
 | 
			
		||||
			grid-row: 1 / 3;
 | 
			
		||||
		}
 | 
			
		||||
		&.is-mode-3-item-1 {
 | 
			
		||||
			grid-column: 2 / 3;
 | 
			
		||||
			grid-row: 1 / 2;
 | 
			
		||||
		}
 | 
			
		||||
		&.is-mode-3-item-2 {
 | 
			
		||||
			grid-column: 2 / 3;
 | 
			
		||||
			grid-row: 2 / 3;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.is-mode-4 {
 | 
			
		||||
	display: grid;
 | 
			
		||||
	grid-template-columns: 1fr 1fr;
 | 
			
		||||
	grid-template-rows: auto auto;
 | 
			
		||||
	.item {
 | 
			
		||||
		&.is-mode-4-item-0 {
 | 
			
		||||
			grid-column: 1 / 3;
 | 
			
		||||
			grid-row: 1 / 2;
 | 
			
		||||
		}
 | 
			
		||||
		&.is-mode-4-item-1 {
 | 
			
		||||
			grid-column: 1 / 2;
 | 
			
		||||
			grid-row: 2 / 3;
 | 
			
		||||
		}
 | 
			
		||||
		&.is-mode-4-item-2 {
 | 
			
		||||
			grid-column: 2 / 3;
 | 
			
		||||
			grid-row: 2 / 3;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,86 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style
 | 
			
		||||
		:styleSpacing="styleSpacing"
 | 
			
		||||
		:styleColor="styleColor"
 | 
			
		||||
		:position="position"
 | 
			
		||||
		:statusBar="statusBar"
 | 
			
		||||
		:flowInner="false"
 | 
			
		||||
		:index="index"
 | 
			
		||||
	>
 | 
			
		||||
		<view class="search-bar" @tap="toSearch">
 | 
			
		||||
			<view class="search-bar__inner" :style="style" :class="[`is-${mode}`]">
 | 
			
		||||
				<cl-icon name="search" :margin="[0, 12, 0, 0]"> </cl-icon>
 | 
			
		||||
				<cl-text :value="placeholder" :color="styleColor.color"></cl-text>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup name="fix-search">
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { useCool } from "/@/cool";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
const { router } = useCool();
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	mode: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "mode-1",
 | 
			
		||||
	},
 | 
			
		||||
	backgroundColor: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "#f6f7fa",
 | 
			
		||||
	},
 | 
			
		||||
	placeholder: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "请输入关键字进行搜索",
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
	statusBar: Boolean,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function toSearch() {
 | 
			
		||||
	router.push("/pages/goods/search");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const style = computed(() => {
 | 
			
		||||
	const { borderTopLR, borderBottomLR } = props.styleSpacing;
 | 
			
		||||
 | 
			
		||||
	const { color } = props.styleColor;
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		color: color,
 | 
			
		||||
		backgroundColor: props.backgroundColor,
 | 
			
		||||
		borderRadius: `${borderTopLR}rpx ${borderTopLR}rpx ${borderBottomLR}rpx ${borderBottomLR}rpx`,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.search-bar {
 | 
			
		||||
	width: 100%;
 | 
			
		||||
 | 
			
		||||
	&__inner {
 | 
			
		||||
		display: flex;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		height: 70rpx;
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		background-color: #f7f7f7;
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
		padding: 0 24rpx;
 | 
			
		||||
	}
 | 
			
		||||
	.is-mode-1 {
 | 
			
		||||
		justify-content: flex-start;
 | 
			
		||||
	}
 | 
			
		||||
	.is-mode-2 {
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
	}
 | 
			
		||||
	.is-mode-3 {
 | 
			
		||||
		justify-content: flex-end;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,151 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<view class="fix-suspension" :style="baseStyle">
 | 
			
		||||
		<view class="menu" :style="innerStyle" @click="toggleMenu">
 | 
			
		||||
			<image class="icon" :src="list[0].icon"></image>
 | 
			
		||||
		</view>
 | 
			
		||||
		<view class="menu-slide">
 | 
			
		||||
			<view
 | 
			
		||||
				v-for="(item, index) in visibleItems"
 | 
			
		||||
				:key="index"
 | 
			
		||||
				class="sub-menu"
 | 
			
		||||
				:style="subMenuStyle(index)"
 | 
			
		||||
				@click="toPath(item.link)"
 | 
			
		||||
			>
 | 
			
		||||
				<image class="icon" :src="item.icon"></image>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</view>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-suspension" setup>
 | 
			
		||||
import { computed, ref, type PropType } from "vue";
 | 
			
		||||
import type { Form } from "../../types/form";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	mode: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "left",
 | 
			
		||||
	},
 | 
			
		||||
	backgroundColor: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "rgba(0, 0, 0, 0.8)",
 | 
			
		||||
	},
 | 
			
		||||
	offsetBottom: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 50,
 | 
			
		||||
	},
 | 
			
		||||
	shadow: {
 | 
			
		||||
		type: Boolean,
 | 
			
		||||
		default: false,
 | 
			
		||||
	},
 | 
			
		||||
	list: {
 | 
			
		||||
		type: Array as PropType<Form.Suspension[]>,
 | 
			
		||||
		default: () => [
 | 
			
		||||
			{
 | 
			
		||||
				icon: "https://tsb-yx.oss-cn-guangzhou.aliyuncs.com/app/mini/float-menu.png",
 | 
			
		||||
				tips: "宽高128px",
 | 
			
		||||
				link: {
 | 
			
		||||
					name: "",
 | 
			
		||||
					type: "",
 | 
			
		||||
					appid: "",
 | 
			
		||||
					page: "",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		],
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
const emits = defineEmits(["jump"]);
 | 
			
		||||
 | 
			
		||||
const baseStyle = computed(() => {
 | 
			
		||||
	return {
 | 
			
		||||
		bottom: `${props.offsetBottom}rpx`,
 | 
			
		||||
		[props.mode]: "10px",
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const innerStyle = computed(() => {
 | 
			
		||||
	return {
 | 
			
		||||
		background: props.backgroundColor,
 | 
			
		||||
		boxShadow: props.shadow ? "0 4px 10px rgba(0, 0, 0, 0.3)" : undefined,
 | 
			
		||||
	};
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const isMenuOpen = ref(false);
 | 
			
		||||
const toggleMenu = () => {
 | 
			
		||||
	if (props.list.length > 1) {
 | 
			
		||||
		isMenuOpen.value = !isMenuOpen.value;
 | 
			
		||||
	} else {
 | 
			
		||||
		toPath(props.list[0].link);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 控制显示的子按钮
 | 
			
		||||
const visibleItems = computed(() => {
 | 
			
		||||
	return isMenuOpen.value ? props.list.slice(1) : [];
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 子菜单项的样式和动画效果
 | 
			
		||||
const subMenuStyle = (index: number) => {
 | 
			
		||||
	const offset = 90; // 每个子菜单项的高度
 | 
			
		||||
	return {
 | 
			
		||||
		transition: `transform 0.3s ease, opacity 0.3s ease`,
 | 
			
		||||
		transform: isMenuOpen.value ? `translateY(-${(index + 1) * offset}rpx)` : "translateY(0)",
 | 
			
		||||
		opacity: isMenuOpen.value ? 1 : 0,
 | 
			
		||||
		background: props.backgroundColor,
 | 
			
		||||
		boxShadow: props.shadow ? "0 4px 10px rgba(0, 0, 0, 0.3)" : undefined,
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function toPath(link: Form.Link) {
 | 
			
		||||
	emits("jump", link);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-suspension {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	position: fixed;
 | 
			
		||||
	z-index: 400;
 | 
			
		||||
 | 
			
		||||
	.menu {
 | 
			
		||||
		width: 80rpx;
 | 
			
		||||
		height: 80rpx;
 | 
			
		||||
		border-radius: 50%;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		cursor: pointer;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.icon {
 | 
			
		||||
		width: 50rpx;
 | 
			
		||||
		height: 50rpx;
 | 
			
		||||
		user-select: none;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.menu-slide {
 | 
			
		||||
		position: absolute; /* 依赖于父元素定位 */
 | 
			
		||||
		bottom: 80rpx; /* 让子菜单从主菜单上方展开 */
 | 
			
		||||
		left: 0; /* 根据需要调整位置 */
 | 
			
		||||
		width: 80rpx; /* 确保子菜单能够展示 */
 | 
			
		||||
		transition: all 0.3s ease;
 | 
			
		||||
		overflow: visible; /* 确保子菜单不被裁剪 */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.sub-menu {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		width: 80rpx;
 | 
			
		||||
		height: 80rpx;
 | 
			
		||||
		border-radius: 50%;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		cursor: pointer;
 | 
			
		||||
		z-index: 100;
 | 
			
		||||
		opacity: 0;
 | 
			
		||||
		transition:
 | 
			
		||||
			transform 0.3s ease,
 | 
			
		||||
			opacity 0.3s ease;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										129
									
								
								uni_modules/cool-fixtures/components/fix-title/fix-title.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								uni_modules/cool-fixtures/components/fix-title/fix-title.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,129 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style
 | 
			
		||||
		:styleSpacing="styleSpacing"
 | 
			
		||||
		:styleColor="styleColor"
 | 
			
		||||
		:position="position"
 | 
			
		||||
		:index="index"
 | 
			
		||||
	>
 | 
			
		||||
		<view class="fix-title">
 | 
			
		||||
			<view class="inner">
 | 
			
		||||
				<view class="left" @click="toPath(left.link)">
 | 
			
		||||
					<image v-if="left.icon" :src="left.icon" class="icon" />
 | 
			
		||||
					<view class="text" :style="{ color: left.color }" :class="[`is-${mode}`]">
 | 
			
		||||
						{{ left.text }}
 | 
			
		||||
					</view>
 | 
			
		||||
				</view>
 | 
			
		||||
				<view class="right" @click="toPath(right.link)">
 | 
			
		||||
					<text class="text" :style="{ color: right.color }">{{ right.text }}</text>
 | 
			
		||||
					<image v-if="right.icon" :src="right.icon" class="icon" />
 | 
			
		||||
				</view>
 | 
			
		||||
			</view>
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-title" setup>
 | 
			
		||||
import { type PropType } from "vue";
 | 
			
		||||
import type { Form } from "../../types/form";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	mode: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "left",
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	left: {
 | 
			
		||||
		type: Object as PropType<Form.Title>,
 | 
			
		||||
		default: () => {
 | 
			
		||||
			return {
 | 
			
		||||
				text: "标题内容",
 | 
			
		||||
				color: "#000",
 | 
			
		||||
				icon: "",
 | 
			
		||||
				link: {
 | 
			
		||||
					name: "",
 | 
			
		||||
					type: "",
 | 
			
		||||
					appid: "",
 | 
			
		||||
					page: "",
 | 
			
		||||
				},
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	right: {
 | 
			
		||||
		type: Object as PropType<Form.Title>,
 | 
			
		||||
		default: () => {
 | 
			
		||||
			return {
 | 
			
		||||
				text: "查看",
 | 
			
		||||
				color: "#a8abb2",
 | 
			
		||||
				icon: "",
 | 
			
		||||
				link: {
 | 
			
		||||
					name: "",
 | 
			
		||||
					type: "",
 | 
			
		||||
					appid: "",
 | 
			
		||||
					page: "",
 | 
			
		||||
				},
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
const emits = defineEmits(["jump"]);
 | 
			
		||||
 | 
			
		||||
function toPath(link: Form.Link) {
 | 
			
		||||
	emits("jump", link);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-title {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
 | 
			
		||||
	.inner {
 | 
			
		||||
		height: 80rpx;
 | 
			
		||||
		flex: 1;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: space-around;
 | 
			
		||||
		padding: 20rpx;
 | 
			
		||||
		.left {
 | 
			
		||||
			display: flex;
 | 
			
		||||
			align-items: center;
 | 
			
		||||
			flex: 1;
 | 
			
		||||
			.icon {
 | 
			
		||||
				margin-right: 20rpx;
 | 
			
		||||
				width: 60rpx;
 | 
			
		||||
				height: 60rpx;
 | 
			
		||||
			}
 | 
			
		||||
			.text {
 | 
			
		||||
				flex: 1;
 | 
			
		||||
				font-size: 32rpx;
 | 
			
		||||
				font-weight: bold;
 | 
			
		||||
			}
 | 
			
		||||
			.is-left {
 | 
			
		||||
				text-align: left;
 | 
			
		||||
			}
 | 
			
		||||
			.is-center {
 | 
			
		||||
				text-align: center;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		.right {
 | 
			
		||||
			display: flex;
 | 
			
		||||
			justify-content: flex-end;
 | 
			
		||||
			align-items: center;
 | 
			
		||||
			.icon {
 | 
			
		||||
				margin-left: 20rpx;
 | 
			
		||||
				width: 40rpx;
 | 
			
		||||
				height: 40rpx;
 | 
			
		||||
			}
 | 
			
		||||
			.text {
 | 
			
		||||
				font-size: 24rpx;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,44 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<view class="fix-top-bar" v-if="isShow">
 | 
			
		||||
	<view
 | 
			
		||||
			v-if="fixed"
 | 
			
		||||
			:style="{
 | 
			
		||||
				paddingTop: `${(statusBar ? 0 : statusBarHeight) + 44}px`,
 | 
			
		||||
			}"
 | 
			
		||||
		></view>
 | 
			
		||||
		<cl-topbar
 | 
			
		||||
			:title="title"
 | 
			
		||||
			:color="color"
 | 
			
		||||
			:border="border"
 | 
			
		||||
			:fixed="fixed"
 | 
			
		||||
			:showBack="showBack"
 | 
			
		||||
			:withMp="true"
 | 
			
		||||
			:background-color="backgroundColor"
 | 
			
		||||
		></cl-topbar>
 | 
			
		||||
	</view>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup name="fix-top-bar">
 | 
			
		||||
const { statusBarHeight = 0 } = uni.getSystemInfoSync();
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	backgroundColor: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "#fff",
 | 
			
		||||
	},
 | 
			
		||||
	color: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "black",
 | 
			
		||||
	},
 | 
			
		||||
	title: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "",
 | 
			
		||||
	},
 | 
			
		||||
	isShow: Boolean,
 | 
			
		||||
	showBack: Boolean,
 | 
			
		||||
	border: Boolean,
 | 
			
		||||
	fixed: Boolean,
 | 
			
		||||
	statusBar: Boolean,
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped></style>
 | 
			
		||||
							
								
								
									
										65
									
								
								uni_modules/cool-fixtures/components/fix-video/fix-video.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								uni_modules/cool-fixtures/components/fix-video/fix-video.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style :styleSpacing="styleSpacing" :styleColor="styleColor" :index="index">
 | 
			
		||||
		<div class="fix-video">
 | 
			
		||||
			<div class="inner" v-if="video">
 | 
			
		||||
				<video
 | 
			
		||||
					:src="video"
 | 
			
		||||
					:poster="cover"
 | 
			
		||||
					class="vertical"
 | 
			
		||||
					controls
 | 
			
		||||
					:class="[mode == 'horizontal' ? 'horizontal' : 'vertical']"
 | 
			
		||||
				></video>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-video" setup>
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
	mode: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "horizontal",
 | 
			
		||||
	},
 | 
			
		||||
	video: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "",
 | 
			
		||||
	},
 | 
			
		||||
	cover: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "",
 | 
			
		||||
	},
 | 
			
		||||
	type: {
 | 
			
		||||
		type: String,
 | 
			
		||||
		default: "local",
 | 
			
		||||
	},
 | 
			
		||||
	index: {
 | 
			
		||||
		type: Number,
 | 
			
		||||
		default: 0,
 | 
			
		||||
	},
 | 
			
		||||
	...baseProps,
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-video {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
	video {
 | 
			
		||||
		margin: 0;
 | 
			
		||||
		padding: 0;
 | 
			
		||||
		display: block; /* 避免 inline 元素造成的间隙 */
 | 
			
		||||
		object-fit: cover;
 | 
			
		||||
	}
 | 
			
		||||
	.vertical {
 | 
			
		||||
		width: 750rpx;
 | 
			
		||||
		height: 1334rpx; /* 高度根据宽高比自动调整 */
 | 
			
		||||
		object-fit: contain; /* 保持视频的宽高比,不会拉伸 */
 | 
			
		||||
	}
 | 
			
		||||
	.horizontal {
 | 
			
		||||
		width: 750rpx; /* 屏幕宽度适配 */
 | 
			
		||||
		height: calc(750rpx * 9 / 16); /* 适用于 16:9 的宽高比 */
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -0,0 +1,30 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<fix-base-style :styleSpacing="styleSpacing">
 | 
			
		||||
		<view class="fix-wechat" v-show="is_show">
 | 
			
		||||
			<!-- #ifdef MP-WEIXIN -->
 | 
			
		||||
			<official-account @bindload="load" @binderror="error"></official-account>
 | 
			
		||||
			<!-- #endif -->
 | 
			
		||||
		</view>
 | 
			
		||||
	</fix-base-style>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" name="fix-wechat" setup>
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
import { baseProps } from "../../hooks";
 | 
			
		||||
 | 
			
		||||
defineProps({ ...baseProps });
 | 
			
		||||
const is_show = ref(false);
 | 
			
		||||
function load() {
 | 
			
		||||
	is_show.value = true;
 | 
			
		||||
}
 | 
			
		||||
function error() {
 | 
			
		||||
	is_show.value = false;
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.fix-wechat {
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										8
									
								
								uni_modules/cool-fixtures/config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								uni_modules/cool-fixtures/config.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
import type { ModuleConfig } from "/@/cool";
 | 
			
		||||
 | 
			
		||||
export default (): ModuleConfig => {
 | 
			
		||||
    return {
 | 
			
		||||
        // 描述
 | 
			
		||||
        description: "在coo-uni基础上开发的自定义页面,修改或新增组件非常简单!",
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										36
									
								
								uni_modules/cool-fixtures/hooks/base-props.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								uni_modules/cool-fixtures/hooks/base-props.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
export const baseProps = {
 | 
			
		||||
    styleSpacing: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        default: () => {
 | 
			
		||||
            return {
 | 
			
		||||
                marginTop: 0,
 | 
			
		||||
                marginBottom: 0,
 | 
			
		||||
                marginLR: 0,
 | 
			
		||||
                padding: 0,
 | 
			
		||||
                borderTopLR: 0,
 | 
			
		||||
                borderBottomLR: 0,
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
    styleColor: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        default: () => {
 | 
			
		||||
            return {
 | 
			
		||||
                color: "#000",
 | 
			
		||||
                backgroundColor: "#FFFFFF",
 | 
			
		||||
                opacity: 1,
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
    position: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        default: () => {
 | 
			
		||||
            return {
 | 
			
		||||
                mode: "static",
 | 
			
		||||
                top: 0,
 | 
			
		||||
                zIndex: 1,
 | 
			
		||||
                isSeat: true,
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								uni_modules/cool-fixtures/hooks/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								uni_modules/cool-fixtures/hooks/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
export * from "./base-props";
 | 
			
		||||
							
								
								
									
										54
									
								
								uni_modules/cool-fixtures/pages/detail.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								uni_modules/cool-fixtures/pages/detail.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<cl-page :backgroundColor="data.background" :backgroundImage="data.backgroundImage" :statusBar="!!data.statusBar"
 | 
			
		||||
		:statusBarBackground="data.statusBarColor">
 | 
			
		||||
		<fix-index :list="data.data" :statusBar="!!data.statusBar"></fix-index>
 | 
			
		||||
	</cl-page>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
	import { ref, nextTick } from "vue";
 | 
			
		||||
	import { useCool } from "/@/cool";
 | 
			
		||||
	import { onReady, onPageScroll, onLoad } from "@dcloudio/uni-app";
 | 
			
		||||
	import { useUi } from "/$/cool-ui";
 | 
			
		||||
	defineProps({ id: String });
 | 
			
		||||
	const { service } = useCool();
 | 
			
		||||
	const ui = useUi();
 | 
			
		||||
 | 
			
		||||
	const data = ref({
 | 
			
		||||
		background: "#f5f6fa",
 | 
			
		||||
		backgroundImage: "",
 | 
			
		||||
		statusBarColor: "",
 | 
			
		||||
		statusBar: 1,
 | 
			
		||||
		name: "",
 | 
			
		||||
		data: [],
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	function getPage(id : string) {
 | 
			
		||||
		service.fixtures.mould
 | 
			
		||||
			.getPage({ id })
 | 
			
		||||
			.then((res : any) => {
 | 
			
		||||
				data.value = res;
 | 
			
		||||
			})
 | 
			
		||||
			.catch((e) => {
 | 
			
		||||
				ui.showToast(e.message);
 | 
			
		||||
			})
 | 
			
		||||
			.finally(() => {
 | 
			
		||||
				nextTick().then(() => {
 | 
			
		||||
					uni.hideLoading();
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
	}
 | 
			
		||||
	// 加载数据
 | 
			
		||||
	function loadSuccess(query : any) {
 | 
			
		||||
		getPage(query.id);
 | 
			
		||||
	}
 | 
			
		||||
	onReady(() => { });
 | 
			
		||||
	onLoad((query : any) => {
 | 
			
		||||
		uni.showLoading({
 | 
			
		||||
			title: "加载中...",
 | 
			
		||||
			mask: true,
 | 
			
		||||
		});
 | 
			
		||||
		loadSuccess(query)
 | 
			
		||||
	});
 | 
			
		||||
	onPageScroll(() => { });
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										54
									
								
								uni_modules/cool-fixtures/pages/preview.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								uni_modules/cool-fixtures/pages/preview.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<cl-page :backgroundColor="data.background" :backgroundImage="data.backgroundImage" :statusBar="!!data.statusBar"
 | 
			
		||||
		:statusBarBackground="data.statusBarColor">
 | 
			
		||||
		<fix-index :list="data.form" :statusBar="!!data.statusBar"></fix-index>
 | 
			
		||||
	</cl-page>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
	import { ref, nextTick } from "vue";
 | 
			
		||||
	import { useCool } from "/@/cool";
 | 
			
		||||
	import { onReady, onPageScroll, onLoad } from "@dcloudio/uni-app";
 | 
			
		||||
	import { useUi } from "/$/cool-ui";
 | 
			
		||||
	defineProps({ id: String });
 | 
			
		||||
	const { service } = useCool();
 | 
			
		||||
	const ui = useUi();
 | 
			
		||||
 | 
			
		||||
	const data = ref({
 | 
			
		||||
		background: "#f5f6fa",
 | 
			
		||||
		backgroundImage: "",
 | 
			
		||||
		statusBarColor: "",
 | 
			
		||||
		statusBar: 1,
 | 
			
		||||
		name: "",
 | 
			
		||||
		form: [],
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	function getPage(id : string) {
 | 
			
		||||
		service.fixtures.mould
 | 
			
		||||
			.getPage({ id })
 | 
			
		||||
			.then((res : any) => {
 | 
			
		||||
				data.value = res;
 | 
			
		||||
			})
 | 
			
		||||
			.catch((e) => {
 | 
			
		||||
				ui.showToast(e.message);
 | 
			
		||||
			})
 | 
			
		||||
			.finally(() => {
 | 
			
		||||
				nextTick().then(() => {
 | 
			
		||||
					uni.hideLoading();
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
	}
 | 
			
		||||
	// 加载数据
 | 
			
		||||
	function loadSuccess(query : any) {
 | 
			
		||||
		getPage(query.id);
 | 
			
		||||
	}
 | 
			
		||||
	onReady(() => { });
 | 
			
		||||
	onLoad((query : any) => {
 | 
			
		||||
		uni.showLoading({
 | 
			
		||||
			title: "加载中...",
 | 
			
		||||
			mask: true,
 | 
			
		||||
		});
 | 
			
		||||
		loadSuccess(query)
 | 
			
		||||
	});
 | 
			
		||||
	onPageScroll(() => { });
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										23
									
								
								uni_modules/cool-fixtures/pages_init.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								uni_modules/cool-fixtures/pages_init.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
{
 | 
			
		||||
    "subPackages": [
 | 
			
		||||
      {
 | 
			
		||||
        "root": "uni_modules/cool-fixtures/pages",
 | 
			
		||||
        "pages": [
 | 
			
		||||
            {
 | 
			
		||||
                "path": "detail",
 | 
			
		||||
                "style": {
 | 
			
		||||
                   "navigationStyle": "custom",
 | 
			
		||||
                  "navigationBarTitleText": "自定义页面"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
          {
 | 
			
		||||
            "path": "preview",
 | 
			
		||||
            "style": {
 | 
			
		||||
              "navigationStyle": "custom",
 | 
			
		||||
              "navigationBarTitleText": "页面预览"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										109
									
								
								uni_modules/cool-fixtures/types/form.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								uni_modules/cool-fixtures/types/form.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,109 @@
 | 
			
		||||
export declare namespace Form {
 | 
			
		||||
    interface Menu {
 | 
			
		||||
        text: string,
 | 
			
		||||
        useText: boolean,
 | 
			
		||||
        mode: string,
 | 
			
		||||
        link: Link,
 | 
			
		||||
        icon: string,
 | 
			
		||||
        color: string,
 | 
			
		||||
        backgroundColor: string
 | 
			
		||||
    }
 | 
			
		||||
    interface Link {
 | 
			
		||||
        page: string,
 | 
			
		||||
        appid: string,
 | 
			
		||||
        type: string,
 | 
			
		||||
        name: string
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    interface Banner {
 | 
			
		||||
        pic: string,
 | 
			
		||||
        link: Link
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    interface Spacing {
 | 
			
		||||
        marginTop: number,
 | 
			
		||||
        marginBottom: number,
 | 
			
		||||
        marginLR: number,
 | 
			
		||||
        padding: number,
 | 
			
		||||
        borderTopLR: number,
 | 
			
		||||
        borderBottomLR: number
 | 
			
		||||
    }
 | 
			
		||||
    interface Color {
 | 
			
		||||
        color: string,
 | 
			
		||||
        backgroundColor: string,
 | 
			
		||||
        opacity: number
 | 
			
		||||
    }
 | 
			
		||||
    interface Picture {
 | 
			
		||||
        pic: string,
 | 
			
		||||
        link: Link
 | 
			
		||||
    }
 | 
			
		||||
    interface Title {
 | 
			
		||||
        text: string,
 | 
			
		||||
        text2?: string,
 | 
			
		||||
        color: string,
 | 
			
		||||
        icon: string,
 | 
			
		||||
        link: Link
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    interface Hot {
 | 
			
		||||
        x: number,
 | 
			
		||||
        y: number,
 | 
			
		||||
        w: number,
 | 
			
		||||
        h: number,
 | 
			
		||||
        relativeX: number,
 | 
			
		||||
        relativeY: number,
 | 
			
		||||
        relativeW: number,
 | 
			
		||||
        relativeH: number,
 | 
			
		||||
        index: number,
 | 
			
		||||
        link: Link
 | 
			
		||||
    }
 | 
			
		||||
    interface HotImage {
 | 
			
		||||
        pic: string,
 | 
			
		||||
        link: Link,
 | 
			
		||||
        width: number,
 | 
			
		||||
        height: number,
 | 
			
		||||
        attr: Hot[]
 | 
			
		||||
    }
 | 
			
		||||
    interface RubikCubeMode {
 | 
			
		||||
        type: string,
 | 
			
		||||
        label: string,
 | 
			
		||||
        list: {
 | 
			
		||||
            icon: string,
 | 
			
		||||
            tips: string,
 | 
			
		||||
            link: Link
 | 
			
		||||
        }[]
 | 
			
		||||
    }
 | 
			
		||||
    interface RubikCube {
 | 
			
		||||
        mode: string,
 | 
			
		||||
        gap: number,
 | 
			
		||||
        list: {
 | 
			
		||||
            icon: string,
 | 
			
		||||
            tips: string,
 | 
			
		||||
            link: Link
 | 
			
		||||
        }[]
 | 
			
		||||
    }
 | 
			
		||||
    interface Goods {
 | 
			
		||||
        mode: string,
 | 
			
		||||
        source: string,
 | 
			
		||||
        gap: number,
 | 
			
		||||
        num: number,
 | 
			
		||||
        attribute: number,
 | 
			
		||||
        isVoucher: boolean,
 | 
			
		||||
        isShadow: boolean,
 | 
			
		||||
        type: { name: string, pic?: string, id: number }[],
 | 
			
		||||
        list: {
 | 
			
		||||
            mainPic: string,
 | 
			
		||||
            title: string,
 | 
			
		||||
            price: number,
 | 
			
		||||
            sold: number,
 | 
			
		||||
            attribute?: number,
 | 
			
		||||
            id: number,
 | 
			
		||||
        }[]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    interface Suspension {
 | 
			
		||||
        icon: string;
 | 
			
		||||
        tips: string,
 | 
			
		||||
        link: Link
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -5,11 +5,12 @@
 | 
			
		||||
			`theme-${app.theme.name}`,
 | 
			
		||||
			{
 | 
			
		||||
				'is-fullscreen': fullscreen,
 | 
			
		||||
				'is-safe-area-bottom': fullscreen,
 | 
			
		||||
			},
 | 
			
		||||
		]"
 | 
			
		||||
		:style="{
 | 
			
		||||
			padding: parseRpx(padding),
 | 
			
		||||
			height,
 | 
			
		||||
			height
 | 
			
		||||
		}"
 | 
			
		||||
	>
 | 
			
		||||
		<!-- 加载框 -->
 | 
			
		||||
@ -42,13 +43,15 @@
 | 
			
		||||
	<view
 | 
			
		||||
		class="cl-page__bg"
 | 
			
		||||
		:style="{
 | 
			
		||||
			background,
 | 
			
		||||
			backgroundColor: background,
 | 
			
		||||
			backgroundImage: 'url(' + backgroundImage + ')',
 | 
			
		||||
		}"
 | 
			
		||||
	></view>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, reactive, getCurrentInstance, onMounted } from "vue";
 | 
			
		||||
import { onPageScroll } from "@dcloudio/uni-app";
 | 
			
		||||
import { computed, defineComponent, ref, reactive, getCurrentInstance, onMounted } from "vue";
 | 
			
		||||
import { useApp, useCool} from "/@/cool";
 | 
			
		||||
import { parseRpx } from "/@/cool/utils";
 | 
			
		||||
import { isString } from "lodash-es";
 | 
			
		||||
@ -73,6 +76,11 @@ export default defineComponent({
 | 
			
		||||
		statusBarBackground: String,
 | 
			
		||||
		// 页面背景色
 | 
			
		||||
		backgroundColor: String,
 | 
			
		||||
		// 背景图片
 | 
			
		||||
		backgroundImage: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			default: "",
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	setup(props) {
 | 
			
		||||
@ -89,8 +97,9 @@ export default defineComponent({
 | 
			
		||||
		const { proxy }: any = getCurrentInstance();
 | 
			
		||||
 | 
			
		||||
		// 是否显示导航栏
 | 
			
		||||
		const statusBar = info?.isCustomNavbar ? props.statusBar : false;
 | 
			
		||||
 | 
			
		||||
		const statusBar = computed(() => {
 | 
			
		||||
			return info?.isCustomNavbar ? props.statusBar : false;
 | 
			
		||||
		});
 | 
			
		||||
		// 背景色
 | 
			
		||||
		const background = computed(() => {
 | 
			
		||||
			return (
 | 
			
		||||
@ -112,14 +121,15 @@ export default defineComponent({
 | 
			
		||||
			// #ifdef H5
 | 
			
		||||
			h = windowHeight;
 | 
			
		||||
			// #endif
 | 
			
		||||
 | 
			
		||||
			// #ifndef H5
 | 
			
		||||
			h = screenHeight - statusBarHeight;
 | 
			
		||||
 | 
			
		||||
			h = screenHeight - (statusBar ? statusBarHeight : 0);
 | 
			
		||||
			if (!info?.isCustomNavbar) {
 | 
			
		||||
				h -= 44;
 | 
			
		||||
				h -= statusBarHeight;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// #endif
 | 
			
		||||
			if (props.fullscreen) return h + "px";
 | 
			
		||||
 | 
			
		||||
			return h - (safeAreaInsets?.bottom || 0) + "px";
 | 
			
		||||
		});
 | 
			
		||||
@ -199,11 +209,11 @@ export default defineComponent({
 | 
			
		||||
			uni.createSelectorQuery()
 | 
			
		||||
				.in(proxy)
 | 
			
		||||
				.select(".cl-page")
 | 
			
		||||
				.boundingClientRect((a) => {
 | 
			
		||||
				.boundingClientRect((a: any) => {
 | 
			
		||||
					uni.createSelectorQuery()
 | 
			
		||||
						.in(proxy)
 | 
			
		||||
						.select(".safe-area-bottom")
 | 
			
		||||
						.boundingClientRect((b) => {
 | 
			
		||||
						.boundingClientRect((b: any) => {
 | 
			
		||||
							const scrollTop = top + (a?.height || 0) - (b?.bottom || 0);
 | 
			
		||||
 | 
			
		||||
							uni.pageScrollTo({
 | 
			
		||||
@ -215,13 +225,18 @@ export default defineComponent({
 | 
			
		||||
				})
 | 
			
		||||
				.exec();
 | 
			
		||||
		};
 | 
			
		||||
		const scrollTop = ref(0);
 | 
			
		||||
 | 
			
		||||
		onPageScroll((e: any) => {
 | 
			
		||||
			scrollTop.value = e.scrollTop;
 | 
			
		||||
		});
 | 
			
		||||
		return {
 | 
			
		||||
			app,
 | 
			
		||||
			background,
 | 
			
		||||
			height,
 | 
			
		||||
			refs,
 | 
			
		||||
			setRefs,
 | 
			
		||||
			scrollTop,
 | 
			
		||||
			loader,
 | 
			
		||||
			parseRpx,
 | 
			
		||||
			statusBar,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user