This commit is contained in:
oneao committed 2025-12-18 17:28:06 +08:00
1 parent 1217f7df5f
commit 6e59e8be45
42 files changed
+1256 -430

No files matched your search

@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { FormInstance } from 'element-plus'
import type { RegisterCompanyForm } from '@/types/auth'
import { reactive, ref, watch } from 'vue'
import { reactive, ref } from 'vue'
import AreaSelect from '@/components/AreaSelect.vue'
import IndustrySelect from '@/components/IndustrySelect.vue'
import { validateEmail, validatePhone } from '@/utils/validate'
@@ -15,10 +15,10 @@ const formRules = {
name: [
{ required: true, message: '公司名称不能为空', trigger: 'blur' },
],
industry: [
industries: [
{ required: true, message: '所属行业不能为空', trigger: 'blur' },
],
address: [
areas: [
{ required: true, message: '公司地址不能为空', trigger: 'blur' },
],
email: [{ validator: emailValidator, trigger: 'blur' }],
@@ -55,19 +55,13 @@ function phoneValidator(rule: any, value: any, callback: any) {
}
const form = reactive<RegisterCompanyForm>({
name: '',
industry: [],
address: [],
industries: [],
areas: [],
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
@@ -102,13 +96,13 @@ async function handleSubmit(formEl: FormInstance | undefined) {
</el-col>
<el-col :span="12">
<el-form-item label="所属行业" prop="industry">
<IndustrySelect v-model="form.industry" placeholder="请输入所属行业" />
<IndustrySelect v-model="form.industries" placeholder="请输入所属行业" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公司地址" prop="address">
<AreaSelect
v-model="form.address"
v-model="form.areas"
placeholder="请输入公司地址"
/>
</el-form-item>
@@ -35,8 +35,8 @@ function handleSubmit() {
registerApi({
company: {
name: props.company.name,
industry: props.company.industry.join(','),
address: props.company.address.join(','),
industries: props.company.industries,
areas: props.company.areas,
phone: props.company.phone,
email: props.company.email,
profile: props.company.profile,
@@ -84,7 +84,7 @@ function handleSubmit() {
<div class="info-item">
<span class="info-label">所属行业</span>
<span class="info-value">
{{ company.industry?.join(',') || '-' }}
{{ company.industries?.join(',') || '-' }}
</span>
</div>
</el-col>
@@ -93,7 +93,7 @@ function handleSubmit() {
<div class="info-item">
<span class="info-label">公司地址</span>
<span class="info-value">
{{ company.address?.join('/') || '-' }}
{{ company.areas?.join('/') || '-' }}
</span>
</div>
</el-col>
@@ -51,8 +51,8 @@ const adminForm = ref<RegisterAdminForm>({
})
const companyForm = ref<RegisterCompanyForm>({
name: '',
industry: [],
address: [],
industries: [],
areas: [],
phone: '',
email: '',
profile: '',
@@ -1,8 +1,11 @@
<script lang="ts" setup>
import type { AnchorMenuItem } from '@/components/anchor-menu/type'
import type { CompanyDetail } from '@/types/company'
import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import AnchorMenu from '@/components/anchor-menu/index.vue'
import WangEditor from '@/components/wang-editor/index.vue'
import { getCompanyDetailApi } from '@/request/api/company'
@@ -21,9 +24,34 @@ const companyDetail = ref<CompanyDetail>({
phone: '',
email: '',
description: '',
establishmentDate: '',
users: [],
})
const anchorMenu = ref<AnchorMenuItem[]>([
{
label: '基础信息',
href: 'basic',
children: [],
},
{
label: '公司简介',
href: 'profile',
children: [],
},
{
label: '公司成员',
href: 'users',
children: [],
},
{
label: '公司详细介绍',
href: 'description',
children: [],
},
])
const activeCollapseNames = ref(['1'])
onMounted(() => {
companyId.value = Number(route.query.id) || 0
@@ -45,6 +73,10 @@ onMounted(() => {
ElMessage.error('加载公司信息失败,请稍后再试')
})
})
function handleUpdateEditorDirectory(menus: AnchorMenuItem[]) {
anchorMenu.value[3].children.push(...menus)
}
</script>
<template>
@@ -121,7 +153,7 @@ onMounted(() => {
<div class="info-header">
<span class="info-title">公司详细介绍</span>
</div>
<WangEditor v-model="companyDetail.description" mode="view" />
<WangEditor v-model="companyDetail.description" mode="view" @update:directory="handleUpdateEditorDirectory" />
</div>
</div>
</div>
@@ -131,18 +163,7 @@ onMounted(() => {
导航
</div>
<el-anchor :offset="70">
<el-anchor-link href="#basic">
基础信息
</el-anchor-link>
<el-anchor-link href="#profile">
公司简介
</el-anchor-link>
<el-anchor-link href="#users">
公司成员
</el-anchor-link>
<el-anchor-link href="#description">
公司详细介绍
</el-anchor-link>
<AnchorMenu :menu="anchorMenu" />
</el-anchor>
</el-affix>
</div>
@@ -202,6 +223,18 @@ onMounted(() => {
.info-title {
font-weight: 600;
font-size: 18px;
position: relative;
padding-left: 12px;
}
.info-title::before {
content: '';
position: absolute;
left: 0;
top: 2px;
width: 4px;
height: 25px;
background-color: var(--el-color-primary);
}
}
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import type { CompanyListItem } from '@/types/company'
import type { Company } from '@/types/company'
import { ElMessage } from 'element-plus'
import { onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
@@ -18,7 +18,7 @@ const page = reactive<Pagination>({
const loading = ref<boolean>(true)
const companyList = ref<CompanyListItem[]>([])
const companyList = ref<Company[]>([])
onMounted(() => {
getCompanyList()
@@ -50,7 +50,7 @@ function getCompanyList() {
})
}
function toDetail(data: CompanyListItem) {
function toDetail(data: Company) {
router.push({
path: '/company/detail',
query: {
@@ -137,6 +137,7 @@ function toDetail(data: CompanyListItem) {
layout="prev, pager, next"
background
size="large"
@change="getCompanyList()"
/>
</div>
</div>
@@ -23,7 +23,7 @@ const menuList: NavMenuItem [] = [
},
{
label: '成员设置',
index: '/settings/info/company-user',
index: '/settings/info/user',
icon: 'hugeicons:user-settings-01',
},
],
@@ -1,124 +1,236 @@
<script lang="ts" setup>
import { reactive } from 'vue'
import type { FormInstance } from 'element-plus'
import type { CompanyDetail } from '@/types/company'
import { ElMessage } from 'element-plus'
import { onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import AreaSelect from '@/components/AreaSelect.vue'
import IndustrySelect from '@/components/IndustrySelect.vue'
import SvgIcon from '@/components/SvgIcon.vue'
import WangEditor from '@/components/wang-editor/index.vue'
import { getCompanyDetailApi, updateCompanyApi } from '@/request/api/company'
import { useUserStore } from '@/store/user'
import { uploadImage, uploadToAliyun } from '@/utils/file'
import { validateEmail, validatePhone } from '@/utils/validate'
const form = reactive({
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: '',
const router = useRouter()
const userStore = useUserStore()
const loading = reactive({
save: false,
})
const formRef = ref<FormInstance>()
const formRules = {
name: [
{ required: true, message: '公司名称不能为空', trigger: 'blur' },
],
industries: [
{ required: true, message: '所属行业不能为空', trigger: 'blur' },
],
areas: [
{ required: true, message: '公司地址不能为空', trigger: 'blur' },
],
email: [{ validator: emailValidator, trigger: 'blur' }],
phone: [{ validator: phoneValidator, trigger: 'blur' }],
}
function emailValidator(rule: any, value: any, callback: any) {
// 未填写:直接通过
if (!value) {
callback()
return
}
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 companyDetail = ref<CompanyDetail>({
id: 0,
name: '',
areas: [],
industries: [],
logo: '',
profile: '',
phone: '',
email: '',
description: '',
establishmentDate: '',
users: [],
})
onMounted(() => {
getCompanyDetail()
})
function getCompanyDetail() {
getCompanyDetailApi(userStore.userInfo.company.id).then((res) => {
if (res.isSuccess) {
companyDetail.value = res.data
}
else {
ElMessage.error(res.message || '加载公司信息失败,请稍后再试')
router.push('/')
}
}).catch((err) => {
console.log(err)
ElMessage.error('加载公司信息失败,请稍后再试')
})
}
function handleUploadLogo() {
uploadImage().then((data) => {
uploadToAliyun(data.blob, data.name, data.size).then((res) => {
companyDetail.value.logo = res.url
})
})
}
async function handleSubmit(formEl: FormInstance | undefined) {
if (!formEl)
return
await formEl.validate((valid) => {
if (valid) {
loading.save = true
updateCompanyApi({
id: companyDetail.value.id,
name: companyDetail.value.name,
areas: companyDetail.value.areas,
industries: companyDetail.value.industries,
establishmentDate: companyDetail.value.establishmentDate,
profile: companyDetail.value.profile,
phone: companyDetail.value.phone,
email: companyDetail.value.email,
description: companyDetail.value.description,
logo: companyDetail.value.logo,
}).then((res) => {
if (res.isSuccess) {
userStore.userInfo.company.name = companyDetail.value.name
userStore.userInfo.company.logo = companyDetail.value.logo
ElMessage.success('更新成功')
}
else {
ElMessage.error(res.message || '更新失败,请稍后再试')
}
}).catch((err) => {
console.log(err)
ElMessage.error('更新失败,请稍后再试')
}).finally(() => {
loading.save = false
})
}
})
}
const activeCollapseNames = ref(['1', '2', '3'])
</script>
<template>
<div class="company-wrapper">
<div class="form-item">
<div class="form-header">
<span class="form-title">
基础信息
</span>
</div>
<el-collapse v-model="activeCollapseNames" expand-icon-position="right">
<el-collapse-item title="基础信息" name="1">
<el-form
ref="formRef"
:model="companyDetail"
:rules="formRules"
label-width="auto"
label-position="top"
>
<el-row :gutter="16">
<el-col :span="24">
<el-form-item>
<el-avatar :size="86" :src="companyDetail.logo || '/no-avatar.png'" shape="square" />
<div class="logo-upload">
<el-button type="primary" @click="handleUploadLogo()">
<SvgIcon icon="majesticons:cloud-upload-line" :size="18" style="margin-right: 4px;" />
上传LOGO
</el-button>
<span>支持 JPG、PNG 格式,文件大小不超过 2MB</span>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公司名称" prop="name">
<el-input v-model="companyDetail.name" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属行业" prop="industries">
<IndustrySelect v-model="companyDetail.industries" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="成立时间" prop="establishmentDate">
<el-date-picker
v-model="companyDetail.establishmentDate"
type="date"
placeholder="请选择公司成立时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公司地址" prop="areas">
<AreaSelect v-model="companyDetail.areas" />
</el-form-item>
</el-col>
<el-form
:model="form"
label-width="auto"
label-position="top"
>
<el-row :gutter="16">
<el-col :span="24">
<el-form-item>
<el-avatar :size="86" shape="square" />
<div class="logo-upload">
<el-button type="primary" plain>
上传LOGO
</el-button>
<span>支持 JPG、PNG 格式,文件大小不超过 2MB</span>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公司名称">
<el-input v-model="form.name" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属行业">
<el-select v-model="form.region" placeholder="please select your zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="成立时间">
<el-date-picker
v-model="form.date1"
type="date"
placeholder="Pick a date"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="员工规模">
<el-input v-model="form.desc" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公司地址">
<el-input v-model="form.name" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公司详细地址">
<el-input v-model="form.desc" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系电话">
<el-input v-model="form.name" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系邮箱">
<el-input v-model="form.desc" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div class="form-item">
<div class="form-header">
<span class="form-title">
公司简介
</span>
</div>
<el-form :model="form" label-width="auto">
<el-form-item>
<el-input v-model="form.name" type="textarea" />
</el-form-item>
</el-form>
</div>
<div class="form-item">
<div class="form-header">
<span class="form-title">
公司介绍
</span>
</div>
<WangEditor />
</div>
<el-col :span="12">
<el-form-item label="联系电话" prop="phone">
<el-input v-model="companyDetail.phone" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系邮箱" prop="email">
<el-input v-model="companyDetail.email" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-collapse-item>
<el-collapse-item title="公司简介" name="2">
<el-form
ref="formRef"
:model="companyDetail"
:rules="formRules"
label-width="auto"
>
<el-form-item prop="profile">
<el-input v-model="companyDetail.profile" type="textarea" />
</el-form-item>
</el-form>
</el-collapse-item>
<el-collapse-item title="公司介绍" name="3">
<WangEditor v-model="companyDetail.description" show-direcotry />
</el-collapse-item>
</el-collapse>
<div class="form-footer">
<el-button>重置</el-button>
<el-button type="primary">
<el-button @click="getCompanyDetail()">
<SvgIcon icon="ic:baseline-refresh" :size="18" style="margin-right: 4px;" />
重置
</el-button>
<el-button type="primary" :loading="loading.save" @click="handleSubmit(formRef)">
<SvgIcon icon="majesticons:save-line" :size="18" style="margin-right: 4px;" />
保存
</el-button>
</div>
@@ -129,33 +241,37 @@ const form = reactive({
.company-wrapper {
padding: 24px 32px;
.form-item {
margin-bottom: 16px;
.form-header {
margin-bottom: 16px;
.form-title {
font-size: 18px;
font-weight: 600;
}
}
.logo-upload {
margin-left: 16px;
display: flex;
flex-direction: column;
span {
font-size: 14px;
color: #666;
}
}
:deep(.el-collapse) {
border: none;
}
:deep(.el-collapse-item__wrap) {
border-bottom: none;
}
:deep(.el-collapse-item__header){
font-size: 18px;
font-weight: 600;
}
:deep(.el-collapse-item__content){
padding-bottom: 0px;
}
.logo-upload {
margin-left: 16px;
display: flex;
flex-direction: column;
span {
font-size: 14px;
color: #666;
}
}
.form-footer {
margin-top: 16px;
width: 100%;
display: flex;
justify-content: flex-end;
:deep(.el-button){
:deep(.el-button) {
width: 120px;
}
}
@@ -0,0 +1,249 @@
<script lang="ts" setup>
import type { CompanyUser, QueryCompanyUserParams } from '@/types/company/user'
import { Delete, Plus, Refresh, Search } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { onMounted, reactive, ref } from 'vue'
import { queryCompanyUserApi } from '@/request/api/company/user'
import { useUserStore } from '@/store/user'
const userStore = useUserStore()
const activeCollapseNames = ref(['1'])
const page = reactive<Pagination>({
current: 1,
pageSize: 10,
total: 0,
})
const queryForm = reactive<QueryCompanyUserParams>({
currentPage: page.current,
pageSize: page.pageSize,
companyId: userStore.userInfo.company.id,
name: '',
phone: '',
position: '',
email: '',
canuse: '',
})
const canuseOptions = [
{
value: '',
label: '全部',
},
{
value: '1',
label: '启用',
},
{
value: '0',
label: '禁用',
},
]
const tableData = ref<CompanyUser[]>([])
onMounted(() => {
getCompanyUser()
})
function getCompanyUser() {
queryCompanyUserApi({
...queryForm,
currentPage: page.current,
pageSize: page.pageSize,
}).then((res) => {
if (res.isSuccess) {
tableData.value = res.data.records
page.total = res.data.total
}
else {
ElMessage.error(res.message || '查询失败,请稍后再试')
}
}).catch((err) => {
console.log(err)
ElMessage.error('查询失败,请稍后再试')
})
}
</script>
<template>
<div class="users-wrapper">
<el-collapse v-model="activeCollapseNames">
<el-collapse-item title="搜索条件" name="1">
<el-form :model="queryForm">
<el-row :gutter="16">
<el-col :span="6">
<el-form-item label="名称">
<el-input v-model="queryForm.name" placeholder="名称" clearable />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="职位">
<el-input v-model="queryForm.position" placeholder="职位" clearable />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="邮箱">
<el-input v-model="queryForm.email" placeholder="邮箱" clearable />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="手机">
<el-input v-model="queryForm.phone" placeholder="手机" clearable />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="状态">
<el-select v-model="queryForm.canuse">
<el-option
v-for="item in canuseOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="18">
<el-form-item>
<el-button :icon="Refresh">
重置
</el-button>
<el-button type="primary" :icon="Search">
查询
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-collapse-item>
</el-collapse>
<div class="table-container">
<div class="table-header">
<div class="table-title">
成员列表
</div>
<div class="table-actions">
<el-button type="primary" plain :icon="Plus">
新增
</el-button>
<el-button type="danger" plain :icon="Delete">
批量删除
</el-button>
<el-button :icon="Refresh">
刷新
</el-button>
</div>
</div>
<div class="table-body-wrapper">
<el-table :data="tableData" border stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column prop="name" label="名称" align="center" />
<el-table-column prop="position" label="职位" align="center" />
<el-table-column prop="email" label="邮箱" align="center" />
<el-table-column prop="phone" label="电话" align="center" />
<el-table-column prop="canuse" label="启用" width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.canuse === '1' ? 'primary' : 'warning'">
启用
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right">
<template #default="scope">
<el-button size="small">
修改
</el-button>
<el-button
v-if="scope.row.role !== 0"
size="small"
type="danger"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="table-pagination">
<el-pagination
v-model:current-page="page.current"
:page-size="page.pageSize"
:total="page.total"
layout="prev, pager, next"
background
size="large"
@change="getCompanyUser()"
/>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.users-wrapper {
display: flex;
flex-direction: column;
height: 100%;
padding: 16px 32px 0px 32px;
:deep(.el-collapse) {
border: none;
}
:deep(.el-collapse-item__wrap) {
border-bottom: none;
}
:deep(.el-collapse-item__header) {
font-size: 18px;
font-weight: 500;
}
:deep(.el-collapse-item__content) {
padding-bottom: 0px;
}
.table-container {
flex: 1;
margin-top: 16px;
display: flex;
flex-direction: column;
min-height: 0;
.table-body-wrapper {
flex: 1;
overflow: hidden;
min-height: 0;
:deep(.el-table) {
height: 100%;
}
}
.table-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 56px;
.table-title {
font-size: 18px;
font-weight: 500;
}
}
:deep(.el-table__header) {
.el-table__cell {
background-color: #f5f7fa;
color: #000;
}
}
.table-pagination {
height: 56px;
width: 100%;
display: flex;
justify-content: flex-end;
}
}
}
</style>