feat:增加vant组件库

This commit is contained in:
2026-02-12 17:27:14 +08:00
parent 1b8bd5a7e2
commit 8940b3972a
29 changed files with 241 additions and 269 deletions

View File

@@ -1,101 +0,0 @@
<template>
<el-button ref="buttonRef" type="primary" :icon="Plus"
circle size="large" :style="style" style="font-size: 1.8rem;"
@mousedown="startDrag" @touchstart="startDrag"/>
</template>
<script setup lang="ts">
import { computed, defineEmits, onMounted, reactive, ref } from 'vue'
import { Plus } from '@element-plus/icons-vue'
const buttonRef = ref()
const buttonPos = reactive({
x: 0,
y: 0,
width: 0,
height: 0
})
const startPos = reactive({
x: 0,
y: 0
})
const offsetPos = reactive({
x: 0,
y: 0
})
/**
* 最小拖到阈值
*/
const dragThreshold = 5
const emit = defineEmits(['click'])
/**
* 实时更新位置
*/
const style = computed(() => ({
position: 'absolute',
left: `${buttonPos.x}px`,
top: `${buttonPos.y}px`,
touchAction: 'none'
}))
onMounted(() => {
initButtonPosition()
})
const initButtonPosition = () => {
buttonPos.x = window.innerWidth - 50
buttonPos.y = window.innerHeight - 120
buttonPos.width = buttonRef.value.$el.offsetWidth
buttonPos.height = buttonRef.value.$el.offsetHeight
}
const startDrag = (event) => {
event.preventDefault()
const touch = event.touches ? event.touches[0] : event
startPos.x = touch.clientX
startPos.y = touch.clientY
offsetPos.x = touch.clientX - buttonPos.x
offsetPos.y = touch.clientY - buttonPos.y
document.addEventListener('mousemove', onDrag)
document.addEventListener('mouseup', stopDrag)
document.addEventListener('touchmove', onDrag, { passive: false })
document.addEventListener('touchend', stopDrag)
}
const onDrag = (event) => {
const touch = event.touches ? event.touches[0] : event
// 计算新位置,并限制在边界内
const newX = touch.clientX - offsetPos.x
const newY = touch.clientY - offsetPos.y
buttonPos.x = Math.max(0, Math.min(newX, window.innerWidth - buttonPos.width))
buttonPos.y = Math.max(40, Math.min(newY, window.innerHeight - buttonPos.height - 70))
}
const stopDrag = (event) => {
document.removeEventListener('mousemove', onDrag)
document.removeEventListener('mouseup', stopDrag)
document.removeEventListener('touchmove', onDrag)
document.removeEventListener('touchend', stopDrag)
const touch = event.changedTouches ? event.changedTouches[0] : event
const deltaX = Math.abs(touch.clientX - startPos.x)
const deltaY = Math.abs(touch.clientY - startPos.y)
// 此时为点击
if (deltaX <= dragThreshold && deltaY <= dragThreshold) {
emit('click', event)
}
}
</script>

View File

@@ -12,13 +12,22 @@
{{ appStore.isDark ? '🌙' : '☀️' }}
</el-button>
</div>
<div class="setting-item">
<span>我的</span>
<el-button circle :icon="UserFilled" type="primary" @click="viewProfileClick"/>
</div>
</div>
</template>
<script setup lang="ts">
import { UserFilled } from '@element-plus/icons-vue'
import { useAppStore } from '@/store/app.ts'
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
const appStore = useAppStore()
const router = useRouter()
onMounted(() => {
appStore.themeColor = appStore.getThemeColor()
@@ -27,6 +36,11 @@ onMounted(() => {
const changeThemeColorEvent = (value: string) => {
appStore.setThemeColor(value)
}
const viewProfileClick = () => {
router.push('/profile/index')
appStore.isShowSetting = false
}
</script>
<style scoped lang="scss">

View File

@@ -15,53 +15,52 @@
<el-empty v-else description="暂无成果" />
<div class="record-info">
<div class="top-container">
<div class="info-list">
<div class="info-item">
<span class="title">{{ props.foodItem.name }}</span>
<van-swipe-cell>
<div class="record-info" @click="viewFoodRecipeClick()">
<div class="top-container">
<div class="info-list">
<div class="info-item">
<span class="title">{{ props.foodItem.name }}</span>
</div>
</div>
</div>
<div class="button-info">
<el-button type="primary" :icon="Edit" circle :size="'small'"
@click="editFoodRecipeClick"/>
<el-button type="primary" :icon="MoreFilled" circle :size="'small'"
@click="viewFoodRecipeClick()" />
<el-button type="danger" :icon="DeleteFilled" circle :size="'small'"
@click="deleteFoodRecipeClick()" />
<div class="bottom-container">
<div class="info-list">
<div class="info-item category-item">
<i class="fa-solid fa-utensils"></i>
{{ props.foodItem.category }}
</div>
</div>
<div v-if="props.foodItem?.recordList.length > 0" class="info-list">
<div class="info-item">
<i class="fa-solid fa-flag"></i>
{{ props.foodItem.recordList[foodCardInfo.currentFoodIndex].person}}
</div>
<div class="info-item">
<i class="fa-regular fa-calendar-check"></i>
{{ formatDate(props.foodItem.recordList[foodCardInfo.currentFoodIndex].date) }}
</div>
</div>
<div v-else>
<span>暂无成果</span>
</div>
</div>
</div>
<div class="bottom-container">
<div class="info-list">
<div class="info-item category-item">
<i class="fa-solid fa-utensils"></i>
{{ props.foodItem.category }}
</div>
</div>
<div v-if="props.foodItem?.recordList.length > 0" class="info-list">
<div class="info-item">
<i class="fa-solid fa-flag"></i>
{{ props.foodItem.recordList[foodCardInfo.currentFoodIndex].person}}
</div>
<div class="info-item">
<i class="fa-regular fa-calendar-check"></i>
{{ formatDate(props.foodItem.recordList[foodCardInfo.currentFoodIndex].date) }}
</div>
</div>
<div v-else>
<span>暂无成果</span>
</div>
</div>
</div>
<template #right>
<van-button square type="primary" text="编辑" style="height: 100%;"
@click="editFoodRecipeClick"/>
<van-button square type="danger" text="删除" style="height: 100%;"
@click="deleteFoodRecipeClick()"/>
</template>
</van-swipe-cell>
</div>
</template>
<script setup lang="ts">
import { Edit, MoreFilled, DeleteFilled } from '@element-plus/icons-vue'
import { useFoodStore } from '@/store/food.ts'
import { computed, defineProps, onMounted, PropType, reactive } from 'vue'
import { IFoodRecipe } from '@/types/food.ts'

View File

@@ -1,68 +1,32 @@
<template>
<div v-if="isMobile()" class="mobile-common-query card-item">
<div class="query-item">
<span>分类</span>
<el-segmented v-model="foodStore.queryRecipe.category"
:options="queryCategoryList"
@change="queryFoodSegmented"/>
</div>
</div>
<div v-else class="common-query">
<el-select v-model="foodStore.queryDateType"
@change="foodStore.refreshInfo()"
style="width: 120px;align-self: center;">
<el-option v-for="(item, index) in dateTypeOptions" :key="index"
:label="item.label" :value="item.value"/>
<div class="mobile-common-query">
<el-select
v-model="foodStore.queryRecipe.name"
filterable clearable
placeholder="请选择菜谱名称"
@change="changeFoodNameEvent">
<el-option
v-for="(item, index) in foodStore.foodNameList"
:key="index" :label="item" :value="item"
/>
</el-select>
</div>
</template>
<script setup lang="ts">
import { useFoodStore } from '@/store/food.ts'
import { isMobile } from 'vue3-common/utils/layoutUtil'
import { onMounted, ref } from 'vue'
import { ICategory } from '@/types'
import { onMounted } from 'vue'
import { searchFoodRecipeApi } from '@/apis/food.ts'
const foodStore = useFoodStore()
const queryCategoryList = ref([] as ICategory[])
onMounted(async () => {
await foodStore.queryFoodCategory()
initCategoryList()
await foodStore.queryFoodNameList()
foodStore.queryRecipe.name = ''
})
const dateTypeOptions = [{
label: '按月选择',
value: 'month'
},
{
label: '按年选择',
value: 'year'
}]
const initCategoryList = () => {
queryCategoryList.value = []
queryCategoryList.value.push({
label: '全部',
value: ''
})
foodStore.foodCategoryList.forEach((item) => {
queryCategoryList.value.push({
label: item,
value: item
})
})
}
const queryFoodSegmented = async () => {
foodStore.pageInfo = {
currentPage: 1,
pageSize: 3,
totalSize: 1
}
foodStore.foodRecipeList = []
await foodStore.queryMobileFoodRecipeList()
const changeFoodNameEvent = async (value: string) => {
const result = await searchFoodRecipeApi(value)
foodStore.foodRecipeList = [result]
}
</script>

View File

@@ -1,25 +1,26 @@
<template>
<div class="password-card card-item">
<el-avatar class="avatar-info">{{ passwordItem.title.charAt(0) }}</el-avatar>
<van-swipe-cell>
<div class="password-card card-item" @click="editClick">
<el-avatar class="avatar-info">{{ passwordItem.title.charAt(0) }}</el-avatar>
<div class="title-info">
<span>{{ passwordItem.title }}</span>
<span>{{ passwordItem.username }}</span>
<div class="title-info">
<span>{{ passwordItem.title }}</span>
<span>{{ passwordItem.username }}</span>
</div>
</div>
<div class="button-info">
<el-button type="primary" :icon="Edit" circle
@click="editClick"/>
<el-button type="danger" :icon="DeleteFilled" circle
@click="deleteClick()" />
</div>
</div>
<template #right>
<van-button square type="primary" text="编辑" style="height: 100%;"
@click="editClick"/>
<van-button square type="danger" text="删除" style="height: 100%;"
@click="deleteClick()"/>
</template>
</van-swipe-cell>
</template>
<script setup lang="ts">
import { Edit, DeleteFilled } from '@element-plus/icons-vue'
import { usePasswordStore } from '@/store/password.ts'
import { defineProps, onMounted, PropType } from 'vue'
import { defineProps, PropType } from 'vue'
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
import { useRouter } from 'vue-router'
import { IPassword } from '@/types/password.ts'
@@ -34,10 +35,6 @@ const props = defineProps({
}
})
onMounted(() => {
})
const editClick = async () => {
const id = props.passwordItem?.id
passwordStore.passwordApiType = 'UPDATE'
@@ -59,7 +56,7 @@ const deleteClick = () => {
padding: 10px;
display: grid;
grid-template-columns: 40px 1fr 80px;
grid-template-columns: 40px 1fr;
align-items: center;
gap: 10px;