Files
opc-web/src/views/RegisterView.vue
xujl 8e18e77747 开发了多角色登录功能:实现了普通用户、企业用户和管理员可以分别通过不同入口登录系统,并且支持用账号、邮箱或手机号登录。
开发了权限分配功能:实现了一个可以在后台勾选页面的功能,通过给角色勾选菜单,就能直接控制不同身份的人登录后能看到哪些页面。
开发了实名认证功能:实现了企业可以提交营业执照认证,个人可以提交身份证件和技能认证的功能,管理员在后台可以进行审核。
开发了任务大厅功能:实现了企业可以发布需要做的任务,个人用户能在任务大厅里看到这些任务,并且可以点击申请接单,大家都能看到任务是“进行中”还是“已完成”状态。
开发了专家库与邀约功能:实现了企业可以去专家库里搜索合适的人才,并且可以直接给他们发送工作邀约。
开发了平台数据大屏展示功能:实现了在首页和各自的工作台页面,展示任务数量、收益金额等核心数据的概览面板。
2026-04-28 16:22:50 +08:00

173 lines
6.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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

<script setup lang="ts">
import { ref } from 'vue';
import { Rocket, Plus } from 'lucide-vue-next';
import { useRouter } from 'vue-router';
import { useAuthStore } from '@/stores/auth';
import { ElMessage } from 'element-plus';
import { uploadFileToMinIO } from '@/api';
const router = useRouter();
const authStore = useAuthStore();
const errorMsg = ref('');
const isLoading = ref(false);
const form = ref({
username: '', phone: '', email: '',
nickname: '', password: '', confirmPassword: '',
avatar_url: ''
});
const handleAvatarUpload = async (options: any) => {
try {
const url = await uploadFileToMinIO(options.file, 'avatars');
form.value.avatar_url = url;
ElMessage.success('头像上传成功');
} catch (error) {
ElMessage.error('头像上传失败,请重试');
}
};
const handleRegister = async () => {
if (!form.value.username.trim()) { errorMsg.value = '用户名不能为空'; return; }
if (form.value.password.length < 6) { errorMsg.value = '密码长度至少为 6 位'; return; }
if (form.value.password !== form.value.confirmPassword) { errorMsg.value = '两次输入的密码不一致'; return; }
if (form.value.phone && !/^1[3-9]\d{9}$/.test(form.value.phone)) { errorMsg.value = '请输入有效的11位手机号'; return; }
if (form.value.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.value.email)) { errorMsg.value = '请输入有效的邮箱地址'; return; }
try {
isLoading.value = true;
errorMsg.value = '';
await authStore.register({
username: form.value.username,
phone: form.value.phone || undefined,
email: form.value.email || undefined,
password: form.value.password,
nickname: form.value.nickname || form.value.username,
avatar_url: form.value.avatar_url || undefined
});
await authStore.login({ username: form.value.username, password: form.value.password });
router.push('/user/certification/apply');
} catch (e: any) {
if (e.response?.data) {
const data = e.response.data;
if (typeof data === 'object') {
const errors = Object.entries(data).map(([key, val]) => {
const fn: any = { username: '用户名', phone: '手机号', email: '邮箱', password: '密码', nickname: '昵称' }[key] || key;
return `${fn}: ${Array.isArray(val) ? val[0] : val}`;
});
errorMsg.value = errors.join('; ');
} else {
errorMsg.value = data.detail || '注册失败';
}
} else {
errorMsg.value = '网络错误,请检查后端服务是否运行';
}
} finally {
isLoading.value = false;
}
};
</script>
<template>
<div class="min-h-screen bg-gray-50 flex items-center justify-center px-4 py-12">
<div class="max-w-lg w-full">
<el-card shadow="always" class="!rounded-2xl !p-2">
<div class="text-center mb-6 pt-4">
<div class="mx-auto h-14 w-14 bg-gray-900 rounded-2xl flex items-center justify-center mb-4">
<Rocket class="w-7 h-7 text-blue-400" />
</div>
<h2 class="text-2xl font-bold text-gray-800">加入 <span class="text-blue-600">CorpScale</span></h2>
<p class="text-sm text-gray-400 mt-1">开启您的一人公司智能之旅</p>
</div>
<el-form @submit.prevent="handleRegister" label-position="top">
<el-alert v-if="errorMsg" type="error" :title="errorMsg" :closable="false" class="mb-4" />
<!-- Avatar Upload -->
<div class="flex justify-center mb-6">
<el-upload
action=""
class="avatar-uploader"
:show-file-list="false"
:http-request="handleAvatarUpload"
accept="image/*"
>
<img v-if="form.avatar_url" :src="form.avatar_url" class="w-full h-full object-cover" />
<div v-else class="text-gray-400 flex flex-col items-center">
<Plus class="w-6 h-6 mb-1" />
<span class="text-[10px]">上传头像</span>
</div>
</el-upload>
</div>
<el-form-item label="用户名" required>
<el-input v-model="form.username" placeholder="请输入用户名" size="large" />
</el-form-item>
<el-row :gutter="12">
<el-col :span="12">
<el-form-item label="手机号(可选)">
<el-input v-model="form.phone" placeholder="11位手机号" size="large" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱(可选)">
<el-input v-model="form.email" type="email" placeholder="email@example.com" size="large" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="显示昵称">
<el-input v-model="form.nickname" placeholder="显示昵称(不填则使用用户名)" size="large" />
</el-form-item>
<el-row :gutter="12">
<el-col :span="12">
<el-form-item label="设置密码" required>
<el-input v-model="form.password" type="password" placeholder="至少6位" size="large" show-password />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="确认密码" required>
<el-input v-model="form.confirmPassword" type="password" placeholder="再次输入" size="large" show-password />
</el-form-item>
</el-col>
</el-row>
<el-button type="primary" native-type="submit" :loading="isLoading" class="w-full" size="large">
{{ isLoading ? '正在处理...' : '立即注册账户' }}
</el-button>
<el-divider>
<span class="text-xs text-gray-400">已有账户</span>
</el-divider>
<el-button class="w-full" @click="router.push('/login')">去登录</el-button>
</el-form>
</el-card>
</div>
</div>
</template>
<style scoped>
.avatar-uploader {
display: flex;
justify-content: center;
}
.avatar-uploader :deep(.el-upload) {
width: 96px;
height: 96px;
border-radius: 50%;
border: 2px dashed #e5e7eb;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
cursor: pointer;
transition: border-color 0.3s;
}
.avatar-uploader :deep(.el-upload:hover) {
border-color: #60a5fa;
}
</style>