This commit is contained in:
oneao committed 2025-12-24 22:27:48 +08:00
1 parent f310336188
commit 5221d1cc91
12 files changed
+280 -166

No files matched your search

@@ -8,9 +8,9 @@ import Header from './components/header.vue'
<div class="content">
<RouterView />
</div>
<!-- <footer class="footer">
footer
</footer> -->
<footer class="footer">
TODO
</footer>
</div>
</template>
@@ -23,7 +23,8 @@ import Header from './components/header.vue'
}
.footer {
margin-top: 32px;
background-color: #1f1f1f;
border-top: 1px solid rgb(232.9, 232.9, 232.9);
height: 100px;
display: flex;
align-items: center;
@@ -1,4 +1,4 @@
import type { DashboardData } from '@/types/post'
import type { DashboardData, PostCategory, PostTag } from '@/types/post'
import type { AddPostParams, DeletePostParams, Post, PostEditorForm, QueryPostParams, UpdatePostParams, UpdatePostStatusParams } from '@/types/post/editor'
import { httpGet, httpPostJson } from '@/request/utils'
@@ -29,3 +29,10 @@ export function deletePostApi(params: DeletePostParams) {
export function getDashboardDataApi() {
return httpGet<DashboardData>(`/post/dashboard`)
}
export function getPostTagApi() {
return httpGet<PostTag[]>('/post/tag')
}
export function getPostCategoryApi() {
return httpGet<PostCategory[]>('/post/category')
}
@@ -1,6 +0,0 @@
import type { PostTag } from '@/types/post/tag'
import { httpGet, httpPostJson } from '@/request/utils'
export function getPostTagApi() {
return httpGet<PostTag[]>('/post/tag')
}
@@ -16,3 +16,11 @@ export interface DashboardData {
viewChart: DashboradChart[]
typeChart: DashboradChart[]
}
export interface PostTag {
id: number
name: string
}
export interface PostCategory {
id: number
name: string
}
@@ -1,4 +0,0 @@
export interface PostTag {
id: number
name: string
}
@@ -15,7 +15,7 @@ const dataResult = ref<BasePageResult<any>>({
<div v-if="loading">
<CustomLoading />
</div>
<CustomEmpty v-else-if="dataResult.total === 0" title="🍵 帖子空空如也" sub-title="快来创建第一个贴子吧">
<CustomEmpty v-else-if="dataResult.total === 0" title="🍵 帖子空空如也" sub-title="快来创建第一个贴子吧" :in-container="true">
<template #extra>
<el-button type="primary">
创建帖子
@@ -1,26 +1,77 @@
<script lang="ts" setup>
import Nav from './components/nav.vue' // 导航栏组件
import PostList from './components/post-list.vue' // 帖子列表组件
import Sidebar from './components/sidebar.vue' // 推荐组件
import type { CollapseModelValue } from 'element-plus'
import { ref } from 'vue'
import SvgIcon from '@/components/SvgIcon.vue'
const activeNames = ref(['1'])
function handleChange(val: CollapseModelValue) {
console.log(val)
}
</script>
<template>
<div class="post">
<Nav class="nav" />
<PostList class="post-list" />
<Sidebar class="sidebar" />
<div class="post-wrapper">
<div class="sidebar">
<el-collapse v-model="activeNames" expand-icon-position="left">
<el-collapse-item title="标签" name="1">
<ul class="sidebar-tags">
<li class="sidebar-tag">
<SvgIcon icon="streamline-color:earth-airplane" :size="18" />
<span class="sidebar-tag-text">空运</span>
</li>
</ul>
</el-collapse-item>
</el-collapse>
</div>
</div>
</template>
<style lang="scss" scoped>
.post {
.post-wrapper {
display: flex;
padding: 0px 60px;
width: 100%;
}
.nav {
width: 200px;
padding: 0px 240px;
background-color: #fff;
.sidebar {
background-color: #fff;
height: calc(100vh - var(--header-height));
font-size: 16px;
flex-shrink: 0;
border-right: 1px solid rgb(232.9, 232.9, 232.9);
position: sticky;
top: var(--header-height);
align-self: flex-start;
width: 240px;
padding: 0px 16px;
.sidebar-tags {
display: flex;
flex-direction: column;
gap: 4px;
.sidebar-tag {
height: 32px;
display: inline-flex;
width: 100%;
align-items: center;
border-radius: 4px;
cursor: pointer;
padding: 0px 16px;
font-size: 16px;
&:hover {
background-color: #f2f2f2;
}
.sidebar-tag-text {
margin-left: 12px;
}
}
}
}
}
.sidebar {
width: 250px;
margin-left: 20px;
@@ -1,18 +1,15 @@
<script lang="ts" setup>
import type { FormInstance } from 'element-plus'
import type { PostEditorForm } from '@/types/post/editor'
import type { PostTag } from '@/types/post/tag'
import type { PostCategory, PostTag } from '@/types/post/index'
import type { UploadImageResponse } from '@/utils/file'
import { ElMessage } from 'element-plus'
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
import { useRoute } from 'vue-router'
import CustomLoading from '@/components/CustomLoading.vue'
import ImageUploadCard from '@/components/ImageUploadCard.vue'
import WangEditor from '@/components/wang-editor/index.vue'
import { addPostApi, getPostApi, updatePostApi } from '@/request/api/post'
import { getPostTagApi } from '@/request/api/post/tag'
import { addPostApi, getPostApi, getPostCategoryApi, getPostTagApi, updatePostApi } from '@/request/api/post'
import { PostStatusEnum } from '@/types/post/editor'
import { uploadToAliyun } from '@/utils/file'
const route = useRoute()
@@ -29,6 +26,8 @@ const showPushDrawer = ref<boolean>(false)
const imageUpload = ref<UploadImageResponse | null>(null)
const tagOptions = ref<PostTag[]>([])
const categoryOptions = ref<PostCategory[]>([])
const statusOptions = [
{
label: '草稿',
@@ -64,8 +63,8 @@ const formRules = {
title: [
{ required: true, message: '文章标题不能为空', trigger: 'blur' },
],
summary: [
{ required: true, message: '摘要不能为空', trigger: 'blur' },
tags: [
{ required: true, message: '文章标签不能为空', trigger: 'blur' },
],
status: [
{ required: true, message: '状态不能为空', trigger: 'blur' },
@@ -175,7 +174,11 @@ async function handleSave(formEl?: FormInstance) {
<template>
<div class="editor-container">
<div class="toolbar">
<input ref="titleInputRef" v-model="form.title" placeholder="输入标题" class="editor-title-input" maxlength="100">
<div class="toolbar-main">
<span>文章标题:</span>
<input ref="titleInputRef" v-model="form.title" placeholder="输入标题" class="editor-title-input" maxlength="100">
</div>
<div class="toolbar-extra">
<div v-if="form.updateTime" class="time">
更新于:{{ form.updateTime }}
@@ -267,20 +270,30 @@ async function handleSave(formEl?: FormInstance) {
<style lang="scss" scoped>
.editor-container {
padding: 20px 24px;
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
height: 44px;
.toolbar-main {
display: flex;
align-items: center;
flex: 1;
font-size: 18px;
}
.editor-title-input {
flex: 1;
border: none;
font-size: 24px;
padding: 8px 0px;
box-sizing: border-box;
outline: none;
resize: none;
line-height: 1.5;
border-bottom: 1px solid #dcdfe6;
}
.toolbar-extra {
display: flex;
@@ -20,6 +20,7 @@ const dashboradData = ref<DashboardData>({
viewChart: [],
typeChart: [],
})
const activeCollapseNames = ref(['1', '2'])
onMounted(() => {
loading.value = true
@@ -47,13 +48,8 @@ onMounted(() => {
<CustomLoading v-if="loading" :in-container="true" />
<div v-else class="creator-dashboard">
<div class="card">
<div class="card-header">
<div class="card-title">
数据概览
</div>
</div>
<div class="card-content">
<el-collapse v-model="activeCollapseNames" expand-icon-position="right">
<el-collapse-item title="数据概览" name="1">
<el-row :gutter="16">
<el-col :span="6">
<div class="stat-card">
@@ -96,20 +92,14 @@ onMounted(() => {
</div>
</el-col>
</el-row>
</div>
</div>
<div class="card">
<div class="card-header">
<div class="card-title">
数据报表
</div>
</div>
<div class="card-content">
</el-collapse-item>
<el-collapse-item title="数据报表" name="2">
<PostViewChart :data="dashboradData.viewChart" />
<PostNumberChart :data="dashboradData.numberChart" />
<PostTypeChart :data="dashboradData.typeChart" />
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
@@ -117,6 +107,24 @@ onMounted(() => {
.creator-dashboard {
display: flex;
flex-direction: column;
:deep(.el-collapse) {
border: none;
}
:deep(.el-collapse-item__wrap) {
border-bottom: none;
}
:deep(.el-collapse-item__header) {
font-size: 18px;
font-weight: 600;
padding: 20px 24px;
border-bottom: 1px solid #ddd;
line-height: 0;
}
:deep(.el-collapse-item__content) {
padding: 24px 24px 0;
padding-bottom: 0px;
}
}
.card {
@@ -112,7 +112,7 @@ watch(searchKey, async (val) => {
<template>
<div class="settings-wrapper">
<div class="left-menu">
<div class="menu-search">
<!-- <div class="menu-search">
<el-input
v-model="searchKey"
placeholder="搜索菜单"
@@ -120,7 +120,7 @@ watch(searchKey, async (val) => {
:prefix-icon="Search"
clearable
/>
</div>
</div> -->
<el-menu
ref="menuRef"
@@ -139,10 +139,9 @@ watch(searchKey, async (val) => {
<style lang="scss" scoped>
.settings-wrapper {
display: flex;
background-color: #fff;
padding: 0px 240px;
.left-menu {
background-color: #fff;
height: calc(100vh - var(--header-height));
font-size: 16px;
flex-shrink: 0;
@@ -150,32 +149,43 @@ watch(searchKey, async (val) => {
position: sticky;
top: var(--header-height);
align-self: flex-start;
padding: 12px 16px;
.menu-search {
margin-bottom: 8px;
}
width: 240px;
padding: 0px 16px;
:deep(.el-menu) {
border-right: none;
}
:deep(.el-menu-item) {
border-radius: 8px;
margin: 4px 0px;
}
.el-menu-item {
border-radius: 8px;
margin: 4px 0px;
font-size: 16px;
&:hover {
background-color: #f1f5f9;
}
}
:deep(.el-menu-item.is-active) {
background-color: #d1f0ff;
color: #303133;
font-weight: 600;
}
.el-sub-menu {
margin: 4px 0px;
&.is-active {
.el-sub-menu__title {
font-weight: bold;
}
}
}
:deep(.el-sub-menu) {
margin: 4px 0px;
}
:deep(.el-sub-menu__title) {
border-radius: 8px;
.el-sub-menu__title {
border-radius: 8px;
font-size: 16px;
&:hover {
background-color: #f1f5f9;
}
}
.el-menu-item.is-active {
background-color: #f1f5f9;
color: #000;
}
}
}
@@ -184,6 +194,8 @@ watch(searchKey, async (val) => {
flex-shrink: 0;
border-radius: 0px;
width: 100%;
background-color: #fff;
// border-right: 1px solid rgb(232.9, 232.9, 232.9);
}
}
</style>
@@ -146,86 +146,92 @@ const activeCollapseNames = ref(['1', '2', '3'])
<template>
<div class="company-wrapper">
<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>
<div class="form-main">
<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-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>
<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>
<div class="form-footer">
<el-button @click="getCompanyDetail()">
<el-button
text
bg
@click="getCompanyDetail()"
>
<SvgIcon icon="ic:baseline-refresh" :size="18" style="margin-right: 4px;" />
重置
</el-button>
@@ -239,7 +245,15 @@ const activeCollapseNames = ref(['1', '2', '3'])
<style lang="scss" scoped>
.company-wrapper {
padding-bottom: 16px;
height: 100%;
display: flex;
flex-direction: column;
.form-main {
flex: 1;
padding-bottom: 140px;
}
:deep(.el-collapse) {
border: none;
}
@@ -250,12 +264,12 @@ const activeCollapseNames = ref(['1', '2', '3'])
:deep(.el-collapse-item__header) {
font-size: 18px;
font-weight: 600;
padding: 16px;
padding: 20px 24px;
border-bottom: 1px solid #ddd;
line-height: 0;
}
:deep(.el-collapse-item__content) {
padding: 20px 20px 0px 20px;
padding: 24px 24px 0;
padding-bottom: 0px;
}
@@ -269,13 +283,23 @@ const activeCollapseNames = ref(['1', '2', '3'])
}
}
.form-footer {
margin-top: 16px;
width: 100%;
box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.08);
position: sticky;
bottom: 0;
align-self: flex-end;
height: 56px;
display: flex;
justify-content: flex-end;
align-items: center;
justify-content: center;
background-color: #fff;
:deep(.el-button) {
width: 120px;
width: 100%;
height: 100%;
border: none;
border-radius: 0;
margin-left: 0;
}
}
}
@@ -166,7 +166,7 @@ function handleDelteRows(val?: CompanyUser) {
<template>
<div class="users-wrapper">
<el-collapse v-model="activeCollapseNames">
<el-collapse v-model="activeCollapseNames" expand-icon-position="right">
<el-collapse-item title="搜索条件" name="1">
<el-form :model="queryForm">
<el-row :gutter="16">
@@ -330,13 +330,13 @@ function handleDelteRows(val?: CompanyUser) {
:deep(.el-collapse-item__header) {
font-size: 18px;
font-weight: 600;
padding: 16px;
padding: 20px 24px;
border-bottom: 1px solid #ddd;
line-height: 0;
}
:deep(.el-collapse-item__content) {
padding-bottom: 0px;
padding: 20px 20px 0px 20px;
padding: 24px 24px 0;
}
.table-container {