更新:登录功能
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
@import '../common/abstracts/variable';
|
||||
@import '../common/abstracts/mixin';
|
||||
|
||||
@include b(notice-bar) {
|
||||
display: flex;
|
||||
padding: $-notice-bar-padding;
|
||||
align-items: center;
|
||||
font-size: $-notice-bar-fs;
|
||||
border-radius: $-notice-bar-border-radius;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
@include when(warning) {
|
||||
background: $-notice-bar-warning-bg;
|
||||
color: $-notice-bar-warning-color;
|
||||
}
|
||||
@include when(info) {
|
||||
background: $-notice-bar-info-bg;
|
||||
color: $-notice-bar-info-color;
|
||||
}
|
||||
@include when(danger) {
|
||||
background: $-notice-bar-danger-bg;
|
||||
color: $-notice-bar-danger-color;
|
||||
}
|
||||
@include edeep(prefix) {
|
||||
padding-right: 4px;
|
||||
font-size: $-notice-bar-prefix-size;
|
||||
}
|
||||
|
||||
@include edeep(suffix) {
|
||||
text-align: center;
|
||||
font-size: $-notice-bar-close-size;
|
||||
display: inline-block;
|
||||
background-color: $-notice-bar-close-bg;
|
||||
color: $-notice-bar-close-color;
|
||||
padding: 0;
|
||||
border-radius: 0px 8px 0px 4px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
@include e(wrap) {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
height: $-notice-bar-line-height;
|
||||
overflow: hidden;
|
||||
line-height: $-notice-bar-line-height;
|
||||
}
|
||||
@include e(content) {
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@include m(ellipse) {
|
||||
.wd-notice-bar__content {
|
||||
position: static;
|
||||
@include lineEllipsis;
|
||||
}
|
||||
}
|
||||
@include m(wrap) {
|
||||
|
||||
.wd-notice-bar__wrap {
|
||||
height: auto;
|
||||
}
|
||||
.wd-notice-bar__content {
|
||||
position: static;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
uni_modules/wot-design-uni/components/wd-notice-bar/types.ts
Normal file
67
uni_modules/wot-design-uni/components/wd-notice-bar/types.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import type { ComponentPublicInstance, ExtractPropTypes, PropType } from 'vue'
|
||||
import { baseProps, makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'
|
||||
|
||||
export type NoticeBarType = 'warning' | 'info' | 'danger' | ''
|
||||
export type NoticeBarScrollDirection = 'horizontal' | 'vertical'
|
||||
|
||||
export const noticeBarProps = {
|
||||
...baseProps,
|
||||
/**
|
||||
* 设置通知栏文案
|
||||
*/
|
||||
text: {
|
||||
type: [String, Array] as PropType<string | string[]>,
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* 设置通知栏类型,可选值为:'warning' | 'info' | 'danger'
|
||||
*/
|
||||
type: makeStringProp<NoticeBarType>('warning'),
|
||||
/**
|
||||
* 是否可滚动
|
||||
*/
|
||||
scrollable: makeBooleanProp(true),
|
||||
/**
|
||||
* 滚动延迟时间(秒)
|
||||
*/
|
||||
delay: makeNumberProp(1),
|
||||
/**
|
||||
* 滚动速度(px/s)
|
||||
*/
|
||||
speed: makeNumberProp(50),
|
||||
/**
|
||||
* 是否可关闭
|
||||
*/
|
||||
closable: makeBooleanProp(false),
|
||||
/**
|
||||
* 是否换行显示
|
||||
*/
|
||||
wrapable: makeBooleanProp(false),
|
||||
/**
|
||||
* 设置左侧图标,使用 icon 章节中的图标名
|
||||
*/
|
||||
prefix: String,
|
||||
/**
|
||||
* 文字、图标颜色
|
||||
*/
|
||||
color: String,
|
||||
/**
|
||||
* 背景颜色
|
||||
*/
|
||||
backgroundColor: String,
|
||||
/**
|
||||
* 滚动方向
|
||||
*/
|
||||
direction: makeStringProp<NoticeBarScrollDirection>('horizontal')
|
||||
}
|
||||
|
||||
export type NoticeBarProps = ExtractPropTypes<typeof noticeBarProps>
|
||||
|
||||
export type NoticeBarExpose = {
|
||||
/**
|
||||
* 重置NoticeBar动画
|
||||
*/
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
export type NoticeBarInstance = ComponentPublicInstance<NoticeBarProps, NoticeBarExpose>
|
||||
@@ -0,0 +1,266 @@
|
||||
<template>
|
||||
<view v-if="show" :class="`wd-notice-bar ${customClass} ${noticeBarClass}`" :style="rootStyle">
|
||||
<wd-icon v-if="prefix" custom-class="wd-notice-bar__prefix" :name="prefix"></wd-icon>
|
||||
<slot v-else name="prefix"></slot>
|
||||
<view class="wd-notice-bar__wrap">
|
||||
<view class="wd-notice-bar__content" :style="animation" @transitionend="animationEnd" @click="handleClick">
|
||||
<template v-if="isVertical">
|
||||
<view v-for="item in textArray" :key="item">{{ item }}</view>
|
||||
<view v-if="textArray.length > 1">{{ textArray[0] }}</view>
|
||||
</template>
|
||||
<slot v-else>{{ currentText }}</slot>
|
||||
</view>
|
||||
</view>
|
||||
<wd-icon v-if="closable" custom-class="wd-notice-bar__suffix" name="close-bold" @click="handleClose"></wd-icon>
|
||||
<slot v-else name="suffix"></slot>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'wd-notice-bar',
|
||||
options: {
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import wdIcon from '../wd-icon/wd-icon.vue'
|
||||
import { ref, watch, nextTick, computed, getCurrentInstance, type CSSProperties, onMounted, onActivated, onDeactivated, reactive } from 'vue'
|
||||
import { getRect, isArray, isDef, objToStyle } from '../common/util'
|
||||
import { type NoticeBarExpose, noticeBarProps } from './types'
|
||||
const $wrap = '.wd-notice-bar__wrap'
|
||||
const $content = '.wd-notice-bar__content'
|
||||
|
||||
const props = defineProps(noticeBarProps)
|
||||
const emit = defineEmits(['close', 'next', 'click'])
|
||||
|
||||
const wrapWidth = ref<number>(0)
|
||||
const show = ref<boolean>(true)
|
||||
const currentIndex = ref<number>(0)
|
||||
const textArray = computed(() => (Array.isArray(props.text) ? props.text : [props.text]))
|
||||
const currentText = computed(() => textArray.value[currentIndex.value])
|
||||
const verticalIndex = ref<number>(0)
|
||||
const wrapRect = ref<UniApp.NodeInfo | null>(null) // 外层容器节点信息
|
||||
const contentRect = ref<UniApp.NodeInfo | null>(null) // 内容节点信息
|
||||
const isHorizontal = computed(() => props.direction === 'horizontal')
|
||||
const isVertical = computed(() => props.direction === 'vertical')
|
||||
|
||||
const transitionState = reactive<CSSProperties>({
|
||||
transitionProperty: 'unset',
|
||||
transitionDelay: 'unset',
|
||||
transitionDuration: 'unset',
|
||||
transform: 'none',
|
||||
transitionTimingFunction: 'linear'
|
||||
})
|
||||
|
||||
const animation = computed(() => {
|
||||
return objToStyle(transitionState)
|
||||
})
|
||||
|
||||
const rootStyle = computed(() => {
|
||||
const style: CSSProperties = {}
|
||||
if (isDef(props.color)) {
|
||||
style.color = props.color
|
||||
}
|
||||
|
||||
if (isDef(props.backgroundColor)) {
|
||||
style.background = props.backgroundColor
|
||||
}
|
||||
|
||||
return `${objToStyle(style)}${props.customStyle}`
|
||||
})
|
||||
const noticeBarClass = computed(() => {
|
||||
const { type, wrapable, scrollable } = props
|
||||
|
||||
let noticeBarClasses: string[] = []
|
||||
type && noticeBarClasses.push(`is-${type}`)
|
||||
|
||||
if (isHorizontal.value) {
|
||||
!wrapable && !scrollable && noticeBarClasses.push('wd-notice-bar--ellipse')
|
||||
} else {
|
||||
noticeBarClasses.push('wd-notice-bar--ellipse')
|
||||
}
|
||||
|
||||
wrapable && !scrollable && noticeBarClasses.push('wd-notice-bar--wrap')
|
||||
return noticeBarClasses.join(' ')
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
|
||||
watch(
|
||||
() => props.text,
|
||||
() => {
|
||||
reset()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
startTransition()
|
||||
// #ifdef APP-PLUS
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const currentWebview = currentPage.$getAppWebview!()
|
||||
currentWebview.addEventListener('hide', () => {
|
||||
stopTransition()
|
||||
})
|
||||
currentWebview.addEventListener('show', () => {
|
||||
startTransition()
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
startTransition()
|
||||
})
|
||||
|
||||
onDeactivated(() => {
|
||||
stopTransition()
|
||||
})
|
||||
|
||||
function reset() {
|
||||
stopTransition()
|
||||
startTransition()
|
||||
}
|
||||
|
||||
function startTransition() {
|
||||
nextTick(() => scroll())
|
||||
}
|
||||
|
||||
function stopTransition() {
|
||||
transitionState.transitionProperty = 'unset'
|
||||
transitionState.transitionDelay = 'unset'
|
||||
transitionState.transitionDuration = 'unset'
|
||||
transitionState.transform = 'none'
|
||||
transitionState.transitionTimingFunction = 'linear'
|
||||
currentIndex.value = 0
|
||||
verticalIndex.value = 0
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
show.value = false
|
||||
emit('close')
|
||||
}
|
||||
|
||||
function setTransition({ duration, delay, translate }: { duration: number; delay: number; translate: number }) {
|
||||
transitionState.transitionProperty = 'all'
|
||||
transitionState.transitionDelay = `${delay}s`
|
||||
transitionState.transitionDuration = `${duration}s`
|
||||
transitionState.transform = `${props.direction === 'vertical' ? 'translateY' : 'translateX'}(${translate}px)`
|
||||
transitionState.transitionTimingFunction = 'linear'
|
||||
}
|
||||
|
||||
function queryRect() {
|
||||
return Promise.all([getRect($wrap, false, proxy), getRect($content, false, proxy)])
|
||||
}
|
||||
|
||||
async function verticalAnimate(height: number) {
|
||||
const translate = -(height / (textArray.value.length + 1)) * (currentIndex.value + 1)
|
||||
setTransition({
|
||||
duration: height / (textArray.value.length + 1) / props.speed,
|
||||
delay: props.delay,
|
||||
translate
|
||||
})
|
||||
}
|
||||
|
||||
async function scroll() {
|
||||
const [wRect, cRect] = await queryRect()
|
||||
if (!wRect.width || !cRect.width || !cRect.height) return
|
||||
wrapRect.value = wRect
|
||||
contentRect.value = cRect
|
||||
wrapWidth.value = wRect.width
|
||||
|
||||
if (isHorizontal.value) {
|
||||
if (props.scrollable) {
|
||||
setTransition({
|
||||
duration: cRect.width / props.speed,
|
||||
delay: props.delay,
|
||||
translate: -cRect.width
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (textArray.value.length > 1) {
|
||||
verticalAnimate(cRect.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (currentIndex.value >= textArray.value.length - 1) {
|
||||
currentIndex.value = 0
|
||||
} else {
|
||||
currentIndex.value++
|
||||
}
|
||||
emit('next', currentIndex.value)
|
||||
}
|
||||
|
||||
function animationEnd() {
|
||||
if (isHorizontal.value) {
|
||||
setTransition({
|
||||
duration: 0,
|
||||
delay: 0,
|
||||
translate: wrapWidth.value + 1
|
||||
})
|
||||
} else {
|
||||
if (++verticalIndex.value >= textArray.value.length) {
|
||||
verticalIndex.value = 0
|
||||
setTransition({
|
||||
duration: 0,
|
||||
delay: 0,
|
||||
translate: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
next() // 更换下一条文本
|
||||
|
||||
nextTick(async () => {
|
||||
try {
|
||||
const [wRect, cRect] = await queryRect()
|
||||
wrapRect.value = wRect
|
||||
contentRect.value = cRect
|
||||
wrapWidth.value = wRect.width || 0
|
||||
} catch (error) {
|
||||
// console.error(error)
|
||||
}
|
||||
|
||||
if (!contentRect.value || !contentRect.value.width || !contentRect.value.height) return
|
||||
|
||||
if (isHorizontal.value) {
|
||||
setTransition({
|
||||
duration: (wrapWidth.value + contentRect.value.width) / props.speed,
|
||||
delay: props.delay,
|
||||
translate: -contentRect.value.width
|
||||
})
|
||||
} else {
|
||||
verticalAnimate(contentRect.value.height)
|
||||
}
|
||||
})
|
||||
|
||||
clearTimeout(timer)
|
||||
}, 20)
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
const result = isArray(props.text)
|
||||
? {
|
||||
index: currentIndex.value,
|
||||
text: props.text[currentIndex.value]
|
||||
}
|
||||
: {
|
||||
index: 0,
|
||||
text: props.text
|
||||
}
|
||||
emit('click', result)
|
||||
}
|
||||
|
||||
defineExpose<NoticeBarExpose>({ reset })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
Reference in New Issue
Block a user