This commit is contained in:
那些花儿
2025-05-06 18:05:47 +08:00
parent 4eef7a632a
commit 69be7b9019
11 changed files with 255 additions and 142 deletions
@@ -67,9 +67,8 @@ public class ClientAuthController {
@ApiOperation(value = "注册", nickname = "/register", httpMethod = "POST")
public Result register(ClientRegisterBody registerBody) {
try {
// String token = clientAuthService.doRegister(registerBody);
// return Result.success().addData(token);
return Result.success();
String token = clientAuthService.doRegister(registerBody);
return Result.success().addData(token);
} catch (Exception e) {
log.error(e.getMessage(), e);
return Result.error(e.getMessage());
@@ -130,6 +129,7 @@ public class ClientAuthController {
@SaCheckLogin(type = StpClientUtil.TYPE)
public Result info() {
Client_user user = dao.fetch(Client_user.class, StpClientUtil.STP.getLoginIdAsLong());
dao.fetchLinks(user, "unit");
return Result.success(user);
}
@@ -1,6 +1,7 @@
package com.budwk.app.client.auth.models;
import com.budwk.app.base.model.BaseModel;
import com.budwk.app.sys.models.Sys_unit;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
@@ -62,4 +63,7 @@ public class Client_user extends BaseModel implements Serializable {
@ColDefine(type = ColType.VARCHAR, width = 255)
@Comment("openId")
private String openId;
@One(field = "unitId")
private Sys_unit unit;
}
@@ -92,7 +92,10 @@ public class ClientActivityController {
SqlExpressionGroup group = new SqlExpressionGroup();
group.or(ActUser::getUserId, "=", StpClientUtil.STP.getLoginIdAsLong());
group.or(ActUser::getSignUpUserId, "=", StpClientUtil.STP.getLoginIdAsLong());
List<ActUser> list = dao.query(ActUser.class, Cnd.where(ActUser::getActivityId, "=", activityId).and(group));
ActUser actUser = dao.fetch(ActUser.class, Cnd.where(ActUser::getActivityId, "=", activityId).and(group));
List<ActUser> list = dao.query(ActUser.class, Cnd.where(ActUser::getActivityId, "=", activityId)
.and(ActUser::getSignUpUserId, "=", actUser.getSignUpUserId()));
return Result.success(list);
}
+2 -2
View File
@@ -23,7 +23,7 @@ jetty:
#结合ftp使用,或用nginx代理ftp路径
#staticPath: E:/project/NutzWk/upload
#开发模式静态资源
staticPathLocal: E:/projects/hmc_gbhd/src/main/resources/static
staticPathLocal: E:/project/hmd_gbhd/src/main/resources/static
security:
tokenName: token
@@ -69,7 +69,7 @@ xsssql:
beetl:
RESOURCE:
#本地路径,开发时设置本地路径,便于调试
rootLocal: E:/projects/hmc_gbhd/src/main/resources/views/
rootLocal: E:/project/hmd_gbhd/src/main/resources/views/
root: views/
DELIMITER_STATEMENT_START: '<!--#'
DELIMITER_STATEMENT_END: '#-->'
File diff suppressed because one or more lines are too long
@@ -37,6 +37,8 @@
<script src="${base!}/assets/platform/plugins/adminLte/adminlte.min.js"></script>
<!-- echarts -->
<script src="${base!}/assets/platform/plugins/echarts/echarts.min.js"></script>
<!-- qrcode -->
<script src="${base!}/assets/platform/plugins/qrcode/qrcode.min.js"></script>
<!-- 自定义工具类 -->
<script src="${base!}/assets/platform/module/mixins/initTableMixins.js?v=1.0.0"></script>
<!-- main -->
@@ -0,0 +1,62 @@
const actQrCode = {
template: /*language=HTML*/ `
<el-dialog title="二维码" :visible.sync="visible" width="500px">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="活动报名二维码" name="signUp">
<div id="signUpCode" style="width:150px; height:150px;margin: 0 auto"></div>
</el-tab-pane>
<el-tab-pane label="活动签到二维码" name="signIn">
<div id="signInCode" style="width:150px; height:150px;margin: 0 auto"></div>
</el-tab-pane>
</el-tabs>
<template #footer>
<span class="dialog-footer">
<el-button size="small" @click="visible = false">取 消</el-button>
<el-button size="small" type="primary" @click="download">下载</el-button>
</span>
</template>
</el-dialog>
`,
data() {
return {
visible: false,
actId: "",
activeName: "signUp"
}
},
methods: {
handleClick(tab, event) {
},
onOpen(id) {
this.visible = true
this.actId = id
this.$nextTick(() => {
this.renderCode()
})
},
renderCode() {
let signUpCode = new QRCode(document.getElementById("signUpCode"), {
width: 150,
height: 150
})
signUpCode.makeCode("/pages/activity/detail/index?id=" + this.actId)
let signInCode = new QRCode(document.getElementById("signInCode"), {
width: 150,
height: 150
})
signInCode.makeCode("/pages/activity/scan-sign/index?id=" + this.actId)
},
download() {
// 获取base64
const src = $("#" + this.activeName + "Code img").attr("src")
// 利用a标签进行下载
const a = document.createElement("a")
a.href = src
a.download = (this.activeName === "signUp" ? "报名" : "签到") + "二维码.png"
a.click()
a.remove()
}
}
}
@@ -37,6 +37,7 @@ layout("/layouts/platform_1.html"){
<el-table-column label="活动结束时间" prop="endTime"></el-table-column>
<el-table-column label="操作" fixed="right" width="250">
<template #default="scope">
<el-button type="text" @click="$refs.actQrCodeRef.onOpen(scope.row.id)">二维码</el-button>
<el-button type="text" @click="$refs.addOrUpdateRef.onOpen(scope.row.id)">修改</el-button>
<el-button type="text" @click="$refs.itemFormRef.onOpen(scope.row.id)">项目管理</el-button>
<el-button type="text" class="text-danger" @click="onTableRowDelete(scope.row.id)">删除</el-button>
@@ -47,17 +48,20 @@ layout("/layouts/platform_1.html"){
</el-card>
<add-or-update ref="addOrUpdateRef" @refresh="doSearch"></add-or-update>
<item-form ref="itemFormRef"></item-form>
<act-qr-code ref="actQrCodeRef"></act-qr-code>
</div>
<script>
<!--#include('addOrUpdate.js'){}#-->
<!--#include('itemForm.js'){}#-->
<!--#include('actQrCode.js'){}#-->
new Vue({
el: "#app",
mixins: [initTableMixins],
components: {
"add-or-update": addOrUpdate,
"item-form": itemForm
"item-form": itemForm,
"act-qr-code": actQrCode
},
data() {
return {
@@ -1,14 +1,5 @@
<template>
<view class="page-container">
<!-- <view class="act-info">-->
<!-- <view class="act-title">奋进新征程 运动促健康 2024晴江第六届"牧城之夜"荧光跑</view>-->
<!-- <view class="act-meta">-->
<!-- <view class="act-meta-item">22222</view>-->
<!-- <view class="act-meta-item">22222222222222222222</view>-->
<!-- </view>-->
<!-- </view>-->
<view class="form-area">
<t-form
ref="formRef"
@@ -18,22 +9,21 @@
label-align="left"
scroll-to-first-error="auto"
>
<view class="form-block">
<view class="form-block-title">
<t-icon name="institution"></t-icon>
单位信息
</view>
<t-form-item label="参加单位" name="unitName" :rules="[{required:true,message: '请输入单位名称'}]">
<!-- <t-input v-model="formData.unitName" borderless placeholder="请输入参加单位名称"></t-input>-->
<search-picker v-model="formData.unitName" :options="unitOptions" placeholder="请选择单位"></search-picker>
</t-form-item>
<t-form-item label="联系人" name="contactName" :rules="[{required:true,message: '请输入联系人姓名'}]">
<t-input v-model="formData.contactName" borderless placeholder="请输入联系人姓名"></t-input>
</t-form-item>
<t-form-item label="联系方式" name="contactPhone" :rules="[{required:true,message: '请输入联系电话'}]">
<t-input v-model="formData.contactPhone" borderless placeholder="请输入联系电话"></t-input>
</t-form-item>
</view>
<!-- <view class="form-block">-->
<!-- <view class="form-block-title">-->
<!-- <t-icon name="institution"></t-icon>-->
<!-- 单位信息-->
<!-- </view>-->
<!-- <t-form-item label="参加单位" name="unitName" :rules="[{required:true,message: '请输入单位名称'}]">-->
<!-- <t-input v-model="formData.unitName" borderless readonly placeholder="请输入参加单位名称"></t-input>-->
<!-- </t-form-item>-->
<!-- <t-form-item label="联系人" name="contactName" :rules="[{required:true,message: '请输入联系人姓名'}]">-->
<!-- <t-input v-model="formData.contactName" borderless placeholder="请输入联系人姓名"></t-input>-->
<!-- </t-form-item>-->
<!-- <t-form-item label="联系方式" name="contactPhone" :rules="[{required:true,message: '请输入联系电话'}]">-->
<!-- <t-input v-model="formData.contactPhone" borderless placeholder="请输入联系电话"></t-input>-->
<!-- </t-form-item>-->
<!-- </view>-->
<view class="form-block">
<view class="form-block-title">
@@ -79,14 +69,23 @@
<t-radio :block="false" name="radio" :value="false" label="否"/>
</t-radio-group>
</t-form-item>
<t-form-item label="是否联系人" :name="`userList[${index}].isStay`"
:rules="[{required:true,message: '请选择是否住宿'}]">
<t-radio-group v-model="user.isStay" class="radio-box">
<t-radio :block="false" name="radio" :value="true" label="是"/>
<t-radio :block="false" name="radio" :value="false" label="否"/>
</t-radio-group>
</t-form-item>
</view>
</view>
</t-form>
<view class="add-user" @click="formData.userList.push({})">
<t-icon name="plus" size="24px"></t-icon>
<text>添加人员</text>
</view>
</view>
<!-- 悬浮添加按钮 -->
<view class="float-add-btn" @click="formData.userList.push({})">
<t-icon name="plus" size="24px" color="#fff"></t-icon>
</view>
<view class="footer">
<view class="agreement-checkbox">
<t-checkbox label="多选" icon="rectangle" v-model:checked="agreementChecked" default-checked>
@@ -105,6 +104,14 @@
{{ isSignUp ? '重新提交报名信息' : '提交报名'}}
</t-button>
</view>
<!-- 引导 -->
<t-guide
v-model="guideCurrent"
:steps="guideSteps"
>
</t-guide>
</view>
</template>
@@ -113,9 +120,32 @@ import {ref} from 'vue'
import {useRequest} from '@/utils/request';
import {Dialog, Toast} from "tdesign-mobile-vue";
import {onLoad} from "@dcloudio/uni-app";
import SearchPicker from "@/components/SearchPicker.vue";
import {useUserStore} from "@/store";
const request = useRequest()
const userStore= useUserStore()
// 引导部分
const guideCurrent = ref(-1)
const guideSteps = [
{
element:()=> document.querySelector('.float-add-btn'),
title: '添加活动报名人员',
placement: 'top'
},
{
element:()=> document.querySelector('.t-form'),
title: '填写报名信息',
placement: 'top'
},
{
element:()=> document.querySelector('.footer'),
title: '提交报名信息',
placement: 'top'
}
]
// 报名信息
const actId = ref(null)
const isSignUp = ref(null)
const agreementChecked = ref(false)
@@ -124,18 +154,17 @@ const formData = ref({
userList: [{}]
})
const unitOptions = ref([
{value:'单位1',label:'单位1'},
{value:'单位2',label:'单位2'},
{value:'单位3',label:'单位3'},
])
onLoad((option) => {
if (option.id) {
actId.value = option.id
formData.value.activityId = option.id
listSignUpUser()
getIsSignUp()
// 没报名进行引导
if(formData.userList.length === 0){
guideCurrent.value = 0
formData.value.unitName = userStore.userInfo?.unit.name
}
} else {
Toast({message: 'ID不存在', theme: 'error'});
}
@@ -308,7 +337,6 @@ async function removeUser(index) {
.form-area {
flex: 1;
overflow-y: auto;
//margin-top: 100px;
margin-bottom: 130px;
}
@@ -379,16 +407,23 @@ async function removeUser(index) {
}
}
.add-user {
padding: 15px;
text-align: center;
color: #e74c3c;
font-weight: 500;
cursor: pointer;
.float-add-btn {
position: fixed;
right: 32rpx;
bottom: 160rpx;
width: 110rpx;
height: 110rpx;
border-radius: 50%;
background-color: #e74c3c;
display: flex;
justify-content: center;
align-items: center;
background: #ffffff;
justify-content: center;
z-index: 100;
transition: transform 0.2s;
}
.float-add-btn:active {
transform: scale(0.95);
}
.radio-box {
@@ -14,10 +14,10 @@
</view>
<!-- 小提示 -->
<view class="success-tips">
<t-icon name="info-circle-filled" size="16px" />
<text>活动开始前我们将通过短信通知您</text>
</view>
<!-- <view class="success-tips">-->
<!-- <t-icon name="info-circle-filled" size="16px" />-->
<!-- <text>活动开始前我们将通过短信通知您</text>-->
<!-- </view>-->
<!-- 底部按钮 -->
<view class="action-buttons">
@@ -106,7 +106,7 @@ const viewRegistrationDetails = () => {
image {
width: 100%;
height: 100%;
animation: float 3s ease-in-out infinite;
//animation: float 3s ease-in-out infinite;
}
}
+87 -85
View File
@@ -68,7 +68,7 @@
<input
class="input"
type="text"
v-model="formData.name"
v-model="formData.userName"
placeholder="请输入姓名"
maxlength="20"
/>
@@ -113,7 +113,7 @@
<input
class="input"
type="number"
v-model="formData.verificationCode"
v-model="formData.smsCode"
placeholder="请输入验证码"
maxlength="6"
/>
@@ -145,37 +145,37 @@
<!-- 搜索框 -->
<view class="search-box">
<t-icon name="search" size="22px" class="search-icon" />
<t-icon name="search" size="22px" class="search-icon"/>
<input
class="search-input"
type="text"
v-model="searchKeyword"
placeholder="搜索单位"
@input="handleSearch"
class="search-input"
type="text"
v-model="searchKeyword"
placeholder="搜索单位"
@input="handleSearch"
/>
<t-icon
v-if="searchKeyword"
name="close-circle-filled"
size="22px"
class="clear-icon"
@click="clearSearch"
v-if="searchKeyword"
name="close-circle-filled"
size="22px"
class="clear-icon"
@click="clearSearch"
/>
</view>
<!-- 单位列表 -->
<scroll-view
scroll-y
class="units-scroll-view"
scroll-with-animation
:scroll-into-view="scrollViewId"
scroll-y
class="units-scroll-view"
scroll-with-animation
:scroll-into-view="scrollViewId"
>
<view
v-for="unit in filteredUnits"
:key="unit.id"
:id="'unit-' + unit.id"
class="unit-item"
:class="{'unit-selected': selectedUnitId === unit.id}"
@click="selectUnit(unit)"
v-for="unit in filteredUnits"
:key="unit.id"
:id="'unit-' + unit.id"
class="unit-item"
:class="{'unit-selected': selectedUnitId === unit.id}"
@click="selectUnit(unit)"
>
<text class="unit-name">{{ unit.name }}</text>
<t-icon v-if="selectedUnitId === unit.id" name="check" size="22px" class="check-icon"/>
@@ -191,18 +191,21 @@
</template>
<script setup>
import { ref, reactive, computed, onMounted, watch, nextTick, getCurrentInstance } from 'vue';
import { Toast } from 'tdesign-mobile-vue';
import { useRequest } from "@/utils/request";
import {ref, reactive, computed, onMounted, watch, nextTick, getCurrentInstance} from 'vue';
import {Dialog, Toast} from 'tdesign-mobile-vue';
import {useRequest} from "@/utils/request";
import {useUserStore} from "@/store";
const userStore = useUserStore();
// 表单数据
const formData = reactive({
name: '',
userName: '',
phone: '',
unitId: '',
unitName: '',
idCard: '',
verificationCode: ''
smsCode: ''
});
// 验证码倒计时
@@ -223,21 +226,10 @@ const filteredUnits = computed(() => {
return unitList.value;
}
return unitList.value.filter(unit =>
unit.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
unit.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
);
});
// 表单验证
const isFormValid = computed(() => {
return formData.name &&
formData.phone &&
formData.phone.length === 11 &&
formData.unitId &&
formData.idCard &&
formData.idCard.length === 18 &&
formData.verificationCode;
});
// 当选择器打开时,预设选中当前已选单位
watch(showUnitPicker, (newVal) => {
if (newVal && formData.unitId) {
@@ -270,7 +262,7 @@ const selectUnit = (unit) => {
// 确认选择
const confirmUnitSelection = () => {
if (!selectedUnitId.value) {
Toast({ message: '请选择单位', theme: 'warning' });
Toast({message: '请选择单位', theme: 'warning'});
return;
}
@@ -285,22 +277,22 @@ const confirmUnitSelection = () => {
// 获取单位列表
const getUnitList = async () => {
try {
const { code, data, msg } = await useRequest().get('/open/common/units');
const {code, data, msg} = await useRequest().get('/open/common/units');
if (code === 0) {
unitList.value = data || [];
} else {
Toast({ message: msg || '获取单位列表失败', theme: 'error' });
Toast({message: msg || '获取单位列表失败', theme: 'error'});
}
} catch (error) {
console.error('获取单位列表失败', error);
Toast({ message: '获取单位列表失败', theme: 'error' });
Toast({message: '获取单位列表失败', theme: 'error'});
}
};
// 发送验证码
const sendVerificationCode = async () => {
if (!formData.phone || formData.phone.length !== 11) {
Toast({ message: '请输入正确的手机号', theme: 'error' });
Toast({message: '请输入正确的手机号', theme: 'error'});
return;
}
@@ -308,20 +300,17 @@ const sendVerificationCode = async () => {
sendingCode.value = true;
try {
const { code, msg } = await useRequest().post('/client/auth/send-verification-code', {
phone: formData.phone,
type: 'register'
});
const {code, msg} = await useRequest().post('/client/auth//send-reg-code/' + formData.phone);
if (code === 0) {
Toast({ message: '验证码已发送' });
Toast({message: '验证码已发送'});
startCountdown();
} else {
Toast({ message: msg || '发送验证码失败', theme: 'error' });
Toast({message: msg || '发送验证码失败', theme: 'error'});
}
} catch (error) {
console.error('发送验证码失败', error);
Toast({ message: '发送验证码失败', theme: 'error' });
Toast({message: '发送验证码失败', theme: 'error'});
} finally {
sendingCode.value = false;
}
@@ -340,54 +329,67 @@ const startCountdown = () => {
// 注册
const handleRegister = async () => {
if (!isFormValid.value) {
Toast({ message: '请完善注册信息', theme: 'error' });
return;
if (!formData.userName) {
Toast('请输入姓名');
return
}
if (!formData.phone) {
Toast('请输入手机号码');
return
}
if (!formData.phone.match(/^1[3456789]\d{9}$/)) {
Toast('请输入正确的手机号码');
return
}
if (!formData.idCard) {
Toast('请输入身份证号');
return
}
if (!formData.unitId) {
Toast('请选择单位');
return
}
if (!formData.idCard.match(/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/)) {
Toast('请输入正确的身份证号');
return
}
if (!formData.smsCode) {
Toast('请输入验证码');
return
}
registering.value = true;
try {
const { code, msg } = await useRequest().post('/client/auth/register', {
name: formData.name,
phone: formData.phone,
unitId: formData.unitId,
idCard: formData.idCard,
verificationCode: formData.verificationCode
});
const {code, msg, data} = await useRequest().post('/client/auth/register', formData);
if (code === 0) {
Toast({ message: '注册成功' });
// 注册成功后跳转到登录页
setTimeout(() => {
uni.navigateTo({
url: '/pages/login/index'
});
}, 1500);
Dialog.confirm({
title: '注册成功',
content: '是否立即进入系统?',
confirmBtn: '确定',
cancelBtn: '取消',
onConfirm: () => {
userStore.setToken(data)
userStore.getUserInfo().then(() => {
uni.switchTab({
url: '/pages/index/index'
})
}).catch(e => {
Toast({message: e.message, theme: 'error'})
})
}
})
} else {
Toast({ message: msg || '注册失败', theme: 'error' });
Toast({message: msg || '注册失败', theme: 'error'});
}
} catch (error) {
console.error('注册失败', error);
Toast({ message: '注册失败', theme: 'error' });
Toast({message: '注册失败', theme: 'error'});
} finally {
registering.value = false;
}
};
// 显示协议
const showAgreement = () => {
uni.navigateTo({
url: '/pages/agreement/index'
});
};
// 显示隐私政策
const showPrivacy = () => {
uni.navigateTo({
url: '/pages/privacy/index'
});
};
// 跳转登录页面
const goToLogin = () => {
uni.navigateTo({