u
This commit is contained in:
1 parent
ff327ba2fd
commit
1217f7df5f
10 files changed
+503
-6
No files matched your search
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { computed, useAttrs } from 'vue'
|
||||
|
||||
interface Props {
|
||||
/** 图标名称 */
|
||||
icon?: string
|
||||
/** 图标颜色 */
|
||||
color?: string
|
||||
/** 图标大小(px) */
|
||||
fontSize?: number | string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const attrs = useAttrs()
|
||||
|
||||
const bindAttrs = computed(() => {
|
||||
const style: Record<string, string> = {}
|
||||
|
||||
if (props.color) {
|
||||
style.color = props.color
|
||||
}
|
||||
|
||||
if (props.fontSize) {
|
||||
style.fontSize
|
||||
= typeof props.fontSize === 'number'
|
||||
? `${props.fontSize}px`
|
||||
: props.fontSize
|
||||
}
|
||||
|
||||
return {
|
||||
...attrs,
|
||||
style: {
|
||||
...(attrs.style as Record<string, string>),
|
||||
...style,
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Icon
|
||||
v-if="icon"
|
||||
:icon="icon"
|
||||
v-bind="bindAttrs"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import type { NavMenuItem } from './type'
|
||||
import SvgIcon from '@/components/SvgIcon.vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'NavMenu',
|
||||
})
|
||||
defineProps<{
|
||||
menu: NavMenuItem []
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-for="item in menu" :key="item.index">
|
||||
<!-- 有子菜单 -->
|
||||
<el-sub-menu v-if="item.children?.length" :index="item.index">
|
||||
<template #title>
|
||||
<el-icon v-if="item.icon">
|
||||
<SvgIcon :icon="item.icon" />
|
||||
</el-icon>
|
||||
<span>{{ item.label }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 递归:注意这里一定是数组 -->
|
||||
<NavMenu :menu="item.children" />
|
||||
</el-sub-menu>
|
||||
|
||||
<!-- 单菜单 -->
|
||||
<el-menu-item v-else :index="item.index">
|
||||
<el-icon v-if="item.icon">
|
||||
<SvgIcon :icon="item.icon" />
|
||||
</el-icon>
|
||||
{{ item.label }}
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface NavMenuItem {
|
||||
label: string
|
||||
index: string
|
||||
icon?: string
|
||||
children?: NavMenuItem []
|
||||
}
|
||||
@@ -22,6 +22,16 @@ const router = createRouter({
|
||||
path: '/company/detail',
|
||||
component: () => import('@/views/company/detail/index.vue'),
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
component: () => import('@/views/settings/index.vue'),
|
||||
children: [
|
||||
{
|
||||
path: '/settings/info/company',
|
||||
component: () => import('@/views/settings/info/company.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -16,10 +16,20 @@ declare module 'vue' {
|
||||
HelloWorld: typeof import('./../components/HelloWorld.vue')['default']
|
||||
ICarbonAccessibility: typeof import('~icons/carbon/accessibility')['default']
|
||||
'IFa7Solid:userShield': typeof import('~icons/fa7-solid/user-shield')['default']
|
||||
'IHugeicons:building02': typeof import('~icons/hugeicons/building02')['default']
|
||||
'IHugeicons:edit01': typeof import('~icons/hugeicons/edit01')['default']
|
||||
'IHugeicons:edit04': typeof import('~icons/hugeicons/edit04')['default']
|
||||
'IHugeicons:home03': typeof import('~icons/hugeicons/home03')['default']
|
||||
'IHugeicons:materialAndTexture': typeof import('~icons/hugeicons/material-and-texture')['default']
|
||||
'IHugeicons:setting07': typeof import('~icons/hugeicons/setting07')['default']
|
||||
'IHugeicons:userSettings01': typeof import('~icons/hugeicons/user-settings01')['default']
|
||||
'IIc:outlineSettings': typeof import('~icons/ic/outline-settings')['default']
|
||||
'IIc:twotoneHeadphones': typeof import('~icons/ic/twotone-headphones')['default']
|
||||
'IIconoir:infoCircleSolid': typeof import('~icons/iconoir/info-circle-solid')['default']
|
||||
'IMajesticons:settingsCogLine': typeof import('~icons/majesticons/settings-cog-line')['default']
|
||||
'IMaterialSymbols:call': typeof import('~icons/material-symbols/call')['default']
|
||||
'IMaterialSymbols:chatBubbleOutline': typeof import('~icons/material-symbols/chat-bubble-outline')['default']
|
||||
'IMaterialSymbols:databaseOutline': typeof import('~icons/material-symbols/database-outline')['default']
|
||||
'IMaterialSymbols:folderCopyOutline': typeof import('~icons/material-symbols/folder-copy-outline')['default']
|
||||
'IMaterialSymbols:fullscreen': typeof import('~icons/material-symbols/fullscreen')['default']
|
||||
'IMaterialSymbols:fullscreenExit': typeof import('~icons/material-symbols/fullscreen-exit')['default']
|
||||
@@ -38,18 +48,25 @@ declare module 'vue' {
|
||||
'IRi:information2Line': typeof import('~icons/ri/information2-line')['default']
|
||||
'IRi:informationFill': typeof import('~icons/ri/information-fill')['default']
|
||||
'IRi:message3Fill': typeof import('~icons/ri/message3-fill')['default']
|
||||
'ISolar:buildings2Broken': typeof import('~icons/solar/buildings2-broken')['default']
|
||||
'ISolar:chatRoundBold': typeof import('~icons/solar/chat-round-bold')['default']
|
||||
'ISolar:chatRoundCallBroken': typeof import('~icons/solar/chat-round-call-broken')['default']
|
||||
'ISolar:databaseBoldDuotone': typeof import('~icons/solar/database-bold-duotone')['default']
|
||||
'ISolar:eyeBroken': typeof import('~icons/solar/eye-broken')['default']
|
||||
'ISolar:eyeClosedBroken': typeof import('~icons/solar/eye-closed-broken')['default']
|
||||
'IStreamlineUltimate:informationCircleBold': typeof import('~icons/streamline-ultimate/information-circle-bold')['default']
|
||||
'ITdesign:notification': typeof import('~icons/tdesign/notification')['default']
|
||||
'ITdesign:search': typeof import('~icons/tdesign/search')['default']
|
||||
'ITdesign:setting1': typeof import('~icons/tdesign/setting1')['default']
|
||||
Menu: typeof import('../components/nav-menu/index.vue')['default']
|
||||
MenuItem: typeof import('../components/nav-menu/index.vue')['default']
|
||||
NavMenu: typeof import('./../components/nav-menu/index.vue')['default']
|
||||
Preview: typeof import('./../components/wang-editor/modules/preview.vue')['default']
|
||||
QueryCompany: typeof import('./../components/QueryCompany.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SafeImage: typeof import('./../components/SafeImage.vue')['default']
|
||||
SvgIcon: typeof import('./../components/SvgIcon.vue')['default']
|
||||
UploadProgress: typeof import('./../components/upload-progress/index.vue')['default']
|
||||
WangEditor: typeof import('./../components/wang-editor/index.vue')['default']
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { h } from 'vue'
|
||||
import SvgIcon from '@/components/SvgIcon.vue'
|
||||
|
||||
/**
|
||||
* 图标渲染
|
||||
* - 用于vue的render函数
|
||||
*/
|
||||
export function useIconRender() {
|
||||
interface IconConfig {
|
||||
/**
|
||||
* 图标名称(iconify图标的名称)
|
||||
* - 例如:mdi-account 或者 mdi:account
|
||||
*/
|
||||
icon?: string
|
||||
/** 图标颜色 */
|
||||
color?: string
|
||||
/** 图标大小 */
|
||||
fontSize?: number
|
||||
}
|
||||
|
||||
interface IconStyle {
|
||||
color?: string
|
||||
fontSize?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 图标渲染
|
||||
* @param config
|
||||
* @property icon - 图标名称(iconify图标的名称), 例如:mdi-account 或者 mdi:account
|
||||
* @property localIcon - 本地svg图标文件名(assets/svg文件夹下)
|
||||
* @property color - 图标颜色
|
||||
* @property fontSize - 图标大小
|
||||
*/
|
||||
const iconRender = (config: IconConfig) => {
|
||||
const { color, fontSize, icon } = config
|
||||
|
||||
const style: IconStyle = {}
|
||||
|
||||
if (color) {
|
||||
style.color = color
|
||||
}
|
||||
if (fontSize) {
|
||||
style.fontSize = `${fontSize}px`
|
||||
}
|
||||
|
||||
if (!icon) {
|
||||
window.console.warn('没有传递图标名称,请确保给icon或localIcon传递有效值!')
|
||||
}
|
||||
|
||||
return () => h(SvgIcon, { icon, style })
|
||||
}
|
||||
|
||||
return {
|
||||
iconRender,
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="company-detail-page">
|
||||
<div class="detail-wrapper">
|
||||
<div class="detail-container">
|
||||
<div class="header-region">
|
||||
<el-avatar shape="square" :size="64" class="logo" :src="companyDetail.logo || '/no-avatar.png'" />
|
||||
@@ -150,7 +150,7 @@ onMounted(() => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.company-detail-page {
|
||||
.detail-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -1,12 +1,170 @@
|
||||
<script lang="ts" setup>
|
||||
import type { NavMenuItem } from '@/components/nav-menu/type'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import NavMenu from '@/components/nav-menu/index.vue'
|
||||
|
||||
const searchKey = ref('')
|
||||
const route = useRoute()
|
||||
|
||||
const menuRef = ref()
|
||||
|
||||
const menuList: NavMenuItem [] = [
|
||||
{
|
||||
label: '资料设置',
|
||||
index: '/settings/info',
|
||||
icon: 'hugeicons:setting-07',
|
||||
children: [
|
||||
{
|
||||
label: '公司信息设置',
|
||||
index: '/settings/info/company',
|
||||
icon: 'hugeicons:building-02',
|
||||
},
|
||||
{
|
||||
label: '成员设置',
|
||||
index: '/settings/info/company-user',
|
||||
icon: 'hugeicons:user-settings-01',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '创作者中心',
|
||||
index: '/settings/forum',
|
||||
icon: 'hugeicons:edit-01',
|
||||
children: [
|
||||
{
|
||||
label: '首页',
|
||||
index: '/settings/forum/home',
|
||||
icon: 'hugeicons:home-03',
|
||||
},
|
||||
{
|
||||
label: '写文章',
|
||||
index: '/settings/forum/editor',
|
||||
icon: 'hugeicons:edit-04',
|
||||
},
|
||||
{
|
||||
label: '帖子管理',
|
||||
index: '/settings/forum/post',
|
||||
icon: 'hugeicons:material-and-texture',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 递归过滤菜单
|
||||
*/
|
||||
function filterMenu(
|
||||
list: NavMenuItem[],
|
||||
keyword: string,
|
||||
): NavMenuItem[] {
|
||||
if (!keyword)
|
||||
return list
|
||||
|
||||
return list
|
||||
.map((item) => {
|
||||
const hitSelf = item.label.includes(keyword)
|
||||
|
||||
// 递归检查子节点是否命中(但不裁剪 children)
|
||||
const hitChildren = item.children
|
||||
? filterMenu(item.children, keyword).length > 0
|
||||
: false
|
||||
|
||||
// 自己命中 或 任一子节点命中 → 保留整个节点
|
||||
if (hitSelf || hitChildren) {
|
||||
return item
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
.filter(Boolean) as NavMenuItem[]
|
||||
}
|
||||
|
||||
const filteredMenu = computed(() =>
|
||||
filterMenu(menuList, searchKey.value.trim()),
|
||||
)
|
||||
function collectOpeneds(menu: NavMenuItem[], result: string[] = []) {
|
||||
for (const item of menu) {
|
||||
if (item.children?.length) {
|
||||
result.push(item.index)
|
||||
collectOpeneds(item.children, result)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
watch(searchKey, async (val) => {
|
||||
await nextTick()
|
||||
|
||||
if (!menuRef.value)
|
||||
return
|
||||
|
||||
if (val) {
|
||||
const openeds = collectOpeneds(filteredMenu.value)
|
||||
openeds.forEach((index) => {
|
||||
menuRef.value!.open(index)
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="">
|
||||
settings
|
||||
<div class="settings-wrapper">
|
||||
<div class="left-menu">
|
||||
<el-input
|
||||
v-model="searchKey"
|
||||
placeholder="搜索菜单"
|
||||
size="large"
|
||||
:prefix-icon="Search"
|
||||
clearable
|
||||
/>
|
||||
|
||||
<el-menu
|
||||
ref="menuRef"
|
||||
router
|
||||
:default-active="route.path"
|
||||
>
|
||||
<NavMenu :menu="filteredMenu" />
|
||||
</el-menu>
|
||||
</div>
|
||||
|
||||
<div class="right-view">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//
|
||||
.settings-wrapper {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
margin-top: 30px;
|
||||
padding: 0px 240px;
|
||||
height: 800px;
|
||||
|
||||
.left-menu {
|
||||
font-size: 16px;
|
||||
overflow-y: auto;
|
||||
width: 240px;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
flex-shrink: 0;
|
||||
:deep(.el-input__wrapper) {
|
||||
margin: 12px 12px 4px 12px;
|
||||
}
|
||||
|
||||
:deep(.el-menu) {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.right-view {
|
||||
width: 1200px;
|
||||
flex-shrink: 0;
|
||||
overflow-y: auto;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,163 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue'
|
||||
import WangEditor from '@/components/wang-editor/index.vue'
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
region: '',
|
||||
date1: '',
|
||||
date2: '',
|
||||
delivery: false,
|
||||
type: [],
|
||||
resource: '',
|
||||
desc: '',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="company-wrapper">
|
||||
<div class="form-item">
|
||||
<div class="form-header">
|
||||
<span class="form-title">
|
||||
基础信息
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
<div class="form-footer">
|
||||
<el-button>重置</el-button>
|
||||
<el-button type="primary">
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
:deep(.el-button){
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -29,7 +29,11 @@ export default defineConfig((configEnv) => {
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
Icons(),
|
||||
Icons({
|
||||
compiler: 'vue3',
|
||||
scale: 1,
|
||||
defaultClass: 'inline-block',
|
||||
}),
|
||||
Components({
|
||||
dts: 'src/types/auto-import.d.ts',
|
||||
resolvers: [IconsResolver()],
|
||||
|
||||
Reference in new issue
Block a user