增加轮休申请 临时外出申请菜单

This commit is contained in:
BianLzhaoMin 2025-02-17 16:14:40 +08:00
parent 5921d123cf
commit 69c2047690
12 changed files with 3110 additions and 961 deletions

View File

@ -1,136 +1,149 @@
{
"pages": [
//pageshttps://uniapp.dcloud.io/collocation/pages
// {
// "path": "pages/index/index",
// "style": {
// "navigationBarTitleText": "uni-app"
// }
// },
//
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/face/index",
"style": {
"navigationBarTitleText": "人脸录入",
"navigationBarBackgroundColor": "#ffffff"
}
},
{
"path": "pages/clock/index",
"style": {
"navigationBarTitleText": "考勤打卡",
"navigationBarBackgroundColor": "#ffffff"
}
},
//
{
"path": "pages/my/index",
"style": {
"navigationBarTitleText": "我的"
}
},
//
{
"path": "pages/evection/index",
"style": {
"navigationBarTitleText": "出差报备"
}
},
//
{
"path": "pages/evection/recordList",
"style": {
"navigationBarTitleText": "报备记录"
}
},
//
{
"path": "pages/evection/details",
"style": {
"navigationBarTitleText": "出差报备详情"
}
},
//
{
"path": "pages/holiday/index",
"style": {
"navigationBarTitleText": "休假报备"
}
},
//
{
"path": "pages/holiday/recordList",
"style": {
"navigationBarTitleText": "报备记录"
}
},
//
{
"path": "pages/holiday/details",
"style": {
"navigationBarTitleText": "休假报备详情"
}
},
//
{
"path": "pages/password/index",
"style": {
"navigationBarTitleText": "修改密码"
}
},
{
"path" : "pages/panel/index",
"style" :
{
"navigationBarTitleText" : "面板",
"navigationBarBackgroundColor": "#ffffff"
}
},
{
"path" : "pages/clock/detail",
"style" :
{
"navigationBarTitleText" : "人脸考勤",
"navigationBarBackgroundColor": "#ffffff"
}
}
],
"tabBar": {
"color": "#2c2c2c",
"selectedColor": "#1296db",
"borderStyle": "black",
"backgroundColor": "#FFFFFF",
"iconWidth": "24px",
"list": [{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/home.png",
"selectedIconPath": "static/homeSelected.png"
},
{
"pagePath": "pages/my/index",
"text": "我的",
"iconPath": "static/workSpace.png",
"selectedIconPath": "static/workSpaceSelected.png"
}
]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}
"pages": [
//pageshttps://uniapp.dcloud.io/collocation/pages
// {
// "path": "pages/index/index",
// "style": {
// "navigationBarTitleText": "uni-app"
// }
// },
//
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/face/index",
"style": {
"navigationBarTitleText": "人脸录入",
"navigationBarBackgroundColor": "#ffffff"
}
},
{
"path": "pages/clock/index",
"style": {
"navigationBarTitleText": "考勤打卡",
"navigationBarBackgroundColor": "#ffffff"
}
},
//
{
"path": "pages/my/index",
"style": {
"navigationBarTitleText": "我的"
}
},
//
{
"path": "pages/evection/index",
"style": {
"navigationBarTitleText": "出差报备"
}
},
//
{
"path": "pages/evection/recordList",
"style": {
"navigationBarTitleText": "报备记录"
}
},
//
{
"path": "pages/evection/details",
"style": {
"navigationBarTitleText": "出差报备详情"
}
},
//
{
"path": "pages/holiday/index",
"style": {
"navigationBarTitleText": "休假报备"
}
},
//
{
"path": "pages/holiday/recordList",
"style": {
"navigationBarTitleText": "报备记录"
}
},
//
{
"path": "pages/holiday/details",
"style": {
"navigationBarTitleText": "休假报备详情"
}
},
//
{
"path": "pages/password/index",
"style": {
"navigationBarTitleText": "修改密码"
}
},
{
"path": "pages/panel/index",
"style": {
"navigationBarTitleText": "面板",
"navigationBarBackgroundColor": "#ffffff"
}
},
{
"path": "pages/clock/detail",
"style": {
"navigationBarTitleText": "人脸考勤",
"navigationBarBackgroundColor": "#ffffff"
}
},
//
{
"path": "pages/stagger-holidays/index",
"style": {
"navigationBarTitleText": "轮休申请"
}
},
//
{
"path": "pages/temporary-outing/index",
"style": {
"navigationBarTitleText": "临时外出申请"
}
}
],
"tabBar": {
"color": "#2c2c2c",
"selectedColor": "#1296db",
"borderStyle": "black",
"backgroundColor": "#FFFFFF",
"iconWidth": "24px",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/home.png",
"selectedIconPath": "static/homeSelected.png"
},
{
"pagePath": "pages/my/index",
"text": "我的",
"iconPath": "static/workSpace.png",
"selectedIconPath": "static/workSpaceSelected.png"
}
]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}

View File

@ -1,217 +1,233 @@
<template>
<view class="container">
<!-- 用户信息区域 -->
<view class="user-info">
<image class="avatar" :src="userInfo.avatar" mode="aspectFill"></image>
<view class="user-detail">
<text class="username">{{userInfo.name}}</text>
<text class="phone">{{userInfo.phone}}</text>
</view>
</view>
<view class="container">
<!-- 用户信息区域 -->
<view class="user-info">
<image class="avatar" :src="userInfo.avatar" mode="aspectFill"></image>
<view class="user-detail">
<text class="username">{{ userInfo.name }}</text>
<text class="phone">{{ userInfo.phone }}</text>
</view>
</view>
<!-- 插画区域 -->
<view class="illustration">
<image src="/static/banner.png" mode="aspectFit"></image>
</view>
<!-- 插画区域 -->
<view class="illustration">
<image src="/static/banner.png" mode="aspectFit"></image>
</view>
<!-- 申请报备区域 -->
<view class="section">
<view class="section-title">申请报备</view>
<view class="grid-box">
<view class="grid-item" @click="handleNavigate('/pages/evection/index')">
<image src="/static/evection.png" mode="aspectFit"></image>
<text class="centered-text">出差报备</text>
</view>
<view class="grid-item" @click="handleNavigate('/pages/holiday/index')">
<image src="/static/holiday.png" mode="aspectFit"></image>
<text class="centered-text">休假报备</text>
</view>
</view>
</view>
<!-- 申请报备区域 -->
<view class="section">
<view class="section-title">申请报备</view>
<view class="grid-box">
<view class="grid-item" @click="handleNavigate('/pages/evection/index')">
<image src="/static/evection.png" mode="aspectFit"></image>
<text class="centered-text">出差报备</text>
</view>
<view class="grid-item" @click="handleNavigate('/pages/holiday/index')">
<image src="/static/holiday.png" mode="aspectFit"></image>
<text class="centered-text">休假报备</text>
</view>
<view class="grid-item" @click="handleNavigate('/pages/stagger-holidays/index')">
<image src="/static/lx_icon.png" mode="aspectFit"></image>
<text class="centered-text">轮休申请</text>
</view>
<view class="grid-item" @click="handleNavigate('/pages/temporary-outing/index')">
<image src="/static/go_out.png" mode="aspectFit"></image>
<text class="centered-text">临时外出申请</text>
</view>
</view>
</view>
<!-- 考勤打卡区域 -->
<view class="section" v-if="isPd==1">
<view class="section-title">考勤打卡</view>
<view class="grid-box">
<view class="grid-item" @click="handleNavigate('/pages/clock/index')">
<image src="/static/clockIn.png" mode="aspectFit"></image>
<text class="centered-text">考勤打卡</text>
</view>
</view>
</view>
</view>
<!-- 考勤打卡区域 -->
<view class="section" v-if="isPd == 1">
<view class="section-title">考勤打卡</view>
<view class="grid-box">
<view class="grid-item" @click="handleNavigate('/pages/clock/index')">
<image src="/static/clockIn.png" mode="aspectFit"></image>
<text class="centered-text">考勤打卡</text>
</view>
</view>
</view>
</view>
</template>
<script>
import {selectFaceInfo,getUsetInfo} from "@/api/index.js"
export default {
data() {
return {
userInfo: {
token: '',
userId: '',
name: '',
phone: '',
avatar: '/static/defaultHead.png'
},
isPd: 0,
}
import { selectFaceInfo, getUsetInfo } from '@/api/index.js'
export default {
data() {
return {
userInfo: {
token: '',
userId: '',
name: '',
phone: '',
avatar: '/static/defaultHead.png',
},
isPd: 0,
}
},
mounted() {
//
this.userInfo.token = uni.getStorageSync('token') || ''
this.userInfo.userId = uni.getStorageSync('userId') || ''
this.userInfo.name = uni.getStorageSync('username') || ''
this.userInfo.phone = uni.getStorageSync('phone') || ''
if (/^1\d{10}$/.test(this.userInfo.phone)) {
this.userInfo.phone = this.maskPhoneNumber(this.userInfo.phone)
}
this.isPd = uni.getStorageSync('isPd') || 0
console.log('isPd', this.isPd)
if (Number(this.isPd) !== 0) {
this.selectFaceInfoEvent()
}
},
onLoad() {},
onShow() {
this.getUsetInfoEvent()
},
methods: {
//
getUsetInfoEvent() {
getUsetInfo(uni.getStorageSync('userId')).then((res) => {
if (res.res == 1) {
if (res.obj && res.obj.appliedFace) {
if (
res.obj.appliedFace == '' ||
res.obj.appliedFace == null ||
res.obj.appliedFace == 'null'
) {
this.userInfo.avatar = '/static/defaultHead.png'
} else {
this.userInfo.avatar = res.obj.appliedFace
}
}
}
})
},
},
mounted() {
//
this.userInfo.token = uni.getStorageSync('token') || '';
this.userInfo.userId = uni.getStorageSync('userId') || '';
this.userInfo.name = uni.getStorageSync('username') || '';
this.userInfo.phone = uni.getStorageSync('phone') || '';
if(/^1\d{10}$/.test(this.userInfo.phone)) {
this.userInfo.phone = this.maskPhoneNumber(this.userInfo.phone)
}
this.isPd = uni.getStorageSync('isPd') || 0;
console.log("isPd", this.isPd)
if(Number(this.isPd) !== 0) {
this.selectFaceInfoEvent()
}
},
onLoad() {
},
onShow() {
this.getUsetInfoEvent()
},
methods: {
//
getUsetInfoEvent() {
getUsetInfo(uni.getStorageSync('userId')).then(res => {
if(res.res == 1) {
if(res.obj && res.obj.appliedFace) {
if(res.obj.appliedFace == '' || res.obj.appliedFace == null || res.obj.appliedFace == 'null') {
this.userInfo.avatar = '/static/defaultHead.png'
}else{
this.userInfo.avatar = res.obj.appliedFace
}
}
}
})
},
maskPhoneNumber(phoneNumber) {
return phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
},
//
selectFaceInfoEvent() {
selectFaceInfo().then(res => {
if(res.obj == '103') {
uni.showModal({
title: '提示',
content: '您当前还未录入人脸信息,请先录入',
success: res => {
if(res.confirm) {
uni.navigateTo({
url: '/pages/face/index'
})
}
}
});
}
})
},
handleNavigate(url) {
uni.navigateTo({
url
})
}
}
}
maskPhoneNumber(phoneNumber) {
return phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
},
//
selectFaceInfoEvent() {
selectFaceInfo().then((res) => {
if (res.obj == '103') {
uni.showModal({
title: '提示',
content: '您当前还未录入人脸信息,请先录入',
success: (res) => {
if (res.confirm) {
uni.navigateTo({
url: '/pages/face/index',
})
}
},
})
}
})
},
handleNavigate(url) {
uni.navigateTo({
url,
})
},
},
}
</script>
<style lang="scss">
.container {
min-height: 100vh;
padding: 30rpx;
background-color: #fff;
}
<style lang="scss" scoped>
.container {
min-height: 100vh;
padding: 30rpx;
background-color: #fff;
}
.user-info {
display: flex;
align-items: center;
margin-bottom: 30rpx;
.user-info {
display: flex;
align-items: center;
margin-bottom: 30rpx;
.avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.user-detail {
display: flex;
flex-direction: column;
.user-detail {
display: flex;
flex-direction: column;
.username {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 6rpx;
}
.username {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 6rpx;
}
.phone {
font-size: 28rpx;
color: #666;
}
}
}
.phone {
font-size: 28rpx;
color: #666;
}
}
}
.illustration {
margin: 40rpx 0;
text-align: center;
.illustration {
margin: 40rpx 0;
text-align: center;
image {
width: 100%;
height: 300rpx;
}
}
image {
width: 100%;
height: 300rpx;
}
}
.section {
margin-bottom: 40rpx;
.section {
margin-bottom: 40rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.grid-box {
display: flex;
flex-wrap: wrap;
margin: 0 -10rpx;
.grid-box {
display: flex;
flex-wrap: wrap;
// margin: 0 -10rpx;
.grid-item {
width: calc(30% - 20rpx);
margin: 10rpx;
padding: 30rpx;
background-color: #f8f8f8;
border-radius: 12rpx;
text-align: center;
.grid-item {
// width: calc(30% - 20rpx);
width: calc((100% - 10rpx) / 2);
margin-right: 10rpx;
margin-top: 10rpx;
padding: 30rpx;
background-color: #f8f8f8;
border-radius: 12rpx;
text-align: center;
box-sizing: border-box;
image {
width: 80rpx;
height: 80rpx;
margin-bottom: 16rpx;
}
image {
width: 80rpx;
height: 80rpx;
margin-bottom: 16rpx;
}
text {
font-size: 28rpx;
color: #333;
}
}
}
}
text {
font-size: 28rpx;
color: #333;
}
}
.centered-text {
text-align: center;
display: block;
}
</style>
.grid-item:nth-child(2n) {
margin-right: 0;
}
}
}
.centered-text {
text-align: center;
display: block;
}
</style>

View File

@ -1,440 +1,454 @@
<template>
<view class="login-container">
<!-- 顶部图标和标题 -->
<view class="header">
<image class="logo" src="/static/logo.png" mode="aspectFit"></image>
<text class="title">欢迎登录考勤系统</text>
</view>
<view class="login-container">
<!-- 顶部图标和标题 -->
<view class="header">
<image class="logo" src="/static/logo.png" mode="aspectFit"></image>
<text class="title">欢迎登录考勤系统</text>
</view>
<!-- 表单区域 -->
<view class="form-box">
<!-- 手机号输入框 -->
<view class="menu-item-left">
<image style="width: 20px; height: 20px; margin-right: 5px; vertical-align: middle;"
src="/static/phone.png" mode="aspectFit"></image>
<text class="menu-text" style="vertical-align: middle;">手机号</text>
</view>
<view class="input-item">
<uni-icons type="person" size="20" color="#999"></uni-icons>
<input type="number" v-model="form.phone" placeholder="请输入手机号" maxlength="11" />
</view>
<!-- 表单区域 -->
<view class="form-box">
<!-- 手机号输入框 -->
<view class="menu-item-left">
<image
style="width: 20px; height: 20px; margin-right: 5px; vertical-align: middle"
src="/static/phone.png"
mode="aspectFit"
></image>
<text class="menu-text" style="vertical-align: middle">手机号</text>
</view>
<view class="input-item">
<uni-icons type="person" size="20" color="#999"></uni-icons>
<input
type="number"
v-model="form.phone"
placeholder="请输入手机号"
maxlength="11"
/>
</view>
<view class="menu-item-left">
<image style="width: 20px; height: 20px; margin-right: 5px; vertical-align: middle;"
src="/static/password.png" mode="aspectFit"></image>
<text class="menu-text" style="vertical-align: middle;">密码</text>
</view>
<!-- 密码输入框 -->
<view class="input-item">
<uni-icons type="locked" size="20" color="#999"></uni-icons>
<input type="password" v-model="form.password" placeholder="请输入密码" />
</view>
<view class="menu-item-left">
<image
style="width: 20px; height: 20px; margin-right: 5px; vertical-align: middle"
src="/static/password.png"
mode="aspectFit"
></image>
<text class="menu-text" style="vertical-align: middle">密码</text>
</view>
<!-- 密码输入框 -->
<view class="input-item">
<uni-icons type="locked" size="20" color="#999"></uni-icons>
<input type="password" v-model="form.password" placeholder="请输入密码" />
</view>
<!-- 登录按钮 -->
<button class="login-btn" @click="handleLogin">登录</button>
<!-- 登录按钮 -->
<button class="login-btn" @click="handleLogin">登录</button>
<!-- 一键登录链接 -->
<!-- <view class="quick-login" @click="handleQuickLogin">
<!-- 一键登录链接 -->
<!-- <view class="quick-login" @click="handleQuickLogin">
本机号码一键登录
</view> -->
<button class="login-btn" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">
一键登录
</button>
<button class="login-btn" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">
一键登录
</button>
<!-- https://blog.csdn.net/weixin_73318685/article/details/141530696 -->
<!-- <button bindphoneoneclicklogin="onHandleLogin" open-type="phoneOneClickLogin">本机号码一键登录</button> -->
</view>
<!-- https://blog.csdn.net/weixin_73318685/article/details/141530696 -->
<!-- <button bindphoneoneclicklogin="onHandleLogin" open-type="phoneOneClickLogin">本机号码一键登录</button> -->
</view>
<!-- 版本号 -->
<view class="version">{{version}}</view>
</view>
<!-- 版本号 -->
<view class="version">{{ version }}</view>
</view>
</template>
<script>
import {
loginApi,
getPhonenumberLogin,
getPhonenumberByCode
} from '../../api/index.js'
import {
encryptCBC,
decryptCBC
} from '../../utils/http.js'
export default {
data() {
return {
form: {
phone: '',
password: ''
},
version: '1.0.6'
}
},
onLoad() {
// develop trial release
this.checkForUpdates();
import { loginApi, getPhonenumberLogin, getPhonenumberByCode } from '../../api/index.js'
import { encryptCBC, decryptCBC } from '../../utils/http.js'
export default {
data() {
return {
form: {
phone: '15240004260',
password: 'GZkq@123456!',
},
version: '1.0.6',
}
},
onLoad() {
// develop trial release
this.checkForUpdates()
let miniProgram = wx.getAccountInfoSync().miniProgram
if (miniProgram == 'release' || miniProgram == 'trial') {
this.version = wx.getAccountInfoSync().miniProgram.version || this.version
}
var isLoginOut = uni.getStorageSync('isLoginOut');
if (isLoginOut != "yes") {
wx.login({
success: (res) => {
if (res.code) {
const data = {
code: res.code
};
getPhonenumberByCode(data).then(response => {
console.log("responsessss", response)
if (response.resMsg === 'success') {
var phoneLogin = response.obj;
console.log('phoneLogin', phoneLogin);
if (phoneLogin != "" && phoneLogin != null) {
this.getLogin(phoneLogin);
}
}
}).catch(err => {
console.log("登录失败,请稍后再试");
})
} else {
uni.showModal({
title: '提示',
content: '请允许授权,用于该服务',
showCancel: false
});
}
}
})
}
},
methods: {
//
handleLogin() {
if (!this.form.phone || !this.form.password) {
uni.showToast({
title: '请输入手机号和密码',
icon: 'none'
});
return;
}
if (this.form.phone.length != 11) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
});
return;
}
let miniProgram = wx.getAccountInfoSync().miniProgram
if (miniProgram == 'release' || miniProgram == 'trial') {
this.version = wx.getAccountInfoSync().miniProgram.version || this.version
}
var isLoginOut = uni.getStorageSync('isLoginOut')
if (isLoginOut != 'yes') {
wx.login({
success: (res) => {
if (res.code) {
const data = {
code: res.code,
}
getPhonenumberByCode(data)
.then((response) => {
console.log('responsessss', response)
if (response.resMsg === 'success') {
// var phoneLogin = response.obj;
// console.log('phoneLogin', phoneLogin);
// if (phoneLogin != "" && phoneLogin != null) {
// this.getLogin(phoneLogin);
// }
}
})
.catch((err) => {
console.log('登录失败,请稍后再试')
})
} else {
uni.showModal({
title: '提示',
content: '请允许授权,用于该服务',
showCancel: false,
})
}
},
})
}
},
methods: {
//
handleLogin() {
if (!this.form.phone || !this.form.password) {
uni.showToast({
title: '请输入手机号和密码',
icon: 'none',
})
return
}
if (this.form.phone.length != 11) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none',
})
return
}
wx.login({
success: (res) => {
if (res.code) {
const data = {
username: encryptCBC(this.form.phone), // 使
password: encryptCBC(this.form.password),
flage: '1',
code: res.code
};
// return
loginApi(data).then(response => {
if (response.status === 'success') {
console.log('登录成功', response);
uni.setStorageSync('token', response.token)
uni.setStorageSync('userId', response.id)
uni.setStorageSync('phone', response.phone)
uni.setStorageSync('username', response.username)
uni.setStorageSync('isCader', response.isCader)
uni.setStorageSync('isFace', response.isFace)
uni.setStorageSync('isPd', response.isPd)
uni.setStorageSync('isLoginOut', "no");
uni.showToast({
icon: 'success',
title: '登录成功!',
duration: 2000,
success: () => {
uni.switchTab({
url: '/pages/index/index'
})
}
})
} else {
//
uni.showToast({
title: response.msg || '登录失败,请稍后再试',
icon: 'none'
});
}
}).catch(error => {
console.error('登录请求出错', error);
uni.showToast({
title: '网络请求异常,请检查您的网络连接',
icon: 'none'
});
});
} else {
uni.showModal({
title: '提示',
content: '请允许授权,用于该服务',
showCancel: false
});
}
}
})
},
wx.login({
success: (res) => {
if (res.code) {
const data = {
username: encryptCBC(this.form.phone), // 使
password: encryptCBC(this.form.password),
flage: '1',
code: res.code,
}
// return
loginApi(data)
.then((response) => {
if (response.status === 'success') {
console.log('登录成功', response)
uni.setStorageSync('token', response.token)
uni.setStorageSync('userId', response.id)
uni.setStorageSync('phone', response.phone)
uni.setStorageSync('username', response.username)
uni.setStorageSync('isCader', response.isCader)
uni.setStorageSync('isFace', response.isFace)
uni.setStorageSync('isPd', response.isPd)
uni.setStorageSync('isLoginOut', 'no')
uni.showToast({
icon: 'success',
title: '登录成功!',
duration: 2000,
success: () => {
uni.switchTab({
url: '/pages/index/index',
})
},
})
} else {
//
uni.showToast({
title: response.msg || '登录失败,请稍后再试',
icon: 'none',
})
}
})
.catch((error) => {
console.error('登录请求出错', error)
uni.showToast({
title: '网络请求异常,请检查您的网络连接',
icon: 'none',
})
})
} else {
uni.showModal({
title: '提示',
content: '请允许授权,用于该服务',
showCancel: false,
})
}
},
})
},
//
async onGetPhoneNumber(e) {
console.log(e, '打印手机号信息');
try {
wx.login({
success: (res) => {
if (res.code) {
const data = {
code: res.code
};
this.sendCodeToGetPhonenumber(data, e);
} else {
uni.showModal({
title: '提示',
content: '请允许授权,用于该服务',
showCancel: false
});
}
}
})
} catch (error) {
console.error('获取手机号失败:', error);
}
},
//
async onGetPhoneNumber(e) {
console.log(e, '打印手机号信息')
try {
wx.login({
success: (res) => {
if (res.code) {
const data = {
code: res.code,
}
this.sendCodeToGetPhonenumber(data, e)
} else {
uni.showModal({
title: '提示',
content: '请允许授权,用于该服务',
showCancel: false,
})
}
},
})
} catch (error) {
console.error('获取手机号失败:', error)
}
},
sendCodeToGetPhonenumber(data, e) {
getPhonenumberByCode(data).then(response => {
console.log("responsessss", response)
if (response.resMsg === 'success') {
var phoneLogin = response.obj;
console.log('phoneLogin', phoneLogin);
if (phoneLogin != "" && phoneLogin != null) {
this.getLogin(phoneLogin);
} else {
console.log("没有获取到openid", e);
if (e.detail.errMsg == "getPhoneNumber:ok") {
var code = e.detail.code;
this.sendPhoneInfoToServer(code);
} else {
console.error('用户拒绝提供手机号');
}
}
} else {
uni.showToast({
title: response.msg || '登录失败,请稍后再试',
icon: 'none'
});
}
}).catch(err => {
uni.showToast({
title: '登录失败,请稍后再试',
icon: 'none'
});
})
},
sendCodeToGetPhonenumber(data, e) {
getPhonenumberByCode(data)
.then((response) => {
console.log('responsessss', response)
if (response.resMsg === 'success') {
var phoneLogin = response.obj
console.log('phoneLogin', phoneLogin)
if (phoneLogin != '' && phoneLogin != null) {
this.getLogin(phoneLogin)
} else {
console.log('没有获取到openid', e)
if (e.detail.errMsg == 'getPhoneNumber:ok') {
var code = e.detail.code
this.sendPhoneInfoToServer(code)
} else {
console.error('用户拒绝提供手机号')
}
}
} else {
uni.showToast({
title: response.msg || '登录失败,请稍后再试',
icon: 'none',
})
}
})
.catch((err) => {
uni.showToast({
title: '登录失败,请稍后再试',
icon: 'none',
})
})
},
sendPhoneInfoToServer(code) {
var data = {
code: code
}
getPhonenumberLogin(data).then(response => {
console.log("responsessss", response)
if (response.resMsg === 'success') {
console.log('手机号', response.obj);
var phoneLogin = response.obj;
this.getLogin(phoneLogin)
} else {
uni.showToast({
title: response.msg || '登录失败,请稍后再试',
icon: 'none'
});
}
}).catch(err => {
uni.showToast({
title: '登录失败,请稍后再试',
icon: 'none'
});
})
},
sendPhoneInfoToServer(code) {
var data = {
code: code,
}
getPhonenumberLogin(data)
.then((response) => {
console.log('responsessss', response)
if (response.resMsg === 'success') {
console.log('手机号', response.obj)
var phoneLogin = response.obj
this.getLogin(phoneLogin)
} else {
uni.showToast({
title: response.msg || '登录失败,请稍后再试',
icon: 'none',
})
}
})
.catch((err) => {
uni.showToast({
title: '登录失败,请稍后再试',
icon: 'none',
})
})
},
getLogin(phoneLogin) {
var username = decryptCBC(phoneLogin);
console.log("useraname", username);
wx.login({
success: (res) => {
if (res.code) {
const data = {
username: encryptCBC(username + "wechat"), // 使
flage: '1',
code: res.code
};
// return
loginApi(data).then(response => {
if (response.status === 'success') {
console.log('登录成功', response);
uni.setStorageSync('token', response.token)
uni.setStorageSync('userId', response.id)
uni.setStorageSync('phone', response.phone)
uni.setStorageSync('username', response.username)
uni.setStorageSync('isCader', response.isCader)
uni.setStorageSync('isFace', response.isFace)
uni.setStorageSync('isPd', response.isPd)
uni.setStorageSync('isLoginOut', "no");
uni.showToast({
icon: 'success',
title: '登录成功!',
duration: 2000,
success: () => {
uni.switchTab({
url: '/pages/index/index'
})
}
})
} else {
//
uni.showToast({
title: response.msg || '登录失败,请稍后再试',
icon: 'none'
});
}
}).catch(error => {
console.error('登录请求出错', error);
uni.showToast({
title: '网络请求异常,请检查您的网络连接',
icon: 'none'
});
});
} else {
uni.showModal({
title: '提示',
content: '请允许授权,用于该服务',
showCancel: false
});
}
}
})
},
/**
* 检查并处理小程序更新
*/
checkForUpdates() {
const updateManager = uni.getUpdateManager();
getLogin(phoneLogin) {
var username = decryptCBC(phoneLogin)
console.log('useraname', username)
wx.login({
success: (res) => {
if (res.code) {
const data = {
username: encryptCBC(username + 'wechat'), // 使
flage: '1',
code: res.code,
}
// return
loginApi(data)
.then((response) => {
if (response.status === 'success') {
console.log('登录成功', response)
uni.setStorageSync('token', response.token)
uni.setStorageSync('userId', response.id)
uni.setStorageSync('phone', response.phone)
uni.setStorageSync('username', response.username)
uni.setStorageSync('isCader', response.isCader)
uni.setStorageSync('isFace', response.isFace)
uni.setStorageSync('isPd', response.isPd)
uni.setStorageSync('isLoginOut', 'no')
uni.showToast({
icon: 'success',
title: '登录成功!',
duration: 2000,
success: () => {
uni.switchTab({
url: '/pages/index/index',
})
},
})
} else {
//
uni.showToast({
title: response.msg || '登录失败,请稍后再试',
icon: 'none',
})
}
})
.catch((error) => {
console.error('登录请求出错', error)
uni.showToast({
title: '网络请求异常,请检查您的网络连接',
icon: 'none',
})
})
} else {
uni.showModal({
title: '提示',
content: '请允许授权,用于该服务',
showCancel: false,
})
}
},
})
},
/**
* 检查并处理小程序更新
*/
checkForUpdates() {
const updateManager = uni.getUpdateManager()
//
updateManager.onCheckForUpdate((res) => {
console.log('[App] 是否有新版本:', res.hasUpdate);
});
//
updateManager.onCheckForUpdate((res) => {
console.log('[App] 是否有新版本:', res.hasUpdate)
})
//
updateManager.onUpdateReady(() => {
this.promptForUpdate(updateManager);
});
//
updateManager.onUpdateReady(() => {
this.promptForUpdate(updateManager)
})
//
updateManager.onUpdateFailed(() => {
console.error('[App] 新版本下载失败');
});
},
//
updateManager.onUpdateFailed(() => {
console.error('[App] 新版本下载失败')
})
},
/**
* 提示用户应用新版本
* @param {Object} updateManager - 更新管理对象
*/
promptForUpdate(updateManager) {
uni.showModal({
title: '更新提示',
content: '新版本已准备好,是否重启应用?',
showCancel: false,
success: (res) => {
if (res.confirm) {
console.log('[App] 用户确认重启应用');
updateManager.applyUpdate();
}
},
});
},
},
onHandleLogin(e) {
console.log(e)
}
}
/**
* 提示用户应用新版本
* @param {Object} updateManager - 更新管理对象
*/
promptForUpdate(updateManager) {
uni.showModal({
title: '更新提示',
content: '新版本已准备好,是否重启应用?',
showCancel: false,
success: (res) => {
if (res.confirm) {
console.log('[App] 用户确认重启应用')
updateManager.applyUpdate()
}
},
})
},
},
onHandleLogin(e) {
console.log(e)
},
}
</script>
<style lang="scss">
.login-container {
min-height: 100vh;
padding: 0 50rpx;
background-color: #fff;
.login-container {
min-height: 100vh;
padding: 0 50rpx;
background-color: #fff;
.header {
padding-top: 100rpx;
// text-align: center;
.header {
padding-top: 100rpx;
// text-align: center;
.logo {
width: 45px;
height: 45px;
}
.logo {
width: 45px;
height: 45px;
}
.title {
display: block;
margin-top: 20rpx;
font-size: 16px;
color: #333;
font-weight: bold;
}
}
.title {
display: block;
margin-top: 20rpx;
font-size: 16px;
color: #333;
font-weight: bold;
}
}
.form-box {
margin-top: 80rpx;
.form-box {
margin-top: 80rpx;
.input-item {
display: flex;
align-items: center;
height: 100rpx;
margin-bottom: 30rpx;
padding: 0 20rpx;
border-bottom: 1rpx solid #eee;
.input-item {
display: flex;
align-items: center;
height: 100rpx;
margin-bottom: 30rpx;
padding: 0 20rpx;
border-bottom: 1rpx solid #eee;
input {
flex: 1;
margin-left: 20rpx;
font-size: 32rpx;
}
}
input {
flex: 1;
margin-left: 20rpx;
font-size: 32rpx;
}
}
.login-btn {
margin-top: 60rpx;
height: 90rpx;
line-height: 90rpx;
background-color: #4080FF;
color: #fff;
font-size: 32rpx;
border-radius: 45rpx;
}
.login-btn {
margin-top: 60rpx;
height: 90rpx;
line-height: 90rpx;
background-color: #4080ff;
color: #fff;
font-size: 32rpx;
border-radius: 45rpx;
}
.quick-login {
margin-top: 30rpx;
text-align: center;
font-size: 28rpx;
color: #666;
}
}
.quick-login {
margin-top: 30rpx;
text-align: center;
font-size: 28rpx;
color: #666;
}
}
.version {
position: fixed;
bottom: 60rpx;
left: 0;
right: 0;
text-align: center;
font-size: 24rpx;
color: #999;
}
}
.version {
position: fixed;
bottom: 60rpx;
left: 0;
right: 0;
text-align: center;
font-size: 24rpx;
color: #999;
}
}
</style>

View File

@ -1,260 +1,271 @@
<template>
<view class="container">
<!-- 用户信息区域 -->
<view class="user-info">
<image class="avatar" :src="userInfo.avatar" mode="aspectFill"></image>
<view class="user-detail">
<text class="username">{{userInfo.name}}</text>
<text class="phone">{{userInfo.phone}}</text>
</view>
</view>
<view class="container">
<!-- 用户信息区域 -->
<view class="user-info">
<image class="avatar" :src="userInfo.avatar" mode="aspectFill"></image>
<view class="user-detail">
<text class="username">{{ userInfo.name }}</text>
<text class="phone">{{ userInfo.phone }}</text>
</view>
</view>
<view class="menu-list">
<view class="menu-item" @tap="onChangePassword">
<view class="menu-item-left">
<image style="width: 20px; height: 20px; margin-right: 10px;" src="/static/updatePassword.png"
mode="aspectFit"></image>
<text class="menu-text">修改密码</text>
</view>
<image style="width: 20px; height: 20px;" src="/static/more.png" mode="aspectFit"></image>
</view>
<view class="menu-list">
<view class="menu-item" @tap="onChangePassword">
<view class="menu-item-left">
<image
style="width: 20px; height: 20px; margin-right: 10px"
src="/static/updatePassword.png"
mode="aspectFit"
></image>
<text class="menu-text">修改密码</text>
</view>
<image
style="width: 20px; height: 20px"
src="/static/more.png"
mode="aspectFit"
></image>
</view>
<view class="menu-item" @tap="onCheckForUpdates">
<view class="menu-item-left">
<image style="width: 20px; height: 20px; margin-right: 10px;" src="/static/version.png"
mode="aspectFit"></image>
<text class="menu-text">检查新版本</text>
</view>
<text class="version">{{version}}</text>
</view>
</view>
<view class="menu-item" @tap="onCheckForUpdates">
<view class="menu-item-left">
<image
style="width: 20px; height: 20px; margin-right: 10px"
src="/static/version.png"
mode="aspectFit"
></image>
<text class="menu-text">检查新版本</text>
</view>
<text class="version">{{ version }}</text>
</view>
</view>
<button class="logout-btn" @tap="onLogout">退出登录</button>
</view>
<button class="logout-btn" @tap="onLogout">退出登录</button>
</view>
</template>
<script>
import {
getUsetInfo
} from "@/api/index.js"
export default {
data() {
return {
userInfo: {
name: '',
phone: '',
avatar: '/static/defaultHead.png'
},
version: '1.0.6'
}
},
onLoad() {
// develop trial release
let miniProgram = wx.getAccountInfoSync().miniProgram
if (miniProgram == 'release' || miniProgram == 'trial') {
this.version = wx.getAccountInfoSync().miniProgram.version || this.version
}
},
onShow() {
this.getUsetInfoEvent()
},
mounted() {
//
this.userInfo.name = uni.getStorageSync('username') || '';
this.userInfo.phone = uni.getStorageSync('phone') || '';
if (/^1\d{10}$/.test(this.userInfo.phone)) {
this.userInfo.phone = this.maskPhoneNumber(this.userInfo.phone)
}
},
methods: {
//
getUsetInfoEvent() {
getUsetInfo(uni.getStorageSync('userId')).then(res => {
if (res.res == 1) {
if (res.obj && res.obj.appliedFace) {
if (res.obj.appliedFace == '' || res.obj.appliedFace == null || res.obj.appliedFace ==
'null') {
this.userInfo.avatar = '/static/defaultHead.png'
} else {
this.userInfo.avatar = res.obj.appliedFace
}
}
}
})
},
maskPhoneNumber(phoneNumber) {
return phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
},
onChangePassword() {
uni.navigateTo({
url: '/pages/password/index'
})
},
/**
* 检查并处理小程序更新
*/
onCheckForUpdates() {
const updateManager = uni.getUpdateManager();
import { getUsetInfo } from '@/api/index.js'
export default {
data() {
return {
userInfo: {
name: '',
phone: '',
avatar: '/static/defaultHead.png',
},
version: '1.0.6',
}
},
onLoad() {
// develop trial release
let miniProgram = wx.getAccountInfoSync().miniProgram
if (miniProgram == 'release' || miniProgram == 'trial') {
this.version = wx.getAccountInfoSync().miniProgram.version || this.version
}
},
onShow() {
this.getUsetInfoEvent()
},
mounted() {
//
this.userInfo.name = uni.getStorageSync('username') || ''
this.userInfo.phone = uni.getStorageSync('phone') || ''
if (/^1\d{10}$/.test(this.userInfo.phone)) {
this.userInfo.phone = this.maskPhoneNumber(this.userInfo.phone)
}
},
methods: {
//
getUsetInfoEvent() {
getUsetInfo(uni.getStorageSync('userId')).then((res) => {
if (res.res == 1) {
if (res.obj && res.obj.appliedFace) {
if (
res.obj.appliedFace == '' ||
res.obj.appliedFace == null ||
res.obj.appliedFace == 'null'
) {
this.userInfo.avatar = '/static/defaultHead.png'
} else {
this.userInfo.avatar = res.obj.appliedFace
}
}
}
})
},
maskPhoneNumber(phoneNumber) {
return phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
},
onChangePassword() {
uni.navigateTo({
url: '/pages/password/index',
})
},
/**
* 检查并处理小程序更新
*/
onCheckForUpdates() {
const updateManager = uni.getUpdateManager()
//
updateManager.onCheckForUpdate((res) => {
console.log('[App] 是否有新版本:', res.hasUpdate);
if(!res.hasUpdate){
uni.showToast({
title: '已是最新版本',
icon: 'none'
});
}
});
//
updateManager.onCheckForUpdate((res) => {
console.log('[App] 是否有新版本:', res.hasUpdate)
if (!res.hasUpdate) {
uni.showToast({
title: '已是最新版本',
icon: 'none',
})
}
})
//
updateManager.onUpdateReady(() => {
this.promptForUpdate(updateManager);
});
//
updateManager.onUpdateReady(() => {
this.promptForUpdate(updateManager)
})
//
updateManager.onUpdateFailed(() => {
console.error('[App] 新版本下载失败');
});
},
//
updateManager.onUpdateFailed(() => {
console.error('[App] 新版本下载失败')
})
},
/**
* 提示用户应用新版本
* @param {Object} updateManager - 更新管理对象
*/
promptForUpdate(updateManager) {
uni.showModal({
title: '更新提示',
content: '新版本已准备好,是否重启应用?',
showCancel: false,
success: (res) => {
if (res.confirm) {
console.log('[App] 用户确认重启应用');
updateManager.applyUpdate();
}
},
});
},
onLogout() {
uni.showModal({
title: '提示',
content: '确认退出登录?',
success: (res) => {
if (res.confirm) {
// 退
uni.clearStorageSync()
uni.setStorageSync('isLoginOut', "yes");
uni.reLaunch({
url: '/pages/login/index'
})
}
}
})
}
}
}
/**
* 提示用户应用新版本
* @param {Object} updateManager - 更新管理对象
*/
promptForUpdate(updateManager) {
uni.showModal({
title: '更新提示',
content: '新版本已准备好,是否重启应用?',
showCancel: false,
success: (res) => {
if (res.confirm) {
console.log('[App] 用户确认重启应用')
updateManager.applyUpdate()
}
},
})
},
onLogout() {
uni.showModal({
title: '提示',
content: '确认退出登录?',
success: (res) => {
if (res.confirm) {
// 退
uni.clearStorageSync()
uni.setStorageSync('isLoginOut', 'yes')
uni.reLaunch({
url: '/pages/login/index',
})
}
},
})
},
},
}
</script>
<style lang="scss">
.container {
min-height: 100vh;
background-color: #f5f5f5;
padding: 32rpx;
}
.container {
min-height: 100vh;
background-color: #f5f5f5;
padding: 32rpx;
}
.user-info {
display: flex;
align-items: center;
margin-bottom: 30rpx;
.user-info {
display: flex;
align-items: center;
margin-bottom: 30rpx;
.avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.user-detail {
display: flex;
flex-direction: column;
.user-detail {
display: flex;
flex-direction: column;
.username {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 6rpx;
}
.username {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 6rpx;
}
.phone {
font-size: 28rpx;
color: #666;
}
}
}
.phone {
font-size: 28rpx;
color: #666;
}
}
}
.menu-list {
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
}
.menu-list {
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
}
.menu-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-bottom: 2rpx solid #f5f5f5;
}
.menu-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-bottom: 2rpx solid #f5f5f5;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-item-left {
display: flex;
align-items: center;
}
.menu-item-left {
display: flex;
align-items: center;
}
.iconfont {
font-size: 40rpx;
margin-right: 20rpx;
color: #666;
}
.iconfont {
font-size: 40rpx;
margin-right: 20rpx;
color: #666;
}
.menu-text {
font-size: 32rpx;
color: #333;
}
.menu-text {
font-size: 32rpx;
color: #333;
}
.version {
font-size: 32rpx;
color: #999;
}
.version {
font-size: 32rpx;
color: #999;
}
.icon-arrow {
color: #999;
margin-right: 0;
}
.icon-arrow {
color: #999;
margin-right: 0;
}
.logout-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
text-align: center;
background-color: #4080FF;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
margin-top: 60rpx;
}
.logout-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
text-align: center;
background-color: #4080ff;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
margin-top: 60rpx;
}
/* 去除按钮默认边框 */
.logout-btn::after {
border: none;
}
/* 去除按钮默认边框 */
.logout-btn::after {
border: none;
}
/* 按钮点击效果 */
.logout-btn:active {
opacity: 0.8;
}
/* 按钮点击效果 */
.logout-btn:active {
opacity: 0.8;
}
</style>

View File

@ -0,0 +1,241 @@
<template>
<view class="container">
<view class="form-container">
<view class="form-item">
<text class="label required">姓名</text>
<input type="text" v-model="formData.userName" disabled />
</view>
<view class="form-item">
<text class="label required">休假类型</text>
<input type="text" v-model="formData.leaveType" disabled />
</view>
<view class="form-item">
<text class="label required">休假开始时间</text>
<input type="text" v-model="formData.leaveStartDate" disabled />
</view>
<view class="form-item">
<text class="label required">休假结束时间</text>
<input type="text" v-model="formData.leaveEndDate" disabled />
</view>
<view class="form-item">
<text class="label required">休假时长</text>
<input type="number" v-model="formData.leaveDuration" disabled />
</view>
<view class="form-item">
<text class="label">地点</text>
<input type="text" v-model="formData.location" disabled />
</view>
<view class="form-item">
<text class="label required" style="width: 300px;">是否请示领导同意</text>
<input type="text" :value="formData.isAgree === '1' ? '是' : '否'" disabled />
</view>
<view class="form-item">
<text class="label required">代理主持工作人员</text>
<input type="text" v-model="formData.hostUserName" disabled />
</view>
<view class="form-item">
<text class="label required">休假事由</text>
<textarea v-model="formData.leaveReason" disabled />
</view>
<view class="form-item">
<text class="label">备注</text>
<textarea v-model="formData.remark" disabled />
</view>
</view>
</view>
</template>
<script>
import { getLeaveReporting } from '../../api/index.js'
export default {
data() {
return {
uuid: '',
datas:{
uuid:''
},
formData: {
userName: '',
type:'',
leaveType:'',
travelersName: '',
leaveStartDate: '',
leaveEndDate: '',
leaveDuration: '',
location: '',
isAgree: '1',
hostUserName: '',
leaveReason: '',
remark: ''
}
}
},
onLoad(option) {
this.uuid = option.uuid
console.log('接收到的id:', this.uuid)
this.fetchEvectionDetail()
},
methods: {
fetchEvectionDetail() {
try {
this.datas.uuid=this.uuid;
getLeaveReporting(this.datas).then(response=>{
if (response && response.data) {
console.log("response.data",response.data)
this.formData = {
...this.formData,
...response.data
}
}
})
} catch (error) {
console.error('获取出差详情失败:', error)
uni.showToast({
title: '获取出差详情失败',
icon: 'none'
})
}
}
}
}
</script>
<style>
.container {
padding: 20rpx;
}
.header {
display: flex;
align-items: center;
padding: 20rpx 0;
}
.back-icon {
margin-right: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
}
.tabs {
display: flex;
border-bottom: 1rpx solid #eee;
background: #fff;
}
.tab {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 32rpx;
color: #333;
position: relative;
}
.tab.active {
color: #007AFF;
}
.tab.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120rpx;
height: 4rpx;
background: #007AFF;
}
/* .tabs {
display: flex;
margin: 20rpx 0;
}
.tab {
padding: 20rpx;
margin-right: 20rpx;
}
.tab.active {
color: #4080FF;
border-bottom: 4rpx solid #4080FF;
} */
.form-container {
background: #fff;
padding: 20rpx;
border-radius: 12rpx;
}
.form-item {
margin-bottom: 30rpx;
}
.label {
display: block;
margin-bottom: 10rpx;
font-size: 28rpx;
}
.required::after {
content: '*';
color: #ff4d4f;
margin-left: 4rpx;
}
input, .picker-value {
width: 95%;
height: 80rpx;
line-height: 80rpx;
padding: 0 20rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
}
textarea {
width: 95%;
height: 160rpx;
padding: 20rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
}
.radio {
margin-right: 30rpx;
}
.button-group {
margin-top: 40rpx;
padding: 20rpx;
}
.submit-btn {
background: #4080FF;
color: #fff;
margin-bottom: 20rpx;
}
.cancel-btn {
background: #f5f5f5;
}
.error-message {
color: #ff4d4f;
font-size: 24rpx;
margin-top: 8rpx;
}
</style>

View File

@ -0,0 +1,659 @@
<template>
<!-- 轮休申请 -->
<view class="container">
<view class="tabs">
<text class="tab" :class="{ active: activeTab === 'form' }" @tap="switchTab('form')">
轮休申请
</text>
<text
class="tab"
:class="{ active: activeTab === 'records' }"
@tap="switchTab('records')"
>
轮休记录
</text>
</view>
<view v-if="activeTab === 'form'" class="form-container">
<view>
<text v-if="isFormDisabled" style="color: #3370ff; margin-bottom: 10px">
请确认填写信息是否准确保存后不可修改若修改可联系相关工作人员在统计报表核对时修改
</text>
</view>
<view class="form-item">
<text class="label required">申请人</text>
<input
type="text"
v-model="formData.userName"
placeholder="请输入申请人姓名"
disabled
/>
<text v-if="errors.userName" class="error-message">{{ errors.userName }}</text>
</view>
<view class="form-item">
<text class="label required">职务</text>
<input type="text" placeholder="请输入职务" disabled />
</view>
<view class="form-item">
<text class="label required">所属部门</text>
<input type="text" placeholder="请输入所属部门" disabled />
</view>
<view class="form-item">
<text class="label required">休假类型</text>
<picker
@change="onLeaveTypeChange"
:disabled="isFormDisabled"
:value="leaveTypeIndex"
:range="leaveTypes"
range-key="label"
>
<view class="picker-value">
{{ formData.leaveType ? formData.leaveType : '请选择休假类型' }}
</view>
</picker>
<text v-if="errors.leaveType" class="error-message">{{ errors.leaveType }}</text>
</view>
<view class="form-item">
<text class="label required">轮休开始时间</text>
<picker
mode="date"
:value="formData.leaveStartDate"
@change="onStartDateChange"
:disabled="isFormDisabled"
>
<view class="picker-value">{{ formData.leaveStartDate }}</view>
</picker>
<text v-if="errors.leaveStartDate" class="error-message">
{{ errors.leaveStartDate }}
</text>
</view>
<view class="form-item">
<text class="label required">轮休结束时间</text>
<picker
mode="date"
:value="formData.leaveEndDate"
@change="onEndDateChange"
:disabled="isFormDisabled"
>
<view class="picker-value">{{ formData.leaveEndDate }}</view>
</picker>
<text v-if="errors.leaveEndDate" class="error-message">
{{ errors.leaveEndDate }}
</text>
</view>
<view class="form-item">
<text class="label required">休假时长</text>
<input type="number" v-model="formData.leaveDuration" disabled />
</view>
<!-- <view class="form-item">
<text class="label">地点</text>
<input
type="text"
v-model="formData.location"
placeholder="请输入"
:disabled="isFormDisabled"
/>
</view> -->
<view class="form-item">
<text class="label required" style="width: 300px">是否请示领导同意</text>
<radio-group @change="onApprovalChange">
<label class="radio">
<radio
value="1"
:disabled="isFormDisabled"
checked
:checked="formData.isAgree === '1'"
/>
</label>
<label class="radio">
<radio
value="0"
:disabled="isFormDisabled"
:checked="formData.isAgree === '0'"
/>
</label>
</radio-group>
</view>
<view class="form-item" style="margin-bottom: 20rpx">
<text
class="label required"
style="display: block; margin-bottom: 10rpx; width: 300px"
>
代理主持工作人员
</text>
<view class="selected-values" style="margin-bottom: 10rpx">
{{ hostUserNames }}
</view>
<view
v-if="!isFormDisabled"
class="toggle-button"
@tap="toggleCheckboxGroups"
style="
background-color: #f0f0f0;
width: 55px;
line-height: 35px;
text-align: center;
padding: 10rpx 20rpx;
border-radius: 10rpx;
display: inline-block;
margin-bottom: 10rpx;
"
>
{{ isCheckboxGroupVisibles ? '收起' : '展开' }}
</view>
<view
class="checkbox-group-container"
v-if="!isFormDisabled && isCheckboxGroupVisibles"
>
<checkbox-group
@change="onHostUserChange"
v-if="isCheckboxGroupVisibles"
style="display: flex; flex-wrap: wrap"
>
<view
class="search-container"
style="display: flex; margin-bottom: 10rpx; width: 100%"
>
<input
v-model="searchQuerys"
placeholder="搜索人名"
style="
flex: 1;
padding: 10rpx;
border: 1px solid #ccc;
border-radius: 4px;
height: 30px;
"
/>
<button
@tap="searchPersons"
style="
line-height: 22px;
font-size: 13px;
margin-left: 10rpx;
padding: 10px 20px;
background-color: #007aff;
color: white;
border: none;
border-radius: 4px;
text-align: center;
"
>
搜索
</button>
</view>
<label
v-for="(option, index) in displayedPersonOptionss"
:key="index"
style="width: 33.33%; padding: 10rpx; box-sizing: border-box"
>
<checkbox
:value="option.userId"
:checked="option.checked"
style="transform: scale(0.7)"
/>
<text style="font-size: 28rpx">{{ option.username }}</text>
</label>
</checkbox-group>
</view>
</view>
<!-- <view class="form-item">
<text class="label required">休假事由</text>
<textarea
v-model="formData.leaveReason"
placeholder="请输入休假事由"
:disabled="isFormDisabled"
/>
<text v-if="errors.leaveReason" class="error-message">
{{ errors.leaveReason }}
</text>
</view> -->
<view class="form-item">
<text class="label">备注</text>
<textarea v-model="formData.remark" :disabled="isFormDisabled" />
</view>
<view class="button-group">
<button
class="submit-btn"
@tap="onSubmit"
:disabled="isFormDisabled && !isDataUploaded"
>
{{ isDataUploaded ? '确认提交' : '保存' }}
</button>
<button class="cancel-btn" @tap="onCancel">取消</button>
</view>
</view>
<RecordList v-if="activeTab === 'records'" />
</view>
</template>
<script>
import { getPersonSelect, addHoliday, getHolidayType, getDays } from '../../api/index.js'
import RecordList from './recordList.vue'
export default {
components: {
RecordList,
},
data() {
return {
isFormDisabled: false,
isDataUploaded: false,
showConfirmDialog: false,
searchQuerys: '',
activeTab: 'form',
formData: {
userName: '',
leaveType: null,
leaveStartDate: '',
leaveEndDate: '',
leaveDuration: '',
location: '',
isAgree: '1',
representative: '',
hostUserId: '',
hostUserName: '',
leaveReason: '',
remark: '',
},
userInfo: {
token: '',
userId: '',
name: '',
phone: '',
},
leaveTypes: [],
leaveTypeIndex: -1,
today: new Date().toISOString().substr(0, 10), // YYYY-MM-DD
errors: {},
hostUserOptions: [], //
filteredPersonOptionss: [],
hostUserId: [], // ID
hostUserNames: '', //
isCheckboxGroupVisibles: false,
}
},
mounted() {
//
this.userInfo.token = uni.getStorageSync('token') || ''
this.userInfo.userId = uni.getStorageSync('userId') || ''
this.userInfo.name = uni.getStorageSync('username') || ''
this.userInfo.phone = uni.getStorageSync('phone') || ''
this.formData.userName = uni.getStorageSync('username') || ''
},
created() {
// this.gethostUserSelect()
this.getHolidayType()
},
computed: {
displayedPersonOptionss() {
return this.filteredPersonOptionss.map((person) => ({
...person,
checked: this.hostUserId.includes(person.userId),
}))
},
},
methods: {
toggleCheckboxGroups() {
this.isCheckboxGroupVisibles = !this.isCheckboxGroupVisibles
},
switchTab(tab) {
this.activeTab = tab
},
getHolidayType() {
getHolidayType()
.then((response) => {
this.leaveTypes = response.data.map((type) => ({
value: type.value,
label: type.label || type.type, // 使label使type
}))
if (this.leaveTypes.length > 0) {
this.formData.leaveType = this.leaveTypes[0].label
this.leaveTypeIndex = 0
}
})
.catch((error) => {
console.error('获取休假类型失败:', error)
//
})
.finally(() => {
this.isLoading = false
})
},
onLeaveTypeChange(e) {
const index = e.detail.value
this.leaveTypeIndex = index
this.formData.leaveType = this.leaveTypes[index].label
console.log(this.formData.leaveType)
console.log('休假类型已更改为:', this.formData.leaveType)
},
onStartDateChange(e) {
const startDate = e.detail.value
this.formData.leaveStartDate = startDate
this.validateAndCalculateDuration(startDate, this.formData.leaveEndDate)
},
onEndDateChange(e) {
const endDate = e.detail.value
this.formData.leaveEndDate = endDate
this.validateAndCalculateDuration(this.formData.leaveStartDate, endDate)
},
validateAndCalculateDuration(startDate, endDate) {
//
this.errors.leaveStartDate = ''
this.errors.leaveEndDate = ''
// Date
const start = new Date(startDate)
const end = new Date(endDate)
const today = new Date(this.today)
//
if (start < today) {
this.errors.leaveStartDate = '开始日期不能早于今天'
this.formData.leaveStartDate = ''
return
}
//
if (end < start) {
this.errors.leaveEndDate = '结束日期不能早于开始日期'
this.formData.leaveEndDate = ''
return
}
let dateData = {
startTime: startDate,
endTime: endDate,
}
if (startDate != null && startDate !== '' && endDate != null && endDate !== '') {
getDays(dateData).then((response) => {
if (response.status === 200) {
console.log(response)
this.formData.leaveDuration = response.data
} else {
this.formData.leaveDuration = ''
}
})
}
},
onApprovalChange(e) {
this.formData.isApproved = e.detail.value
},
onRepresentativeChange(e) {
this.formData.representative = e.detail.value
},
validateForm() {
this.errors = {}
if (!this.formData.userName.trim()) {
this.errors.userName = '请输入姓名'
}
if (!this.formData.leaveType) {
this.errors.leaveType = '请选择休假类型'
}
if (!this.formData.leaveStartDate) {
this.errors.leaveStartDate = '请选择休假开始时间'
}
if (!this.formData.leaveEndDate) {
this.errors.leaveEndDate = '请选择休假结束时间'
}
if (!this.formData.leaveDuration) {
this.errors.leaveDuration = '请输入休假时长'
}
if (!this.formData.hostUserId) {
this.errors.hostUserId = '请选择代理主持工作人员'
}
if (!this.formData.leaveReason.trim()) {
this.errors.leaveReason = '请输入休假事由'
}
return Object.keys(this.errors).length === 0
},
gethostUserSelect() {
// getPersonSelectAPIPromise
getPersonSelect().then((response) => {
this.hostUserOptions = response.data
this.filteredPersonOptionss = [...this.hostUserOptions] //
console.log('this.hostUserOptions', this.hostUserOptions)
})
},
searchPersons() {
if (this.searchQuerys.trim() === '') {
this.filteredPersonOptionss = [...this.hostUserOptions]
} else {
this.filteredPersonOptionss = this.hostUserOptions.filter((person) =>
person.username.toLowerCase().includes(this.searchQuerys.toLowerCase()),
)
}
//
const selectedPersons = this.hostUserOptions.filter((person) =>
this.hostUserId.includes(person.userId),
)
this.filteredPersonOptionss = [
...new Set([...selectedPersons, ...this.filteredPersonOptionss]),
]
},
onHostUserChange(e) {
// IDID
this.hostUserId = [...new Set([...this.hostUserId, ...e.detail.value])]
// ID
this.hostUserId = this.hostUserId.filter((id) => e.detail.value.includes(id))
this.formData.hostUserId = this.hostUserId.join(',')
this.updateSelectedNamess()
},
updateSelectedNamess() {
this.hostUserNames = this.hostUserOptions
.filter((person) => this.hostUserId.includes(person.userId))
.map((person) => person.username)
.join(', ')
this.formData.hostUserName = this.hostUserNames
},
onSubmit() {
//
if (this.validateForm()) {
if (!this.isDataUploaded) {
this.isFormDisabled = true
console.log('禁用表单元素')
//
this.isDataUploaded = true
} else {
console.log('提交表单:', this.formData)
addHoliday(this.formData).then((response) => {
if (response.status == 200) {
uni.showToast({
title: '提交成功',
icon: 'success',
})
//
setTimeout(() => {
uni.navigateBack({
delta: 1, //
})
}, 1500) // 便
} else {
uni.showToast({
title: '提交失败,请稍后再试',
icon: 'none',
})
}
//
this.isFormDisabled = false
this.isDataUploaded = false
})
}
} else {
uni.showToast({
title: '请填写所有必填项',
icon: 'none',
})
}
},
onCancel() {
if (this.isDataUploaded) {
// ""
this.isFormDisabled = false
this.isDataUploaded = false
//
// this.resetForm(); //
} else {
//
uni.navigateBack()
}
},
},
}
</script>
<style>
.container {
padding: 20rpx;
}
.header {
display: flex;
align-items: center;
padding: 20rpx 0;
}
.back-icon {
margin-right: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
}
.tabs {
display: flex;
border-bottom: 1rpx solid #eee;
background: #fff;
}
.tab {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 32rpx;
color: #333;
position: relative;
}
.tab.active {
color: #007aff;
}
.tab.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120rpx;
height: 4rpx;
background: #007aff;
}
.form-container {
background: #fff;
padding: 20rpx;
border-radius: 12rpx;
}
.form-item {
margin-bottom: 30rpx;
}
.label {
display: block;
margin-bottom: 10rpx;
font-size: 28rpx;
}
.required::after {
content: '*';
color: #ff4d4f;
margin-left: 4rpx;
}
.checkbox-group-container {
max-height: 300px;
overflow-y: auto;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.checkbox-group-container::-webkit-scrollbar {
width: 5px;
}
.checkbox-group-container::-webkit-scrollbar-track {
background: #f1f1f1;
}
.checkbox-group-container::-webkit-scrollbar-thumb {
background: #888;
border-radius: 5px;
}
.checkbox-group-container::-webkit-scrollbar-thumb:hover {
background: #555;
}
input,
.picker-value {
width: 95%;
height: 80rpx;
line-height: 80rpx;
padding: 0 20rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
}
textarea {
width: 95%;
height: 160rpx;
padding: 20rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
}
.radio {
margin-right: 30rpx;
}
.button-group {
margin-top: 40rpx;
padding: 20rpx;
}
.submit-btn {
background: #4080ff;
color: #fff;
margin-bottom: 20rpx;
}
.cancel-btn {
background: #f5f5f5;
}
.error-message {
color: #ff4d4f;
font-size: 24rpx;
margin-top: 8rpx;
}
</style>

View File

@ -0,0 +1,145 @@
<template>
<view class="records-container">
<view
class="record-item"
v-for="(record, index) in records"
:key="index"
@tap="showDetail(record.uuid)"
style="cursor: pointer"
>
<view class="record-header">
<text class="label">姓名</text>
<text class="value">{{ record.userName }}</text>
<text class="detail-link">详情</text>
</view>
<view class="record-content">
<view class="record-row">
<text class="label">休假类型</text>
<text class="value">{{ record.leaveType }}</text>
</view>
<view class="record-row">
<text class="label">休假开始时间</text>
<text class="value">{{ record.leaveStartDate }}</text>
</view>
<view class="record-row">
<text class="label">休假结束时间</text>
<text class="value">{{ record.leaveEndDate }}</text>
</view>
<view class="record-row">
<text class="label">休假时长</text>
<text class="value">{{ record.leaveDuration }}</text>
</view>
<view class="btn-container">
<text>详情</text>
<text>修改</text>
<text>删除</text>
</view>
</view>
</view>
</view>
</template>
<script>
import { listLeaveReporting } from '../../api/index.js'
export default {
name: 'RecordList',
data() {
return {
records: [{ userName: '李思思' }],
}
},
created() {
//
this.fetchRecords()
},
methods: {
fetchRecords() {
listLeaveReporting().then((response) => {
console.log('response', response)
this.records = response.data
this.records = [{ userName: '李思思' }]
console.log('records', this.records)
})
},
showDetail(id) {
console.log('id', id)
//
uni.navigateTo({
url: `/pages/holiday/details?uuid=${id}`,
})
},
},
}
</script>
<style lang="scss">
.records-container {
padding: 20rpx;
}
.record-item {
background: #fff;
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
}
.record-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.detail-link {
color: #007aff;
font-size: 28rpx;
}
.record-row {
display: flex;
margin-bottom: 8rpx;
}
.label {
color: #666;
font-size: 28rpx;
width: 110px;
}
.value {
color: #333;
font-size: 28rpx;
flex: 1;
}
.btn-container {
display: flex;
justify-content: space-around;
text {
width: 30%;
height: 68rpx;
line-height: 68rpx;
text-align: center;
font-size: 28rpx;
border-radius: 4rpx;
}
& text:first-child {
border: 1px solid #4080ff;
color: #4080ff;
}
& text:nth-child(2) {
border: 1px solid #e6a23c;
color: #e6a23c;
}
& text:last-child {
border: 1px solid #f56c6c;
color: #f56c6c;
}
}
</style>

View File

@ -0,0 +1,241 @@
<template>
<view class="container">
<view class="form-container">
<view class="form-item">
<text class="label required">姓名</text>
<input type="text" v-model="formData.userName" disabled />
</view>
<view class="form-item">
<text class="label required">休假类型</text>
<input type="text" v-model="formData.leaveType" disabled />
</view>
<view class="form-item">
<text class="label required">休假开始时间</text>
<input type="text" v-model="formData.leaveStartDate" disabled />
</view>
<view class="form-item">
<text class="label required">休假结束时间</text>
<input type="text" v-model="formData.leaveEndDate" disabled />
</view>
<view class="form-item">
<text class="label required">休假时长</text>
<input type="number" v-model="formData.leaveDuration" disabled />
</view>
<view class="form-item">
<text class="label">地点</text>
<input type="text" v-model="formData.location" disabled />
</view>
<view class="form-item">
<text class="label required" style="width: 300px;">是否请示领导同意</text>
<input type="text" :value="formData.isAgree === '1' ? '是' : '否'" disabled />
</view>
<view class="form-item">
<text class="label required">代理主持工作人员</text>
<input type="text" v-model="formData.hostUserName" disabled />
</view>
<view class="form-item">
<text class="label required">休假事由</text>
<textarea v-model="formData.leaveReason" disabled />
</view>
<view class="form-item">
<text class="label">备注</text>
<textarea v-model="formData.remark" disabled />
</view>
</view>
</view>
</template>
<script>
import { getLeaveReporting } from '../../api/index.js'
export default {
data() {
return {
uuid: '',
datas:{
uuid:''
},
formData: {
userName: '',
type:'',
leaveType:'',
travelersName: '',
leaveStartDate: '',
leaveEndDate: '',
leaveDuration: '',
location: '',
isAgree: '1',
hostUserName: '',
leaveReason: '',
remark: ''
}
}
},
onLoad(option) {
this.uuid = option.uuid
console.log('接收到的id:', this.uuid)
this.fetchEvectionDetail()
},
methods: {
fetchEvectionDetail() {
try {
this.datas.uuid=this.uuid;
getLeaveReporting(this.datas).then(response=>{
if (response && response.data) {
console.log("response.data",response.data)
this.formData = {
...this.formData,
...response.data
}
}
})
} catch (error) {
console.error('获取出差详情失败:', error)
uni.showToast({
title: '获取出差详情失败',
icon: 'none'
})
}
}
}
}
</script>
<style>
.container {
padding: 20rpx;
}
.header {
display: flex;
align-items: center;
padding: 20rpx 0;
}
.back-icon {
margin-right: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
}
.tabs {
display: flex;
border-bottom: 1rpx solid #eee;
background: #fff;
}
.tab {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 32rpx;
color: #333;
position: relative;
}
.tab.active {
color: #007AFF;
}
.tab.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120rpx;
height: 4rpx;
background: #007AFF;
}
/* .tabs {
display: flex;
margin: 20rpx 0;
}
.tab {
padding: 20rpx;
margin-right: 20rpx;
}
.tab.active {
color: #4080FF;
border-bottom: 4rpx solid #4080FF;
} */
.form-container {
background: #fff;
padding: 20rpx;
border-radius: 12rpx;
}
.form-item {
margin-bottom: 30rpx;
}
.label {
display: block;
margin-bottom: 10rpx;
font-size: 28rpx;
}
.required::after {
content: '*';
color: #ff4d4f;
margin-left: 4rpx;
}
input, .picker-value {
width: 95%;
height: 80rpx;
line-height: 80rpx;
padding: 0 20rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
}
textarea {
width: 95%;
height: 160rpx;
padding: 20rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
}
.radio {
margin-right: 30rpx;
}
.button-group {
margin-top: 40rpx;
padding: 20rpx;
}
.submit-btn {
background: #4080FF;
color: #fff;
margin-bottom: 20rpx;
}
.cancel-btn {
background: #f5f5f5;
}
.error-message {
color: #ff4d4f;
font-size: 24rpx;
margin-top: 8rpx;
}
</style>

View File

@ -0,0 +1,666 @@
<template>
<!-- 轮休申请 -->
<view class="container">
<view class="tabs">
<text class="tab" :class="{ active: activeTab === 'form' }" @tap="switchTab('form')">
外出申请
</text>
<text
class="tab"
:class="{ active: activeTab === 'records' }"
@tap="switchTab('records')"
>
外出记录
</text>
</view>
<view v-if="activeTab === 'form'" class="form-container">
<view>
<text v-if="isFormDisabled" style="color: #3370ff; margin-bottom: 10px">
请确认填写信息是否准确保存后不可修改若修改可联系相关工作人员在统计报表核对时修改
</text>
</view>
<view class="form-item">
<text class="label required">申请人</text>
<input
type="text"
v-model="formData.userName"
placeholder="请输入申请人姓名"
disabled
/>
<text v-if="errors.userName" class="error-message">{{ errors.userName }}</text>
</view>
<view class="form-item">
<text class="label required">职务</text>
<input type="text" v-model="formData.userName" placeholder="请输入职务" disabled />
<text v-if="errors.userName" class="error-message">{{ errors.userName }}</text>
</view>
<view class="form-item">
<text class="label required">所属部门</text>
<input
type="text"
v-model="formData.userName"
placeholder="请输入所属部门"
disabled
/>
<text v-if="errors.userName" class="error-message">{{ errors.userName }}</text>
</view>
<!-- <view class="form-item">
<text class="label required">休假类型</text>
<picker
@change="onLeaveTypeChange"
:disabled="isFormDisabled"
:value="leaveTypeIndex"
:range="leaveTypes"
range-key="label"
>
<view class="picker-value">
{{ formData.leaveType ? formData.leaveType : '请选择休假类型' }}
</view>
</picker>
<text v-if="errors.leaveType" class="error-message">{{ errors.leaveType }}</text>
</view> -->
<view class="form-item">
<text class="label required">外出开始时间</text>
<picker
mode="date"
:value="formData.leaveStartDate"
@change="onStartDateChange"
:disabled="isFormDisabled"
>
<view class="picker-value">{{ formData.leaveStartDate }}</view>
</picker>
<text v-if="errors.leaveStartDate" class="error-message">
{{ errors.leaveStartDate }}
</text>
</view>
<view class="form-item">
<text class="label required">外出结束时间</text>
<picker
mode="date"
:value="formData.leaveEndDate"
@change="onEndDateChange"
:disabled="isFormDisabled"
>
<view class="picker-value">{{ formData.leaveEndDate }}</view>
</picker>
<text v-if="errors.leaveEndDate" class="error-message">
{{ errors.leaveEndDate }}
</text>
</view>
<view class="form-item">
<text class="label required">外出时长</text>
<input type="number" v-model="formData.leaveDuration" disabled />
</view>
<view class="form-item">
<text class="label">地点</text>
<input
type="text"
v-model="formData.location"
placeholder="请输入"
:disabled="isFormDisabled"
/>
</view>
<view class="form-item">
<text class="label required" style="width: 300px">是否请示领导同意</text>
<radio-group @change="onApprovalChange">
<label class="radio">
<radio
value="1"
:disabled="isFormDisabled"
checked
:checked="formData.isAgree === '1'"
/>
</label>
<label class="radio">
<radio
value="0"
:disabled="isFormDisabled"
:checked="formData.isAgree === '0'"
/>
</label>
</radio-group>
</view>
<view class="form-item" style="margin-bottom: 20rpx">
<text
class="label required"
style="display: block; margin-bottom: 10rpx; width: 300px"
>
代理主持工作人员
</text>
<view class="selected-values" style="margin-bottom: 10rpx">
{{ hostUserNames }}
</view>
<view
v-if="!isFormDisabled"
class="toggle-button"
@tap="toggleCheckboxGroups"
style="
background-color: #f0f0f0;
width: 55px;
line-height: 35px;
text-align: center;
padding: 10rpx 20rpx;
border-radius: 10rpx;
display: inline-block;
margin-bottom: 10rpx;
"
>
{{ isCheckboxGroupVisibles ? '收起' : '展开' }}
</view>
<view
class="checkbox-group-container"
v-if="!isFormDisabled && isCheckboxGroupVisibles"
>
<checkbox-group
@change="onHostUserChange"
v-if="isCheckboxGroupVisibles"
style="display: flex; flex-wrap: wrap"
>
<view
class="search-container"
style="display: flex; margin-bottom: 10rpx; width: 100%"
>
<input
v-model="searchQuerys"
placeholder="搜索人名"
style="
flex: 1;
padding: 10rpx;
border: 1px solid #ccc;
border-radius: 4px;
height: 30px;
"
/>
<button
@tap="searchPersons"
style="
line-height: 22px;
font-size: 13px;
margin-left: 10rpx;
padding: 10px 20px;
background-color: #007aff;
color: white;
border: none;
border-radius: 4px;
text-align: center;
"
>
搜索
</button>
</view>
<label
v-for="(option, index) in displayedPersonOptionss"
:key="index"
style="width: 33.33%; padding: 10rpx; box-sizing: border-box"
>
<checkbox
:value="option.userId"
:checked="option.checked"
style="transform: scale(0.7)"
/>
<text style="font-size: 28rpx">{{ option.username }}</text>
</label>
</checkbox-group>
</view>
</view>
<view class="form-item">
<text class="label required">休假事由</text>
<textarea
v-model="formData.leaveReason"
placeholder="请输入休假事由"
:disabled="isFormDisabled"
/>
<text v-if="errors.leaveReason" class="error-message">
{{ errors.leaveReason }}
</text>
</view>
<view class="form-item">
<text class="label">备注</text>
<textarea v-model="formData.remark" :disabled="isFormDisabled" />
</view>
<view class="button-group">
<button
class="submit-btn"
@tap="onSubmit"
:disabled="isFormDisabled && !isDataUploaded"
>
{{ isDataUploaded ? '确认提交' : '保存' }}
</button>
<button class="cancel-btn" @tap="onCancel">取消</button>
</view>
</view>
<RecordList v-if="activeTab === 'records'" />
</view>
</template>
<script>
import { getPersonSelect, addHoliday, getHolidayType, getDays } from '../../api/index.js'
import RecordList from './recordList.vue'
export default {
components: {
RecordList,
},
data() {
return {
isFormDisabled: false,
isDataUploaded: false,
showConfirmDialog: false,
searchQuerys: '',
activeTab: 'form',
formData: {
userName: '',
leaveType: null,
leaveStartDate: '',
leaveEndDate: '',
leaveDuration: '',
location: '',
isAgree: '1',
representative: '',
hostUserId: '',
hostUserName: '',
leaveReason: '',
remark: '',
},
userInfo: {
token: '',
userId: '',
name: '',
phone: '',
},
leaveTypes: [],
leaveTypeIndex: -1,
today: new Date().toISOString().substr(0, 10), // YYYY-MM-DD
errors: {},
hostUserOptions: [], //
filteredPersonOptionss: [],
hostUserId: [], // ID
hostUserNames: '', //
isCheckboxGroupVisibles: false,
}
},
mounted() {
//
this.userInfo.token = uni.getStorageSync('token') || ''
this.userInfo.userId = uni.getStorageSync('userId') || ''
this.userInfo.name = uni.getStorageSync('username') || ''
this.userInfo.phone = uni.getStorageSync('phone') || ''
this.formData.userName = uni.getStorageSync('username') || ''
},
created() {
this.gethostUserSelect()
this.getHolidayType()
},
computed: {
displayedPersonOptionss() {
return this.filteredPersonOptionss.map((person) => ({
...person,
checked: this.hostUserId.includes(person.userId),
}))
},
},
methods: {
toggleCheckboxGroups() {
this.isCheckboxGroupVisibles = !this.isCheckboxGroupVisibles
},
switchTab(tab) {
this.activeTab = tab
},
getHolidayType() {
getHolidayType()
.then((response) => {
this.leaveTypes = response.data.map((type) => ({
value: type.value,
label: type.label || type.type, // 使label使type
}))
if (this.leaveTypes.length > 0) {
this.formData.leaveType = this.leaveTypes[0].label
this.leaveTypeIndex = 0
}
})
.catch((error) => {
console.error('获取休假类型失败:', error)
//
})
.finally(() => {
this.isLoading = false
})
},
onLeaveTypeChange(e) {
const index = e.detail.value
this.leaveTypeIndex = index
this.formData.leaveType = this.leaveTypes[index].label
console.log(this.formData.leaveType)
console.log('休假类型已更改为:', this.formData.leaveType)
},
onStartDateChange(e) {
const startDate = e.detail.value
this.formData.leaveStartDate = startDate
this.validateAndCalculateDuration(startDate, this.formData.leaveEndDate)
},
onEndDateChange(e) {
const endDate = e.detail.value
this.formData.leaveEndDate = endDate
this.validateAndCalculateDuration(this.formData.leaveStartDate, endDate)
},
validateAndCalculateDuration(startDate, endDate) {
//
this.errors.leaveStartDate = ''
this.errors.leaveEndDate = ''
// Date
const start = new Date(startDate)
const end = new Date(endDate)
const today = new Date(this.today)
//
if (start < today) {
this.errors.leaveStartDate = '开始日期不能早于今天'
this.formData.leaveStartDate = ''
return
}
//
if (end < start) {
this.errors.leaveEndDate = '结束日期不能早于开始日期'
this.formData.leaveEndDate = ''
return
}
let dateData = {
startTime: startDate,
endTime: endDate,
}
if (startDate != null && startDate !== '' && endDate != null && endDate !== '') {
getDays(dateData).then((response) => {
if (response.status === 200) {
console.log(response)
this.formData.leaveDuration = response.data
} else {
this.formData.leaveDuration = ''
}
})
}
},
onApprovalChange(e) {
this.formData.isApproved = e.detail.value
},
onRepresentativeChange(e) {
this.formData.representative = e.detail.value
},
validateForm() {
this.errors = {}
if (!this.formData.userName.trim()) {
this.errors.userName = '请输入姓名'
}
if (!this.formData.leaveType) {
this.errors.leaveType = '请选择休假类型'
}
if (!this.formData.leaveStartDate) {
this.errors.leaveStartDate = '请选择休假开始时间'
}
if (!this.formData.leaveEndDate) {
this.errors.leaveEndDate = '请选择休假结束时间'
}
if (!this.formData.leaveDuration) {
this.errors.leaveDuration = '请输入休假时长'
}
if (!this.formData.hostUserId) {
this.errors.hostUserId = '请选择代理主持工作人员'
}
if (!this.formData.leaveReason.trim()) {
this.errors.leaveReason = '请输入休假事由'
}
return Object.keys(this.errors).length === 0
},
gethostUserSelect() {
// getPersonSelectAPIPromise
getPersonSelect().then((response) => {
this.hostUserOptions = response.data
this.filteredPersonOptionss = [...this.hostUserOptions] //
console.log('this.hostUserOptions', this.hostUserOptions)
})
},
searchPersons() {
if (this.searchQuerys.trim() === '') {
this.filteredPersonOptionss = [...this.hostUserOptions]
} else {
this.filteredPersonOptionss = this.hostUserOptions.filter((person) =>
person.username.toLowerCase().includes(this.searchQuerys.toLowerCase()),
)
}
//
const selectedPersons = this.hostUserOptions.filter((person) =>
this.hostUserId.includes(person.userId),
)
this.filteredPersonOptionss = [
...new Set([...selectedPersons, ...this.filteredPersonOptionss]),
]
},
onHostUserChange(e) {
// IDID
this.hostUserId = [...new Set([...this.hostUserId, ...e.detail.value])]
// ID
this.hostUserId = this.hostUserId.filter((id) => e.detail.value.includes(id))
this.formData.hostUserId = this.hostUserId.join(',')
this.updateSelectedNamess()
},
updateSelectedNamess() {
this.hostUserNames = this.hostUserOptions
.filter((person) => this.hostUserId.includes(person.userId))
.map((person) => person.username)
.join(', ')
this.formData.hostUserName = this.hostUserNames
},
onSubmit() {
//
if (this.validateForm()) {
if (!this.isDataUploaded) {
this.isFormDisabled = true
console.log('禁用表单元素')
//
this.isDataUploaded = true
} else {
console.log('提交表单:', this.formData)
addHoliday(this.formData).then((response) => {
if (response.status == 200) {
uni.showToast({
title: '提交成功',
icon: 'success',
})
//
setTimeout(() => {
uni.navigateBack({
delta: 1, //
})
}, 1500) // 便
} else {
uni.showToast({
title: '提交失败,请稍后再试',
icon: 'none',
})
}
//
this.isFormDisabled = false
this.isDataUploaded = false
})
}
} else {
uni.showToast({
title: '请填写所有必填项',
icon: 'none',
})
}
},
onCancel() {
if (this.isDataUploaded) {
// ""
this.isFormDisabled = false
this.isDataUploaded = false
//
// this.resetForm(); //
} else {
//
uni.navigateBack()
}
},
},
}
</script>
<style>
.container {
padding: 20rpx;
}
.header {
display: flex;
align-items: center;
padding: 20rpx 0;
}
.back-icon {
margin-right: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
}
.tabs {
display: flex;
border-bottom: 1rpx solid #eee;
background: #fff;
}
.tab {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 32rpx;
color: #333;
position: relative;
}
.tab.active {
color: #007aff;
}
.tab.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120rpx;
height: 4rpx;
background: #007aff;
}
.form-container {
background: #fff;
padding: 20rpx;
border-radius: 12rpx;
}
.form-item {
margin-bottom: 30rpx;
}
.label {
display: block;
margin-bottom: 10rpx;
font-size: 28rpx;
}
.required::after {
content: '*';
color: #ff4d4f;
margin-left: 4rpx;
}
.checkbox-group-container {
max-height: 300px;
overflow-y: auto;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.checkbox-group-container::-webkit-scrollbar {
width: 5px;
}
.checkbox-group-container::-webkit-scrollbar-track {
background: #f1f1f1;
}
.checkbox-group-container::-webkit-scrollbar-thumb {
background: #888;
border-radius: 5px;
}
.checkbox-group-container::-webkit-scrollbar-thumb:hover {
background: #555;
}
input,
.picker-value {
width: 95%;
height: 80rpx;
line-height: 80rpx;
padding: 0 20rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
}
textarea {
width: 95%;
height: 160rpx;
padding: 20rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
}
.radio {
margin-right: 30rpx;
}
.button-group {
margin-top: 40rpx;
padding: 20rpx;
}
.submit-btn {
background: #4080ff;
color: #fff;
margin-bottom: 20rpx;
}
.cancel-btn {
background: #f5f5f5;
}
.error-message {
color: #ff4d4f;
font-size: 24rpx;
margin-top: 8rpx;
}
</style>

View File

@ -0,0 +1,143 @@
<template>
<view class="records-container">
<view
class="record-item"
v-for="(record, index) in records"
:key="index"
@tap="showDetail(record.uuid)"
style="cursor: pointer"
>
<view class="record-header">
<text class="label">姓名</text>
<text class="value">{{ record.userName }}</text>
<text class="detail-link">详情</text>
</view>
<view class="record-content">
<view class="record-row">
<text class="label">休假类型</text>
<text class="value">{{ record.leaveType }}</text>
</view>
<view class="record-row">
<text class="label">休假开始时间</text>
<text class="value">{{ record.leaveStartDate }}</text>
</view>
<view class="record-row">
<text class="label">休假结束时间</text>
<text class="value">{{ record.leaveEndDate }}</text>
</view>
<view class="record-row">
<text class="label">休假时长</text>
<text class="value">{{ record.leaveDuration }}</text>
</view>
</view>
<view class="btn-container">
<text>详情</text>
<text>修改</text>
<text>删除</text>
</view>
</view>
</view>
</template>
<script>
import { listLeaveReporting } from '../../api/index.js'
export default {
name: 'RecordList',
data() {
return {
records: [{ userName: '李思思' }],
}
},
created() {
//
this.fetchRecords()
},
methods: {
fetchRecords() {
listLeaveReporting().then((response) => {
console.log('response', response)
// this.records = response.data
console.log('records', this.records)
})
},
showDetail(id) {
console.log('id', id)
//
uni.navigateTo({
url: `/pages/holiday/details?uuid=${id}`,
})
},
},
}
</script>
<style lang="scss">
.records-container {
padding: 20rpx;
}
.record-item {
background: #fff;
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
}
.record-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.detail-link {
color: #007aff;
font-size: 28rpx;
}
.record-row {
display: flex;
margin-bottom: 8rpx;
}
.label {
color: #666;
font-size: 28rpx;
width: 110px;
}
.value {
color: #333;
font-size: 28rpx;
flex: 1;
}
.btn-container {
display: flex;
justify-content: space-around;
text {
width: 30%;
height: 68rpx;
line-height: 68rpx;
text-align: center;
font-size: 28rpx;
border-radius: 4rpx;
}
& text:first-child {
border: 1px solid #4080ff;
color: #4080ff;
}
& text:nth-child(2) {
border: 1px solid #e6a23c;
color: #e6a23c;
}
& text:last-child {
border: 1px solid #f56c6c;
color: #f56c6c;
}
}
</style>

BIN
src/static/go_out.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
src/static/lx_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB