更新:登录功能

This commit is contained in:
2025-11-04 12:37:04 +08:00
commit a21fb92916
897 changed files with 51500 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
@import "./../common/abstracts/_mixin.scss";
@import "./../common/abstracts/variable.scss";
.wot-theme-dark {
@include b(index-bar) {
@include e(index) {
color: $-color-white;
}
}
}
@include b(index-bar) {
position: relative;
width: 100%;
height: 100%;
@include e(content) {
width: 100%;
height: 100%;
}
@include e(sidebar) {
position: absolute;
top: 50%;
right: 4px;
transform: translateY(-50%);
}
@include e(index) {
font-size: 12px;
font-weight: $-fw-medium;
color: $-color-title;
padding: 4px 6px;
@include when(active) {
color: $-color-theme;
}
}
}

View File

@@ -0,0 +1,23 @@
import type { InjectionKey } from 'vue'
import type { ExtractPropTypes } from 'vue'
import { makeBooleanProp } from '../common/props'
export type AnchorIndex = number | string
export const indexBarProps = {
/**
* @description 索引是否吸顶
*/
sticky: makeBooleanProp(false)
}
export type IndexBarProps = ExtractPropTypes<typeof indexBarProps>
export type InderBarProvide = {
props: { sticky?: boolean }
anchorState: {
activeIndex: AnchorIndex | null // 当前激活的索引
}
}
export const indexBarInjectionKey: InjectionKey<InderBarProvide> = Symbol('wd-index-bar')

View File

@@ -0,0 +1,156 @@
<template>
<view class="wd-index-bar" :id="indexBarId">
<!-- #ifdef MP-DINGTALK -->
<view class="wd-index-bar" :id="indexBarId">
<!-- #endif -->
<scroll-view :scrollTop="scrollState.scrollTop" :scroll-y="true" class="wd-index-bar__content" @scroll="hanleScroll">
<slot></slot>
</scroll-view>
<view
class="wd-index-bar__sidebar"
@touchstart.stop.prevent="handleTouchStart"
@touchmove.stop.prevent="handleTouchMove"
@touchend.stop.prevent="handleTouchEnd"
@touchcancel.stop.prevent="handleTouchEnd"
>
<view class="wd-index-bar__index" :class="{ 'is-active': item.index === state.activeIndex }" v-for="item in children" :key="item.index">
{{ item.index }}
</view>
</view>
<!-- #ifdef MP-DINGTALK -->
</view>
<!-- #endif -->
</view>
</template>
<script setup lang="ts">
import type { AnchorIndex } from './type'
import { indexBarInjectionKey, indexBarProps } from './type'
import { ref, getCurrentInstance, onMounted, reactive, nextTick, watch } from 'vue'
import { getRect, isDef, uuid, pause } from '../common/util'
import { useChildren } from '../composables/useChildren'
const props = defineProps(indexBarProps)
const indexBarId = ref<string>(`indexBar${uuid()}`)
const { proxy } = getCurrentInstance()!
const state = reactive({
activeIndex: null as AnchorIndex | null
})
const { linkChildren, children } = useChildren(indexBarInjectionKey)
linkChildren({ props, anchorState: state })
watch(
() => children,
(newValue) => {
if (!newValue.length) {
state.activeIndex = null // 或者设置为一个默认值,如第一个子项的索引
return
}
if (!isDef(state.activeIndex) || !newValue.find((item) => item.index === state.activeIndex)) {
state.activeIndex = newValue[0].index
}
},
{ deep: true, immediate: true }
)
const scrollState = reactive({
scrollTop: 0, // 即将滚动到的位置
prevScrollTop: 0, // 上次记录的位置
// 滚动距离
touching: false
})
// 组件距离页面顶部的高度
let offsetTop = 0
let sidebarInfo = {
// 侧边栏距离顶部的高度
offsetTop: 0,
// 高度固定24px
indexHeight: 24
}
function init() {
setTimeout(() => {
Promise.all([
getRect(`#${indexBarId.value}`, false, proxy),
getRect('.wd-index-bar__sidebar', false, proxy),
getRect('.wd-index-bar__index', false, proxy)
]).then(([bar, sidebar, index]) => {
offsetTop = bar.top!
sidebarInfo.offsetTop = sidebar.top!
sidebarInfo.indexHeight = index.height!
})
}, 100)
}
onMounted(() => {
init()
})
function hanleScroll(scrollEvent: any) {
if (scrollState.touching) {
return
}
const { detail } = scrollEvent
const scrolltop = Math.floor(detail.scrollTop)
const anchor = children.find((item, index) => {
if (!isDef(children[index + 1])) return true
if (item.$.exposed!.top.value - offsetTop <= scrolltop && children[index + 1].$.exposed!.top.value - offsetTop > scrolltop) return true
return false
})
if (isDef(anchor) && state.activeIndex !== anchor.index) {
state.activeIndex = anchor.index
}
scrollState.prevScrollTop = scrolltop
}
function getAnchorByPageY(pageY: number) {
const y = pageY - sidebarInfo.offsetTop
let idx = Math.floor(y / sidebarInfo.indexHeight)
if (idx < 0) idx = 0
else if (idx > children.length - 1) idx = children.length - 1
return children[idx]
}
function handleTouchStart() {
scrollState.touching = true
}
function handleTouchMove(e: TouchEvent) {
const clientY = e.touches[0].pageY
if (state.activeIndex === getAnchorByPageY(clientY).index) {
return
}
state.activeIndex = getAnchorByPageY(clientY).index
setScrollTop(getAnchorByPageY(clientY).$.exposed!.top.value - offsetTop)
}
async function handleTouchEnd(e: TouchEvent) {
const clientY = e.changedTouches[0].pageY
state.activeIndex = getAnchorByPageY(clientY).index
setScrollTop(getAnchorByPageY(clientY).$.exposed!.top.value - offsetTop)
await pause()
scrollState.touching = false
}
function setScrollTop(top: number) {
if (scrollState.scrollTop === top) {
scrollState.scrollTop = scrollState.prevScrollTop
nextTick(() => {
scrollState.scrollTop = top
})
} else {
scrollState.scrollTop = top
}
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>