榆钱落尽槿花稀 448712ece5 feat: 添加积分申请系统基础功能与UI组件
本次提交主要包含以下内容:

1. 新增积分申请系统核心功能:
   - 添加登录页面及API接口
   - 实现积分申请记录查看功能
   - 集成微信小程序分享功能
   - 添加请求管理工具类

2. 引入Tuniao UI组件库:
   - 添加时间线、折叠面板、表格等UI组件
   - 集成头像组、单选框组等交互组件
   - 配置全局样式和主题颜色

3. 基础架构搭建:
   - 配置项目manifest和pages.json路由
   - 添加状态管理store
   - 实现自定义导航栏适配
   - 添加工具函数(加解密、数字处理等)

4. 静态资源:
   - 添加项目logo和背景图片
   - 配置uni.scss全局样式变量

本次提交为系统基础功能搭建,后续将进一步完善积分申请流程和审批功能。
2025-05-27 16:40:02 +08:00

411 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view
class="tn-subsection-class tn-subsection"
:class="[subsectionBackgroundColorClass]"
:style="[subsectionStyle]"
>
<!-- 滑块 -->
<block v-for="(item, index) in listInfo" :key="index">
<view
class="tn-subsection__item tn-text-ellipsis"
:class="[
'section-item-' + index,
noBorderRight(index)
]"
:style="[itemStyle(index)]"
@tap="click(index)"
>
<view class="tn-subsection__item--text tn-text-ellipsis" :style="[textStyle(index)]">
{{ item.name }}
</view>
</view>
</block>
<!-- 背景 -->
<view
class="tn-subsection__bg"
:class="[itemBarClass]"
:style="[itemBarStyle]"
></view>
</view>
</template>
<script>
import componentsColorMixin from '../../libs/mixin/components_color.js'
export default {
mixins: [componentsColorMixin],
name: 'tn-subsection',
props: {
// 模式选择
// button 按钮模式 subsection 分段模式
mode: {
type: String,
default: 'subsection'
},
// 组件高度
height: {
type: Number,
default: 60
},
// tab的数据
list: {
type: Array,
default() {
return []
}
},
// 当前活动tab的index
current: {
type: [Number, String],
default: 0
},
// 激活时的字体颜色
activeColor: {
type: String,
default: '#FFFFFF'
},
// 未激活时的字体颜色
inactiveColor: {
type: String,
default: '#AAAAAA'
},
// 激活tab的字体是否加粗
bold: {
type: Boolean,
default: false
},
backgroundColor: {
type: String,
default: '#F4F4F4'
},
// 滑块的颜色
buttonColor: {
type: String,
default: '#01BEFF'
},
// 当mode为button时生效圆角的值单位rpx
borderRadius: {
type: Number,
default: 10
},
// 是否开启动画
animation: {
type: Boolean,
default: true
},
// 动画类型
// cubic-bezier -> 贝塞尔曲线
animationType: {
type: String,
default: ''
},
// 滑动滑块的是否,是否触发震动
vibrateShort: {
type: Boolean,
default: false
}
},
data() {
return {
// 列表数据
listInfo: [],
// 子元素的背景样式
itemBgStyle: {
width: 0,
left: 0,
backgroundColor: '#ffffff',
height: '100%'
},
// 当前选中的滑块
currentIndex: this.current,
buttonPadding: 3,
// 组件初始化的是否current变换不应该震动
firstVibrateShort: true
}
},
watch: {
current: {
handler(val) {
this.currentIndex = val
this.changeSectionStatus(val)
},
immediate: true
}
},
created() {
// 将list的数据传入listInfo数组
// 接受直接数组形式,或者数组元素为对象的形式,如:['开启', '关闭'],或者[{name: '开启'}, {name: '关闭'}]
this.listInfo = this.list.map((item, index) => {
if (typeof item !== 'object') {
let obj = {
width: 0,
name: item
}
return obj
} else {
item.width = 0
return obj
}
})
},
computed: {
// 设置mode=subsection时滑块没有样式
noBorderRight() {
return index => {
if (this.mode !== 'subsection') return
let clazz = ''
// 不显示右边的边距
if (index < this.list.length - 1) clazz += ' tn-subsection__item--none-border-right'
// 显示整个组件的左右边圆角
if (index === 0) clazz += ' tn-subsection__item--first'
if (index === this.list.length - 1) clazz += ' tn-subsection__item--last'
return clazz
}
},
// 文字的样式
textStyle() {
return index => {
let style = {}
// 设置字体颜色
if (index === this.currentIndex) {
style.color = this.activeColor
} else {
style.color = this.inactiveColor
}
// 字体加粗
if (index === this.currentIndex && this.bold) style.fontWeight = 'bold'
// 文字大小
style.fontSize = (this.fontSize || 26) + this.fontUnit
return style
}
},
// 每个分段器item的样式
itemStyle() {
return index => {
let style = {}
if (this.fontSizeStyle) {
style.fontSize = this.fontSizeStyle
}
if (this.mode === 'subsection') {
// 设置border的样式
style.borderColor = this.buttonColor
style.borderWidth = '1rpx'
style.borderStyle = 'solid'
}
return style
}
},
// mode = button时设置外层view的样式
subsectionStyle() {
let style = {}
style.height = this.height + 'rpx'
if (this.mode === 'button') {
style.backgroundColor = this.backgroundColorStyle
style.padding = `${this.buttonPadding}px`
style.borderRadius = `${this.borderRadius}rpx`
}
return style
},
// mode = button时设置外层view的背景class
subsectionBackgroundColorClass() {
let clazz = ''
if (this.mode === 'button' && this.backgroundColorClass) {
clazz = this.backgroundColorClass
}
return clazz
},
itemBarClass() {
let clazz = ''
const buttonBgClass = this.$t.color.getBackgroundColorInternalClass(this.buttonColor)
if (this.animation) {
clazz += ' tn-subsection__bg__animation'
if (this.animationType) {
clazz += ` tn-subsection__bg__animation--${this.animationType}`
}
}
if (buttonBgClass) {
clazz += ` ${buttonBgClass}`
}
return clazz
},
// 滑块样式
itemBarStyle() {
let style = {}
const buttonBgStyle = this.$t.color.getBackgroundColorStyle(this.buttonColor)
if (buttonBgStyle) {
style.backgroundColor = this.buttonColor
}
style.zIndex = 1
if (this.mode === 'button') {
style.borderRadius = `${this.borderRadius}rpx`
style.bottom = `${this.buttonPadding}px`
style.height = (this.height - (this.buttonPadding * 4)) + 'rpx'
style.zIndex = 0
}
return Object.assign(this.itemBgStyle, style)
}
},
mounted() {
// 等待加载组件完成
setTimeout(() => {
this.getTabsInfo()
}, 10)
},
methods: {
// 改变滑块样式
changeSectionStatus(val) {
if (this.mode === 'subsection') {
// 根据滑块在最左和最右时,显示对应的圆角
if (val === this.list.length - 1) {
this.itemBgStyle.borderRadius = `0 ${this.buttonPadding}px ${this.buttonPadding}px 0`
}
if (val === 0) {
this.itemBgStyle.borderRadius = `${this.buttonPadding}px 0 0 ${this.buttonPadding}px`
}
if (val > 0 && val < this.list.length - 1) {
this.itemBgStyle.borderRadius = '0'
}
}
// 更新滑块的位置
setTimeout(() => {
this.itemBgLeft()
}, 10)
if (this.vibrateShort && !this.firstVibrateShort) {
// 使手机产生短促震动微信小程序有效APP(HX 2.6.8)和H5无效
// #ifndef H5
uni.vibrateShort();
// #endif
}
this.firstVibrateShort = false
},
// 获取各个tab的节点信息
getTabsInfo() {
let view = uni.createSelectorQuery().in(this)
for (let i = 0; i < this.list.length; i++) {
view.select('.section-item-' + i).boundingClientRect()
}
view.exec(res => {
// 如果没有获取到,则重新获取
if (!res.length) {
setTimeout(() => {
this.getTabsInfo()
return
}, 10)
}
// 将每个分段器的宽度放入listInfo中
res.map((item, index) => {
this.listInfo[index].width = item.width
})
// 初始化滑块的宽度
if (this.mode === 'subsection') {
this.itemBgStyle.width = this.listInfo[0].width + 'px'
} else if (this.mode === 'button') {
this.itemBgStyle.width = this.listInfo[0].width + 'px'
}
// 初始化滑块的位置
this.itemBgLeft()
})
},
// 设置滑块的位置
itemBgLeft() {
let left = 0
// 计算当前活跃item到组件左边的距离
this.listInfo.map((item, index) => {
if (index < this.currentIndex) left += item.width
})
// 根据不同的模式,计算滑块的位置
if (this.mode === 'subsection') {
this.itemBgStyle.left = left + 'px'
} else if (this.mode === 'button') {
this.itemBgStyle.left = left + this.buttonPadding + 'px'
}
},
// 点击事件
click(index) {
// 不允许点击当前激活的选项
if (index === this.currentIndex) return
this.currentIndex = index
this.changeSectionStatus(index)
this.$emit('change', {
index: Number(index),
name: this.listInfo[index]['name']
})
}
}
}
</script>
<style lang="scss" scoped>
.tn-subsection {
/* #ifndef APP-PLUS */
display: flex;
flex-direction: row;
/* #endif */
align-items: center;
overflow: hidden;
position: relative;
&__item {
/* #ifndef APP-PLUS */
display: flex;
flex-direction: row;
/* #endif */
flex: 1;
text-align: center;
font-size: 26rpx;
height: 100%;
align-items: center;
justify-content: center;
color: #FFFFFF;
padding: 0 6rpx;
&--text {
transition: all 0.3s;
color: #FFFFFF;
/* #ifndef APP-PLUS */
display: flex;
flex-direction: row;
/* #endif */
align-items: center;
position: relative;
z-index: 3;
}
&--first {
border-top-left-radius: 8rpx;
border-bottom-left-radius: 8rpx;
}
&--last {
border-top-right-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
&--none-border-right {
border-right: none !important;
}
}
&__bg {
background-color: $tn-main-color;
position: absolute;
z-index: -1;
transition-property: all;
transition-duration: 0s;
transition-timing-function: linear;
&__animation {
transition-duration: 0.25s !important;
&--cubic-bezier {
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55) !important;
}
}
}
}
</style>