build: delete demo
This commit is contained in:
parent
2cab29bacd
commit
82a1342614
@ -34,14 +34,8 @@ export default defineUniPages({
|
|||||||
{
|
{
|
||||||
iconPath: 'static/tabbar/example.png',
|
iconPath: 'static/tabbar/example.png',
|
||||||
selectedIconPath: 'static/tabbar/exampleHL.png',
|
selectedIconPath: 'static/tabbar/exampleHL.png',
|
||||||
pagePath: 'pages/demo/index',
|
pagePath: 'pages/index/i18n',
|
||||||
text: '示例',
|
text: '多语言',
|
||||||
},
|
|
||||||
{
|
|
||||||
iconPath: 'static/tabbar/personal.png',
|
|
||||||
selectedIconPath: 'static/tabbar/personalHL.png',
|
|
||||||
pagePath: 'pages/my/index',
|
|
||||||
text: '我的',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: 'auto import component' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>欢迎使用 vitess-uni-app</h1>
|
|
||||||
<AppTest> 这个组件会自动导入 </AppTest>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
@ -1,21 +0,0 @@
|
|||||||
<route lang="json5" type="page">
|
|
||||||
{
|
|
||||||
style: { navigationBarTitleText: 'enum' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="">enum</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { TestEnum } from '@/typings.d'
|
|
||||||
|
|
||||||
type T = TestEnum.A
|
|
||||||
const a = 'a' as T
|
|
||||||
console.log(a)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
//
|
|
||||||
</style>
|
|
@ -1,42 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: '微信分享' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="text-green">微信分享页</view>
|
|
||||||
<view class="text-green-500">请在微信小程序中体验,或者开发者工具</view>
|
|
||||||
<view>1) 默认是不激活”发送给朋友“和”分享到朋友圈“的,如下图</view>
|
|
||||||
<image
|
|
||||||
src="https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/wx-share/wx-share-before.png"
|
|
||||||
mode="widthFix"
|
|
||||||
/>
|
|
||||||
<view>2) 增加了onShareAppMessage和onShareTimeline后,就可以微信分享了,如下图</view>
|
|
||||||
<image
|
|
||||||
src="https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/wx-share/wx-share-after.png"
|
|
||||||
mode="widthFix"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
|
||||||
/** 激活“分享给好友” */
|
|
||||||
onShareAppMessage((options: Page.ShareAppMessageOption): Page.CustomShareContent => {
|
|
||||||
console.log('options:', options)
|
|
||||||
return {
|
|
||||||
title: '自定义分享标题',
|
|
||||||
path: '/pages/index/index?id=xxx',
|
|
||||||
imageUrl:
|
|
||||||
'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/pretty-girl.png',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
/** 激活“分享到朋友圈”, 注意:需要先激活“分享给好友” */
|
|
||||||
onShareTimeline((): Page.ShareTimelineContent => {
|
|
||||||
return {
|
|
||||||
title: '自定义分享标题',
|
|
||||||
query: 'a=1&b=2',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
@ -1,19 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
style: { navigationBarTitleText: '自定义导航栏', navigationStyle: 'custom' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<fly-navbar />
|
|
||||||
<view class="bg-green-300 min-h-20" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
|
|
||||||
<view class="p-4"> 自定义导航栏,设置"navigationStyle":"custom" </view>
|
|
||||||
<view class="p-4"> 通常页面顶部有一个图片或背景色 </view>
|
|
||||||
</view>
|
|
||||||
<fly-content :line="20" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
// 获取屏幕边界到安全区域距离
|
|
||||||
const { safeAreaInsets } = uni.getSystemInfoSync()
|
|
||||||
</script>
|
|
@ -1,40 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: 'pinia+持久化' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="flex justify-center items-center text-blue-500 mt-4 mb-4">
|
|
||||||
<view class="w-20">Count: {{ countStore.count }}</view>
|
|
||||||
<button class="ml-2 mr-2" @click="countStore.decrement">-1</button>
|
|
||||||
<button class="ml-2 mr-2" @click="countStore.increment">+1</button>
|
|
||||||
<button class="ml-2 mr-2" @click="countStore.reset">重置</button>
|
|
||||||
</view>
|
|
||||||
<view class="m-8 text-4 leading-8">
|
|
||||||
<view class="text-center">{{ userStore.userInfo }}</view>
|
|
||||||
<view class="text-center">请观察小程序的store,可以看到是可以正常设置的</view>
|
|
||||||
<button @click="setUserInfo">设置UserInfo</button>
|
|
||||||
<button @click="clearUserInfo" class="mt-4">清除UserInfo</button>
|
|
||||||
<button @click="resetUserStore" class="mt-4">reset UserStore</button>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useCountStore, useUserStore } from '@/store'
|
|
||||||
|
|
||||||
const countStore = useCountStore()
|
|
||||||
|
|
||||||
const userStore = useUserStore()
|
|
||||||
|
|
||||||
const setUserInfo = () => {
|
|
||||||
userStore.setUserInfo({ nickname: 'fly', avatar: '', token: 'abcdef' })
|
|
||||||
}
|
|
||||||
const clearUserInfo = () => {
|
|
||||||
userStore.clearUserInfo()
|
|
||||||
}
|
|
||||||
const resetUserStore = () => {
|
|
||||||
userStore.reset()
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,67 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: '请求',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="mt-6">
|
|
||||||
<!-- http://localhost:9100/#/pages/index/request -->
|
|
||||||
<button @click="getFoo" class="my-4">测试 GET 请求</button>
|
|
||||||
<view class="text-xl">请求数据如下</view>
|
|
||||||
<view class="text-green h-10">{{ JSON.stringify(data) }}</view>
|
|
||||||
<view class="text-xl">完整数据</view>
|
|
||||||
<view class="text-green h-20">{{ JSON.stringify(originalData) }}</view>
|
|
||||||
<button @click="postFoo" class="my-4">测试 POST 请求</button>
|
|
||||||
<view class="text-xl">请求数据如下</view>
|
|
||||||
<view class="text-green h-10">{{ JSON.stringify(data2) }}</view>
|
|
||||||
|
|
||||||
<button class="my-8" type="warn" @click="reset">一键清空数据</button>
|
|
||||||
|
|
||||||
<view class="my-2">使用的是 laf 云后台</view>
|
|
||||||
<view class="text-green-400">我的推荐码,可以获得佣金</view>
|
|
||||||
<!-- #ifdef H5 -->
|
|
||||||
<view class="my-2 text-center">
|
|
||||||
<a class="my-2 text-center" :href="recommendUrl" target="_blank">{{ recommendUrl }}</a>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<!-- #ifndef H5 -->
|
|
||||||
<view class="my-2 text-left text-sm">{{ recommendUrl }}</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { getFooAPI, postFooAPI, IFooItem } from '@/service/foo'
|
|
||||||
import { IResData } from '@/typings'
|
|
||||||
|
|
||||||
const recommendUrl = ref('http://laf.run/signup?code=ohaOgIX')
|
|
||||||
|
|
||||||
onLoad(() => {
|
|
||||||
getFoo()
|
|
||||||
postFoo()
|
|
||||||
})
|
|
||||||
const originalData = ref<IResData<IFooItem>>()
|
|
||||||
const data = ref<IFooItem>()
|
|
||||||
const getFoo = async () => {
|
|
||||||
const res = await getFooAPI('菲鸽')
|
|
||||||
data.value = res.result
|
|
||||||
originalData.value = res
|
|
||||||
}
|
|
||||||
|
|
||||||
const data2 = ref<IFooItem>()
|
|
||||||
const postFoo = async () => {
|
|
||||||
const res = await postFooAPI('菲鸽2')
|
|
||||||
data2.value = res.result
|
|
||||||
}
|
|
||||||
|
|
||||||
const reset = () => {
|
|
||||||
data.value = undefined
|
|
||||||
data2.value = undefined
|
|
||||||
originalData.value = undefined
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,145 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: '通屏+下拉刷新+自定义导航栏',
|
|
||||||
enablePullDownRefresh: false,
|
|
||||||
backgroundColor: '#23c09c', // 这个背景色要与页面的.top-section的背景图差不多,这样下拉刷新看起来才比较协调
|
|
||||||
'app-plus': {
|
|
||||||
titleNView: {
|
|
||||||
type: 'transparent',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'mp-weixin': {
|
|
||||||
navigationStyle: 'custom',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<view class="fly-navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
|
|
||||||
<!-- 1/3,多于1个页面,用返回图标 -->
|
|
||||||
<navigator v-if="pages.length > 1" open-type="navigateBack" class="left-icon">
|
|
||||||
<view class="i-carbon-chevron-left text-current"></view>
|
|
||||||
</navigator>
|
|
||||||
<!-- 2/3,只有1个页面,如果不是tabbar,需要首页图标 -->
|
|
||||||
<!-- 这种情况一般出现在用户直接打开分享出去的详情页面,或者使用redirectTo等API -->
|
|
||||||
<navigator
|
|
||||||
v-else-if="!isTabbar"
|
|
||||||
open-type="switchTab"
|
|
||||||
url="/pages/index/index"
|
|
||||||
class="left-icon"
|
|
||||||
>
|
|
||||||
<view class="i-carbon-home text-current"></view>
|
|
||||||
</navigator>
|
|
||||||
<!-- 3/3,如果当前页就是tabbar页,不用去首页,也就是什么图标都不需要 -->
|
|
||||||
<view class="title">{{ '我是标题' }}</view>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<scroll-view
|
|
||||||
enable-back-to-top
|
|
||||||
scroll-y
|
|
||||||
class="scroll-view-bg flex-1 h-full"
|
|
||||||
id="scroller"
|
|
||||||
refresher-enabled
|
|
||||||
@scrolltolower="onScrollToLower"
|
|
||||||
@refresherrefresh="onRefresherRefresh"
|
|
||||||
:refresher-triggered="isTriggered"
|
|
||||||
>
|
|
||||||
<view class="top-section" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
|
|
||||||
<view class="pt-1">顶部区域</view>
|
|
||||||
<view>可以是标题,也可以是个人中心头像等</view>
|
|
||||||
<view>建议本区域高度不低于200rpx</view>
|
|
||||||
</view>
|
|
||||||
<view class="p-2 leading-6 bg-white">
|
|
||||||
注意,上面的导航栏渐变效果仅微信端支持,且上面的导航栏无法抽为组件引入使用,否则滚动效果没有了。如果不只是微信小程序使用,可以
|
|
||||||
onPageScroll 实现全端效果一样,另外如果是app端,还可以配置 titleNView。参考
|
|
||||||
https://uniapp.dcloud.net.cn/tutorial/page.html#onpagescroll 。
|
|
||||||
</view>
|
|
||||||
<view class="bg-white">
|
|
||||||
<fly-content :line="30" />
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import useNavbarWeixin from '@/hooks/useNavbarWeixin'
|
|
||||||
import { onPullDownRefresh } from '@dcloudio/uni-app'
|
|
||||||
|
|
||||||
const { pages, isTabbar, onScrollToLower, safeAreaInsets } = useNavbarWeixin()
|
|
||||||
|
|
||||||
// 发现原生下拉刷新效果并不好,在微信里面只有顶部导航栏下拉才生效,页面区域下拉不生效,体验不好,结合自定义下拉刷新效果很好
|
|
||||||
onPullDownRefresh(() => {
|
|
||||||
setTimeout(function fn() {
|
|
||||||
console.log('refresh - onPullDownRefresh')
|
|
||||||
// 关闭动画
|
|
||||||
uni.stopPullDownRefresh()
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 当前下拉刷新状态
|
|
||||||
const isTriggered = ref(false)
|
|
||||||
// 自定义下拉刷新被触发
|
|
||||||
const onRefresherRefresh = async () => {
|
|
||||||
// 开始动画
|
|
||||||
isTriggered.value = true
|
|
||||||
setTimeout(function fn() {
|
|
||||||
console.log('refresh - onRefresherRefresh')
|
|
||||||
// 关闭动画
|
|
||||||
isTriggered.value = false
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.scroll-view-bg {
|
|
||||||
// 这个背景色要与.top-section的背景图差不多,这样下拉刷新看起来才比较协调
|
|
||||||
background-color: #23c09c;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 这个区域最好要大于200rpx,效果会更好
|
|
||||||
.top-section {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 200rpx;
|
|
||||||
padding: 40rpx 0;
|
|
||||||
line-height: 2;
|
|
||||||
color: #fff;
|
|
||||||
background-image: url('https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/top-bg.png');
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fly-navbar {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 9;
|
|
||||||
width: 750rpx;
|
|
||||||
color: #000;
|
|
||||||
background-color: transparent;
|
|
||||||
|
|
||||||
.left-icon {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 44px;
|
|
||||||
height: 44px;
|
|
||||||
font-size: 44rpx;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 44px;
|
|
||||||
font-size: 32rpx;
|
|
||||||
color: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,16 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: 'UniUI Icons 使用' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="m-4">
|
|
||||||
<uni-icons type="contact" size="30"></uni-icons>
|
|
||||||
<uni-icons type="contact" size="30" color="red"></uni-icons>
|
|
||||||
<view class="text-blue-300"
|
|
||||||
>注意在微信小程序中,不支持改颜色,即设置了颜色也会变成默认的#333, BUG</view
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
@ -1,14 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: 'UniUI 使用' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<uni-card>
|
|
||||||
<text>这是一个基础卡片示例,内容较少,此示例展示了一个没有任何属性不带阴影的卡片。</text>
|
|
||||||
</uni-card>
|
|
||||||
<view>微信里面下面的 uni-badge 显示不出来,BUG</view>
|
|
||||||
<uni-badge text="99"></uni-badge>
|
|
||||||
</template>
|
|
@ -1,20 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: 'UnoCss Icons 使用' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="m-4">
|
|
||||||
<view class="mb-2">
|
|
||||||
这里只装了carbon的图表库,网址:
|
|
||||||
<a href="https://icones.js.org/collection/carbon" target="_blank"
|
|
||||||
>https://icones.js.org/collection/carbon </a
|
|
||||||
>(非H5环境,请使用浏览器打开)
|
|
||||||
</view>
|
|
||||||
<view class="i-carbon-car" />
|
|
||||||
<view class="i-carbon-car text-red" />
|
|
||||||
<button class="i-carbon-sun dark:i-carbon-moon" />
|
|
||||||
</view>
|
|
||||||
</template>
|
|
@ -1,15 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: 'UnoCss 使用' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="flex flex-col justify-center items-center text-5 h-8 leading-8 mt-20">
|
|
||||||
<view class="text-green-500">文字颜色 text-light-50</view>
|
|
||||||
<view class="text-red-500">文字颜色 text-red-500</view>
|
|
||||||
<view class="bg-green-500">背景色 bg-light-50</view>
|
|
||||||
<view class="bg-red-500">背景色 bg-red-500</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
@ -1,49 +0,0 @@
|
|||||||
<route lang="json5" type="page">
|
|
||||||
{
|
|
||||||
style: { navigationBarTitleText: 'uv ui' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="mt-10">
|
|
||||||
<uv-button type="primary" @click="remakeQc">重新生成二维码</uv-button>
|
|
||||||
<view class="center mt-8">
|
|
||||||
<uv-qrcode
|
|
||||||
ref="qrcode"
|
|
||||||
size="200px"
|
|
||||||
value="https://h5.uvui.cn"
|
|
||||||
:options="options"
|
|
||||||
></uv-qrcode>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
console.log(uni.$uv.os())
|
|
||||||
console.log(uni.$uv.sys())
|
|
||||||
console.log(uni.$uv.trim(' abc ')) // 去除两端空格
|
|
||||||
console.log(uni.$uv.random(1, 3)) // 获取随机数
|
|
||||||
const qrcode = ref(null)
|
|
||||||
const options = reactive({
|
|
||||||
size: 300,
|
|
||||||
useDynamicSize: false,
|
|
||||||
margin: 10,
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
// 指定二维码前景,一般可在中间放logo
|
|
||||||
foregroundImageSrc: '/static/logo.svg',
|
|
||||||
})
|
|
||||||
const remakeQc = () => {
|
|
||||||
qrcode.value.remake({
|
|
||||||
success: () => {
|
|
||||||
console.log('生成成功')
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.log(err)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
//
|
|
||||||
</style>
|
|
@ -1,22 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: '开启 vConsole' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="text-5 h-8 leading-8">
|
|
||||||
<view class="text-red-500">在非正式版小程序里面已经集成了VConsole</view>
|
|
||||||
<view class="text-blue-500 mb-2">开启方式如下面</view>
|
|
||||||
<image
|
|
||||||
src="https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/vconsole/1.png"
|
|
||||||
mode="widthFix"
|
|
||||||
/>
|
|
||||||
<view class="text-blue-500 m-2">然后页面上会出现一个 `vConsole` 的调试按钮,如下图</view>
|
|
||||||
<image
|
|
||||||
src="https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/vconsole/2.png"
|
|
||||||
mode="widthFix"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
@ -1,81 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="bg-slate-100 p-4">
|
|
||||||
<view class="bg-slate-100 w-full" v-for="item in listData" :key="item.id">
|
|
||||||
<view class="font-800">{{ item.title }}</view>
|
|
||||||
<view v-for="itemDetail in item.list" :key="itemDetail.path" class="mt-3">
|
|
||||||
<view
|
|
||||||
class="flex bg-white items-center justify-between p-3 mb-2"
|
|
||||||
@click="goDetailPage(itemDetail.path)"
|
|
||||||
>
|
|
||||||
<text class="flex-1 text-4 text-dark">{{ itemDetail.title }}</text>
|
|
||||||
<text class="i-carbon-chevron-right"></text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts" name="TestIndex">
|
|
||||||
import pagesJson from '@/pages.json'
|
|
||||||
|
|
||||||
/** 基本功能 */
|
|
||||||
const baseDemos = pagesJson.pages
|
|
||||||
.filter((e) => e.path.startsWith('pages/demo/base'))
|
|
||||||
.map((e) => ({
|
|
||||||
title: e.style?.navigationBarTitleText || '默认页面标题',
|
|
||||||
path: e.path,
|
|
||||||
}))
|
|
||||||
|
|
||||||
/** 页面功能 */
|
|
||||||
const pageDemos = pagesJson.pages
|
|
||||||
.filter((e) => e.path.startsWith('pages/demo/page'))
|
|
||||||
.map((e) => ({
|
|
||||||
title: e.style?.navigationBarTitleText || '默认页面标题',
|
|
||||||
path: e.path,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const listData = reactive([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: '基础功能',
|
|
||||||
list: baseDemos,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: '页面功能',
|
|
||||||
list: pageDemos,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const goDetailPage = (path: string) => {
|
|
||||||
const url = `/${path}`
|
|
||||||
uni.navigateTo({
|
|
||||||
url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
width: 200rpx;
|
|
||||||
height: 200rpx;
|
|
||||||
margin: 200rpx auto 50rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-area {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 36rpx;
|
|
||||||
color: #8f8f94;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,130 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: '动态时钟' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="mt-4 h-10 text-center">动态时钟</view>
|
|
||||||
<view class="clock-box">
|
|
||||||
<view class="clock" :style="{ '--ds': ds, '--dm': dm, '--dh': dh }">
|
|
||||||
<view class="clock-pane">
|
|
||||||
<text class="clock-num" :style="{ '--i': n }" v-for="n in 12" :key="n">{{ n }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="clock-hour"></view>
|
|
||||||
<view class="clock-min"></view>
|
|
||||||
<view class="clock-sec"></view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
const d = new Date()
|
|
||||||
const h = d.getHours()
|
|
||||||
const m = d.getMinutes()
|
|
||||||
const s = d.getSeconds()
|
|
||||||
const ds = ref(s)
|
|
||||||
const dm = ref(m + s / 60)
|
|
||||||
const dh = ref(h + m / 60 + s / 3600)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.clock-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 350px;
|
|
||||||
height: 350px;
|
|
||||||
font-size: 24px;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 2px 2px 20px #0000001a;
|
|
||||||
--step: 60s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock::before {
|
|
||||||
position: absolute;
|
|
||||||
width: 300px;
|
|
||||||
height: 300px;
|
|
||||||
content: '';
|
|
||||||
background: repeating-conic-gradient(from -0.5deg, #333 0 1deg, transparent 0deg 30deg),
|
|
||||||
repeating-conic-gradient(from -0.5deg, #ccc 0 1deg, transparent 0deg 6deg);
|
|
||||||
border-radius: 50%;
|
|
||||||
mask: radial-gradient(transparent 145px, red 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-pane {
|
|
||||||
position: absolute;
|
|
||||||
width: 250px;
|
|
||||||
height: 250px;
|
|
||||||
transform: translateX(-125px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-num {
|
|
||||||
position: absolute;
|
|
||||||
offset-path: path(
|
|
||||||
'M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z'
|
|
||||||
);
|
|
||||||
offset-distance: calc(var(--i) * 10% / 1.2 - 25%);
|
|
||||||
offset-rotate: 0deg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-hour {
|
|
||||||
position: absolute;
|
|
||||||
width: 4px;
|
|
||||||
height: 60px;
|
|
||||||
background: #333;
|
|
||||||
transform: translateY(-50%) rotate(0);
|
|
||||||
transform-origin: center bottom;
|
|
||||||
animation: clock calc(var(--step) * 60 * 12) infinite linear;
|
|
||||||
animation-delay: calc(-1 * var(--step) * var(--dh) * 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-min {
|
|
||||||
position: absolute;
|
|
||||||
width: 4px;
|
|
||||||
height: 90px;
|
|
||||||
background: #333;
|
|
||||||
transform: translateY(-50%) rotate(0);
|
|
||||||
transform-origin: center bottom;
|
|
||||||
animation: clock calc(var(--step) * 60) infinite linear;
|
|
||||||
animation-delay: calc(-1 * var(--step) * var(--dm));
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-sec {
|
|
||||||
position: absolute;
|
|
||||||
width: 2px;
|
|
||||||
height: 120px;
|
|
||||||
background: red;
|
|
||||||
transform: translateY(-50%) rotate(0);
|
|
||||||
transform-origin: center bottom;
|
|
||||||
animation: clock var(--step) infinite steps(60);
|
|
||||||
animation-delay: calc(-1 * var(--step) * var(--ds) / 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-sec::after {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 50%;
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
content: '';
|
|
||||||
background: #fff;
|
|
||||||
border: 4px solid #333;
|
|
||||||
border-radius: 50%;
|
|
||||||
transform: translate(-50%, 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes clock {
|
|
||||||
to {
|
|
||||||
transform: translateY(-50%) rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,152 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: '动态时钟-抗锯齿' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="mt-4 h-10 text-center">动态时钟</view>
|
|
||||||
<view class="clock-box">
|
|
||||||
<view class="clock" :style="{ '--ds': ds, '--dm': dm, '--dh': dh }">
|
|
||||||
<view class="clock-pane">
|
|
||||||
<text class="clock-num" :style="{ '--i': n }" v-for="n in 12" :key="n">{{ n }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="clock-scales">
|
|
||||||
<text class="clock-scale" :style="{ '--i': n }" v-for="n in 60" :key="n"></text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="clock-hour"></view>
|
|
||||||
<view class="clock-min"></view>
|
|
||||||
<view class="clock-sec"></view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
const d = new Date()
|
|
||||||
const h = d.getHours()
|
|
||||||
const m = d.getMinutes()
|
|
||||||
const s = d.getSeconds()
|
|
||||||
const ds = ref(s)
|
|
||||||
const dm = ref(m + s / 60)
|
|
||||||
const dh = ref(h + m / 60 + s / 3600)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.clock-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 350px;
|
|
||||||
height: 350px;
|
|
||||||
font-size: 24px;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 2px 2px 20px #0000001a;
|
|
||||||
--step: 60s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-pane {
|
|
||||||
position: absolute;
|
|
||||||
width: 250px;
|
|
||||||
height: 250px;
|
|
||||||
transform: translateX(-125px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-scales {
|
|
||||||
position: absolute;
|
|
||||||
width: 250px;
|
|
||||||
height: 250px;
|
|
||||||
transform: translate(125px, -25px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-scale {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 2px;
|
|
||||||
height: 4px;
|
|
||||||
background: #ccc;
|
|
||||||
transform-origin: 0 150px;
|
|
||||||
|
|
||||||
&:nth-child(5n + 1) {
|
|
||||||
width: 4px;
|
|
||||||
height: 6px;
|
|
||||||
background: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@for $i from 1 through 60 {
|
|
||||||
.clock-scale:nth-child(#{$i}) {
|
|
||||||
transform: rotate(#{($i - 1) * 6deg});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-num {
|
|
||||||
position: absolute;
|
|
||||||
offset-path: path(
|
|
||||||
'M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z'
|
|
||||||
);
|
|
||||||
offset-distance: calc(var(--i) * 10% / 1.2 - 25%);
|
|
||||||
offset-rotate: 0deg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-hour {
|
|
||||||
position: absolute;
|
|
||||||
width: 4px;
|
|
||||||
height: 60px;
|
|
||||||
background: #333;
|
|
||||||
transform: translateY(-50%) rotate(0);
|
|
||||||
transform-origin: center bottom;
|
|
||||||
animation: clock calc(var(--step) * 60 * 12) infinite linear;
|
|
||||||
animation-delay: calc(-1 * var(--step) * var(--dh) * 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-min {
|
|
||||||
position: absolute;
|
|
||||||
width: 4px;
|
|
||||||
height: 90px;
|
|
||||||
background: #333;
|
|
||||||
transform: translateY(-50%) rotate(0);
|
|
||||||
transform-origin: center bottom;
|
|
||||||
animation: clock calc(var(--step) * 60) infinite linear;
|
|
||||||
animation-delay: calc(-1 * var(--step) * var(--dm));
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-sec {
|
|
||||||
position: absolute;
|
|
||||||
width: 2px;
|
|
||||||
height: 120px;
|
|
||||||
background: red;
|
|
||||||
transform: translateY(-50%) rotate(0);
|
|
||||||
transform-origin: center bottom;
|
|
||||||
animation: clock var(--step) infinite steps(60);
|
|
||||||
animation-delay: calc(-1 * var(--step) * var(--ds) / 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock-sec::after {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 50%;
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
content: '';
|
|
||||||
background: #fff;
|
|
||||||
border: 4px solid #333;
|
|
||||||
border-radius: 50%;
|
|
||||||
transform: translate(-50%, 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes clock {
|
|
||||||
to {
|
|
||||||
transform: translateY(-50%) rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,88 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'default',
|
|
||||||
style: { navigationBarTitleText: '页面悬浮球' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view>
|
|
||||||
<movable-area class="movable-area">
|
|
||||||
<movable-view
|
|
||||||
:style="`--size:${ballSize}px`"
|
|
||||||
class="movable-view"
|
|
||||||
direction="all"
|
|
||||||
:x="x"
|
|
||||||
:y="y"
|
|
||||||
@change="onChange"
|
|
||||||
@touchend.prevent="onTouchEnd"
|
|
||||||
>
|
|
||||||
<view class="w-full h-full rounded-full bg-green-400"></view>
|
|
||||||
</movable-view>
|
|
||||||
</movable-area>
|
|
||||||
<view>页面其他元素</view>
|
|
||||||
<view>可以正常触发点击事件吗?答案是可以的</view>
|
|
||||||
<button @click="onClick">按钮</button>
|
|
||||||
<view>{{ x }}</view>
|
|
||||||
<view>{{ y }}</view>
|
|
||||||
<view @click="onSet">点击设置</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup name="FloatingBubble">
|
|
||||||
const { windowHeight, windowWidth } = uni.getSystemInfoSync()
|
|
||||||
|
|
||||||
const ballSize = 60
|
|
||||||
const x = ref(windowWidth - ballSize) // 靠右侧
|
|
||||||
const y = ref(windowHeight - ballSize - 20) // 距离底部20px
|
|
||||||
|
|
||||||
const middleX = (windowWidth - ballSize) / 2
|
|
||||||
|
|
||||||
const onChange: UniHelper.MovableViewOnChange = (e) => {
|
|
||||||
const { x: _x, y: _y } = e.detail
|
|
||||||
x.value = _x
|
|
||||||
y.value = _y
|
|
||||||
}
|
|
||||||
// TODO: 期望最终落点不靠左右两边时,会自动回到两边,有一定的动画效果
|
|
||||||
const onTouchEnd = (e) => {
|
|
||||||
console.log('onTouchEnd', e)
|
|
||||||
// TODO:为啥这里设置的不生效了,原生不会移动到设置的地方,onSet里面可以。这里直接执行onSet也不行
|
|
||||||
// 这里被我解决了
|
|
||||||
const tid = setTimeout(() => {
|
|
||||||
if (x.value < middleX) {
|
|
||||||
x.value = 0
|
|
||||||
} else {
|
|
||||||
x.value = windowWidth - ballSize
|
|
||||||
}
|
|
||||||
clearTimeout(tid)
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onClick = () => {
|
|
||||||
uni.showToast({
|
|
||||||
title: 'yes',
|
|
||||||
icon: 'none',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const onSet = () => {
|
|
||||||
x.value = 100
|
|
||||||
y.value = 100
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.movable-area {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 100;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
pointer-events: none; // 设置area元素不可点击,则事件便会下移至页面下层元素
|
|
||||||
.movable-view {
|
|
||||||
width: var(--size);
|
|
||||||
height: var(--size);
|
|
||||||
pointer-events: auto; // 必须设置,否则无法点击
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,84 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: '%app.name%',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="center flex-col mt-6">
|
|
||||||
<view class="text-green-500">多语言测试</view>
|
|
||||||
<view class="m-4">{{ $t('app.name') }}</view>
|
|
||||||
<view class="m-4">{{ $t('weight', { heavy: 100 }) }}</view>
|
|
||||||
|
|
||||||
<view class="text-green-500 mt-12">切换语言 </view>
|
|
||||||
<view class="uni-list">
|
|
||||||
<radio-group @change="radioChange" class="radio-group">
|
|
||||||
<label class="uni-list-cell uni-list-cell-pd" v-for="item in languages" :key="item.value">
|
|
||||||
<view>
|
|
||||||
<radio :value="item.value" :checked="item.value === current" />
|
|
||||||
</view>
|
|
||||||
<view>{{ item.name }}</view>
|
|
||||||
</label>
|
|
||||||
</radio-group>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- http://localhost:9100/#/pages/index/i18n -->
|
|
||||||
<button @click="testI18n" class="mt-20 mb-44">测试弹窗</button>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import i18n from '@/locale/index'
|
|
||||||
import { testI18n } from '@/utils/index'
|
|
||||||
|
|
||||||
const current = ref(uni.getLocale())
|
|
||||||
const languages = [
|
|
||||||
{
|
|
||||||
value: 'zh-Hans',
|
|
||||||
name: '中文',
|
|
||||||
checked: 'true',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'en',
|
|
||||||
name: '英文',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const radioChange = (evt) => {
|
|
||||||
// console.log(evt)
|
|
||||||
current.value = evt.detail.value
|
|
||||||
// 下面2句缺一不可!!!
|
|
||||||
uni.setLocale(evt.detail.value)
|
|
||||||
i18n.global.locale = evt.detail.value
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.uni-list {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-group {
|
|
||||||
width: 200px;
|
|
||||||
margin: 10px auto;
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uni-list-cell {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: #bcecd1;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,38 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: '图片压缩' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import testBgImg from './test-bg.png'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="m-4 text-center">
|
|
||||||
<view class="mb-2 text-orange-500">
|
|
||||||
原始图片是一个很大的,2.5M,build之后生成的图片只有1.1M,体积下降 56%
|
|
||||||
</view>
|
|
||||||
<!-- #ifdef MP -->
|
|
||||||
<image
|
|
||||||
src="https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/img-min/bg-1.png"
|
|
||||||
mode="scaleToFill"
|
|
||||||
/>
|
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifndef MP -->
|
|
||||||
<image :src="testBgImg" mode="scaleToFill" />
|
|
||||||
<!-- #endif -->
|
|
||||||
<view class="mb-4">对比图如下2图,如果看不清请看代码原图</view>
|
|
||||||
<image
|
|
||||||
src="https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/img-min/before.png"
|
|
||||||
mode="widthFix"
|
|
||||||
class="w-full"
|
|
||||||
/>
|
|
||||||
<image
|
|
||||||
src="https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/img-min/after.png"
|
|
||||||
mode="widthFix"
|
|
||||||
class="w-full"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.9 MiB |
@ -1,127 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: '九宫格抽奖' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="mt-4 h-10 text-center">九宫格抽奖</view>
|
|
||||||
<view class="lottery-box">
|
|
||||||
<view class="lottery-list">
|
|
||||||
<view
|
|
||||||
class="lottery-item"
|
|
||||||
:class="{
|
|
||||||
active: n === activeIndex,
|
|
||||||
btn: n === btnIndex, // 最中间那个是展示按钮
|
|
||||||
}"
|
|
||||||
v-for="n in numList"
|
|
||||||
:key="n"
|
|
||||||
@click="handleClick(n)"
|
|
||||||
>
|
|
||||||
<view v-if="n === btnIndex">点击抽奖</view>
|
|
||||||
<view v-else> {{ n }}</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { reactive, computed } from 'vue'
|
|
||||||
|
|
||||||
const giftLen = 8 // 九宫格有8个礼物
|
|
||||||
const loop = 4 // 设置转多少圈,在最后一圈会慢下来
|
|
||||||
const totalStep = giftLen * loop // 总的步数 32
|
|
||||||
const lastLoopStep = totalStep - giftLen // 最后一圈,24
|
|
||||||
const numList = [1, 2, 3, 8, -1, 4, 7, 6, 5]
|
|
||||||
const btnIndex = numList[4] // 最中间那个是展示按钮
|
|
||||||
|
|
||||||
const state = reactive({
|
|
||||||
lottery: 0, // 奖品
|
|
||||||
step: -1, // 目前转动的步数
|
|
||||||
stopStep: totalStep, // 停下来的时需要走的步数
|
|
||||||
speed: 2, // 转动速度,我们是通过定时器去实现转动效果的,所以这也就是定时器的执行频率
|
|
||||||
timer: null, // 定时器ID
|
|
||||||
loading: false,
|
|
||||||
})
|
|
||||||
// 通过目前转动的步数来对8取模得到当前转到的格子索引
|
|
||||||
const activeIndex = computed(() => {
|
|
||||||
return (state.step % 8) + 1
|
|
||||||
})
|
|
||||||
|
|
||||||
function run() {
|
|
||||||
// 当前步数大于等于目标步数
|
|
||||||
if (state.step >= state.stopStep) {
|
|
||||||
// 清空定时器,停止转动
|
|
||||||
clearTimeout(state.timer)
|
|
||||||
// 将初始化步数为最终奖品的步数,转动速度也置为初始速度,下次才能正确转动
|
|
||||||
state.step = state.lottery
|
|
||||||
state.speed = 2
|
|
||||||
state.loading = false
|
|
||||||
console.log(`恭喜获得${activeIndex.value}号奖品`)
|
|
||||||
uni.showModal({
|
|
||||||
title: `恭喜获得${activeIndex.value}号奖品`,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 转动到最后一圈时,增加speed,也就是定时器执行间隔时间变长,转动速度变慢
|
|
||||||
if (state.step > lastLoopStep + state.lottery) {
|
|
||||||
state.speed++
|
|
||||||
}
|
|
||||||
// 抽奖函数每执行一次,当前步数加一
|
|
||||||
state.step++
|
|
||||||
// 重新开启定时器执行抽奖函数
|
|
||||||
state.timer = setTimeout(run, state.speed * 30)
|
|
||||||
}
|
|
||||||
// 点击抽奖之后调用的函数
|
|
||||||
function handleClick(n) {
|
|
||||||
if (n !== btnIndex) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (state.loading) return
|
|
||||||
state.loading = true
|
|
||||||
// 最终获得的奖品,实际业务中是通过接口获取的,这里使用随机数来模拟下
|
|
||||||
state.lottery = Math.ceil(Math.random() * giftLen)
|
|
||||||
console.log(state.lottery)
|
|
||||||
// 计算总共要转动的步数,转4圈后再转到奖品处
|
|
||||||
state.stopStep = state.lottery + totalStep
|
|
||||||
// 执行抽奖函数
|
|
||||||
run()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="css">
|
|
||||||
.lottery-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-list {
|
|
||||||
--size: 100px;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
width: calc(3 * var(--size) + 3px);
|
|
||||||
border-right: 1px solid #ccc;
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-item {
|
|
||||||
width: var(--size);
|
|
||||||
height: var(--size);
|
|
||||||
line-height: var(--size);
|
|
||||||
text-align: center;
|
|
||||||
border-top: 1px solid #ccc;
|
|
||||||
border-left: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-item.active {
|
|
||||||
color: #fff;
|
|
||||||
background-color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-item.btn {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,216 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: '大转盘抽奖' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="text-center">
|
|
||||||
<view class="container">
|
|
||||||
<view class="prize-list" :style="styleObj">
|
|
||||||
<view
|
|
||||||
class="prize-item"
|
|
||||||
v-for="(item, index) in prizeList"
|
|
||||||
:key="item.id"
|
|
||||||
:style="prizeStyle(index)"
|
|
||||||
>
|
|
||||||
<image :src="item.pic" class="gift-img" />
|
|
||||||
<text class="gift-name">{{ item.name }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="lottery-btn" @click="start"> </view>
|
|
||||||
</view>
|
|
||||||
<view class="text-blue-600 my-2">目标是实现如下的效果,但是我感觉只用css还是太难了</view>
|
|
||||||
<image
|
|
||||||
src="https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery/target.png"
|
|
||||||
mode="widthFix"
|
|
||||||
width="552px"
|
|
||||||
/>
|
|
||||||
<!-- <image :src="targetImg" mode="widthFix" width="552px" /> -->
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, computed } from 'vue'
|
|
||||||
// TODO: fix 微信小程序里面会报错
|
|
||||||
// import targetImg from 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery/target.png'
|
|
||||||
|
|
||||||
// 后台配置的奖品数据
|
|
||||||
const prizeList = [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
name: '双肩包',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/backpack.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: '积木',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/jimu.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: '红包',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/red-envelope.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: '茶具',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/tea-set.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: '可爱脸',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/tushetou.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
name: '挖掘机',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/wajueji.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
name: '无辜脸',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/xiaolian.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 7,
|
|
||||||
name: '烟灰缸',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/yanhuigang.jpg',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
let isRunning = false // 是否正在抽奖
|
|
||||||
const baseRunAngle = 360 * 5 // 总共转动角度 至少5圈
|
|
||||||
let prizeId = 0 // 中奖id
|
|
||||||
|
|
||||||
// 平均每个奖品角度
|
|
||||||
const rotateAngle = computed(() => {
|
|
||||||
const _degree = 360 / prizeList.length
|
|
||||||
return _degree
|
|
||||||
})
|
|
||||||
// 要执行总角度数
|
|
||||||
const totalRunAngle = ref(baseRunAngle - (prizeId + 0.5) * rotateAngle.value)
|
|
||||||
|
|
||||||
// 计算绘制转盘背景
|
|
||||||
const bgColor = (() => {
|
|
||||||
const [c1, c2] = ['#5352b3', '#363589']
|
|
||||||
// repeating-conic-gradient(red 0 15deg, blue 15deg 30deg);
|
|
||||||
return `background: repeating-conic-gradient(${c1} 0 ${rotateAngle.value}deg,
|
|
||||||
${c2} ${rotateAngle.value}deg ${2 * rotateAngle.value}deg);`
|
|
||||||
})()
|
|
||||||
|
|
||||||
const styleObj = ref(bgColor)
|
|
||||||
// 每个奖品布局
|
|
||||||
const prizeStyle = computed(() => {
|
|
||||||
const _degree = rotateAngle.value
|
|
||||||
return (i) => {
|
|
||||||
// 外框大小设置为90vw,里面是一半,45vw
|
|
||||||
return `
|
|
||||||
width: ${2 * 45 * Math.sin(((_degree / 2) * Math.PI) / 180)}vw;
|
|
||||||
height: 45vw;
|
|
||||||
transform: rotate(${_degree * i + _degree / 2}deg);
|
|
||||||
transform-origin: 50% 100%;
|
|
||||||
`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取随机数
|
|
||||||
const getRandomNum = () => {
|
|
||||||
const num = Math.floor(Math.random() * prizeList.length)
|
|
||||||
return num
|
|
||||||
}
|
|
||||||
|
|
||||||
const stopRun = () => {
|
|
||||||
isRunning = false
|
|
||||||
const prizeName = prizeList.find((e) => e.id === prizeId)!.name
|
|
||||||
uni.showModal({
|
|
||||||
title: `恭喜你中奖 ${prizeName}`,
|
|
||||||
success() {
|
|
||||||
styleObj.value = `${bgColor} transform: rotate(0deg);`
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const startRun = () => {
|
|
||||||
console.log(isRunning, totalRunAngle.value)
|
|
||||||
// 设置动效
|
|
||||||
styleObj.value = `${bgColor} transform: rotate(${totalRunAngle.value}deg); transition: all 4s ease;`
|
|
||||||
setTimeout(stopRun, 4000)
|
|
||||||
}
|
|
||||||
const start = () => {
|
|
||||||
if (!isRunning) {
|
|
||||||
isRunning = true
|
|
||||||
|
|
||||||
console.log('开始抽奖,后台请求中奖奖品')
|
|
||||||
// 请求返回的奖品编号 这里使用随机数
|
|
||||||
prizeId = getRandomNum()
|
|
||||||
totalRunAngle.value = baseRunAngle - (prizeId + 0.5) * rotateAngle.value
|
|
||||||
console.log('中奖ID>>>', prizeId, prizeList[prizeId], totalRunAngle.value)
|
|
||||||
nextTick(() => {
|
|
||||||
startRun()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.container {
|
|
||||||
position: relative;
|
|
||||||
width: 90vw;
|
|
||||||
height: 90vw;
|
|
||||||
margin: 20px auto;
|
|
||||||
border: 10px solid #98d3fc;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prize-list {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
// 使用outline代替border可以省很多定位的问题
|
|
||||||
// outline: 10px solid #98d3fc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prize-item {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
left: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: auto;
|
|
||||||
|
|
||||||
// border: 2px solid red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prize-item .gift-img {
|
|
||||||
display: block;
|
|
||||||
width: 30%;
|
|
||||||
height: 20%;
|
|
||||||
margin: 20px auto 10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prize-item .gift-name {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 20px;
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
width: 80px;
|
|
||||||
height: 96px;
|
|
||||||
margin: auto;
|
|
||||||
cursor: pointer;
|
|
||||||
background: url('https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/btn-enable.png')
|
|
||||||
no-repeat center / 100% 100%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,196 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: '九宫格抽奖' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
<template>
|
|
||||||
<view>
|
|
||||||
<view class="container">
|
|
||||||
<view
|
|
||||||
class="gift-item"
|
|
||||||
:class="{ active: currentIndex === index }"
|
|
||||||
v-for="(item, index) in prizeList"
|
|
||||||
:key="index"
|
|
||||||
@click="start(index)"
|
|
||||||
>
|
|
||||||
<image :src="item.pic" class="gift-img" />
|
|
||||||
<text v-if="index !== 4" class="gift-name">{{ item.name }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, computed } from 'vue'
|
|
||||||
|
|
||||||
const currentIndex = ref(0) // 当前位置
|
|
||||||
// 后台配置的奖品数据
|
|
||||||
const prizeList = [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
name: '双肩包',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/backpack.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: '积木',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/jimu.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: '红包',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/red-envelope.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: '茶具',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/tea-set.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
name: '可爱脸',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/tushetou.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
name: '挖掘机',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/wajueji.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 7,
|
|
||||||
name: '无辜脸',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/xiaolian.jpg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 8,
|
|
||||||
name: '烟灰缸',
|
|
||||||
pic: 'https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery-prize/yanhuigang.jpg',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
const startBtn = {
|
|
||||||
id: 4,
|
|
||||||
name: '开始按钮',
|
|
||||||
pic: 'https://img2.baidu.com/it/u=1497996119,382735686&fm=253',
|
|
||||||
}
|
|
||||||
// 九宫格中间位置插入开始按钮
|
|
||||||
prizeList.splice(4, 0, startBtn)
|
|
||||||
|
|
||||||
// 奖品高亮顺序
|
|
||||||
const prizeSort = [0, 1, 2, 5, 8, 7, 6, 3]
|
|
||||||
|
|
||||||
// 获取随机数
|
|
||||||
const getRandomNum = () => prizeSort[Math.floor(Math.random() * prizeSort.length)]
|
|
||||||
|
|
||||||
let isRunning = false // 是否正在抽奖
|
|
||||||
let speed = 10 // 抽奖转动速度
|
|
||||||
let timerIns = null // 定时器实例
|
|
||||||
let currentRunCount = 0 // 已跑次数
|
|
||||||
const totalRunCount = 32 // 总共跑动次数,8的倍数即可
|
|
||||||
let prizeId = 0 // 中奖id(0-8,不能是4)
|
|
||||||
|
|
||||||
// 要执行总步数
|
|
||||||
const totalRunStep = computed(() => {
|
|
||||||
return totalRunCount + prizeSort.indexOf(prizeId)
|
|
||||||
})
|
|
||||||
|
|
||||||
const stopRun = () => {
|
|
||||||
// eslint-disable-next-line no-unused-expressions
|
|
||||||
timerIns && clearTimeout(timerIns)
|
|
||||||
}
|
|
||||||
const startRun = () => {
|
|
||||||
stopRun()
|
|
||||||
console.log(currentRunCount, totalRunStep.value)
|
|
||||||
// 要执行总步数
|
|
||||||
// 已走步数超过
|
|
||||||
if (currentRunCount > totalRunStep.value) {
|
|
||||||
isRunning = false
|
|
||||||
const prizeName = prizeList.find((e) => e.id === prizeId)!.name
|
|
||||||
uni.showModal({
|
|
||||||
title: `恭喜你中奖 ${prizeName}`,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
currentIndex.value = prizeSort[currentRunCount % 8]
|
|
||||||
// 如果当前步数超过了2/3则速度慢下来
|
|
||||||
if (currentRunCount > Math.floor((totalRunCount * 2) / 3)) {
|
|
||||||
speed += Math.floor(currentRunCount / 3)
|
|
||||||
console.log('速度>>>>', speed)
|
|
||||||
}
|
|
||||||
|
|
||||||
timerIns = setTimeout(() => {
|
|
||||||
currentRunCount++
|
|
||||||
startRun()
|
|
||||||
}, speed)
|
|
||||||
}
|
|
||||||
|
|
||||||
const start = (i) => {
|
|
||||||
if (i === 4 && !isRunning) {
|
|
||||||
// 重置数据
|
|
||||||
currentRunCount = 0
|
|
||||||
speed = 100
|
|
||||||
isRunning = true
|
|
||||||
|
|
||||||
console.log('开始抽奖,后台请求中奖奖品')
|
|
||||||
// 请求返回的奖品编号 这里使用随机数 但不能为4
|
|
||||||
// const prizeId = getRandomNum()
|
|
||||||
// console.log('中奖ID>>>', prizeId, prizeList[prizeId])
|
|
||||||
// prizeId = prizeId
|
|
||||||
// 模拟接口延时返回 如果接口突然报错如何处理?直接调用stopRun()方法停止转动
|
|
||||||
setTimeout(() => {
|
|
||||||
prizeId = getRandomNum()
|
|
||||||
console.log('中奖ID>>>', prizeId, prizeList[prizeId])
|
|
||||||
// 拿到数据才可以跑
|
|
||||||
}, 2000)
|
|
||||||
startRun()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
width: 90vw;
|
|
||||||
height: 90vw;
|
|
||||||
margin: 20px auto;
|
|
||||||
background: #98d3fc;
|
|
||||||
border: 1px solid #98d3fc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gift-item {
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 30vw;
|
|
||||||
height: 30vw;
|
|
||||||
border: 2px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gift-item:nth-of-type(5) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gift-item .gift-img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gift-item .gift-name {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 20px;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 20px;
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
background: rgb(0 0 0 / 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.active {
|
|
||||||
border: 2px solid red;
|
|
||||||
box-shadow: 2px 2px 30px #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,181 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: { navigationBarTitleText: '大转盘抽奖' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="mt-4 h-10 text-center">大转盘抽奖</view>
|
|
||||||
<div class="lottery-box">
|
|
||||||
<div class="lottery-list">
|
|
||||||
<div class="lottery-item" v-for="(n, index) in giftLen" :key="n">
|
|
||||||
<div class="lottery-item-inner">
|
|
||||||
<div class="lottery-item-gift">奖品{{ index + 1 }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="pointer"
|
|
||||||
@click="handleClick"
|
|
||||||
:style="{ transform: `rotate(${state.stopDeg}deg)` }"
|
|
||||||
>
|
|
||||||
<div>开始</div>
|
|
||||||
<div>抽奖</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<view class="leading-8">
|
|
||||||
<view class="mt-8 text-center text-green-600">下面是调试过程图片</view>
|
|
||||||
<view class="mb-8 text-center text-green-600">欢迎感兴趣的玩家继续优化</view>
|
|
||||||
<view class="text-center text-blue-600">计算lottery-item-inner节点的padding-left值</view>
|
|
||||||
<image
|
|
||||||
src="https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery/lottery2-1.png"
|
|
||||||
mode="widthFix"
|
|
||||||
class="w-full"
|
|
||||||
/>
|
|
||||||
<view class="text-center text-blue-600">调整lottery-item-gift节点</view>
|
|
||||||
<image
|
|
||||||
src="https://cip-shopping-page-0eysug01066a9e-1302818703.tcloudbaseapp.com/fly/lottery/lottery2-2.png"
|
|
||||||
mode="widthFix"
|
|
||||||
class="w-full"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
const giftLen = 8
|
|
||||||
const deg = 360 / giftLen // 每份的角度
|
|
||||||
const loop = 4 // 转多少圈,4圈
|
|
||||||
const state = reactive({
|
|
||||||
lottery: 0, // 本次抽奖的奖品索引
|
|
||||||
lastLottery: 0, // 上一次抽奖的奖品索引
|
|
||||||
stopDeg: 0, // 最终要旋转的角度
|
|
||||||
loading: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
function handleClick() {
|
|
||||||
if (state.loading) return
|
|
||||||
state.loading = true
|
|
||||||
// 最终获得的奖品索引,实际业务中是通过接口获取的,这里使用随机数0~9来模拟下
|
|
||||||
state.lottery = Math.floor(Math.random() * giftLen)
|
|
||||||
console.log(state.lottery)
|
|
||||||
// 最终的旋转角度,指针指向本次奖品的旋转角度+指针从上一次的奖品指向回归0的旋转角度+ 默认转动三圈
|
|
||||||
state.stopDeg += (state.lottery + (giftLen - state.lastLottery)) * deg + loop * 360
|
|
||||||
|
|
||||||
// uni不支持addEventListener所以改用下面的
|
|
||||||
setTimeout(() => {
|
|
||||||
state.lastLottery = state.lottery
|
|
||||||
state.loading = false
|
|
||||||
// alert(`恭喜获得奖品${state.lottery + 1}`)
|
|
||||||
uni.showModal({
|
|
||||||
title: `恭喜获得奖品${state.lottery + 1}`,
|
|
||||||
})
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.lottery-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-list {
|
|
||||||
--size: 600rpx;
|
|
||||||
--half: calc(var(--size) / 2);
|
|
||||||
--len: 8; // 与代码 giftLen 长度一致
|
|
||||||
--deg: calc(360 / var(--len) * 1deg);
|
|
||||||
--deg-num: calc(360 / var(--len));
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
width: var(--size);
|
|
||||||
height: var(--size);
|
|
||||||
border: 2px solid #f55;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-item {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: var(--half);
|
|
||||||
width: var(--half);
|
|
||||||
height: var(--size);
|
|
||||||
overflow: hidden; // 把这个注释掉可以看到最初的模样
|
|
||||||
// background-color: #ff5350a1; // 放开这个可以看到最初的模样
|
|
||||||
transform-origin: left center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-item-inner {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: calc(-1 * var(--half));
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: var(--half);
|
|
||||||
height: var(--size);
|
|
||||||
padding-left: calc(((1 - sin(var(--deg-num))) * var(--size)));
|
|
||||||
font-size: 12px;
|
|
||||||
border-radius: var(--half) 0 0 var(--half);
|
|
||||||
transform: rotate(var(--deg));
|
|
||||||
transform-origin: right center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-item-inner .lottery-item-gift {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
transform: rotate(calc(-0.5 * var(--deg))) translateY(16px)
|
|
||||||
translateX(calc(0.5 * var(--half) * (1 - 1 / cos(0.5 * var(--deg)))));
|
|
||||||
transform-origin: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-item:nth-child(2n + 1) .lottery-item-inner {
|
|
||||||
background: #fef6e0a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lottery-item:nth-child(2n) .lottery-item-inner {
|
|
||||||
background: #ffffffa1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TIPS: 与上面的--len一致
|
|
||||||
@for $i from 1 through 8 {
|
|
||||||
.lottery-item:nth-child(#{$i}) {
|
|
||||||
transform: rotate(calc(($i - 1 - 0.5) * var(--deg)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.pointer {
|
|
||||||
--pointer-size: 40px;
|
|
||||||
--pointer-padding: calc(var(--pointer-size) / 5);
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
top: calc(var(--half) - var(--pointer-size) / 2 - var(--pointer-padding));
|
|
||||||
left: calc(var(--half) - var(--pointer-size) / 2 - var(--pointer-padding));
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: var(--pointer-size);
|
|
||||||
height: var(--pointer-size);
|
|
||||||
padding: var(--pointer-padding);
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #ffffffd1;
|
|
||||||
border: 1px solid #ff5350;
|
|
||||||
border-radius: 50%;
|
|
||||||
transition: transform 3s cubic-bezier(0.2, 0.93, 0.43, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pointer::after {
|
|
||||||
--caret-size: 8px;
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
bottom: calc(var(--pointer-size) + var(--pointer-padding) * 2);
|
|
||||||
left: calc(var(--pointer-size) / 2 - var(-caret-size) / 2);
|
|
||||||
content: '';
|
|
||||||
border-color: transparent;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: calc(var(--caret-size) * 2) var(--caret-size);
|
|
||||||
border-bottom-color: #ff5350;
|
|
||||||
transform-origin: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,266 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'default',
|
|
||||||
style: { navigationBarTitleText: '签字板' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="canvas-box flex flex-col box-border p-3" :class="{ 'full-screen': isFullScreen }">
|
|
||||||
<canvas
|
|
||||||
canvas-id="canvas"
|
|
||||||
class="w-full b b-dashed b-rd b-gray-300 canvas"
|
|
||||||
:disable-scroll="true"
|
|
||||||
@touchstart="touchStart"
|
|
||||||
@touchmove="touchMove"
|
|
||||||
@touchend="touchEnd"
|
|
||||||
@mousedown="touchStart"
|
|
||||||
@mousemove="touchMove"
|
|
||||||
@mouseup="touchEnd"
|
|
||||||
/>
|
|
||||||
<view class="btns flex justify-between text-center box-border">
|
|
||||||
<view class="btn-box flex">
|
|
||||||
<view class="btn bg-gray-100 b-rd b-gray-300 c-gray-500 p-1" @click="handFullScreen">
|
|
||||||
{{ isFullScreen ? '退出全屏' : '全屏' }}
|
|
||||||
</view>
|
|
||||||
<view class="btn bg-gray-100 b-rd b-gray-300 c-gray-500 p-1 ml-2" @click="clear">清空</view>
|
|
||||||
<view class="btn bg-gray-100 b-rd b-gray-300 c-gray-500 p-1 ml-2" @click="withdraw">
|
|
||||||
撤回
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="btn-box flex">
|
|
||||||
<view class="btn bg-sky-500 b-rd c-white p-1" @click="save">保存</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup name="sign">
|
|
||||||
const isFullScreen = ref(false)
|
|
||||||
const isSigned = ref(false)
|
|
||||||
let ctx = null
|
|
||||||
let isButtonDown = false
|
|
||||||
let points = []
|
|
||||||
let allPoints = []
|
|
||||||
|
|
||||||
// 初始化画布
|
|
||||||
function initCanvas() {
|
|
||||||
ctx = uni.createCanvasContext('canvas')
|
|
||||||
// 设置画笔样式
|
|
||||||
ctx.lineWidth = 4
|
|
||||||
ctx.lineCap = 'round'
|
|
||||||
ctx.lineJoin = 'round'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重设画板大小
|
|
||||||
function onResize() {
|
|
||||||
initCanvas() // 重新初始化canvas
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘画
|
|
||||||
function draw(w?) {
|
|
||||||
const point1 = points[0]
|
|
||||||
const point2 = points[1]
|
|
||||||
|
|
||||||
if (!w) {
|
|
||||||
allPoints[allPoints.length - 1].push(JSON.parse(JSON.stringify(points)))
|
|
||||||
}
|
|
||||||
|
|
||||||
points.shift()
|
|
||||||
ctx.moveTo(point1.X, point1.Y)
|
|
||||||
ctx.lineTo(point2.X, point2.Y)
|
|
||||||
ctx.stroke()
|
|
||||||
ctx.draw(true)
|
|
||||||
isSigned.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触摸开始,获取到起点
|
|
||||||
function touchStart() {
|
|
||||||
allPoints.push([])
|
|
||||||
ctx.beginPath() // 每次触摸开始,开启新的路径
|
|
||||||
isButtonDown = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触摸移动,获取到路径点
|
|
||||||
function touchMove(e) {
|
|
||||||
if (isButtonDown) {
|
|
||||||
let movePoint = {}
|
|
||||||
if (e.changedTouches[0].x) {
|
|
||||||
movePoint = { X: e.changedTouches[0].x, Y: e.changedTouches[0].y }
|
|
||||||
} else {
|
|
||||||
const X = e.changedTouches[0].pageX - e.currentTarget.offsetLeft
|
|
||||||
const Y = e.changedTouches[0].pageY - e.currentTarget.offsetTop
|
|
||||||
movePoint = { X, Y }
|
|
||||||
}
|
|
||||||
points.push(movePoint) // 存点
|
|
||||||
const len = points.length
|
|
||||||
if (len >= 2) {
|
|
||||||
draw() // 绘制路径
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触摸结束,将未绘制的点清空防止对后续路径产生干扰
|
|
||||||
function touchEnd() {
|
|
||||||
allPoints = allPoints.filter((e) => {
|
|
||||||
return e.length > 0
|
|
||||||
})
|
|
||||||
points = []
|
|
||||||
isButtonDown = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空, 传入true表示清空全部,不传传表示撤回一步
|
|
||||||
function clear(reset?: boolean) {
|
|
||||||
if (reset) allPoints = []
|
|
||||||
ctx.clearRect(0, 0, 1000, 1000)
|
|
||||||
ctx.draw(true)
|
|
||||||
isSigned.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 全屏
|
|
||||||
function handFullScreen() {
|
|
||||||
clear(true)
|
|
||||||
isFullScreen.value = !isFullScreen.value
|
|
||||||
const tid = setTimeout(() => {
|
|
||||||
onResize()
|
|
||||||
clearTimeout(tid)
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 撤回
|
|
||||||
function withdraw() {
|
|
||||||
// 清除画布
|
|
||||||
clear()
|
|
||||||
if (allPoints.length <= 1) {
|
|
||||||
allPoints = []
|
|
||||||
points = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 删除最后一个路径
|
|
||||||
allPoints.pop()
|
|
||||||
// 循环路径,重新绘制
|
|
||||||
allPoints.forEach((e) => {
|
|
||||||
e.forEach((r) => {
|
|
||||||
points = JSON.parse(JSON.stringify(r))
|
|
||||||
draw(1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// h5保存方法
|
|
||||||
function saveCanvasAsImage(dataURL, imageName?) {
|
|
||||||
// 创建一个Image元素
|
|
||||||
const img = new Image()
|
|
||||||
|
|
||||||
// 设置img的src属性为数据URL
|
|
||||||
img.src = dataURL
|
|
||||||
|
|
||||||
// 创建一个链接元素用于下载图片
|
|
||||||
const link = document.createElement('a')
|
|
||||||
|
|
||||||
// 设置下载的文件名
|
|
||||||
link.download = imageName || 'canvas-image'
|
|
||||||
|
|
||||||
// 触发点击,下载图片
|
|
||||||
link.href = img.src
|
|
||||||
link.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存
|
|
||||||
const save = () => {
|
|
||||||
if (!isSigned.value) {
|
|
||||||
uni.showToast({
|
|
||||||
title: '请签名',
|
|
||||||
icon: 'none',
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
uni.canvasToTempFilePath({
|
|
||||||
canvasId: 'canvas',
|
|
||||||
success: (res) => {
|
|
||||||
// 获取图片路径
|
|
||||||
const { tempFilePath } = res
|
|
||||||
// 保存图片到相册
|
|
||||||
// #ifdef H5
|
|
||||||
const name = `sign-${new Date().getTime()}`
|
|
||||||
saveCanvasAsImage(tempFilePath, name)
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifndef H5
|
|
||||||
uni.saveImageToPhotosAlbum({
|
|
||||||
filePath: tempFilePath,
|
|
||||||
success: () => {
|
|
||||||
uni.showToast({
|
|
||||||
title: '图片保存成功',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error(err)
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: '图片保存失败',
|
|
||||||
icon: 'none',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
fail: () => {
|
|
||||||
uni.showToast({
|
|
||||||
title: '转换图片失败',
|
|
||||||
icon: 'none',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
initCanvas()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
$padding: 30rpx;
|
|
||||||
|
|
||||||
.canvas-box {
|
|
||||||
.canvas {
|
|
||||||
height: 300rpx;
|
|
||||||
transition: height 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btns {
|
|
||||||
margin-top: 10rpx;
|
|
||||||
transition: transform 0.3s;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
width: auto;
|
|
||||||
height: 50rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-screen {
|
|
||||||
flex-direction: row;
|
|
||||||
height: calc(100vh - 88rpx);
|
|
||||||
|
|
||||||
.canvas {
|
|
||||||
width: calc(100% - 100rpx);
|
|
||||||
height: 100%;
|
|
||||||
margin-left: 100rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btns {
|
|
||||||
position: absolute;
|
|
||||||
align-items: center;
|
|
||||||
width: calc(100vh - (88rpx + $padding * 2));
|
|
||||||
height: 100rpx;
|
|
||||||
transform: translate(100rpx, 0) rotate(90deg);
|
|
||||||
transform-origin: top left;
|
|
||||||
|
|
||||||
.btn-box {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,188 +0,0 @@
|
|||||||
<route lang="json5" type="page">
|
|
||||||
{
|
|
||||||
style: { navigationBarTitleText: 'waterfall' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="waterfall">
|
|
||||||
<uv-waterfall
|
|
||||||
ref="waterfall"
|
|
||||||
v-model="list"
|
|
||||||
:add-time="10"
|
|
||||||
:left-gap="leftGap"
|
|
||||||
:right-gap="rightGap"
|
|
||||||
:column-gap="columnGap"
|
|
||||||
@changeList="changeList"
|
|
||||||
>
|
|
||||||
<!-- 第一列数据 -->
|
|
||||||
<template v-slot:list1>
|
|
||||||
<!-- 为了磨平部分平台的BUG,必须套一层view -->
|
|
||||||
<view>
|
|
||||||
<view v-for="item in lists.list1" :key="item.id" class="waterfall-item">
|
|
||||||
<view class="waterfall-item__image" :style="[imageStyle(item)]">
|
|
||||||
<image
|
|
||||||
:src="item.image"
|
|
||||||
mode="widthFix"
|
|
||||||
:style="{ width: item.width + 'px' }"
|
|
||||||
></image>
|
|
||||||
</view>
|
|
||||||
<view class="waterfall-item__ft">
|
|
||||||
<view class="waterfall-item__ft__title">
|
|
||||||
<text class="value">{{ item.title }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="waterfall-item__ft__desc uv-line-2">
|
|
||||||
<text class="value">{{ item.desc }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
<!-- 第二列数据 -->
|
|
||||||
<template v-slot:list2>
|
|
||||||
<!-- 为了磨平部分平台的BUG,必须套一层view -->
|
|
||||||
<view>
|
|
||||||
<view v-for="item in lists.list2" :key="item.id" class="waterfall-item">
|
|
||||||
<view class="waterfall-item__image" :style="[imageStyle(item)]">
|
|
||||||
<image
|
|
||||||
:src="item.image"
|
|
||||||
mode="widthFix"
|
|
||||||
:style="{ width: item.width + 'px' }"
|
|
||||||
></image>
|
|
||||||
</view>
|
|
||||||
<view class="waterfall-item__ft">
|
|
||||||
<view class="waterfall-item__ft__title">
|
|
||||||
<text class="value">{{ item.title }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="waterfall-item__ft__desc uv-line-2">
|
|
||||||
<text class="value">{{ item.desc }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
</uv-waterfall>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
const list = ref([]) // 瀑布流全部数据
|
|
||||||
const lists = reactive({
|
|
||||||
list1: [], // 瀑布流第一列数据
|
|
||||||
list2: [], // 瀑布流第二列数据
|
|
||||||
})
|
|
||||||
const leftGap = ref(10)
|
|
||||||
const rightGap = ref(10)
|
|
||||||
const columnGap = ref(10)
|
|
||||||
|
|
||||||
const imageStyle = computed(() => {
|
|
||||||
return (item) => {
|
|
||||||
const v = uni.upx2px(750) - leftGap.value - rightGap.value - columnGap.value
|
|
||||||
const w = v / 2
|
|
||||||
const rate = w / item.w
|
|
||||||
const h = rate * item.h
|
|
||||||
return {
|
|
||||||
width: `${w}px`,
|
|
||||||
height: `${h}px`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
onLoad(async () => {
|
|
||||||
const { data } = await getData()
|
|
||||||
list.value = data
|
|
||||||
})
|
|
||||||
|
|
||||||
const waterfall = ref()
|
|
||||||
// 如果页面还没渲染结束,页面就跳走,但此时@changeList回调还在返回数据,可能会造成渲染出错,所以要想办法停止渲染
|
|
||||||
onHide(() => {
|
|
||||||
waterfall.value.clear()
|
|
||||||
})
|
|
||||||
// 这点非常重要:e.name在这里返回是list1或list2,要手动将数据追加到相应列
|
|
||||||
const changeList = (e: { name: 'list1' | 'list2'; value: any }) => {
|
|
||||||
lists[e.name].push(e.value)
|
|
||||||
}
|
|
||||||
// 模拟的后端数据6
|
|
||||||
const getData = (): Promise<{ data: any[] }> => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const imgs = [
|
|
||||||
{ url: 'https://via.placeholder.com/100x110.png/3c9cff/fff', width: 100, height: 110 },
|
|
||||||
{ url: 'https://via.placeholder.com/200x220.png/f9ae3d/fff', width: 200, height: 220 },
|
|
||||||
{ url: 'https://via.placeholder.com/300x340.png/5ac725/fff', width: 300, height: 340 },
|
|
||||||
{ url: 'https://via.placeholder.com/400x400.png/f56c6c/fff', width: 400, height: 400 },
|
|
||||||
{ url: 'https://via.placeholder.com/500x510.png/909399/fff', width: 500, height: 510 },
|
|
||||||
{ url: 'https://via.placeholder.com/600x606.png/3c9cff/fff', width: 600, height: 606 },
|
|
||||||
{ url: 'https://via.placeholder.com/310x422.png/f1a532/fff', width: 310, height: 422 },
|
|
||||||
{ url: 'https://via.placeholder.com/320x430.png/3c9cff/fff', width: 320, height: 430 },
|
|
||||||
{ url: 'https://via.placeholder.com/330x424.png/f9ae3d/fff', width: 330, height: 424 },
|
|
||||||
{ url: 'https://via.placeholder.com/340x435.png/5ac725/fff', width: 340, height: 435 },
|
|
||||||
{ url: 'https://via.placeholder.com/350x440.png/f56c6c/fff', width: 350, height: 440 },
|
|
||||||
{ url: 'https://via.placeholder.com/380x470.png/909399/fff', width: 380, height: 470 },
|
|
||||||
]
|
|
||||||
const arr = []
|
|
||||||
const doFn = (i: number) => {
|
|
||||||
const randomIndex = Math.floor(Math.random() * 10)
|
|
||||||
return {
|
|
||||||
id: uni.$uv.guid(),
|
|
||||||
allowEdit: i === 0,
|
|
||||||
image: imgs[randomIndex].url,
|
|
||||||
w: imgs[randomIndex].width,
|
|
||||||
h: imgs[randomIndex].height,
|
|
||||||
title:
|
|
||||||
i % 2 === 0
|
|
||||||
? `(${list.value.length + i + 1})体验uv-ui框架`
|
|
||||||
: `(${list.value.length + i + 1})uv-ui支持多平台`,
|
|
||||||
desc:
|
|
||||||
i % 2 === 0
|
|
||||||
? `(${list.value.length + i + 1})欢迎使用uv-ui,uni-app生态专用的UI框架`
|
|
||||||
: `(${list.value.length + i})开发者编写一套代码, 可发布到iOS、Android、H5、以及各种小程序`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 模拟异步
|
|
||||||
setTimeout(() => {
|
|
||||||
for (let i = 0; i < 20; i++) {
|
|
||||||
arr.push(doFn(i))
|
|
||||||
}
|
|
||||||
resolve({ data: arr })
|
|
||||||
}, 200)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
page {
|
|
||||||
background: #f1f1f1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.waterfall-item {
|
|
||||||
margin-top: 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 6px;
|
|
||||||
/* stylelint-disable-next-line selector-class-pattern */
|
|
||||||
.waterfall-item__ft {
|
|
||||||
padding: 20rpx;
|
|
||||||
background: #fff;
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
margin-bottom: 10rpx;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 48rpx;
|
|
||||||
|
|
||||||
.value {
|
|
||||||
font-size: 32rpx;
|
|
||||||
color: #303133;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__desc .value {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #606266;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__btn {
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,55 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
layout: 'demo',
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: '请求',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="mt-6">
|
|
||||||
<!-- http://localhost:9000/#/pages/index/request -->
|
|
||||||
<button @click="getFoo" class="my-4">测试 GET 请求</button>
|
|
||||||
<view class="text-xl">请求数据如下</view>
|
|
||||||
<view class="text-green h-10">{{ JSON.stringify(data) }}</view>
|
|
||||||
<button @click="postFoo" class="my-4">测试 POST 请求</button>
|
|
||||||
<view class="text-xl">请求数据如下</view>
|
|
||||||
<view class="text-green h-10">{{ JSON.stringify(data2) }}</view>
|
|
||||||
|
|
||||||
<view class="my-2">使用的是 laf 云后台</view>
|
|
||||||
<view class="text-green-400">我的推荐码,可以获得佣金</view>
|
|
||||||
<!-- #ifdef H5 -->
|
|
||||||
<view class="my-2 text-center">
|
|
||||||
<a class="my-2 text-center" :href="recommendUrl" target="_blank">{{ recommendUrl }}</a>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<!-- #ifndef H5 -->
|
|
||||||
<view class="my-2 text-left text-sm">{{ recommendUrl }}</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { getFooAPI, postFooAPI, IFooItem } from '@/service/foo'
|
|
||||||
|
|
||||||
const recommendUrl = ref('http://laf.run/signup?code=ohaOgIX')
|
|
||||||
|
|
||||||
onLoad(() => {
|
|
||||||
getFoo()
|
|
||||||
postFoo()
|
|
||||||
})
|
|
||||||
|
|
||||||
const data = ref<IFooItem>()
|
|
||||||
const getFoo = async () => {
|
|
||||||
const res = await getFooAPI('菲鸽')
|
|
||||||
data.value = res.result
|
|
||||||
}
|
|
||||||
|
|
||||||
const data2 = ref<IFooItem>()
|
|
||||||
const postFoo = async () => {
|
|
||||||
const res = await postFooAPI('菲鸽2')
|
|
||||||
data2.value = res.result
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,39 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
style: { navigationBarTitleText: '登录' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="p-4">
|
|
||||||
<view class="flex items-center leading-6" v-if="hasLogin">
|
|
||||||
<image class="w-8 h-8 rounded-full" :src="userStore.userInfo?.avatar"></image>
|
|
||||||
<view class="ml-2">{{ userStore.userInfo?.nickname }}</view>
|
|
||||||
</view>
|
|
||||||
<view class="flex items-center leading-6" v-else @click="show = true">
|
|
||||||
<view class="i-carbon-user-avatar"></view>
|
|
||||||
<view class="ml-2">点击显示微信头像</view>
|
|
||||||
</view>
|
|
||||||
<fly-login v-model="show" />
|
|
||||||
<fly-content :line="10" />
|
|
||||||
<button v-if="hasLogin" class="mt-2" @click="logout">退出登录</button>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup name="WxLogin">
|
|
||||||
import { useUserStore } from '@/store'
|
|
||||||
|
|
||||||
const show = ref(false)
|
|
||||||
const userStore = useUserStore()
|
|
||||||
const hasLogin = computed(() => userStore.userInfo?.nickname)
|
|
||||||
const logout = () => {
|
|
||||||
uni.showModal({
|
|
||||||
title: '确认退出当前账号?',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
userStore.clearUserInfo()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,35 +0,0 @@
|
|||||||
<route lang="json5">
|
|
||||||
{
|
|
||||||
style: { navigationBarTitleText: '我的' },
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
<template>
|
|
||||||
<view class="ml-4">wx的openid: </view>
|
|
||||||
<view class="ml-4">{{ openId }}</view>
|
|
||||||
<wx-login />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useUserStore } from '@/store'
|
|
||||||
import { http } from '@/utils/http'
|
|
||||||
import WxLogin from './components/wx-login.vue'
|
|
||||||
|
|
||||||
const userStore = useUserStore()
|
|
||||||
const openId = ref('')
|
|
||||||
|
|
||||||
// 用户登录,获取openId
|
|
||||||
uni.login({
|
|
||||||
provider: 'weixin',
|
|
||||||
success: async ({ code }) => {
|
|
||||||
const res = await http<{ session_key: string; openid: string }>({
|
|
||||||
method: 'GET',
|
|
||||||
url: '/weixin/jscode2session',
|
|
||||||
data: {
|
|
||||||
code,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
openId.value = res.result.openid
|
|
||||||
userStore.setUserInfo({ openid: res.result.openid })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
|
Loading…
x
Reference in New Issue
Block a user