榆钱落尽槿花稀 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

402 lines
11 KiB
Vue
Raw Permalink 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-number-box-class tn-number-box">
<!-- -->
<view
class="tn-number-box__btn__minus"
:class="[
backgroundColorClass,
fontColorClass,
{'tn-number-box__btn--disabled': disabled || inputValue <= min}
]"
:style="{
backgroundColor: backgroundColorStyle,
height: $t.string.getLengthUnitValue(inputHeight),
color: fontColorStyle,
fontSize: fontSizeStyle
}"
@touchstart.stop.prevent="touchStart('minus')"
@touchend.stop.prevent="clearTimer"
>
<view class="tn-icon-reduce"></view>
</view>
<!-- 输入框 -->
<input
v-model="inputValue"
:disabled="disabledInput || disabled"
:cursor-spacing="getCursorSpacing"
class="tn-number-box__input"
:class="[
fontColorClass,
{'tn-number-box__input--disabled': disabledInput || disabled}
]"
:style="{
width: $t.string.getLengthUnitValue(inputWidth),
height: $t.string.getLengthUnitValue(inputHeight),
color: fontColorStyle,
fontSize: fontSizeStyle,
backgroundColor: backgroundColorStyle
}"
@blur="blurInput"
@focus="focusInput"
/>
<!-- -->
<view
class="tn-number-box__btn__plus"
:class="[
backgroundColorClass,
fontColorClass,
{'tn-number-box__btn--disabled': disabled || inputValue >= max}
]"
:style="{
backgroundColor: backgroundColorStyle,
height: $t.string.getLengthUnitValue(inputHeight),
color: fontColorStyle,
fontSize: fontSizeStyle
}"
@touchstart.stop.prevent="touchStart('plus')"
@touchend.stop.prevent="clearTimer"
>
<view class="tn-icon-add"></view>
</view>
</view>
</template>
<script>
import componentsColor from '../../libs/mixin/components_color.js'
export default {
mixins: [componentsColor],
name: 'tn-number-box',
props: {
value: {
type: Number,
default: 1
},
// 索引
index: {
type: [Number, String],
default: ''
},
// 最小值
min: {
type: Number,
default: 0
},
// 最大值
max: {
type: Number,
default: 99999
},
// 步进值
step: {
type: Number,
default: 1
},
// 禁用
disabled: {
type: Boolean,
default: false
},
// 是否禁用输入
disabledInput: {
type: Boolean,
default: false
},
// 输入框的宽度
inputWidth: {
type: Number,
default: 88
},
// 输入框的高度
inputHeight: {
type: Number,
default: 50
},
// 输入框和键盘之间的距离
cursorSpacing: {
type: Number,
default: 100
},
// 是否开启长按进行连续递增减
longPress: {
type: Boolean,
default: true
},
// 长按触发间隔
longPressTime: {
type: Number,
default: 250
},
// 是否只能输入正整数
positiveInteger: {
type: Boolean,
default: true
}
},
computed: {
getCursorSpacing() {
return Number(uni.upx2px(this.cursorSpacing))
}
},
data() {
return {
// 输入框的值
inputValue: 1,
// 长按定时器
longPressTimer: null,
// 标记值的改变是来自外部还是内部
changeFromInner: false,
// 内部定时器
innerChangeTimer: null
}
},
watch: {
value(val) {
// 只有value的改变是来自外部的时候才去同步inputValue的值否则会造成循环错误
if (!this.changeFromInner) {
this.updateInputValue()
// 因为inputValue变化后会触发this.handleChange()在其中changeFromInner会再次被设置为true
// 造成外面修改值也导致被认为是内部修改的混乱这里进行this.$nextTick延时保证在运行周期的最后处
// 将changeFromInner设置为false
this.$nextTick(() => {
this.changeFromInner = false
})
}
},
inputValue(newVal, oldVal) {
// 为了让用户能够删除所有输入值,重新输入内容,删除所有值后,内容为空字符串
if (newVal === '') return
let value = 0
// 首先判断是否数值并且在min和max之间如果不是使用原来值
let isNumber = this.$t.test.number(newVal)
if (isNumber && newVal >= this.min && newVal <= this.max) value = newVal
else value = oldVal
// 判断是否只能输入大于等于0的整数
if (this.positiveInteger) {
// 小于0或者带有小数点
if (newVal < 0 || String(newVal).indexOf('.') !== -1) {
value = Math.floor(newVal)
// 双向绑定input的值必须要使用$nextTick修改显示的值
this.$nextTick(() => {
this.inputValue = value
})
}
}
this.handleChange(value, 'change')
},
min() {
this.updateInputValue()
},
max() {
this.updateInputValue()
}
},
created() {
this.updateInputValue()
},
methods: {
// 开始点击按钮
touchStart(func) {
// 先执行一遍方法否则会造成松开手时就执行了clearTimer导致无法实现功能
this[func]()
// 如果没有开启长按功能,直接返回
if (!this.longPress) return
// 清空长按定时器,防止重复注册
if (this.longPressTimer) {
clearInterval(this.longPressTimer)
this.longPressTimer = null
}
this.longPressTimer = setInterval(() => {
// 执行加减操作
this[func]()
}, this.longPressTime)
},
// 清除定时器
clearTimer() {
this.$nextTick(() => {
if (this.longPressTimer) {
clearInterval(this.longPressTimer)
this.longPressTimer = null
}
})
},
// 减
minus() {
this.computeValue('minus')
},
// 加
plus() {
this.computeValue('plus')
},
// 处理小数相加减出现溢出问题
calcPlus(num1, num2) {
let baseNum = 0, baseNum1 = 0, baseNum2 = 0
try {
baseNum1 = num1.toString().split('.')[1].length
} catch(e) {
baseNum1 = 0
}
try {
baseNum2 = num2.toString().split('.')[1].length
} catch(e) {
baseNum2 = 0
}
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2))
// 精度
let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2
return ((num1 * baseNum + num2 * baseNum) / baseNum).toFixed(precision)
},
calcMinus(num1, num2) {
let baseNum = 0, baseNum1 = 0, baseNum2 = 0
try {
baseNum1 = num1.toString().split('.')[1].length
} catch(e) {
baseNum1 = 0
}
try {
baseNum2 = num2.toString().split('.')[1].length
} catch(e) {
baseNum2 = 0
}
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2))
// 精度
let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2
return ((num1 * baseNum - num2 * baseNum) / baseNum).toFixed(precision)
},
// 处理操作后的值
computeValue(type) {
uni.hideKeyboard()
if (this.disabled) return
let value = 0
if (type === 'minus') {
// 减
value = this.calcMinus(this.inputValue, this.step)
} else if (type === 'plus') {
// 加
value = this.calcPlus(this.inputValue, this.step)
}
// 判断是否比最小值小和操作最大值
if (value < this.min || value > this.max) return
this.inputValue = value
this.handleChange(value, type)
},
// 处理用户手动输入
blurInput(event) {
let val = 0,
value = event.detail.value
// 如果为非0-9数字组成或者其第一位数值为0直接让其等于min值
// 这里不直接判断是否正整数是因为用户传递的props min值可能为0
if (!/(^\d+$)/.test(value) || value[0] == 0) {
val = this.min
} else {
val = +value
}
if (val > this.max) {
val = this.max
} else if (val < this.min) {
val = this.min
}
this.$nextTick(() => {
this.inputValue = val
})
this.handleChange(val, 'blur')
},
// 获取焦点
focusInput() {
this.$emit('focus')
},
// 初始化inputValue
updateInputValue() {
let value = this.value
if (value <= this.min) {
value = this.min
} else if (value >= this.max) {
value = this.max
}
this.inputValue = Number(value)
},
// 处理值改变状态
handleChange(value, type) {
if (this.disabled) return
// 清除定时器,防止混乱
if (this.innerChangeTimer) {
clearTimeout(this.innerChangeTimer)
this.innerChangeTimer = null
}
// 内部修改值
this.changeFromInner = true
// 一定时间内清除changeFromInner标记否则内部值改变后
// 外部通过程序修改value值将会无效
this.innerChangeTimer = setTimeout(() => {
this.changeFromInner = false
}, 150)
this.$emit('input', Number(value))
this.$emit(type, {
value: Number(value),
index: this.index
})
}
}
}
</script>
<style lang="scss" scoped>
.tn-number-box {
display: inline-flex;
align-items: center;
&__btn {
&__plus,&__minus {
width: 60rpx;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: $tn-font-holder-color;
}
&__plus {
border-radius: 0 8rpx 8rpx 0;
}
&__minus {
border-radius: 8rpx 0 0 8rpx;
}
&--disabled {
color: $tn-font-sub-color !important;
background: $tn-font-holder-color !important;
}
}
&__input {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
position: relative;
text-align: center;
box-sizing: border-box;
padding: 0 4rpx;
margin: 0 6rpx;
background-color: $tn-font-holder-color;
&--disabled {
color: $tn-font-sub-color !important;
background: $tn-font-holder-color !important;
}
}
}
</style>