feat: 拖拽组件优化

master
xjs 2025-03-19 17:40:18 +08:00
parent 2a44fe0747
commit ed37c3cddd
31 changed files with 7473 additions and 199 deletions

View File

@ -65,7 +65,7 @@ export default defineUniPages({
preloadRule: {
'pages/home/index/index': {
network: 'all',
packages: ['__APP__'],
packages: ['pages-evaluation-sub'],
},
},
condition: {
@ -80,7 +80,7 @@ export default defineUniPages({
},
permission: {
'scope.userLocation': {
desc: '你的位置信息将用于小程序位置接口的效果展示', // 高速公路行驶持续后台定位
desc: '你的位置信息将用于小程序位置接口的效果展示',
},
},
})

View File

@ -0,0 +1,109 @@
<template>
<view class="custom-tabbar pb-safe">
<view class="tabbar-content">
<view
class="tabbar-item"
v-for="item in tabbarList"
:key="item.id"
:class="[item.centerItem ? 'center-item' : '']"
@click="changeItem(item)"
>
<view class="item-top">
<image
class="w-full h-full object-contain item-icon"
:src="currentPage == item.id ? item.selectIcon : item.icon"
></image>
</view>
<view class="item-bottom" :class="[currentPage == item.id ? 'item-active' : '']">
<text>{{ item.text }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { TabesItem } from '@/service/app/types'
import { tabbarList } from '@/hooks/useTabbarList'
defineProps({
currentPage: {
type: Number,
default: 0,
},
})
const changeItem = (item: TabesItem) => {
uni.switchTab({
url: item.path,
})
}
onMounted(() => {
uni.hideTabBar()
})
</script>
<style scoped>
.custom-tabbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
border-top: 1px solid #f5f5f5;
z-index: 999;
}
.tabbar-content {
display: flex;
height: 100rpx;
padding: 0 20rpx;
position: relative;
}
.tabbar-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
padding: 10rpx 0;
}
.item-top {
display: flex;
align-items: center;
justify-content: center;
}
.item-bottom {
font-size: 20rpx;
margin-top: 6rpx;
color: #999;
}
.item-active {
color: #3370ff;
}
.center-item {
position: relative;
}
.item-icon {
width: 48rpx;
height: 48rpx;
}
.center-item .item-icon {
width: 98rpx;
height: 98rpx;
}
.center-item .item-bottom {
position: absolute;
bottom: 5rpx;
}
</style>

View File

@ -1,33 +1,9 @@
<template>
<wd-tabbar :bordered="false" safeAreaInsetBottom :placeholder="true" fixed>
<view
class="tabbar-item h-[100rpx] flex flex-col justify-center w-full items-center text-center"
v-for="(item, index) in tabbarList"
:key="item.id"
:class="[item.centerItem ? 'center-item' : '']"
@click="changeItem(item)"
>
<view class="item-top w-[48rpx] h-[48rpx] p-[10rpx]">
<image
class="w-full h-full object-container"
:src="currentPage == item.id ? item.selectIcon : item.icon"
></image>
</view>
<view
class="item-bottom text-[20rpx] mt-[6rpx]"
:class="[currentPage == item.id ? 'item-active text-[#3370ff]' : '']"
>
<text>{{ item.text }}</text>
</view>
</view>
</wd-tabbar>
<custom-tab-bar :current-page="currentPage" :safe-area-inset-bottom="true" />
</template>
<script setup lang="ts">
import { TabesItem } from '@/service/app/types'
import { tabbarList } from '@/hooks/useTabbarList'
const tabbar = ref(1)
import CustomTabBar from './CustomTabBar.vue'
defineProps({
currentPage: {
@ -35,35 +11,4 @@ defineProps({
default: 0,
},
})
const changeItem = (item: TabesItem) => {
uni.switchTab({
url: item.path,
})
}
onMounted(() => {
uni.hideTabBar()
})
</script>
<style scoped>
view {
box-sizing: border-box;
padding: 0;
margin: 0;
}
.center-item {
position: relative;
}
.center-item .item-top {
width: 98rpx;
height: 98rpx;
}
.center-item .item-bottom {
position: absolute;
bottom: 5rpx;
}
</style>

View File

@ -24,7 +24,8 @@ const props = defineProps({
},
})
const systemInfo = uni.getSystemInfoSync()
const systemInfo = uni.getWindowInfo()
const position = ref({ x: props.initialX, y: props.initialY })
const startPosition = ref({ x: 0, y: 0 })
const startTime = ref(0)

View File

@ -91,13 +91,16 @@ const props = defineProps({
const emit = defineEmits(['clickLeft'])
//
const systemInfo = uni.getSystemInfoSync()
const systemInfo = uni.getWindowInfo()
const deviceInfo = uni.getDeviceInfo()
const statusBarHeight = systemInfo.statusBarHeight || 0
//
const navHeight = computed(() => {
//
const { platform, screenWidth } = systemInfo
const { screenWidth } = systemInfo
const { platform } = deviceInfo
// pxrpx
const ratio = 750 / screenWidth

View File

@ -9,7 +9,7 @@ export default () => {
// 页面滚动到底部时的操作,通常用于加载更多数据
const onScrollToLower = () => {}
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
const { safeAreaInsets } = uni.getWindowInfo()
// #ifdef MP-WEIXIN
// 基于小程序的 Page 类型扩展 uni-app 的 Page

View File

@ -2,7 +2,6 @@ import { TabesItem } from '@/service/app'
import { sysDictType } from '@/service/index/api'
const tabbarList = ref<TabesItem[]>([])
const app = getApp()
sysDictType({ id: 619330547859525 }).then((res) => {
const { code, result } = res

View File

@ -50,10 +50,10 @@ import {
getWxUserInfo,
setWxInfo,
} from '@/service/index/api'
import { useUserStore } from '@/store'
import { useUserStore } from '@/store/user'
import { City } from '@/types/app-type'
const props = defineProps({
defineProps({
show: {
type: Boolean,
default: false,
@ -129,11 +129,15 @@ const getUserInfo = async (code: string) => {
zyBatches: any[]
batchDataUrl: string
batchName: string
avatar: string
nickName: string
}
userStore.setEstimatedAchievement(infoData.userExtend)
userStore.setZyBatches(infoData.zyBatches)
userStore.setBatchDataUrl(infoData.batchDataUrl)
userStore.setBatchName(infoData.batchName)
userStore.setUserAvatar(infoData.avatar)
userStore.setUserNickName(infoData.nickName)
if (resp.code === 200) {
//

View File

@ -91,13 +91,15 @@ const props = defineProps({
const emit = defineEmits(['clickLeft'])
//
const systemInfo = uni.getSystemInfoSync()
const systemInfo = uni.getWindowInfo()
const deviceInfo = uni.getDeviceInfo()
const statusBarHeight = systemInfo.statusBarHeight || 0
//
const navHeight = computed(() => {
//
const { platform, screenWidth } = systemInfo
const { screenWidth } = systemInfo
const { platform } = deviceInfo
// pxrpx
const ratio = 750 / screenWidth

View File

@ -91,13 +91,15 @@ const props = defineProps({
const emit = defineEmits(['clickLeft'])
//
const systemInfo = uni.getSystemInfoSync()
const systemInfo = uni.getWindowInfo()
const deviceInfo = uni.getDeviceInfo()
const statusBarHeight = systemInfo.statusBarHeight || 0
//
const navHeight = computed(() => {
//
const { platform, screenWidth } = systemInfo
const { screenWidth } = systemInfo
const { platform } = deviceInfo
// pxrpx
const ratio = 750 / screenWidth

File diff suppressed because one or more lines are too long

View File

@ -24,8 +24,8 @@
:style="[
{ 'z-index': activeIndex === index ? 9 : 1 },
{ top: activeIndex === index ? scrollInfo.y + 'px' : 0 },
isList ? { height: item.height + 'px' } : getItemStyle,
{ position: 'absolute' },
]"
class="base-drag-wrapper"
>
@ -58,6 +58,7 @@
{ 'z-index': activeIndex === index ? 9 : 1 },
{ top: activeIndex === index ? scrollInfo.y + 'px' : 0 },
isList ? { height: item.height + 'px' } : getItemStyle,
{ position: 'absolute' },
]"
class="base-drag-wrapper"
>
@ -260,10 +261,13 @@ const getAreaStyle = computed(() => {
const w = getRealWidth(width.value)
let h: string | number = props.height
if (h === 'auto') {
// / * = 使viewMaxHeightitem item
h = isList.value
? viewMaxHeight.value
: Math.ceil(showList.value.length / props.column) * getItemHeight.value
if (isList.value) {
//
h = showList.value.reduce((total, item) => total + (item.height || defaultHeight), 0)
} else {
//
h = Math.ceil(showList.value.length / props.column) * getItemHeight.value
}
}
let style = {
width: w + 'px',
@ -329,115 +333,182 @@ const getPosition = (index: number, list = cloneList.value): [number, number] =>
return [x, y]
}
//
//
interface Task {
index: number
priority: number
timestamp: number
}
const taskQueue = ref<Task[]>([])
const isProcessing = ref(false)
const lastFrameTime = ref(0)
const FRAME_DURATION = 16 // 16ms
const IDLE_TIMEOUT = 50 //
const defaultHeight = 400 // 400rpx
const viewportTop = ref(0)
const viewportHeight = ref(0)
const viewportBottom = computed(() => viewportTop.value + viewportHeight.value)
//
const calculatePriority = (index: number, y: number, height: number): number => {
let priority = 0
//
if (y <= viewportBottom.value && y + height >= viewportTop.value) {
priority = 3
}
//
else if (y < viewportBottom.value + 200) {
priority = 2
}
//
else {
priority = 1
}
return priority
}
//
const processTask = (task: Task) => {
return new Promise<void>((resolve) => {
const item = showList.value[task.index]
if (!item) {
resolve()
return
}
//
const query = uni.createSelectorQuery().in(instance.proxy)
query
.selectAll('.slotContent')
.boundingClientRect((data) => {
if (data && Array.isArray(data) && data[task.index]) {
//
item.height = data[task.index].height
// y
let y = 0
for (let i = 0; i < task.index; i++) {
y += data[i].height || defaultHeight
}
// x
const x = (task.index % props.column) * Number(getItemWidth.value)
//
item.x = x
item.y = y
//
showList.value[task.index] = { ...item }
console.log('更新高度和位置:', task.index, item.height, item.x, item.y)
}
resolve()
})
.exec()
})
}
//
const scheduleTask = async () => {
if (isProcessing.value || taskQueue.value.length === 0) return
isProcessing.value = true
const currentTime = Date.now()
const timeSinceLastFrame = currentTime - lastFrameTime.value
//
if (timeSinceLastFrame < FRAME_DURATION) {
await new Promise((resolve) => setTimeout(resolve, FRAME_DURATION - timeSinceLastFrame))
}
//
const task = taskQueue.value.reduce((prev, curr) => (prev.priority > curr.priority ? prev : curr))
//
taskQueue.value = taskQueue.value.filter((t) => t.index !== task.index)
//
await processTask(task)
lastFrameTime.value = Date.now()
isProcessing.value = false
//
if (taskQueue.value.length > 0) {
setTimeout(() => {
scheduleTask()
}, 0)
}
}
//
const initList = (list: ItemType[] = [], changeheight: boolean = false): Promise<void> => {
return new Promise((resolve) => {
const newList = deepCopy(list)
// itemxykey
// 使
showList.value = newList.map((item, index) => {
const [x, y] = getPosition(index)
let data = {
let y = 0
//
for (let i = 0; i < index; i++) {
y += newList[i]?.height || defaultHeight
}
const x = (index % props.column) * Number(getItemWidth.value)
return {
...item,
x,
y,
height: item.height || defaultHeight,
dropId: index + 1,
key: 'slot' + Math.random() + index,
}
let key = 'slot' + Math.random() + index
// xykey
if (x === item?.x && y === item?.y) {
if (activeIndex.value !== index) {
// key
key = item.key
}
}
//
data.key = key
return data
})
cloneList.value = deepCopy(showList.value)
nextTick(() => {
showArea.value = true
})
if (changeheight && props.itemHeight === 'auto') {
// item
setTimeout(async () => {
// #ifdef APP-NVUE
showArea.value = false
//
taskQueue.value = showList.value.map((_, index) => ({
index,
priority: 0,
timestamp: Date.now(),
}))
const calculateHeights = async () => {
let max = 0
let viewMaxHeightVal = 0
const promises = slotContent.value.map((content, index) => {
return new Promise<void>((resolve) => {
dom.getComponentRect(content, (res: any) => {
let size = res.size
if (isList.value) {
cloneList.value[index].height = size.height
}
viewMaxHeightVal += size.height
if (size.height > max) {
max = size.height
}
resolve()
})
})
})
await Promise.all(promises)
viewMaxHeight.value = viewMaxHeightVal
itemMaxHeight.value = max + 'px'
nextTick(() => {
initList(cloneList.value).then(resolve)
})
}
await calculateHeights()
// #endif
// #ifndef APP-NVUE
const query = uni.createSelectorQuery().in(instance.proxy)
query
.selectAll('.slotContent')
.boundingClientRect((data) => {
let domList = JSON.parse(JSON.stringify(data))
let max = 0
let viewMaxHeightVal = 0
for (let i = 0; i < domList.length; i++) {
let height = domList[i].height
if (isList.value) {
cloneList.value[i].height = height
}
viewMaxHeightVal += height
if (height > max) {
max = height
}
}
viewMaxHeight.value = viewMaxHeightVal //
itemMaxHeight.value = max + 'px'
initList(cloneList.value).then(resolve)
})
.exec()
// #endif
}, 0)
//
nextTick(() => {
console.log('开始处理任务队列:', taskQueue.value.length)
scheduleTask()
})
} else {
resolve()
}
})
}
//
const handleScroll = (e: any) => {
const scrollTop = e.detail.scrollTop
viewportTop.value = scrollTop
//
taskQueue.value = taskQueue.value.map((task) => ({
...task,
priority: calculatePriority(
task.index,
showList.value[task.index].y,
showList.value[task.index].height,
),
}))
}
//
//
const showPlaceholder = (i: number): boolean => {
// ,
let isShow = false
if (moveToIndex.value > activeIndex.value) {
// i-1;
if (moveToIndex.value === i - 1) {
isShow = true
}
} else if (moveToIndex.value === activeIndex.value) {
return false //
} else if (moveToIndex.value < activeIndex.value) {
@ -663,8 +734,10 @@ const getViewCallback = (data: any, size: number) => {
return
}
width.value = data.width
showArea.value = true //
nextTick(() => {
initList(showList.value, true) // dom
console.log('初始化列表,数据长度:', showList.value.length)
initList(showList.value, true)
})
}
@ -693,7 +766,9 @@ showList.value = deepCopy(props.list) || []
//
onMounted(() => {
setLeoDrag()
nextTick(() => {
setLeoDrag()
})
})
//
@ -712,12 +787,21 @@ defineExpose({
display: flex;
align-items: center;
transition: top 0.4s;
position: absolute;
left: 0;
top: 0;
flex-wrap: wrap;
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
}
.movable-area {
position: relative;
width: 100%;
overflow: hidden;
}
.slotContent {
display: flex;
/* #ifndef APP-NVUE */

View File

@ -91,13 +91,15 @@ const props = defineProps({
const emit = defineEmits(['clickLeft'])
//
const systemInfo = uni.getSystemInfoSync()
const systemInfo = uni.getWindowInfo()
const deviceInfo = uni.getDeviceInfo()
const statusBarHeight = systemInfo.statusBarHeight || 0
//
const navHeight = computed(() => {
//
const { platform, screenWidth } = systemInfo
const { screenWidth } = systemInfo
const { platform } = deviceInfo
// pxrpx
const ratio = 750 / screenWidth

View File

@ -23,7 +23,7 @@ withDefaults(
}>(),
{
backgroundColor: 'rgba(0, 0, 0, 0.7)',
zIndex: 1000,
zIndex: 10,
lockScroll: true,
},
)

View File

@ -89,8 +89,6 @@ const rankDiff = (index: number, item) => {
const recompileData = computed(() => {
let _data = props.data.map((item, index) => {
console.log(props.score)
item['rankDiff'] = rankDiff(index, item)
item['lineDiff'] = item['score'] - props.score
return item

View File

@ -0,0 +1,21 @@
<template>
<view class="flex items-center gap-[26rpx] text-[22rpx] text-[#000]">
<view v-for="(model, index) of tModel" :key="model.type" class="flex items-center gap-[6rpx]">
<view
class="w-[16rpx] h-[16rpx] rounded-full"
:style="{ backgroundColor: calcTypeName(model.type).roundedBgColor }"
></view>
<text class="text-[22rpx]">{{ calcTypeName(model.type)?.text }}({{ model.count }})</text>
</view>
</view>
</template>
<script lang="ts" setup>
import { useUserStore } from '@/store/user'
import { countModel, calcTypeName } from '../composable/useWishesList'
const userStore = useUserStore()
const userWhishList = computed(() => userStore.userInfo.wishList)
const { tModel } = countModel(userWhishList.value)
tModel.value = tModel.value
</script>

View File

@ -14,24 +14,12 @@
<text class="i-carbon-help"></text>
</view>
<view v-if="type === 1" class="flex items-center gap-[26rpx] text-[22rpx] text-[#000]">
<view v-for="(model, index) of tModel" :key="model.type" class="flex items-center gap-[6rpx]">
<view
class="w-[16rpx] h-[16rpx] rounded-full"
:style="{ backgroundColor: calcTypeName(model.type).roundedBgColor }"
></view>
<text class="text-[22rpx]">{{ calcTypeName(model.type)?.text }}({{ model.count }})</text>
</view>
</view>
<HeaderModelTip v-if="type === 1" />
</view>
</template>
<script lang="ts" setup>
import { useUserStore } from '@/store/user'
import { countModel, calcTypeName } from '../composable/useWishesList'
const userStore = useUserStore()
import HeaderModelTip from './HeaderModelTip.vue'
defineProps({
userInfo: {
type: Object,
@ -42,9 +30,6 @@ defineProps({
default: 0,
},
})
const userWhishList = computed(() => userStore.userInfo.wishList)
const { tModel } = countModel(userWhishList.value)
</script>
<style lang="scss" scoped>

View File

@ -63,8 +63,8 @@
</view>
<view class="h-[2rpx] bg-[#EDEDED] w-full"></view>
<view
class="overflow-hidden transition-all duration-300"
:style="{ maxHeight: isCollapsed ? '0' : '2000px' }"
class="overflow-auto transition-all duration-300"
:style="{ maxHeight: isCollapsed ? '0' : '100vh' }"
>
<DragSort v-model:list="college.vItems" ref="majorDrop" @get-list="getMajorList">
<template #content="{ data }">

View File

@ -83,7 +83,7 @@
:class="`h-[80rpx] rounded-[8rpx] bg-[#1580FF] flex items-center justify-center text-[#fff] ${majorCount === 0 ? 'disabled-btn' : ''}`"
@click="handlePreview"
>
预览志愿表({{ majorCount }})
预览志愿表{{ majorCount ? `(${majorCount})` : '' }}
</view>
</view>
</template>
@ -163,7 +163,7 @@ const handleTypeModelChange = ({ item }) => {
}
const majorCount = computed(() => {
return userStore.userInfo.wishList.reduce((a, b) => a + b.vItems.length, 0)
return userStore.userInfo.wishList.reduce((a, b) => a + (b?.vItems?.length || 0), 0)
})
const queryList = (page: number, pageSize: number) => {

View File

@ -18,6 +18,7 @@
<template #content="{ data }">
<view
class="drop"
id="wishListContainer"
:style="[{ transform: data.index === data.activeIndex ? 'scale(1.05)' : 'scale(1)' }]"
>
<SortCollege
@ -33,12 +34,13 @@
</template>
</DragSortList>
<view class="flex items-center pb-safe button-group px-[32rpx] pt-[32rpx]">
<button
<!-- <button
plain
class="border-[#f5f5f5]! flex-auto bg-[#f5f5f5]! rounded-[8rpx]! text-[#1580FF]! text-[32rpx]! font-normal! mr-[22rpx]"
@click="exportPdf"
>
下载PDF
</button>
</button> -->
<button
class="border-[#1580FF]! flex-auto bg-[#1580FF]! rounded-[8rpx]! text-[#fff]! text-[32rpx]! font-normal!"
@click="messageBoxShow = true"
@ -51,9 +53,9 @@
<template>
<input
type="text"
class="w-auto p-[20rpx] text-center input-border mb-[16rpx]"
class="w-auto p-[20rpx] text-center input-border mb-[16rpx] z-[-1]"
v-model="wishListName"
placeholder="请输入志愿表名字"
:placeholder="messageBoxShow ? '请输入志愿表名字' : ''"
confirm-type="done"
@confirm="handleSave"
/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

View File

@ -0,0 +1,11 @@
<route lang="json5" type="page">
{
style: {
navigationBarTitleText: '六纬VIP会员卡',
},
}
</route>
<template></template>
<script setup lang="ts"></script>

View File

@ -81,14 +81,17 @@
{
"path": "pages/ucenter/index/index",
"type": "page",
"style": {}
"style": {
"navigationStyle": "custom"
},
"needLogin": true
}
],
"preloadRule": {
"pages/home/index/index": {
"network": "all",
"packages": [
"__APP__"
"pages-evaluation-sub"
]
}
},
@ -111,6 +114,13 @@
{
"root": "pages-sub",
"pages": [
{
"path": "ucenter/openVip",
"type": "page",
"style": {
"navigationBarTitleText": "六纬VIP会员卡"
}
},
{
"path": "customerService/index/index",
"type": "page",

View File

@ -1,8 +1,215 @@
<route lang="json5" type="page">
{
style: {
navigationStyle: 'custom',
},
needLogin: true,
}
</route>
<template>
<view class="">用户信息</view>
<TabBar :current-page="4"></TabBar>
<view class="bg-[#F8F8F8] h-screen flex flex-col gap-[32rpx]">
<view class="background">
<Navbar safeAreaInsetTop bg-color="transparent" placeholder :bordered="false"></Navbar>
<view class="flex px-[32rpx] gap-[24rpx] custom-user-info">
<image
:src="userStore.userInfo.avatar"
class="w-[112rpx] h-[112rpx] rounded-full z-[1]"
mode="scaleToFill"
/>
<view class="flex flex-col gap-[6rpx] py-[8rpx] z-[1] text-white">
<text class="text-[32rpx] font-semibold">{{ userStore.userInfo.nickname }}</text>
<text class="text-[24rpx]">
{{ userStore.userInfo.estimatedAchievement.provinceName }}·{{
userStore.userInfo.estimatedAchievement.expectedScore
}}
{{ userStore.userInfo.estimatedAchievement.subjectGroup.split(',').join('/') }}
</text>
</view>
</view>
<view
class="absolute bottom-0 py-[16rpx] pl-[32rpx] pr-[24rpx] left-[50%] translate-x-[-50%] flex items-center w-max custom-vip-bg"
v-if="!userStore.userInfo.estimatedAchievement.isVIP"
>
<image
src="/static/images/ucenter/vip.png"
mode="scaleToFill"
class="w-[64rpx] h-[64rpx]"
/>
<view class="h-[32rpx] w-[1rpx] bg-[#fff] mx-[20rpx]"></view>
<text class="text-white text-[24rpx]">诸多分析特权免费查看</text>
<button
class="w-[136rpx]! h-[52rpx]! text-[#803D00]! text-[24rpx]! custom-button-bg font-normal flex! items-center! justify-center! rounded-[80rpx]! p-[0]! ml-[70rpx]!"
@click="goVip"
>
立即开通
</button>
</view>
<view class="diagonal-lines"></view>
</view>
<view
class="mx-[32rpx] flex items-center justify-center text-[#303030] text-[24rpx] bg-[#fff] pt-[24rpx] pb-[18rpx] gap-[144rpx] rounded-[8rpx]"
>
<view class="flex flex-col items-center gap-[16rpx]">
<image
src="@/pages-sub/static/images/ucenter/wish-list.png"
class="w-[51rpx] h-[51rpx]"
mode="scaleToFill"
/>
<text>志愿表</text>
</view>
<view class="flex flex-col items-center gap-[16rpx]">
<image
src="@/pages-sub/static/images/ucenter/evaluation-list.png"
class="w-[51rpx] h-[51rpx]"
mode="scaleToFill"
/>
<text>测评结果</text>
</view>
<view class="flex flex-col items-center gap-[16rpx]">
<image
src="@/pages-sub/static/images/ucenter/star.png"
class="w-[51rpx] h-[51rpx]"
mode="scaleToFill"
/>
<text>收藏</text>
</view>
</view>
<view class="mx-[32rpx] bg-[#fff] rounded-[8rpx]">
<view class="flex items-center justify-between mx-[32rpx] py-[28rpx] not-last-child">
<text>我的预约</text>
<view class="i-carbon-chevron-down rotate-270 text-[#BFBFBF]"></view>
</view>
<view class="flex items-center justify-between mx-[32rpx] py-[28rpx]">
<text>我的预约</text>
<view class="i-carbon-chevron-down rotate-270 text-[#BFBFBF]"></view>
</view>
</view>
<view class="mx-[32rpx] bg-[#fff] rounded-[8rpx]">
<view class="flex items-center justify-between mx-[32rpx] py-[28rpx] not-last-child">
<text>设置</text>
<view class="i-carbon-chevron-down rotate-270 text-[#BFBFBF]"></view>
</view>
</view>
<TabBar :current-page="4"></TabBar>
<FabButton :initial-x="0" :initial-y="100" />
</view>
</template>
<script lang="ts" setup>
import TabBar from '@/components/bar/TabBar.vue'
import Navbar from '@/components/navbar/Navbar.vue'
import FabButton from '@/components/fab/FabButton.vue'
import { useUserStore } from '@/store'
const userStore = useUserStore()
const goVip = () => {
const { platform } = uni.getDeviceInfo()
switch (platform) {
case 'android':
uni.navigateTo({
url: '/pages-sub/ucenter/openVip',
})
break
case 'ios':
uni.showModal({
title: 'iso系统暂不支持线上支付',
})
break
default:
uni.navigateTo({
url: '/pages-sub/ucenter/openVip',
})
break
}
}
</script>
<style lang="scss" scoped>
.background {
position: relative;
width: 100%;
height: 420rpx;
overflow: hidden;
background: linear-gradient(135deg, #33ccff 0%, #3399ff 100%);
}
/* 左下角大圆 */
.background::before {
content: '';
position: absolute;
width: 400rpx;
height: 400rpx;
bottom: -200rpx;
left: -100rpx;
border-radius: 50%;
background-color: rgba(150, 230, 255, 0.4);
z-index: 0;
}
/* 左上角中等圆 */
.background::after {
content: '';
position: absolute;
width: 300rpx;
height: 300rpx;
top: -150rpx;
right: 40%;
border-radius: 50%;
background-color: rgba(150, 230, 255, 0.3);
}
/* 右上角斜线 */
.diagonal-lines {
position: absolute;
top: 30rpx;
right: 30rpx;
}
.diagonal-lines::before {
content: '';
position: absolute;
width: 120rpx;
height: 20rpx;
top: 70rpx;
right: -80rpx;
background-color: rgba(150, 230, 255, 0.5);
border-radius: 10rpx;
transform: rotate(-45deg);
}
.diagonal-lines::after {
content: '';
position: absolute;
width: 160rpx;
height: 30rpx;
top: 130rpx;
right: -80rpx;
background-color: rgba(150, 230, 255, 0.5);
border-radius: 20rpx;
transform: rotate(-45deg);
}
.custom-button-bg {
background: linear-gradient(135deg, #ffcd6c 0%, #fff6c5 100%);
}
.custom-vip-bg {
background: linear-gradient(135deg, #000000 0%, #525252 100%);
border-radius: 20rpx 20rpx 0 0;
}
.custom-user-info {
position: absolute;
bottom: 172rpx;
}
.not-last-child {
border-bottom: 2rpx solid #f5f5f5;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

View File

@ -9,6 +9,7 @@ interface NavigateToOptions {
"/pages/evaluation/index/index" |
"/pages/expert/index/index" |
"/pages/ucenter/index/index" |
"/pages-sub/ucenter/openVip" |
"/pages-sub/customerService/index/index" |
"/pages-sub/home/autoFill/index" |
"/pages-sub/home/career/index" |

View File

@ -1,8 +1,5 @@
import { CustomRequestOptions } from '@/interceptors/request'
import { staticBaseUrl, baseUrl } from '@/utils/index'
import { useUserStore } from '@/store'
const userStore = useUserStore()
export const http = <T>(options: CustomRequestOptions) => {
// 1. 返回 Promise 对象

View File

@ -34,7 +34,8 @@
"src/**/*.tsx",
"src/**/*.jsx",
"src/**/*.vue",
"src/**/*.json"
"src/**/*.json",
"src/pages-sub/components/canvas/html2canvas.min.js"
],
"files": ["src/pages-evaluation-sub/uni_modules/lime-echart/static/echarts.min.js"]
}