u
This commit is contained in:
1 parent
1da52dc4f4
commit
276644f473
223 files changed
+9609
-262
No files matched your search
@@ -1,8 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { onMounted } from 'vue'
|
||||
import { getAreaListApi } from './request/api/area'
|
||||
import { getPropertyListApi } from './request/api/industry'
|
||||
import { useCommonStore } from './store/common'
|
||||
import { initBaseApi } from '@/request/api/base'
|
||||
import { useCommonStore } from '@/store/common'
|
||||
|
||||
const commonOptions = useCommonStore()
|
||||
|
||||
@@ -11,14 +11,18 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
function handleInitData() {
|
||||
getAreaListApi().then((res) => {
|
||||
commonOptions.areaOptions.splice(0, commonOptions.areaOptions.length, ...res.data || [])
|
||||
})
|
||||
|
||||
getPropertyListApi().then((res) => {
|
||||
commonOptions.industryOptions.splice(0, commonOptions.industryOptions.length, ...res.data || [])
|
||||
initBaseApi().then((res) => {
|
||||
console.log(res)
|
||||
if (res.isSuccess) {
|
||||
commonOptions.areaOptions.splice(0, commonOptions.areaOptions.length, ...res.data.areas || [])
|
||||
commonOptions.industryOptions.splice(0, commonOptions.industryOptions.length, ...res.data.industries || [])
|
||||
}
|
||||
else {
|
||||
ElMessage.error(res.message || '初始化失败,请刷新页面后重试')
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
ElMessage.error('初始化失败,请刷新页面后重试')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCommonStore } from '@/store/common'
|
||||
|
||||
const props = defineProps({
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
checkStrictly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const commonStore = useCommonStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-cascader
|
||||
placeholder="请选择地域"
|
||||
:options="commonStore.areaOptions"
|
||||
filterable
|
||||
:props="{
|
||||
label: 'name',
|
||||
value: 'name',
|
||||
children: 'children',
|
||||
multiple: props.multiple,
|
||||
checkStrictly: props.checkStrictly,
|
||||
}"
|
||||
class="area-select-cascader"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.area-select-cascader {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCommonStore } from '@/store/common'
|
||||
|
||||
const commonStore = useCommonStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-select
|
||||
filterable
|
||||
placeholder="请选择行业"
|
||||
value-key="name"
|
||||
multiple
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
:max-collapse-tags="2"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in commonStore.industryOptions"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//
|
||||
</style>
|
||||
@@ -1,7 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue'
|
||||
import type { Area, CompanyQuery, Industry } from '@/types/company/index'
|
||||
import type { Area, CompanyQuery, Industry } from '@/types/base'
|
||||
import { computed } from 'vue'
|
||||
import AreaSelect from './AreaSelect.vue'
|
||||
import IndustrySelect from './IndustrySelect.vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -31,35 +33,16 @@ const query = computed({
|
||||
<template>
|
||||
<div class="query">
|
||||
<div class="query-item">
|
||||
<el-select
|
||||
v-model="query.industries"
|
||||
filterable
|
||||
placeholder="请选择行业"
|
||||
multiple
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
:max-collapse-tags="2"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in props.industries"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<IndustrySelect v-model="query.industries" />
|
||||
</div>
|
||||
<div class="query-item">
|
||||
<el-tree-select
|
||||
<AreaSelect
|
||||
v-model="query.areas"
|
||||
placeholder="请选择地域"
|
||||
:data="areas"
|
||||
filterable
|
||||
multiple
|
||||
check-strictly
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
:max-collapse-tags="2"
|
||||
:render-after-expand="false"
|
||||
/>
|
||||
</div>
|
||||
<div class="query-item">
|
||||
@@ -106,10 +89,20 @@ const query = computed({
|
||||
padding: 0px;
|
||||
}
|
||||
:deep(.el-input__inner) {
|
||||
height: 60px;
|
||||
height: 60px !important;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
:deep(.el-cascader__tags) {
|
||||
padding: 0;
|
||||
}
|
||||
:deep(.el-cascader__search-input) {
|
||||
margin-left: 0px;
|
||||
font-size: 18px;
|
||||
}
|
||||
:deep(.el-input__suffix){
|
||||
padding-right: 12px;
|
||||
}
|
||||
&:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
|
||||
@@ -51,11 +51,13 @@ function exitLogin() {
|
||||
|
||||
<el-dropdown>
|
||||
<div class="user">
|
||||
<el-avatar :size="32" :src="userStore.userInfo.avatar" />
|
||||
<span class="name">
|
||||
{{ userStore.userInfo.name }}
|
||||
</span>
|
||||
<el-avatar :size="36" :src="userStore.userInfo.company.logo ? userStore.userInfo.company.logo : '/no-avatar.png'" />
|
||||
<div class="info">
|
||||
<span class="name">{{ userStore.userInfo.user.name }}</span>
|
||||
<span class="company">{{ userStore.userInfo.company.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
@@ -173,9 +175,22 @@ function exitLogin() {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
.name {
|
||||
margin-left: 8px;
|
||||
font-size: 18px;
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 12px;
|
||||
|
||||
.name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.company {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { AreaResult } from '@/types/company/index'
|
||||
import { httpGet } from '../utils'
|
||||
|
||||
export function getAreaListApi() {
|
||||
return httpGet<AreaResult>(`/webArea/list`)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { forgetApiParams, loginApiParams, loginApiResult, registerApiParams } from '@/types/auth'
|
||||
import { httpPostJson } from '../utils'
|
||||
|
||||
export function loginApi(params: loginApiParams) {
|
||||
return httpPostJson<loginApiResult>(`/auth/login`, params)
|
||||
}
|
||||
export function registerApi(params: registerApiParams) {
|
||||
return httpPostJson<null>(`/auth/register`, params)
|
||||
}
|
||||
export function forgetApi(params: forgetApiParams) {
|
||||
return httpPostJson<null>(`/auth/forget`, params)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { LoginApiResult, LoginEmailCodeApiParams, LoginPasswordApiParams, RegisterApiParams } from '@/types/auth/index'
|
||||
import { httpPostJson } from '@/request/utils'
|
||||
|
||||
export function registerApi(params: RegisterApiParams) {
|
||||
return httpPostJson<void>('/auth/identity/register', params)
|
||||
}
|
||||
|
||||
export function loginByPasswordApi(params: LoginPasswordApiParams) {
|
||||
return httpPostJson<LoginApiResult>('/auth/identity/login/password', params)
|
||||
}
|
||||
|
||||
export function loginByEmailCodeApi(params: LoginEmailCodeApiParams) {
|
||||
return httpPostJson<LoginApiResult>('/auth/identity/login/emailCode', params)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { InitBaseDataResult } from '@/types/base'
|
||||
import { httpGet } from '@/request/utils'
|
||||
|
||||
export function initBaseApi() {
|
||||
return httpGet<InitBaseDataResult>(`/base/init`)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { IndustryResult } from '@/types/company/index'
|
||||
import { httpGet } from '../utils'
|
||||
|
||||
export function getPropertyListApi() {
|
||||
return httpGet<IndustryResult>(`/base/industry/list`)
|
||||
}
|
||||
@@ -57,7 +57,8 @@ axiosInstance.interceptors.request.use(
|
||||
}
|
||||
|
||||
config.headers.Authorization = `Bearer ${userStore.userInfo.token}`
|
||||
config.headers.id = userStore.userInfo.id
|
||||
config.headers['X-Company-Id'] = userStore.userInfo.company.id
|
||||
config.headers['X-User-Id'] = userStore.userInfo.user.id
|
||||
|
||||
return config
|
||||
},
|
||||
@@ -90,11 +91,14 @@ axiosInstance.interceptors.response.use(
|
||||
return Promise.reject(response.data)
|
||||
}
|
||||
else {
|
||||
// 500的话需要做特殊处理
|
||||
resData.isSuccess = resData.code === 200
|
||||
return Promise.resolve(response.data)
|
||||
}
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.log(error)
|
||||
|
||||
if (error.status === 500) {
|
||||
router.push('/error/500')
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Area, CompanyQuery, Industry } from '@/types/company/index'
|
||||
import type { Area, CompanyQuery, Industry } from '@/types/base'
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { reactive, ref } from 'vue'
|
||||
|
||||
@@ -13,10 +14,63 @@ export const useCommonStore = defineStore(
|
||||
name: '',
|
||||
})
|
||||
|
||||
/**
|
||||
* 根据 Area id 获取完整路径,例如 "天津市/滨海新区/塘沽"
|
||||
*/
|
||||
function getAreaFullPathById(id: string): string {
|
||||
// 递归查找节点
|
||||
function findNode(id: string, nodes: Area[]): Area | null {
|
||||
for (const node of nodes) {
|
||||
if (node.id === id)
|
||||
return node
|
||||
if (node.children && node.children.length) {
|
||||
const found = findNode(id, node.children)
|
||||
if (found)
|
||||
return found
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const node = findNode(id, areaOptions)
|
||||
if (!node)
|
||||
return ''
|
||||
|
||||
// 构建完整路径
|
||||
const names: string[] = []
|
||||
let current: Area | undefined | null = node
|
||||
while (current) {
|
||||
names.unshift(current.name)
|
||||
current = current.fatherId ? findNode(current.fatherId, areaOptions) || undefined : undefined
|
||||
}
|
||||
|
||||
return names.join(',')
|
||||
}
|
||||
|
||||
function getAreaIdByFullPath(fullPath: string): string | null {
|
||||
const names = fullPath.split(',').map(s => s.trim())
|
||||
if (!names.length)
|
||||
return null
|
||||
|
||||
let currentNodes = areaOptions
|
||||
let foundNode: Area | null = null
|
||||
|
||||
for (const name of names) {
|
||||
foundNode = currentNodes.find(n => n.name === name) || null
|
||||
if (!foundNode)
|
||||
return null
|
||||
currentNodes = foundNode.children || []
|
||||
}
|
||||
|
||||
return foundNode?.id || null
|
||||
}
|
||||
|
||||
return {
|
||||
areaOptions,
|
||||
industryOptions,
|
||||
companyQuery,
|
||||
getAreaFullPathById,
|
||||
getAreaIdByFullPath,
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -2,19 +2,36 @@ import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
interface IUserInfo {
|
||||
id: string
|
||||
account?: string
|
||||
email?: string
|
||||
name?: string
|
||||
token?: string
|
||||
avatar?: string
|
||||
user: {
|
||||
id: number
|
||||
name: string
|
||||
position: string
|
||||
role: number
|
||||
}
|
||||
company: {
|
||||
id: number
|
||||
logo: string
|
||||
name: string
|
||||
}
|
||||
token: string
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore(
|
||||
'user',
|
||||
() => {
|
||||
const userInfo = ref<IUserInfo>({
|
||||
id: '',
|
||||
user: {
|
||||
id: 0,
|
||||
name: '',
|
||||
position: '',
|
||||
role: 0,
|
||||
},
|
||||
company: {
|
||||
id: 0,
|
||||
logo: '',
|
||||
name: '',
|
||||
},
|
||||
token: '',
|
||||
})
|
||||
|
||||
const setUserInfo = (val: IUserInfo) => {
|
||||
@@ -22,23 +39,25 @@ export const useUserStore = defineStore(
|
||||
}
|
||||
|
||||
const clearUserInfo = () => {
|
||||
userInfo.value.id = ""
|
||||
userInfo.value.account = ""
|
||||
userInfo.value.email = ""
|
||||
userInfo.value.name = ""
|
||||
userInfo.value.token = ""
|
||||
userInfo.value.avatar = ""
|
||||
userInfo.value = {
|
||||
user: {
|
||||
id: 0,
|
||||
name: '',
|
||||
position: '',
|
||||
role: 0,
|
||||
},
|
||||
company: {
|
||||
id: 0,
|
||||
logo: '',
|
||||
name: '',
|
||||
},
|
||||
token: '',
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 如果需要同时验证多个字段
|
||||
// ✅ 判断是否登录
|
||||
const isLogined = computed(() => {
|
||||
return !!(
|
||||
(
|
||||
userInfo.value.token
|
||||
&& userInfo.value.id
|
||||
&& userInfo.value.account
|
||||
)
|
||||
)
|
||||
return !!(userInfo.value.token && userInfo.value.user.id && userInfo.value.company.id)
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,32 +1,30 @@
|
||||
export interface loginApiParams {
|
||||
loginCredential: string
|
||||
export interface LoginPasswordApiParams {
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface loginApiResult {
|
||||
id: string
|
||||
account: string
|
||||
name: string
|
||||
export interface LoginEmailCodeApiParams {
|
||||
email: string
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface LoginApiResult {
|
||||
companyId: number
|
||||
companyLogo: string
|
||||
companyName: string
|
||||
|
||||
userId: number
|
||||
userRole: number
|
||||
userName: string
|
||||
userPosition: string
|
||||
|
||||
token: string
|
||||
avatar: string
|
||||
}
|
||||
|
||||
export interface registerApiParams {
|
||||
account: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface forgetApiParams {
|
||||
forgetCredential: string
|
||||
}
|
||||
|
||||
export interface RegisterCompanyForm {
|
||||
name: string
|
||||
industry: string
|
||||
address: string
|
||||
industry: string[]
|
||||
address: string[]
|
||||
phone: string
|
||||
email: string
|
||||
profile: string
|
||||
@@ -43,3 +41,21 @@ export interface RegisterAdminForm {
|
||||
|
||||
position: string
|
||||
}
|
||||
|
||||
export interface RegisterApiParams {
|
||||
company: {
|
||||
name: string
|
||||
industry: string
|
||||
address: string
|
||||
phone: string
|
||||
email: string
|
||||
profile: string
|
||||
}
|
||||
admin: {
|
||||
name: string
|
||||
phone: string
|
||||
email: string
|
||||
password: string
|
||||
position: string
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,21 @@ export {}
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AreaSelect: typeof import('./../components/AreaSelect.vue')['default']
|
||||
HelloWorld: typeof import('./../components/HelloWorld.vue')['default']
|
||||
ICarbonAccessibility: typeof import('~icons/carbon/accessibility')['default']
|
||||
'IFa7Solid:userShield': typeof import('~icons/fa7-solid/user-shield')['default']
|
||||
'IIc:twotoneHeadphones': typeof import('~icons/ic/twotone-headphones')['default']
|
||||
'IMaterialSymbols:lockOpenRightSharp': typeof import('~icons/material-symbols/lock-open-right-sharp')['default']
|
||||
IMdiAccountBox: typeof import('~icons/mdi/account-box')['default']
|
||||
'IMingcute:eye2Line': typeof import('~icons/mingcute/eye2-line')['default']
|
||||
Industry: typeof import('../components/IndustrySelect.vue')['default']
|
||||
IndustrySelect: typeof import('./../components/IndustrySelect.vue')['default']
|
||||
'INrk:show': typeof import('~icons/nrk/show')['default']
|
||||
'IRi:buildingFill': typeof import('~icons/ri/building-fill')['default']
|
||||
'IRi:buildingLine': typeof import('~icons/ri/building-line')['default']
|
||||
'ISolar:eyeBroken': typeof import('~icons/solar/eye-broken')['default']
|
||||
'ISolar:eyeClosedBroken': typeof import('~icons/solar/eye-closed-broken')['default']
|
||||
'ITdesign:notification': typeof import('~icons/tdesign/notification')['default']
|
||||
'ITdesign:search': typeof import('~icons/tdesign/search')['default']
|
||||
QueryCompany: typeof import('./../components/QueryCompany.vue')['default']
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export interface Area {
|
||||
id: string
|
||||
xh: number
|
||||
name: string
|
||||
fatherId: string
|
||||
children: Area[]
|
||||
}
|
||||
|
||||
export interface Industry {
|
||||
id: number
|
||||
xh: number
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface InitBaseDataResult {
|
||||
industries: Industry[]
|
||||
areas: Area[]
|
||||
}
|
||||
|
||||
export interface CompanyQuery {
|
||||
industries: string[]
|
||||
areas: string[]
|
||||
name: string
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
export interface Area {
|
||||
label: string
|
||||
value: number
|
||||
children?: AreaResult[]
|
||||
}
|
||||
|
||||
export type AreaResult = Area[]
|
||||
|
||||
export interface Industry {
|
||||
label: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export type IndustryResult = Industry[]
|
||||
|
||||
export interface CompanyQuery {
|
||||
industries: string[]
|
||||
areas: string[]
|
||||
name: string
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 校验邮箱格式
|
||||
* @param value 待校验的邮箱字符串
|
||||
* @returns true 格式正确,false 格式错误
|
||||
*/
|
||||
export function validateEmail(value: string): boolean {
|
||||
if (!value)
|
||||
return false // 如果为空,不校验格式,可按需求改成 false
|
||||
const emailReg = /^[\w.%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i
|
||||
return emailReg.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验中国手机号格式(支持 +86 开头)
|
||||
* @param value 待校验的手机号字符串
|
||||
* @returns true 格式正确,false 格式错误
|
||||
*/
|
||||
export function validatePhone(value: string): boolean {
|
||||
if (!value)
|
||||
return false // 如果为空,不校验格式,可按需求改成 false
|
||||
const phoneReg = /^(?:\+?86)?1(?:3\d{3}|5[^4\D]\d{2}|8\d{3}|7(?:[0-35-9]\d{2}|4(?:0\d|1[0-2]|9\d))|9[0-35-9]\d{2}|6[2567]\d{2}|4(?:(?:10|4[01])\d{3}|[68]\d{4}|[579]\d{2}))\d{6}$/
|
||||
return phoneReg.test(value)
|
||||
}
|
||||
@@ -1,14 +1,29 @@
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { loginApi } from '@/request/api/auth'
|
||||
import { loginByEmailCodeApi, loginByPasswordApi } from '@/request/api/auth'
|
||||
import { authCodeSendApi } from '@/request/api/auth/code'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { AuthCodeChannel, AuthCodePurpose } from '@/types/auth/code'
|
||||
import { validateEmail } from '@/utils/validate'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
|
||||
const loginOptions = [
|
||||
{
|
||||
label: '密码登录',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
label: '验证码登录',
|
||||
value: '1',
|
||||
},
|
||||
]
|
||||
const loginMode = ref('0')
|
||||
|
||||
const carouselList = [
|
||||
{
|
||||
title: '值得信赖',
|
||||
@@ -37,59 +52,160 @@ const carouselList = [
|
||||
},
|
||||
]
|
||||
|
||||
// --- 发送验证码状态 & 倒计时 ---
|
||||
const sending = ref(false)
|
||||
const countdown = ref(0)
|
||||
let timer: number | undefined
|
||||
|
||||
function startCountdown(seconds = 60) {
|
||||
countdown.value = seconds
|
||||
timer = window.setInterval(() => {
|
||||
countdown.value -= 1
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer)
|
||||
timer = undefined
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const form = reactive<{
|
||||
loginCredential: string
|
||||
email: string
|
||||
password: string
|
||||
code: string
|
||||
rememberMe: boolean
|
||||
}>({
|
||||
loginCredential: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
rememberMe: true,
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRules = {
|
||||
loginCredential: [
|
||||
email: [
|
||||
{ required: true, message: '邮箱号不能为空', trigger: 'blur' },
|
||||
{ type: 'email', required: true, message: '邮箱格式错误', trigger: 'blur' },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '密码不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (loginMode.value === '0') {
|
||||
if (!value) {
|
||||
callback(new Error('密码不能为空'))
|
||||
return
|
||||
}
|
||||
}
|
||||
callback() // 校验通过
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
code: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (loginMode.value === '1') {
|
||||
if (!value) {
|
||||
callback(new Error('验证码不能为空'))
|
||||
return
|
||||
}
|
||||
}
|
||||
callback() // 校验通过
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const isEmailValid = computed(() => validateEmail(form.email))
|
||||
|
||||
const submitLoading = ref(false)
|
||||
|
||||
function handleSendEmailCode() {
|
||||
if (sending.value || countdown.value > 0 || !isEmailValid.value)
|
||||
return
|
||||
|
||||
sending.value = true
|
||||
|
||||
authCodeSendApi({
|
||||
channel: AuthCodeChannel.EMAIL,
|
||||
purpose: AuthCodePurpose.LOGIN,
|
||||
target: form.email,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
ElMessage.success('发送成功')
|
||||
startCountdown()
|
||||
}
|
||||
else {
|
||||
sending.value = false
|
||||
ElMessage.error(res.message || '发送失败,请检查邮箱是否正确或稍后再试!')
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
|
||||
ElMessage.error('发送失败,请稍后再试')
|
||||
sending.value = false
|
||||
}).finally(() => {
|
||||
sending.value = false
|
||||
})
|
||||
}
|
||||
|
||||
async function submitForm(formEl: FormInstance | undefined) {
|
||||
if (!formEl)
|
||||
return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true
|
||||
loginApi({
|
||||
loginCredential: form.loginCredential,
|
||||
password: form.password,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
userStore.setUserInfo({
|
||||
id: res.data.id,
|
||||
account: res.data.account,
|
||||
email: res.data.email,
|
||||
name: res.data.name,
|
||||
token: res.data.token,
|
||||
avatar: res.data.avatar || '/no-avatar.png',
|
||||
})
|
||||
|
||||
ElMessage.success('登录成功')
|
||||
router.push('/')
|
||||
}
|
||||
else {
|
||||
ElMessage.error(res.message || '登录失败,请稍后再试!')
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
await formEl.validate(async (valid) => {
|
||||
if (!valid)
|
||||
return
|
||||
|
||||
submitLoading.value = true
|
||||
|
||||
try {
|
||||
let res
|
||||
if (loginMode.value === '0') {
|
||||
// 密码登录
|
||||
res = await loginByPasswordApi({
|
||||
email: form.email,
|
||||
password: form.password,
|
||||
})
|
||||
}
|
||||
else {
|
||||
// 验证码登录
|
||||
res = await loginByEmailCodeApi({
|
||||
email: form.email,
|
||||
code: form.code,
|
||||
})
|
||||
}
|
||||
|
||||
if (res.isSuccess) {
|
||||
ElMessage.success('登录成功')
|
||||
|
||||
userStore.setUserInfo({
|
||||
user: {
|
||||
id: res.data.userId,
|
||||
name: res.data.userName,
|
||||
position: res.data.userPosition,
|
||||
role: res.data.userRole,
|
||||
},
|
||||
company: {
|
||||
id: res.data.companyId,
|
||||
logo: res.data.companyLogo,
|
||||
name: res.data.companyName,
|
||||
},
|
||||
token: res.data.token,
|
||||
})
|
||||
|
||||
router.push('/')
|
||||
}
|
||||
else {
|
||||
ElMessage.error(res.message || '登录失败,请稍后再试')
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err)
|
||||
ElMessage.error('登录失败,请稍后再试')
|
||||
}
|
||||
finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -130,8 +246,19 @@ function navigateTo(path: string) {
|
||||
<h4 class="title">
|
||||
登录
|
||||
</h4>
|
||||
<div style="padding: 8px 0px;">
|
||||
<el-segmented
|
||||
v-model="loginMode" :options="loginOptions" size="large"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p class="title-extra">
|
||||
输入你的 邮箱 和 密码 登录账户.
|
||||
<template v-if="loginMode === '0'">
|
||||
输入你的 邮箱 和 密码 登录系统
|
||||
</template>
|
||||
<template v-else-if="loginMode === '1'">
|
||||
输入你的 邮箱 和 验证码 登录系统
|
||||
</template>
|
||||
</p>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
@@ -141,11 +268,14 @@ function navigateTo(path: string) {
|
||||
label-position="top"
|
||||
hide-required-asterisk
|
||||
>
|
||||
<el-form-item label="邮箱" prop="loginCredential">
|
||||
<el-input v-model="form.loginCredential" placeholder="请输入邮箱" size="large" />
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱" size="large" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="password">
|
||||
<el-form-item
|
||||
v-if="loginMode === '0'"
|
||||
prop="password"
|
||||
>
|
||||
<template #label>
|
||||
<span class="form-label-flex">
|
||||
<span>密码</span>
|
||||
@@ -155,11 +285,47 @@ function navigateTo(path: string) {
|
||||
<el-input
|
||||
v-model="form.password"
|
||||
placeholder="请输入密码"
|
||||
show-password
|
||||
size="large"
|
||||
@keyup.enter="submitForm(formRef)"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-else
|
||||
prop="code"
|
||||
>
|
||||
<template #label>
|
||||
<span class="form-label-flex">
|
||||
<span>密码</span>
|
||||
<span class="forgot" @click="navigateTo('/auth/forget')">忘记密码?</span>
|
||||
</span>
|
||||
</template>
|
||||
<el-input
|
||||
v-model="form.code"
|
||||
placeholder="请输入邮箱验证码"
|
||||
class="email-code"
|
||||
size="large"
|
||||
:class="(sending || countdown > 0 || !isEmailValid) ? 'disabled' : ''"
|
||||
>
|
||||
<template #append>
|
||||
<el-button
|
||||
:disabled="sending || countdown > 0 || !isEmailValid"
|
||||
@click="handleSendEmailCode"
|
||||
>
|
||||
<template v-if="sending">
|
||||
发送中...
|
||||
</template>
|
||||
<template v-else-if="countdown > 0">
|
||||
{{ countdown }}s
|
||||
</template>
|
||||
<template v-else>
|
||||
发送验证码
|
||||
</template>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.rememberMe" label="记住我" class="remeber" />
|
||||
</el-form-item>
|
||||
@@ -257,6 +423,20 @@ function navigateTo(path: string) {
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.email-code {
|
||||
:deep(.el-input-group__append) {
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
:deep(.el-input-group__append) {
|
||||
background-color: #f5f7fa;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remeber {
|
||||
:deep(.el-checkbox__inner) {
|
||||
@@ -267,6 +447,7 @@ function navigateTo(path: string) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ElMessage } from 'element-plus'
|
||||
import { computed, onUnmounted, reactive, ref } from 'vue'
|
||||
import { authCodeSendApi, authCodeVerifyApi } from '@/request/api/auth/code'
|
||||
import { AuthCodeChannel, AuthCodePurpose } from '@/types/auth/code'
|
||||
import { validateEmail, validatePhone } from '@/utils/validate'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'prev'): void
|
||||
@@ -12,6 +13,7 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
const submitLoading = ref(false)
|
||||
const isEmailCodeValid = ref(true)
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const form = reactive<RegisterAdminForm>({
|
||||
@@ -53,11 +55,25 @@ const formRules = {
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
phone: [{ validator: phoneValidator, trigger: 'blur' }],
|
||||
}
|
||||
// 校验邮箱
|
||||
const emailRegex = /^\w+(?:[-.]\w+)*@\w+(?:[-.]\w+)*\.\w+(?:[-.]\w+)*$/
|
||||
|
||||
const isEmailValid = computed(() => emailRegex.test(form.email.trim()))
|
||||
function phoneValidator(rule: any, value: any, callback: any) {
|
||||
// 未填写:直接通过
|
||||
if (!value) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
|
||||
if (!validatePhone(value)) {
|
||||
callback(new Error('手机号格式不正确'))
|
||||
}
|
||||
else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
const isEmailValid = computed(() => validateEmail(form.email))
|
||||
|
||||
// --- 发送验证码状态 & 倒计时 ---
|
||||
const sending = ref(false)
|
||||
@@ -118,6 +134,11 @@ async function handleSubmit(formEl: FormInstance | undefined) {
|
||||
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
if (isEmailCodeValid.value) {
|
||||
emit('submit', form)
|
||||
return
|
||||
}
|
||||
|
||||
submitLoading.value = true
|
||||
authCodeVerifyApi({
|
||||
channel: AuthCodeChannel.EMAIL,
|
||||
@@ -127,6 +148,8 @@ async function handleSubmit(formEl: FormInstance | undefined) {
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
emit('submit', form)
|
||||
|
||||
isEmailCodeValid.value = true
|
||||
}
|
||||
else {
|
||||
ElMessage.error(res.message || '验证码无效或已过期,请重新获取')
|
||||
@@ -134,6 +157,8 @@ async function handleSubmit(formEl: FormInstance | undefined) {
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
ElMessage.error('邮箱验证码校验失败,请稍后再试')
|
||||
|
||||
isEmailCodeValid.value = false
|
||||
}).finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
@@ -207,12 +232,12 @@ function handlePrev() {
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="登录密码" prop="password">
|
||||
<el-input v-model="form.password" placeholder="请输入登录密码" />
|
||||
<el-input v-model="form.password" placeholder="请输入登录密码" show-password />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="确认密码" prop="confirmPassword">
|
||||
<el-input v-model="form.confirmPassword" placeholder="请输入确认密码" />
|
||||
<el-input v-model="form.confirmPassword" placeholder="请输入确认密码" show-password />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
|
||||
+32
-10
@@ -1,7 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import type { RegisterCompanyForm } from '@/types/auth'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import AreaSelect from '@/components/AreaSelect.vue'
|
||||
import IndustrySelect from '@/components/IndustrySelect.vue'
|
||||
import { validateEmail, validatePhone } from '@/utils/validate'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'submit', value: RegisterCompanyForm): void
|
||||
@@ -19,6 +22,7 @@ const formRules = {
|
||||
{ required: true, message: '公司地址不能为空', trigger: 'blur' },
|
||||
],
|
||||
email: [{ validator: emailValidator, trigger: 'blur' }],
|
||||
phone: [{ validator: phoneValidator, trigger: 'blur' }],
|
||||
}
|
||||
|
||||
function emailValidator(rule: any, value: any, callback: any) {
|
||||
@@ -28,27 +32,42 @@ function emailValidator(rule: any, value: any, callback: any) {
|
||||
return
|
||||
}
|
||||
|
||||
// 填了就校验格式
|
||||
const emailReg
|
||||
= /^[\w.%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i
|
||||
|
||||
if (!emailReg.test(value)) {
|
||||
if (!validateEmail(value)) {
|
||||
callback(new Error('邮箱格式不正确'))
|
||||
}
|
||||
else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
function phoneValidator(rule: any, value: any, callback: any) {
|
||||
// 未填写:直接通过
|
||||
if (!value) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
|
||||
if (!validatePhone(value)) {
|
||||
callback(new Error('手机号格式不正确'))
|
||||
}
|
||||
else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const form = reactive<RegisterCompanyForm>({
|
||||
name: '',
|
||||
industry: '',
|
||||
address: '',
|
||||
industry: [],
|
||||
address: [],
|
||||
phone: '',
|
||||
email: '',
|
||||
profile: '',
|
||||
})
|
||||
|
||||
watch(() => form.industry, (newVal) => {
|
||||
console.log(newVal)
|
||||
})
|
||||
watch(() => form.address, (newVal) => {
|
||||
console.log(newVal)
|
||||
})
|
||||
async function handleSubmit(formEl: FormInstance | undefined) {
|
||||
if (!formEl)
|
||||
return
|
||||
@@ -83,12 +102,15 @@ async function handleSubmit(formEl: FormInstance | undefined) {
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属行业" prop="industry">
|
||||
<el-input v-model="form.industry" placeholder="请输入所属行业" />
|
||||
<IndustrySelect v-model="form.industry" placeholder="请输入所属行业" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="公司地址" prop="address">
|
||||
<el-input v-model="form.address" placeholder="请输入公司地址" />
|
||||
<AreaSelect
|
||||
v-model="form.address"
|
||||
placeholder="请输入公司地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
|
||||
+131
-17
@@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { PropType } from 'vue'
|
||||
import type { RegisterAdminForm, RegisterCompanyForm } from '@/types/auth'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ref } from 'vue'
|
||||
import { registerApi } from '@/request/api/auth'
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
company: {
|
||||
type: Object as PropType<RegisterCompanyForm>,
|
||||
required: true,
|
||||
@@ -18,11 +20,48 @@ const emit = defineEmits<{
|
||||
(e: 'prev'): void
|
||||
(e: 'submit'): void
|
||||
}>()
|
||||
const showPassword = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
|
||||
const isAgree = ref(false)
|
||||
|
||||
function handleSubmit() {
|
||||
emit('submit')
|
||||
if (!isAgree.value) {
|
||||
return
|
||||
}
|
||||
|
||||
submitLoading.value = true
|
||||
|
||||
registerApi({
|
||||
company: {
|
||||
name: props.company.name,
|
||||
industry: props.company.industry.join(','),
|
||||
address: props.company.address.join(','),
|
||||
phone: props.company.phone,
|
||||
email: props.company.email,
|
||||
profile: props.company.profile,
|
||||
},
|
||||
admin: {
|
||||
name: props.admin.name,
|
||||
phone: props.admin.phone,
|
||||
email: props.admin.email,
|
||||
password: props.admin.password,
|
||||
position: props.admin.position,
|
||||
},
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
ElMessage.success('注册成功')
|
||||
emit('submit')
|
||||
}
|
||||
else {
|
||||
ElMessage.error(res.message || '注册失败,请稍后再试')
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
ElMessage.error('注册失败,请稍后再试')
|
||||
}).finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -33,24 +72,51 @@ function handleSubmit() {
|
||||
公司信息
|
||||
</h2>
|
||||
|
||||
<el-row :gutter="16" style="padding: 12px 0px;">
|
||||
<el-row :gutter="16" class="info-group">
|
||||
<el-col :span="8">
|
||||
公司全称:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">公司全称</span>
|
||||
<span class="info-value">{{ company.name || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
所属行业:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">所属行业</span>
|
||||
<span class="info-value">
|
||||
{{ company.industry?.join(',') || '-' }}
|
||||
</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
公司地址:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">公司地址</span>
|
||||
<span class="info-value">
|
||||
{{ company.address?.join('/') || '-' }}
|
||||
</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
联系电话:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">联系电话</span>
|
||||
<span class="info-value">{{ company.phone || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
公司邮箱:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">公司邮箱</span>
|
||||
<span class="info-value">{{ company.email || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
公司简介:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">公司简介</span>
|
||||
<span class="info-value">{{ company.profile || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
@@ -58,21 +124,45 @@ function handleSubmit() {
|
||||
管理员信息
|
||||
</h2>
|
||||
|
||||
<el-row :gutter="16" style="padding: 12px 0px;">
|
||||
<el-row :gutter="16" class="info-group">
|
||||
<el-col :span="8">
|
||||
管理员名称:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">管理员名称</span>
|
||||
<span class="info-value">{{ admin.name || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
管理员电话:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">管理员电话</span>
|
||||
<span class="info-value">{{ admin.phone || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
管理员邮箱:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">管理员邮箱</span>
|
||||
<span class="info-value">{{ admin.email || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
登录密码:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">登录密码</span>
|
||||
<span class="info-value">{{ showPassword ? admin.password : '******' }}</span>
|
||||
|
||||
<span class="password" @click="showPassword = !showPassword">
|
||||
<i-solar:eye-broken v-if="showPassword" />
|
||||
<i-solar:eye-closed-broken v-else />
|
||||
</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
所属职务:-
|
||||
<div class="info-item">
|
||||
<span class="info-label">所属职务</span>
|
||||
<span class="info-value">{{ admin.position || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
@@ -96,7 +186,7 @@ function handleSubmit() {
|
||||
>
|
||||
上一步
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="!isAgree" @click="handleSubmit()">
|
||||
<el-button type="primary" :disabled="!isAgree" :loading="submitLoading" @click="handleSubmit()">
|
||||
确认提交
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -113,6 +203,24 @@ function handleSubmit() {
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
padding: 6px 0px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #71717a;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #111827;
|
||||
font-weight: 500;
|
||||
word-break: break-all;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
:deep(.el-col) {
|
||||
word-break: break-all;
|
||||
padding: 3px 0px;
|
||||
@@ -125,7 +233,7 @@ function handleSubmit() {
|
||||
.section-title {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
padding-bottom: 6px;
|
||||
padding: 16px 0px;
|
||||
border-bottom: 1px solid #e4e4e7;
|
||||
}
|
||||
.section-subtitle {
|
||||
@@ -134,6 +242,12 @@ function handleSubmit() {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.password {
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.terms {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { RegisterAdminForm, RegisterCompanyForm } from '@/types/auth'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { useRouter } from 'vue-router'
|
||||
import AdminForm from './components/AdminForm.vue'
|
||||
import CompanyForm from './components/CompanyForm.vue'
|
||||
@@ -52,8 +51,8 @@ const adminForm = ref<RegisterAdminForm>({
|
||||
})
|
||||
const companyForm = ref<RegisterCompanyForm>({
|
||||
name: '',
|
||||
industry: '',
|
||||
address: '',
|
||||
industry: [],
|
||||
address: [],
|
||||
phone: '',
|
||||
email: '',
|
||||
profile: '',
|
||||
@@ -71,6 +70,16 @@ function handlePrevStep() {
|
||||
function navigateTo(path: string) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function handleCompanySubmit(data: RegisterCompanyForm) {
|
||||
companyForm.value = data
|
||||
handleNextStep()
|
||||
}
|
||||
|
||||
function handleAdminSubmit(data: RegisterAdminForm) {
|
||||
adminForm.value = data
|
||||
handleNextStep()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -100,7 +109,7 @@ function navigateTo(path: string) {
|
||||
<div class="auth-container">
|
||||
<div class="form-container">
|
||||
<div>
|
||||
<img src="/logo.png" class="logo">
|
||||
<img src="/logo.png" class="logo" @click="navigateTo('/')">
|
||||
<el-steps finish-status="success" :active="activeStep" align-center>
|
||||
<el-step title="公司信息" />
|
||||
<el-step title="管理员信息" />
|
||||
@@ -108,8 +117,8 @@ function navigateTo(path: string) {
|
||||
</el-steps>
|
||||
</div>
|
||||
|
||||
<CompanyForm v-show="activeStep === 0" @submit="handleNextStep" />
|
||||
<AdminForm v-show="activeStep === 1" @submit="handleNextStep" @prev="handlePrevStep()" />
|
||||
<CompanyForm v-show="activeStep === 0" @submit="handleCompanySubmit" />
|
||||
<AdminForm v-show="activeStep === 1" @submit="handleAdminSubmit" @prev="handlePrevStep()" />
|
||||
<ConfirmForm v-show="activeStep === 2" :admin="adminForm" :company="companyForm" @prev="handlePrevStep()" @submit="handleNextStep()" />
|
||||
<ResultForm v-show="activeStep === 3" />
|
||||
|
||||
|
||||
@@ -30,10 +30,11 @@ const tableData = [
|
||||
企业名录
|
||||
</div>
|
||||
|
||||
<el-table :data="tableData" border stripe style="width: 100%">
|
||||
<!-- <el-table :data="tableData" style="width: 100%" :show-header="false">
|
||||
<el-table-column prop="date" label="地区" width="180" />
|
||||
<el-table-column prop="name" label="公司" />
|
||||
</el-table>
|
||||
</el-table> -->
|
||||
<el-empty description="暂无企业名录" style="background-color: #fff;" />
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
@@ -42,10 +43,12 @@ const tableData = [
|
||||
热门推荐
|
||||
</div>
|
||||
<div class="hot-card-list">
|
||||
<div v-for="i in 10" :key="i" class="hot-card-item">
|
||||
<el-empty description="暂无热门推荐" />
|
||||
|
||||
<!-- <div v-for="i in 10" :key="i" class="hot-card-item">
|
||||
<span class="number">{{ i }}</span>
|
||||
<span class="name">xxxxxxxxxx公司公司</span>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user