|
|
@@ -0,0 +1,570 @@
|
|
|
+<template>
|
|
|
+ <view class="container">
|
|
|
+ <view class="bg-shape shadow"></view>
|
|
|
+ <view class="bg-shape2 shadow"></view>
|
|
|
+
|
|
|
+ <view class="register-box">
|
|
|
+ <view class="header">
|
|
|
+ <text class="title">账号注册</text>
|
|
|
+ <text class="subtitle">欢迎开启您的科研工作之旅</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="step-nav">
|
|
|
+ <uv-steps :current="active" activeColor="#3b82f6" inactiveColor="#94a3b8" customStyle="padding: 0 40rpx;">
|
|
|
+ <uv-steps-item title="账号设置"></uv-steps-item>
|
|
|
+ <uv-steps-item title="基本信息"></uv-steps-item>
|
|
|
+ </uv-steps>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <scroll-view scroll-y class="form-scroll-view" :show-scrollbar="false">
|
|
|
+ <view class="form-content">
|
|
|
+ <!-- Step 0: 登录信息 -->
|
|
|
+ <view v-if="active === 0" class="step-wrapper">
|
|
|
+ <view class="form-group">
|
|
|
+ <text class="label">登录账号<text class="required">*</text></text>
|
|
|
+ <uv-input v-model="form.userName" placeholder="请输入登录账号" border="none" @blur="checkUsername" clearable
|
|
|
+ customStyle="background: #f1f5f9; padding: 24rpx 30rpx; border-radius: 20rpx;">
|
|
|
+ <template #prefix>
|
|
|
+ <uv-icon name="account" size="20" color="#94a3b8" customStyle="margin-right: 16rpx"></uv-icon>
|
|
|
+ </template>
|
|
|
+ </uv-input>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-group">
|
|
|
+ <text class="label">密码<text class="required">*</text></text>
|
|
|
+ <uv-input v-model="form.password" :type="showPassword ? 'text' : 'password'" clearable
|
|
|
+ placeholder="请输入密码" border="none" @blur="checkPasswordValidate"
|
|
|
+ customStyle="background: #f1f5f9; padding: 24rpx 30rpx; border-radius: 20rpx;">
|
|
|
+ <template #prefix>
|
|
|
+ <uv-icon name="lock" size="20" color="#94a3b8" customStyle="margin-right: 16rpx"></uv-icon>
|
|
|
+ </template>
|
|
|
+ <template #suffix>
|
|
|
+ <uv-icon :name="showPassword ? 'eye-fill' : 'eye-off'" size="20" color="#94a3b8"
|
|
|
+ @click="showPassword = !showPassword" customStyle="margin-left: 16rpx"></uv-icon>
|
|
|
+ </template>
|
|
|
+ </uv-input>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-group">
|
|
|
+ <text class="label">确认密码<text class="required">*</text></text>
|
|
|
+ <uv-input v-model="form.confirmPassword" :type="showConfirmPassword ? 'text' : 'password'" clearable
|
|
|
+ placeholder="请再次填写密码" border="none"
|
|
|
+ customStyle="background: #f1f5f9; padding: 24rpx 30rpx; border-radius: 20rpx;">
|
|
|
+ <template #prefix>
|
|
|
+ <uv-icon name="lock-fill" size="20" color="#94a3b8" customStyle="margin-right: 16rpx"></uv-icon>
|
|
|
+ </template>
|
|
|
+ <template #suffix>
|
|
|
+ <uv-icon :name="showConfirmPassword ? 'eye-fill' : 'eye-off'" size="20" color="#94a3b8"
|
|
|
+ @click="showConfirmPassword = !showConfirmPassword" customStyle="margin-left: 16rpx"></uv-icon>
|
|
|
+ </template>
|
|
|
+ </uv-input>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="password-hint">
|
|
|
+ <text>提示:密码需包含大小写字母、数字和特殊字符,长度在10-20位</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- Step 1: 个人信息 -->
|
|
|
+ <view v-if="active === 1" class="step-wrapper">
|
|
|
+ <view class="form-group">
|
|
|
+ <text class="label">姓名<text class="required">*</text></text>
|
|
|
+ <uv-input v-model="form.nickName" placeholder="请输入您的姓名" border="none" clearable
|
|
|
+ customStyle="background: #f1f5f9; padding: 24rpx 30rpx; border-radius: 20rpx;">
|
|
|
+ <template #prefix>
|
|
|
+ <uv-icon name="account" size="20" color="#94a3b8" customStyle="margin-right: 16rpx"></uv-icon>
|
|
|
+ </template>
|
|
|
+ </uv-input>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-group">
|
|
|
+ <text class="label">性别<text class="required">*</text></text>
|
|
|
+ <view class="radio-box">
|
|
|
+ <uv-radio-group v-model="form.sex" placement="row">
|
|
|
+ <uv-radio v-for="(item, index) in sexOptions" :key="index" :name="item.dictValue"
|
|
|
+ :label="item.dictLabel" customStyle="margin-right: 40rpx;"></uv-radio>
|
|
|
+ </uv-radio-group>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-group">
|
|
|
+ <text class="label">组织部门<text class="required">*</text></text>
|
|
|
+ <view class="selector-box" @click="handleOpenDept">
|
|
|
+ <uv-icon name="tags" size="20" color="#94a3b8" customStyle="margin-right: 16rpx"></uv-icon>
|
|
|
+ <text v-if="form.deptName" class="val">{{ form.deptName }}</text>
|
|
|
+ <text v-else class="placeholder">请选择所在部门</text>
|
|
|
+ <uv-icon name="arrow-right" size="14" color="#94a3b8" customStyle="margin-left: auto"></uv-icon>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-group" v-if="form.deptId === 999999">
|
|
|
+ <text class="label">单位名称<text class="required">*</text></text>
|
|
|
+ <uv-input v-model="form.unitName" placeholder="请输入单位名称" border="none"
|
|
|
+ customStyle="background: #f1f5f9; padding: 24rpx 30rpx; border-radius: 20rpx;">
|
|
|
+ <template #prefix>
|
|
|
+ <uv-icon name="home" size="20" color="#94a3b8" customStyle="margin-right: 16rpx"></uv-icon>
|
|
|
+ </template>
|
|
|
+ </uv-input>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-group">
|
|
|
+ <text class="label">手机号码<text class="required">*</text></text>
|
|
|
+ <uv-input v-model="form.phone" type="number" maxlength="11" placeholder="用于接收重要通知" border="none" clearable
|
|
|
+ @blur="checkPhone" customStyle="background: #f1f5f9; padding: 24rpx 30rpx; border-radius: 20rpx;">
|
|
|
+ <template #prefix>
|
|
|
+ <uv-icon name="phone" size="20" color="#94a3b8" customStyle="margin-right: 16rpx"></uv-icon>
|
|
|
+ </template>
|
|
|
+ </uv-input>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-group">
|
|
|
+ <text class="label">电子邮箱<text class="required">*</text></text>
|
|
|
+ <uv-input v-model="form.email" placeholder="请输入常用的邮箱" border="none" clearable
|
|
|
+ customStyle="background: #f1f5f9; padding: 24rpx 30rpx; border-radius: 20rpx;">
|
|
|
+ <template #prefix>
|
|
|
+ <uv-icon name="email" size="20" color="#94a3b8" customStyle="margin-right: 16rpx"></uv-icon>
|
|
|
+ </template>
|
|
|
+ </uv-input>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-group">
|
|
|
+ <text class="label">证件号码<text class="required">*</text></text>
|
|
|
+ <view class="id-group">
|
|
|
+ <view class="id-type">{{ idTypeLabel }}</view>
|
|
|
+ <uv-input v-model="form.idCode" placeholder="请输入证件号" border="none" clearable
|
|
|
+ customStyle="flex: 1; background: #f1f5f9; padding: 24rpx 30rpx; border-radius: 20rpx;">
|
|
|
+ </uv-input>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+
|
|
|
+ <view class="footer-actions">
|
|
|
+ <view class="btn-group">
|
|
|
+ <view class="btn-side" v-if="active > 0">
|
|
|
+ <uv-button text="上一步" @click="preStep" plain color="#64748b"
|
|
|
+ customStyle="height: 100rpx; border-radius: 50rpx; border-color: #e2e8f0; font-weight: 600;"></uv-button>
|
|
|
+ </view>
|
|
|
+ <view class="btn-main">
|
|
|
+ <uv-button v-if="active === 0" text="下一步" @click="nextStep" shape="circle"
|
|
|
+ color="linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)"
|
|
|
+ customStyle="height: 100rpx; font-size: 30rpx; font-weight: 600; box-shadow: 0 10rpx 20rpx rgba(37, 99, 235, 0.2); border: none;"></uv-button>
|
|
|
+ <uv-button v-if="active === 1" text="立即提交注册" @click="onRegister" :loading="loading" shape="circle"
|
|
|
+ color="linear-gradient(135deg, #059669 0%, #10b981 100%)"
|
|
|
+ customStyle="height: 100rpx; font-size: 30rpx; font-weight: 600; box-shadow: 0 10rpx 20rpx rgba(16, 185, 129, 0.2); border: none;"></uv-button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <uv-toast ref="toastRef"></uv-toast>
|
|
|
+
|
|
|
+ <!-- 部门选择组件 -->
|
|
|
+ <SelectDept ref="selectDeptRef" :treeData="deptTree" v-model="form.deptId" @select="onDeptSelect" />
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, reactive, computed } from 'vue';
|
|
|
+import { onLoad } from '@dcloudio/uni-app';
|
|
|
+import { useSystemApi, useDeptApi } from '@/api/system/index';
|
|
|
+import { useLoginApi } from '@/api/system/login';
|
|
|
+import { encryptWithBackendConfig } from '@/utils/aesCrypto';
|
|
|
+import smCrypto from 'sm-crypto';
|
|
|
+import SelectDept from '@/components/SelectDept/index.vue';
|
|
|
+
|
|
|
+const sm3 = smCrypto.sm3;
|
|
|
+const systemApi = useSystemApi();
|
|
|
+const deptApi = useDeptApi();
|
|
|
+const loginApi = useLoginApi();
|
|
|
+
|
|
|
+const active = ref(0);
|
|
|
+const loading = ref(false);
|
|
|
+const toastRef = ref();
|
|
|
+
|
|
|
+const selectDeptRef = ref();
|
|
|
+
|
|
|
+const showPassword = ref(false);
|
|
|
+const showConfirmPassword = ref(false);
|
|
|
+
|
|
|
+const deptTree = ref<any[]>([]);
|
|
|
+const sexOptions = ref<any[]>([]);
|
|
|
+const userCertList = ref<any[]>([]);
|
|
|
+
|
|
|
+const form = reactive({
|
|
|
+ userName: '',
|
|
|
+ password: '',
|
|
|
+ confirmPassword: '',
|
|
|
+ nickName: '',
|
|
|
+ userType: '10',
|
|
|
+ deptId: undefined as number | undefined,
|
|
|
+ deptName: '',
|
|
|
+ phone: '',
|
|
|
+ email: '',
|
|
|
+ sex: '30',
|
|
|
+ idType: '20', // 默认身份证
|
|
|
+ idCode: '',
|
|
|
+ registerType: '10',
|
|
|
+ saltValue: '',
|
|
|
+ status: '10',
|
|
|
+ unitName: '',
|
|
|
+});
|
|
|
+
|
|
|
+const idTypeLabel = computed(() => {
|
|
|
+ if (userCertList.value.length > 0) {
|
|
|
+ const cert = userCertList.value.find(item => item.dictValue === form.idType);
|
|
|
+ if (cert) return cert.dictLabel;
|
|
|
+ }
|
|
|
+ return form.idType === '20' ? '身份证' : '其他证件';
|
|
|
+});
|
|
|
+
|
|
|
+const searchTree = (tree: any[], id: number): any => {
|
|
|
+ for (const node of tree) {
|
|
|
+ if (node.id === id) return node;
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
+ const found = searchTree(node.children, id);
|
|
|
+ if (found) return found;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+};
|
|
|
+
|
|
|
+const initData = async () => {
|
|
|
+ try {
|
|
|
+ const results = await Promise.allSettled([
|
|
|
+ systemApi.getDictDataByType('sys_com_sex'),
|
|
|
+ deptApi.getDeptTree({}),
|
|
|
+ systemApi.getDictDataByType('sys_user_certificate')
|
|
|
+ ]);
|
|
|
+
|
|
|
+ const dictRes = results[0].status === 'fulfilled' ? results[0].value : null;
|
|
|
+ const deptRes = results[1].status === 'fulfilled' ? results[1].value : null;
|
|
|
+ const certRes = results[2].status === 'fulfilled' ? results[2].value : null;
|
|
|
+
|
|
|
+ if (dictRes && dictRes.code === 200 && dictRes.data) {
|
|
|
+ sexOptions.value = dictRes.data.values || [];
|
|
|
+ }
|
|
|
+ if (certRes && certRes.code === 200 && certRes.data) {
|
|
|
+ userCertList.value = certRes.data.values || [];
|
|
|
+ }
|
|
|
+ if (deptRes && deptRes.code === 200 && deptRes.data) {
|
|
|
+ let tree = deptRes.data;
|
|
|
+ if (form.userType === '10' && tree.length > 0) {
|
|
|
+ const root = searchTree(tree, 100001);
|
|
|
+ if (root) tree = [root];
|
|
|
+ }
|
|
|
+ deptTree.value = tree;
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ console.error('Init data fail:', err);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const handleOpenDept = () => {
|
|
|
+ if (deptTree.value.length === 0) {
|
|
|
+ uni.showLoading({ title: '加载中...' });
|
|
|
+ initData().then(() => {
|
|
|
+ uni.hideLoading();
|
|
|
+ if (deptTree.value.length > 0) {
|
|
|
+ selectDeptRef.value.open();
|
|
|
+ } else {
|
|
|
+ uni.showModal({ title: '提示', content: '未能获取到部门数据,请稍后重试', showCancel: false });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ selectDeptRef.value.open();
|
|
|
+};
|
|
|
+
|
|
|
+const onDeptSelect = (item: any) => {
|
|
|
+ form.deptName = item.deptName;
|
|
|
+};
|
|
|
+
|
|
|
+const checkUsername = async () => {
|
|
|
+ if (!form.userName) return;
|
|
|
+ try {
|
|
|
+ await loginApi.checkUserNamePhoneExists({ userName: form.userName, phone: '' });
|
|
|
+ } catch (err) { }
|
|
|
+};
|
|
|
+
|
|
|
+const checkPhone = async () => {
|
|
|
+ if (!form.phone || form.phone.length !== 11) return;
|
|
|
+ try {
|
|
|
+ await loginApi.checkUserNamePhoneExists({ userName: '', phone: form.phone });
|
|
|
+ } catch (err) { }
|
|
|
+};
|
|
|
+
|
|
|
+const checkPasswordValidate = async () => {
|
|
|
+ if (!form.password) return;
|
|
|
+ try {
|
|
|
+ await loginApi.validatePassword({ password: form.password });
|
|
|
+ } catch (err) { }
|
|
|
+};
|
|
|
+
|
|
|
+const validateStep0 = () => {
|
|
|
+ if (!form.userName) {
|
|
|
+ toastRef.value.show({ message: '请输入账户名称', type: 'error' });
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!form.password || form.password.length < 10 || form.password.length > 20) {
|
|
|
+ toastRef.value.show({ message: '密码长度在10-20位之间', type: 'error' });
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (form.password !== form.confirmPassword) {
|
|
|
+ toastRef.value.show({ message: '两次密码输入不一致', type: 'error' });
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+};
|
|
|
+
|
|
|
+const validateStep1 = () => {
|
|
|
+ if (!form.nickName) {
|
|
|
+ toastRef.value.show({ message: '请输入姓名', type: 'error' });
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!form.deptId) {
|
|
|
+ toastRef.value.show({ message: '请选择组织部门', type: 'error' });
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!form.phone || form.phone.length !== 11) {
|
|
|
+ toastRef.value.show({ message: '请输入正确的手机号', type: 'error' });
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!form.idCode) {
|
|
|
+ toastRef.value.show({ message: '请输入证件号', type: 'error' });
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+};
|
|
|
+
|
|
|
+const preStep = () => {
|
|
|
+ active.value--;
|
|
|
+};
|
|
|
+
|
|
|
+const nextStep = () => {
|
|
|
+ if (validateStep0()) {
|
|
|
+ active.value++;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const onRegister = async () => {
|
|
|
+ if (validateStep1()) {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ const params = JSON.parse(JSON.stringify(form));
|
|
|
+ params.password = sm3(params.password);
|
|
|
+ params.saltValue = encryptWithBackendConfig(params.password);
|
|
|
+ delete params.confirmPassword;
|
|
|
+
|
|
|
+ const res: any = await loginApi.register(params);
|
|
|
+ if (res.code === 200) {
|
|
|
+ toastRef.value.show({ message: '注册成功,请等待管理员审核', type: 'success' });
|
|
|
+ setTimeout(() => {
|
|
|
+ uni.reLaunch({ url: '/pages/login/register' });
|
|
|
+ }, 3000);
|
|
|
+ } else {
|
|
|
+ toastRef.value.show({ message: res.msg || '注册失败', type: 'error' });
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ console.error(err);
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const goBack = () => {
|
|
|
+ uni.navigateBack();
|
|
|
+};
|
|
|
+
|
|
|
+onLoad(() => {
|
|
|
+ initData();
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.container {
|
|
|
+ min-height: 100vh;
|
|
|
+ position: relative;
|
|
|
+ background: #f8fafc;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ padding: 40rpx 30rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.bg-shape {
|
|
|
+ position: absolute;
|
|
|
+ top: -100rpx;
|
|
|
+ right: -100rpx;
|
|
|
+ width: 500rpx;
|
|
|
+ height: 500rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: linear-gradient(135deg, #3b82f6 0%, #2dd4bf 100%);
|
|
|
+ opacity: 0.15;
|
|
|
+ filter: blur(40rpx);
|
|
|
+}
|
|
|
+
|
|
|
+.bg-shape2 {
|
|
|
+ position: absolute;
|
|
|
+ bottom: -150rpx;
|
|
|
+ left: -100rpx;
|
|
|
+ width: 600rpx;
|
|
|
+ height: 600rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%);
|
|
|
+ opacity: 0.15;
|
|
|
+ filter: blur(50rpx);
|
|
|
+}
|
|
|
+
|
|
|
+.register-box {
|
|
|
+ width: 100%;
|
|
|
+ height: 92vh;
|
|
|
+ background: rgba(255, 255, 255, 0.9);
|
|
|
+ backdrop-filter: blur(20px);
|
|
|
+ border-radius: 40rpx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ position: relative;
|
|
|
+ z-index: 10;
|
|
|
+ border: 2rpx solid rgba(255, 255, 255, 0.5);
|
|
|
+ box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.05);
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.header {
|
|
|
+ padding: 50rpx 40rpx 30rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.title {
|
|
|
+ font-size: 48rpx;
|
|
|
+ font-weight: 800;
|
|
|
+ color: #1e293b;
|
|
|
+ display: block;
|
|
|
+ margin-bottom: 8rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.subtitle {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #64748b;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+.step-nav {
|
|
|
+ padding-bottom: 30rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.form-scroll-view {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ padding: 0 40rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.form-content {
|
|
|
+ padding-bottom: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.form-group {
|
|
|
+ margin-bottom: 30rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.label {
|
|
|
+ font-size: 26rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #334155;
|
|
|
+ margin-bottom: 12rpx;
|
|
|
+ display: block;
|
|
|
+ padding-left: 8rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.required {
|
|
|
+ color: #ef4444;
|
|
|
+ margin-left: 6rpx;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+.password-hint {
|
|
|
+ margin-top: 10rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 20rpx;
|
|
|
+ color: #ef4444;
|
|
|
+ padding-left: 10rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.radio-box {
|
|
|
+ background: #f1f5f9;
|
|
|
+ padding: 24rpx 30rpx;
|
|
|
+ border-radius: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.selector-box {
|
|
|
+ background: #f1f5f9;
|
|
|
+ padding: 24rpx 30rpx;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.selector-box .placeholder {
|
|
|
+ color: #94a3b8;
|
|
|
+ font-size: 28rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.selector-box .val {
|
|
|
+ color: #1e293b;
|
|
|
+ font-size: 28rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.id-group {
|
|
|
+ display: flex;
|
|
|
+ gap: 16rpx;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.id-type {
|
|
|
+ width: 220rpx;
|
|
|
+ height: 88rpx;
|
|
|
+ line-height: 88rpx;
|
|
|
+ background: #e2e8f0;
|
|
|
+ color: #475569;
|
|
|
+ font-size: 24rpx;
|
|
|
+ text-align: center;
|
|
|
+ border-radius: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.footer-actions {
|
|
|
+ padding: 30rpx 40rpx 40rpx;
|
|
|
+ background: rgba(255, 255, 255, 0.5);
|
|
|
+ border-top: 2rpx solid rgba(241, 245, 249, 0.5);
|
|
|
+ border-radius: 0 0 40rpx 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-group {
|
|
|
+ display: flex;
|
|
|
+ gap: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-side {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-main {
|
|
|
+ flex: 2;
|
|
|
+}
|
|
|
+
|
|
|
+.back-link {
|
|
|
+ margin-top: 30rpx;
|
|
|
+ text-align: center;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #3b82f6;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+</style>
|