Browse Source

提取文章分类作为网站导航(废弃)

master
gxwebsoft 1 year ago
parent
commit
d518430c33
  1. 5
      .env.development
  2. 4
      .env.production
  3. 2
      src/api/cms/article/model/index.ts
  4. 126
      src/api/cms/form-record/index.ts
  5. 25
      src/api/cms/form-record/model/index.ts
  6. 143
      src/api/cms/form/index.ts
  7. 31
      src/api/cms/form/model/index.ts
  8. 4
      src/api/cms/navigation/model/index.ts
  9. 41
      src/store/modules/tenant.ts
  10. 119
      src/utils/request.ts
  11. 156
      src/views/article/components/photo-list.vue
  12. 2
      src/views/article/index.vue
  13. 233
      src/views/down/components/list.vue
  14. 150
      src/views/down/detail/components/news-detail.vue
  15. 24
      src/views/down/detail/index.vue
  16. 24
      src/views/down/index.vue
  17. 195
      src/views/down/parent/components/news-list.vue
  18. 24
      src/views/down/parent/index.vue
  19. 377
      src/views/form/components/detail.vue
  20. 24
      src/views/form/index.vue
  21. 2
      src/views/index/index.vue

5
.env.development

@ -1,8 +1,7 @@
VITE_SOCKET_URL=wss://server.gxwebsoft.com VITE_SOCKET_URL=wss://server.gxwebsoft.com
VITE_SERVER_URL=https://server.gxwebsoft.com/api VITE_SERVER_URL=https://server.gxwebsoft.com/api
#VITE_API_URL=https://modules.gxwebsoft.com/api #VITE_API_URL=https://modules.gxwebsoft.com/api
#VITE_API_URL=http://127.0.0.1:9090/api
VITE_API_URL=http://127.0.0.1:9090/api
#VITE_SOCKET_URL=ws://localhost:9191 #VITE_SOCKET_URL=ws://localhost:9191
VITE_API_URL=http://103.233.255.195:9300/api
#VITE_API_URL=http://103.233.255.195:9300/api

4
.env.production

@ -1,5 +1,5 @@
VITE_SERVER_URL=https://server.gxwebsoft.com/api VITE_SERVER_URL=https://server.gxwebsoft.com/api
VITE_SOCKET_URL=wss://server.gxwebsoft.com VITE_SOCKET_URL=wss://server.gxwebsoft.com
#VITE_API_URL=https://modules.gxwebsoft.com/api
VITE_API_URL=http://103.233.255.195:9300/api
VITE_API_URL=https://modules.gxwebsoft.com/api
#VITE_API_URL=http://103.233.255.195:9300/api

2
src/api/cms/article/model/index.ts

@ -38,6 +38,8 @@ export interface Article {
userAvatar?: string; userAvatar?: string;
// 所属门店ID // 所属门店ID
shopId?: string; shopId?: string;
// 附件
files?: string;
// 排序 // 排序
sortNumber?: number; sortNumber?: number;
// 备注 // 备注

126
src/api/cms/form-record/index.ts

@ -0,0 +1,126 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type { FormRecord, FormRecordParam } from './model';
import { MODULES_API_URL } from '@/config/setting';
/**
*
*/
export async function pageFormRecord(params: FormRecordParam) {
const res = await request.get<ApiResult<PageResult<FormRecord>>>(
MODULES_API_URL + '/cms/form-record/page',
{
params
}
);
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function listFormRecord(params?: FormRecordParam) {
const res = await request.get<ApiResult<FormRecord[]>>(
MODULES_API_URL + '/cms/form-record',
{
params
}
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function addFormRecord(data: FormRecord) {
const res = await request.post<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form-record',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function updateFormRecord(data: FormRecord) {
const res = await request.put<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form-record',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function removeFormRecord(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form-record/' + id
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function removeBatchFormRecord(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form-record/batch',
{
data
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* id查询表单设计
*/
export async function getFormRecord(id: number) {
const res = await request.get<ApiResult<FormRecord>>(
MODULES_API_URL + '/cms/form-record/' + id
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* IP是否存在
*/
export async function checkExistence(
field: string,
value: string,
id?: number
) {
const res = await request.get<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form-record/existence',
{
params: { field, value, id }
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}

25
src/api/cms/form-record/model/index.ts

@ -0,0 +1,25 @@
import type { PageParam } from '@/api';
/**
*
*/
export interface FormRecord {
formRecordId?: number;
formId?: number;
name?: string;
formData?: string;
userId?: number;
sortNumber?: number;
comments?: string;
status?: number;
createTime?: string;
layout?: string;
}
/**
*
*/
export interface FormRecordParam extends PageParam {
formRecordId?: string;
name?: number;
}

143
src/api/cms/form/index.ts

@ -0,0 +1,143 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type { Form, FormParam } from './model';
import { MODULES_API_URL } from '@/config/setting';
/**
*
*/
export async function pageForm(params: FormParam) {
const res = await request.get<ApiResult<PageResult<Form>>>(
MODULES_API_URL + '/cms/form/page',
{
params
}
);
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function listForm(params?: FormParam) {
const res = await request.get<ApiResult<Form[]>>(
MODULES_API_URL + '/cms/form',
{
params
}
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function addForm(data: Form) {
const res = await request.post<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function updateForm(data: Form) {
const res = await request.put<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function removeForm(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form/' + id
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function removeBatchForm(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form/batch',
{
data
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function updateFormStatus(formId?: number, status?: number) {
const res = await request.put<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form/status',
{
formId,
status
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* id查询表单设计
*/
export async function getForm(id: number) {
const res = await request.get<ApiResult<Form>>(
MODULES_API_URL + '/cms/form/' + id
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* IP是否存在
*/
export async function checkExistence(
field: string,
value: string,
id?: number
) {
const res = await request.get<ApiResult<unknown>>(
MODULES_API_URL + '/cms/form/existence',
{
params: { field, value, id }
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}

31
src/api/cms/form/model/index.ts

@ -0,0 +1,31 @@
import type { PageParam } from '@/api';
/**
*
*/
export interface Form {
formId?: number;
name?: string;
photo?: string;
background?: string;
submitNumber?: number;
layout?: any;
userId?: number;
sortNumber?: number;
comments?: string;
status?: number;
createTime?: string;
hidePhoto?: any;
hideBackground?: number;
opacity?: number;
data?: any[];
clearCache?: number;
}
/**
*
*/
export interface FormParam extends PageParam {
formId?: number;
name?: number;
}

4
src/api/cms/navigation/model/index.ts

@ -4,6 +4,8 @@
export interface Navigation { export interface Navigation {
// 菜单id // 菜单id
navigationId?: number; navigationId?: number;
// 菜单类型
type?: number;
// 上级id, 0是顶级 // 上级id, 0是顶级
parentId?: number; parentId?: number;
// 菜单名称 // 菜单名称
@ -26,6 +28,8 @@ export interface Navigation {
icon?: string; icon?: string;
// 是否隐藏, 0否,1是(仅注册路由不显示左侧菜单) // 是否隐藏, 0否,1是(仅注册路由不显示左侧菜单)
hide?: number; hide?: number;
// 位置
position?: number;
// 路由元信息 // 路由元信息
meta?: string; meta?: string;
// 创建时间 // 创建时间

41
src/store/modules/tenant.ts

@ -9,12 +9,14 @@ import {useUserStore} from "@/store/modules/user";
import {getSiteInfo} from "@/api/cms/website"; import {getSiteInfo} from "@/api/cms/website";
import {ArrangeCategory} from "@/api/cms/category/model"; import {ArrangeCategory} from "@/api/cms/category/model";
import {Website} from "@/api/cms/website/model"; import {Website} from "@/api/cms/website/model";
import {Navigation} from "@/api/cms/navigation/model";
export interface UserState { export interface UserState {
config: any | null; config: any | null;
menus: MenuItem[] | null | undefined; menus: MenuItem[] | null | undefined;
siteInfo: Website | null; siteInfo: Website | null;
categoryList: ArrangeCategory[] | null | undefined; categoryList: ArrangeCategory[] | null | undefined;
subMenu: Navigation[] | null | undefined;
} }
export const useTenantStore = defineStore({ export const useTenantStore = defineStore({
@ -26,6 +28,8 @@ export const useTenantStore = defineStore({
menus: null, menus: null,
// 企业信息 // 企业信息
siteInfo: null, siteInfo: null,
// 底部链接
subMenu: null,
// 文章分类 // 文章分类
categoryList: null categoryList: null
}), }),
@ -39,7 +43,7 @@ export const useTenantStore = defineStore({
if (!siteInfo) { if (!siteInfo) {
return {}; return {};
} }
const { categoryList,config } = siteInfo;
const { navigations,config } = siteInfo;
this.siteInfo = siteInfo; this.siteInfo = siteInfo;
localStorage.setItem('tenantId', String(siteInfo.tenantId)); localStorage.setItem('tenantId', String(siteInfo.tenantId));
localStorage.setItem('tenantName', String(siteInfo.websiteName)); localStorage.setItem('tenantName', String(siteInfo.websiteName));
@ -51,25 +55,30 @@ export const useTenantStore = defineStore({
// 用户菜单, 过滤掉按钮类型并转为 children 形式 // 用户菜单, 过滤掉按钮类型并转为 children 形式
const { menus, homePath } = formatMenus( const { menus, homePath } = formatMenus(
toTreeData({ toTreeData({
data: categoryList
?.map((d) => {
// 新闻列表
if(d.type == 0){
d.path = `/article/${d.categoryId}`
}
return {
...d,
name: d.title,
path: d.path,
component: d.component
};
}),
idField: 'categoryId',
data: navigations?.filter(d => d.position != 2).map((d) => {
if(d.parentId == 0 && d.type == 0 && d.path != '/'){
d.path = '/' + d.navigationId;
}
return {
...d,
name: d.title,
path: d.path,
component: d.component
};
}),
idField: 'navigationId',
parentIdField: 'parentId' parentIdField: 'parentId'
}) })
); );
this.menus = menus; this.menus = menus;
console.log(menus);
// 处理底部链接
this.subMenu = toTreeData({
data: navigations?.filter(d => d.position != 1).map((d) => {
return { ...d, key: d.navigationId, value: d.navigationId };
}),
idField: 'navigationId',
parentIdField: 'parentId'
});
const userStore = useUserStore(); const userStore = useUserStore();
// 更新网站菜单数据 // 更新网站菜单数据
userStore.setMenu(menus); userStore.setMenu(menus);

119
src/utils/request.ts

@ -2,84 +2,89 @@
* axios * axios
*/ */
import axios from 'axios'; import axios from 'axios';
import type { AxiosResponse } from 'axios';
import { unref } from 'vue';
import type {AxiosResponse} from 'axios';
import {unref} from 'vue';
import router from '@/router'; import router from '@/router';
import { Modal } from 'ant-design-vue';
import {Modal} from 'ant-design-vue';
import { import {
API_BASE_URL,
TOKEN_HEADER_NAME,
LAYOUT_PATH,
TENANT_ID
API_BASE_URL,
TOKEN_HEADER_NAME,
LAYOUT_PATH,
TENANT_ID
} from '@/config/setting'; } from '@/config/setting';
import { getToken, setToken } from './token-util';
import { logout } from './page-tab-util';
import type { ApiResult } from '@/api';
import {getToken, setToken} from './token-util';
import {logout} from './page-tab-util';
import type {ApiResult} from '@/api';
import {getDomainPart1} from "@/utils/domain"; import {getDomainPart1} from "@/utils/domain";
// import {getTenantId} from "@/utils/domain"; // import {getTenantId} from "@/utils/domain";
const service = axios.create({ const service = axios.create({
baseURL: API_BASE_URL
baseURL: API_BASE_URL
}); });
/** /**
* *
*/ */
service.interceptors.request.use( service.interceptors.request.use(
(config) => {
const token = getToken();
// 添加 token 到 header
if (token && config.headers) {
config.headers.common[TOKEN_HEADER_NAME] = token;
(config) => {
const token = getToken();
// 添加 token 到 header
if (token && config.headers) {
config.headers.common[TOKEN_HEADER_NAME] = token;
}
// 获取租户ID
if (config.headers) {
config.headers.common['TenantId'] = TENANT_ID;
// 解析二级域名获取租户ID
if (getDomainPart1()) {
config.headers.common['TenantId'] = getDomainPart1();
}
// 附加企业ID
const companyId = localStorage.getItem('CompanyId');
if (companyId) {
config.headers.common['CompanyId'] = companyId;
}
}
return config;
},
(error) => {
return Promise.reject(error);
} }
// 从配置文件获取租户ID
if (config.headers) {
config.headers.common['TenantId'] = TENANT_ID;
// 解析二级域名获取租户ID
if (getDomainPart1()) {
config.headers.common['TenantId'] = getDomainPart1();
}
}
return config;
},
(error) => {
return Promise.reject(error);
}
); );
/** /**
* *
*/ */
service.interceptors.response.use( service.interceptors.response.use(
(res: AxiosResponse<ApiResult<unknown>>) => {
// 登录过期处理
if (res.data?.code === 401) {
const currentPath = unref(router.currentRoute).path;
if (currentPath == LAYOUT_PATH) {
logout(true);
} else {
Modal.destroyAll();
Modal.info({
title: '系统提示',
content: '登录状态已过期, 请退出重新登录!',
okText: '重新登录',
onOk: () => {
logout(false, currentPath);
}
});
}
return Promise.reject(new Error(res.data.message));
(res: AxiosResponse<ApiResult<unknown>>) => {
// 登录过期处理
if (res.data?.code === 401) {
const currentPath = unref(router.currentRoute).path;
if (currentPath == LAYOUT_PATH) {
logout(true);
} else {
Modal.destroyAll();
Modal.info({
title: '系统提示',
content: '登录状态已过期, 请退出重新登录!',
okText: '重新登录',
onOk: () => {
logout(false, currentPath);
}
});
}
return Promise.reject(new Error(res.data.message));
}
// token 自动续期
const token = res.headers[TOKEN_HEADER_NAME.toLowerCase()];
if (token) {
setToken(token);
}
return res;
},
(error) => {
return Promise.reject(error);
} }
// token 自动续期
const token = res.headers[TOKEN_HEADER_NAME.toLowerCase()];
if (token) {
setToken(token);
}
return res;
},
(error) => {
return Promise.reject(error);
}
); );
export default service; export default service;

156
src/views/article/components/photo-list.vue

@ -0,0 +1,156 @@
<template>
<div class="photo-list-bg" :class="screenWidth > 567 ? 'hidden-sm-and-down' : 'hidden-sm-and-up'">
<div class="breadcrumb">
<a-breadcrumb>
<a-breadcrumb-item>
<a @click="openUrl(`/`)"><HomeOutlined /> 首页</a>
</a-breadcrumb-item>
<a-breadcrumb-item v-if="form.parentName">
<span>{{ form.parentName }}</span>
</a-breadcrumb-item>
<a-breadcrumb-item>
<span>{{ form.title }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
</div>
<a-row :gutter="[16,16]" style="padding: 16px">
<a-col
v-for="(item, index) in list"
:key="index"
v-bind="
styleResponsive
? { xl: 6, lg: 8, md: 12, sm: 12, xs: 12 }
: { span: 6 }
"
>
<a-card :bordered="false" hoverable style="margin-top: 16px">
<template #cover>
<a :href="`/a/${item.articleId}`">
<a-image
:src="`${item.image}`"
:preview="false"
/>
</a>
</template>
<a-card-meta :title="item.title" @click="openUrl(`/a/${item.articleId}`)">
<template #description>
<div
class="project-list-desc"
:title="item.title"
>
</div>
</template>
</a-card-meta>
</a-card>
</a-col>
</a-row>
<div class="pagination">
<a-pagination v-model:current="currentPage" :total="total" @change="reload" />
</div>
</div>
</template>
<script setup lang="ts">
import {timeAgo, toDateString} from "ele-admin-pro";
import {ref, unref, watch} from "vue";
import {Article} from "@/api/cms/article/model";
import {pageArticle} from "@/api/cms/article";
import {useThemeStore} from "@/store/modules/theme";
import {storeToRefs} from "pinia";
import { RightOutlined } from '@ant-design/icons-vue';
import {useRouter} from "vue-router";
import useFormData from "@/utils/use-form-data";
import {getArticleCategory, listArticleCategory} from "@/api/cms/category";
import {ArticleCategory} from "@/api/cms/category/model";
import { HomeOutlined, UserOutlined } from '@ant-design/icons-vue';
import {openUrl} from "@/utils/common";
const { currentRoute } = useRouter();
const themeStore = useThemeStore();
const { screenWidth, styleResponsive } = storeToRefs(themeStore);
const list = ref<Article[]>([]);
const categoryId = ref<number>(0);
const currentPage = ref(1);
const total = ref();
const selectedKeys = ref<string[]>(['sub1']);
const subCategoryList = ref<ArticleCategory[]>();
const ellipsis = ref<boolean>(true);
//
const { form,assignFields } = useFormData<ArticleCategory>({
categoryId: undefined,
title: '',
image: '',
parentId: undefined,
parentName: '',
avatar: ''
});
const linkTo = (url: string) => {
window.location.href = url
}
const reload = () => {
getArticleCategory(categoryId.value).then(data => {
assignFields(data);
if(data.parentId && data.parentId > 0){
listArticleCategory({parentId: data.parentId}).then(list => {
subCategoryList.value = list
})
}
})
pageArticle({categoryId: categoryId.value,page: currentPage.value}).then(res => {
if(res?.list){
list.value = res.list;
total.value = res?.count;
}
})
}
watch(
currentRoute,
(route) => {
const { redirectedFrom } = unref(route);
if(redirectedFrom){
const { params } = redirectedFrom;
const { id } = params;
if (id) {
categoryId.value = Number(id);
selectedKeys.value = []
selectedKeys.value.push(`sub${id}`)
}
}
reload();
},
{ immediate: true }
);
</script>
<style scoped lang="less">
.hidden-sm-and-down{
width: 1180px;
.ant-list-sm .ant-list-item{
padding: 8px 0;
}
}
.hidden-sm-and-up{
width: 100%;
:deep(.ant-list-sm .ant-list-item){
padding: 8px 0;
}
:deep(.ant-image img){
}
}
.photo-list-bg{
margin: 0 auto;
margin-bottom: 20px;
.breadcrumb{
padding-top: 20px;
padding-left: 5px;
}
}
.pagination{
text-align: center;
padding: 20px;
}
</style>

2
src/views/article/index.vue

@ -1,7 +1,7 @@
<template> <template>
<a-layout> <a-layout>
<Header />
<Banner /> <Banner />
<Header />
<a-layout-content> <a-layout-content>
<NewsList /> <NewsList />
</a-layout-content> </a-layout-content>

233
src/views/down/components/list.vue

@ -0,0 +1,233 @@
<template>
<div class="news-list-bg">
<div class="news-list" :style="screenWidth > 980 ? 'width: 1180px' : ''">
<div class="breadcrumb">
<a-breadcrumb>
<a-breadcrumb-item>
<a @click="openUrl(`/`)"><HomeOutlined /> Home</a>
</a-breadcrumb-item>
<a-breadcrumb-item v-if="form.parentName">
<span>{{ form.parentName }}</span>
</a-breadcrumb-item>
<a-breadcrumb-item>
<span>{{ form.title }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
</div>
<template v-if="form.parentId > 0">
<a-row :gutter="[16,16]">
<a-col :xl="5" :lg="12" :md="12" :sm="24">
<a-menu v-model:selectedKeys="selectedKeys">
<a-menu-item v-for="(item,index) in subCategoryList" :key="`sub${item.categoryId}`" @click="linkTo(`/article/${item.categoryId}`)">
{{ item.title }}
</a-menu-item>
</a-menu>
</a-col>
<a-col :xl="19" :lg="12" :md="12" :sm="24">
<template v-if="list">
<a-card :title="form.title">
<a-list :size="`small`" :data-source="list" style="padding: 0px">
<template #renderItem="{ item }">
<a-list-item key="item.title">
<a-list-item-meta>
<template #title>
<div class="title-box">
<a class="title-text" :href="`/article/detail/${item.articleId}`">{{ item.title }}</a>
<div class="ele-text-secondary">{{ item.comments }}</div>
<div class="actions ele-text-placeholder">
<a-space :size="20">
<span>浏览 {{ item.likes }}</span>
<span>{{ toDateString(item.createTime, 'YYYY-MM-dd')}}</span>
</a-space>
</div>
</div>
</template>
<template #avatar>
<a-image :src="item.image" :preview="false" />
</template>
</a-list-item-meta>
</a-list-item>
</template>
</a-list>
</a-card>
<div class="pagination">
<a-pagination v-model:current="currentPage" :total="total" @change="reload" />
</div>
</template>
</a-col>
</a-row>
</template>
<template v-else>
<a-card :title="form.title">
<a-list :size="`small`" :data-source="list" style="padding: 0px">
<template #renderItem="{ item }">
<a-list-item key="item.title">
<a-list-item-meta>
<template #title>
<div class="ele-cell" v-if="item.files.length > 0">
<a class="ele-cell-content" target="_blank" :href="`${item.files[0].url}`">{{ item.files[0].name }}</a>
<div class="actions ele-text-placeholder">
{{ toDateString(item.createTime, 'YYYY-MM-dd')}}
</div>
</div>
</template>
<template #avatar>
<a-image :src="item.image" :preview="false" />
</template>
</a-list-item-meta>
</a-list-item>
</template>
</a-list>
</a-card>
<div class="pagination">
<a-pagination v-model:current="currentPage" :total="total" @change="reload" />
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import {toDateString} from "ele-admin-pro";
import {ref, unref, watch} from "vue";
import {Article} from "@/api/cms/article/model";
import {pageArticle} from "@/api/cms/article";
import {useThemeStore} from "@/store/modules/theme";
import {storeToRefs} from "pinia";
import {getAd} from "@/api/cms/ad";
import { RightOutlined } from '@ant-design/icons-vue';
import {useRouter} from "vue-router";
import useFormData from "@/utils/use-form-data";
import {getArticleCategory, listArticleCategory} from "@/api/cms/category";
import {ArticleCategory} from "@/api/cms/category/model";
import { HomeOutlined, UserOutlined } from '@ant-design/icons-vue';
import {openUrl} from "@/utils/common";
const { currentRoute } = useRouter();
const themeStore = useThemeStore();
const { screenWidth, styleResponsive } = storeToRefs(themeStore);
const list = ref<Article[]>([]);
const newsBg3 = ref('');
const categoryId = ref<number>(0);
const currentPage = ref(1);
const total = ref();
const selectedKeys = ref<string[]>(['sub1']);
const subCategoryList = ref<ArticleCategory[]>();
//
const { form,assignFields } = useFormData<ArticleCategory>({
categoryId: undefined,
title: '',
image: '',
parentId: undefined,
parentName: '',
avatar: ''
});
const linkTo = (url: string) => {
window.location.href = url
}
const reload = () => {
getArticleCategory(categoryId.value).then(data => {
assignFields(data);
if(data.parentId && data.parentId > 0){
listArticleCategory({parentId: data.parentId}).then(list => {
subCategoryList.value = list
})
}
})
pageArticle({categoryId: categoryId.value,page: currentPage.value}).then(res => {
if(res?.list){
list.value = res.list.map(d => {
if(d.files){
d.files = JSON.parse(d.files);
}
return d
});
total.value = res?.count;
}
})
}
watch(
currentRoute,
(route) => {
const { redirectedFrom } = unref(route);
if(redirectedFrom){
const { params } = redirectedFrom;
const { id } = params;
if (id) {
categoryId.value = Number(id);
selectedKeys.value = []
selectedKeys.value.push(`sub${id}`)
}
}
reload();
},
{ immediate: true }
);
</script>
<style scoped lang="less">
.news-list-bg{
padding-bottom: 70px;
.news-list{
.breadcrumb{
padding-bottom: 20px;
padding-left: 5px;
}
margin: 0 auto;
padding: 20px 0;
.title{
margin-top: 10px;
padding-bottom: 20px;
display: flex;
align-items: center;
h4{
color: #ffffff;
font-weight: 500;
font-size: 26px;
}
.title-bar{
width: 6px;
height: 28px;
margin-right: 14px;
background: linear-gradient(to bottom, #b7a381, #e5cc8c);
}
}
.img-list{
display: flex;
justify-content: space-between;
padding-bottom: 30px;
}
}
.title-box{
display: flex;
flex-direction: column;
justify-content: space-around;
height: 120px;
.title-text {
font-weight: bold; font-size: 16px;
color: #333333;
}
.title-text:hover{
color: #ff0000;
}
.desc{
font-size: 14px;
}
.actions{
font-size: 12px;
}
}
}
.pagination{
text-align: center;
padding: 20px;
}
:deep(.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected){
background-color: #ffffff;
color: #999999;
border-bottom: 2px solid #fff1de;
}
</style>

150
src/views/down/detail/components/news-detail.vue

@ -0,0 +1,150 @@
<template>
<div class="news-list-bg">
<div class="news-list" :style="screenWidth > 980 ? 'width: 1180px' : ''">
<div class="breadcrumb">
<a-breadcrumb>
<a-breadcrumb-item>
<a @click="openUrl(`/`)"><HomeOutlined /> Home</a>
</a-breadcrumb-item>
<a-breadcrumb-item>
<a :href="`/article/${form.categoryId}`">{{ form.categoryName }}</a>
</a-breadcrumb-item>
<a-breadcrumb-item>
<span>{{ form.title }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
</div>
<a-card :title="form.title">
<div v-html="form.content"></div>
</a-card>
</div>
</div>
</template>
<script setup lang="ts">
import {toDateString} from "ele-admin-pro";
import {ref, unref, watch} from "vue";
import {Article} from "@/api/cms/article/model";
import {pageArticle,getArticle} from "@/api/cms/article";
import {useThemeStore} from "@/store/modules/theme";
import {storeToRefs} from "pinia";
import {getAd} from "@/api/cms/ad";
import { RightOutlined } from '@ant-design/icons-vue';
import {useRouter} from "vue-router";
import useFormData from "@/utils/use-form-data";
import {getArticleCategory, listArticleCategory} from "@/api/cms/category";
import {ArticleCategory} from "@/api/cms/category/model";
import { HomeOutlined, UserOutlined } from '@ant-design/icons-vue';
import {openUrl} from "@/utils/common";
const { currentRoute } = useRouter();
const themeStore = useThemeStore();
const { screenWidth, styleResponsive } = storeToRefs(themeStore);
const list = ref<Article[]>([]);
const activeKey = ref('1');
const newsBg3 = ref('');
const articleId = ref<number>(0);
const currentPage = ref(1);
const total = ref();
//
const { form,assignFields } = useFormData<Article>({
categoryId: undefined,
categoryName: '',
title: '',
image: '',
content: '',
parentId: undefined,
parentName: '',
source: '',
virtualViews: undefined,
actualViews: undefined
});
// getAd(253).then(data => {
// const { images } = data;
// if(images){
// newsBg3.value = JSON.parse(images)[0].url;
// }
// })
const reload = () => {
// getArticleCategory(categoryId.value).then(data => {
// assignFields(data);
// })
getArticle(articleId.value).then(data => {
assignFields(data)
})
}
watch(
currentRoute,
(route) => {
const { params } = unref(route);
const { id } = params;
if (id) {
articleId.value = Number(id);
}
reload();
},
{ immediate: true }
);
</script>
<style scoped lang="less">
.news-list-bg{
padding-bottom: 70px;
.news-list{
.breadcrumb{
padding-bottom: 20px;
padding-left: 5px;
}
margin: 0 auto;
padding: 20px 0;
.title{
margin-top: 10px;
padding-bottom: 20px;
display: flex;
align-items: center;
h4{
color: #ffffff;
font-weight: 500;
font-size: 26px;
}
.title-bar{
width: 6px;
height: 28px;
margin-right: 14px;
background: linear-gradient(to bottom, #b7a381, #e5cc8c);
}
}
.img-list{
display: flex;
justify-content: space-between;
padding-bottom: 30px;
}
}
.title-box{
display: flex;
flex-direction: column;
justify-content: space-around;
height: 120px;
.title-text {
font-weight: bold; font-size: 16px;
color: #333333;
}
.title-text:hover{
color: #ff0000;
}
.desc{
font-size: 14px;
}
.actions{
font-size: 12px;
}
}
}
.pagination{
text-align: center;
padding: 20px;
}
</style>

24
src/views/down/detail/index.vue

@ -0,0 +1,24 @@
<template>
<a-layout>
<Header />
<Banner />
<a-layout-content>
<NewsDetail />
</a-layout-content>
<Footer />
</a-layout>
</template>
<script lang="ts" setup>
import Banner from "@/components/Banner/index.vue";
import NewsDetail from "./components/news-detail.vue";
</script>
<script lang="ts">
export default {
name: 'ArticleDetail'
};
</script>
<style lang="less">
</style>

24
src/views/down/index.vue

@ -0,0 +1,24 @@
<template>
<a-layout>
<Banner />
<Header />
<a-layout-content>
<List />
</a-layout-content>
<Footer />
</a-layout>
</template>
<script lang="ts" setup>
import Banner from "@/components/Banner/index.vue";
import List from "./components/list.vue";
</script>
<script lang="ts">
export default {
name: 'DownIndex'
};
</script>
<style lang="less">
</style>

195
src/views/down/parent/components/news-list.vue

@ -0,0 +1,195 @@
<template>
<div class="news-list-bg">
<div class="news-list" :style="screenWidth > 980 ? 'width: 1180px' : ''">
<div class="breadcrumb">
<a-breadcrumb>
<a-breadcrumb-item>
<HomeOutlined @click="openUrl(`/`)" />
</a-breadcrumb-item>
<a-breadcrumb-item>
<a :href="`/article/parent/${form.parentId}`">{{ form.parentName }}</a>
</a-breadcrumb-item>
<a-breadcrumb-item>
<span>{{ form.title }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
</div>
{{ form }}
<a-card :title="form.title">
<a-list :size="`small`" :data-source="list" style="padding: 0px">
<template #renderItem="{ item }">
<a-list-item key="item.title">
<a-list-item-meta>
<template #title>
<div class="title-box">
<a class="title-text" :href="`/article/detail/${item.articleId}`">{{ item.title }}</a>
<div class="ele-text-secondary">{{ item.comments }}</div>
<div class="actions ele-text-placeholder">
<a-space :size="20">
<span>浏览 {{ item.likes }}</span>
<span>{{ toDateString(item.createTime, 'YYYY-MM-dd')}}</span>
</a-space>
</div>
</div>
</template>
<template #avatar>
<a-image :src="item.image" :preview="false" />
</template>
</a-list-item-meta>
</a-list-item>
</template>
</a-list>
</a-card>
</div>
</div>
</template>
<script setup lang="ts">
import {toDateString} from "ele-admin-pro";
import {ref, unref, watch} from "vue";
import {Article} from "@/api/cms/article/model";
import {pageArticle} from "@/api/cms/article";
import {useThemeStore} from "@/store/modules/theme";
import {storeToRefs} from "pinia";
import {getAd} from "@/api/cms/ad";
import { RightOutlined } from '@ant-design/icons-vue';
import {useRouter} from "vue-router";
import useFormData from "@/utils/use-form-data";
import {getArticleCategory, listArticleCategory} from "@/api/cms/category";
import {ArticleCategory} from "@/api/cms/category/model";
import { HomeOutlined, UserOutlined } from '@ant-design/icons-vue';
import {openUrl} from "@/utils/common";
const { currentRoute } = useRouter();
const themeStore = useThemeStore();
const { screenWidth, styleResponsive } = storeToRefs(themeStore);
const list = ref<Article[]>([]);
const activeKey = ref('1');
const newsBg3 = ref('');
const categoryId = ref<number>(0);
const routes = [
{
path: '首页',
breadcrumbName: 'First-level Menu',
},
{
path: 'first',
breadcrumbName: 'Second-level Menu',
},
{
path: 'second',
breadcrumbName: 'Third-level Menu',
},
];
//
const { form,assignFields } = useFormData<ArticleCategory>({
categoryId: undefined,
title: '',
image: '',
parentId: undefined,
parentName: '',
avatar: ''
});
getAd(253).then(data => {
const { images } = data;
if(images){
newsBg3.value = JSON.parse(images)[0].url;
}
})
const reload = () => {
getArticleCategory(categoryId.value).then(data => {
console.log(data)
assignFields(data);
console.log(form)
routes.push({
path: '',
breadcrumbName: ''
})
})
pageArticle({categoryId: categoryId.value}).then(res => {
if(res?.list){
list.value = res.list;
}
})
}
watch(
currentRoute,
(route) => {
const { params } = unref(route);
const { id } = params;
if (id) {
categoryId.value = Number(id);
}
reload();
},
{ immediate: true }
);
</script>
<style scoped lang="less">
.news-list-bg{
padding-bottom: 70px;
.news-list{
.breadcrumb{
padding-bottom: 20px;
padding-left: 5px;
}
margin: 0 auto;
padding: 20px 0;
.title{
margin-top: 10px;
padding-bottom: 20px;
display: flex;
align-items: center;
h4{
color: #ffffff;
font-weight: 500;
font-size: 26px;
}
.title-bar{
width: 6px;
height: 28px;
margin-right: 14px;
background: linear-gradient(to bottom, #b7a381, #e5cc8c);
}
}
.img-list{
display: flex;
justify-content: space-between;
padding-bottom: 30px;
}
}
.title-box{
display: flex;
flex-direction: column;
justify-content: space-around;
height: 120px;
.title-text {
font-weight: bold; font-size: 16px;
color: #333333;
}
.title-text:hover{
color: #ff0000;
}
.desc{
font-size: 14px;
}
.actions{
font-size: 12px;
}
}
}
.ant-list-sm .ant-list-item{
padding: 8px 0 !important;
}
.ant-tabs-nav-list .ant-tabs-tab{
background-color: #ff0000 !important;
}
.ant-tabs-nav-list .ant-tabs-tab-active{
background-color: #ff00fe !important;
}
</style>

24
src/views/down/parent/index.vue

@ -0,0 +1,24 @@
<template>
<a-layout>
<Header />
<Banner />
<a-layout-content>
<NewsList />
</a-layout-content>
<Footer />
</a-layout>
</template>
<script lang="ts" setup>
import Banner from "@/components/Banner/index.vue";
import NewsList from "./components/news-list.vue";
</script>
<script lang="ts">
export default {
name: 'ArticleIndex'
};
</script>
<style lang="less">
</style>

377
src/views/form/components/detail.vue

@ -0,0 +1,377 @@
<template>
<div class="news-list-bg" v-if="formInfo">
<div class="news-list" :style="screenWidth > 980 ? 'width: 1180px' : ''">
<div class="breadcrumb">
<a-breadcrumb>
<a-breadcrumb-item>
<a @click="openUrl(`/`)"><HomeOutlined /> Home</a>
</a-breadcrumb-item>
<a-breadcrumb-item>
<span>{{ formInfo.name }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
</div>
<a-space direction="vertical">
<div>{{ form }}</div>
<a-divider />
<div>{{ rules }}</div>
</a-space>
<a-card :title="formInfo.name" :bordered="false" hoverable :head-style="{ backgroundColor: '#e0f0fd', color: '#0096FF', fontSize: '24px'}" :style="{ boxShadow: '2px 0px 10px #ccc', marginBottom: '20px'}">
<a-form ref="formRef"
:model="form"
:rules="rules"
labelAlign="left"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 12, sm: 19, xs: 24 } : { flex: '1' }
">
<template v-for="(item, index) in formInfo.layout" :key="index">
<a-form-item v-if="item.name" :name="item.name">
<template #label>
<a-space>
<span>{{ item.name }}</span>
</a-space>
</template>
<template v-if="item.type == 'text'">
<a-input
allow-clear
:maxlength="200"
v-model:value="form[item.name]"
:placeholder="`${
item.placeholder ? `请填写${item.name}` : '请输入'
}`"
/>
</template>
<template v-if="item.type == 'textarea'">
<a-textarea
:rows="4"
:maxlength="200"
show-count
:placeholder="`请输入${item.name}`"
v-model:value="form[item.name]"
/>
</template>
<template v-if="item.type == 'radio'">
<a-radio-group v-model:value="form[item.name]">
<template
v-for="(radio, radioIndex) in item.options"
:key="radioIndex"
>
<a-radio :value="radio">{{ radio }}</a-radio>
</template>
</a-radio-group>
</template>
<template v-if="item.type == 'checkbox'">
<a-checkbox-group
v-model:value="item.checkedList"
:options="item.options"
/>
</template>
<template v-if="item.type == 'phone'">
<a-input
allow-clear
:maxlength="11"
:placeholder="`${
item.placeholder ? `请填写${item.name}` : '请输入'
}`"
v-model:value="form[item.name]"
/>
</template>
<template v-if="item.type == 'email'">
<a-input
allow-clear
:maxlength="30"
:placeholder="`${
item.placeholder ? `请填写${item.name}` : '请输入'
}`"
v-model:value="form[item.name]"
/>
</template>
<template v-if="item.type == 'time'">
<a-time-picker
v-model:value="timeItem"
format="HH:mm"
@change="onTimeItem(index)"
/>
</template>
<template v-if="item.type == 'date'">
<a-date-picker
v-model:value="item.value"
value-format="YYYY-MM-DD"
/>
</template>
<template v-if="item.type == 'image'">
<ele-image-upload
v-model:value="imageItem"
:limit="1"
:upload-handler="uploadHandlerItem"
@click="onClickItem(index)"
@upload="onUploadItem"
/>
</template>
<template v-if="item.type == 'video'"></template>
</a-form-item>
</template>
<a-form-item label="操作">
<div class="submit-btn">
<a-button type="primary" size="large" @click="save">提交</a-button>
</div>
</a-form-item>
</a-form>
</a-card>
</div>
</div>
</template>
<script setup lang="ts">
import {toDateString} from "ele-admin-pro";
import {reactive, ref, unref, watch} from "vue";
import { getForm } from '@/api/cms/form';
import { addFormRecord } from '@/api/cms/form-record';
import {useThemeStore} from "@/store/modules/theme";
import {storeToRefs} from "pinia";
import {useRouter} from "vue-router";
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import useFormData from "@/utils/use-form-data";
import { HomeOutlined, UserOutlined } from '@ant-design/icons-vue';
import {openUrl} from "@/utils/common";
import {FormInstance} from "ant-design-vue/es/form";
import {message} from "ant-design-vue";
import {Dayjs} from "dayjs";
import {uploadFile} from "@/api/system/file";
const formData = ref<any[]>([
{ id: 1, type: 'text', name: '姓名', checked: true },
{ id: 2, type: 'phone', name: '手机号码', checked: true },
{ id: 3, type: 'date', name: '日期', checked: false }
]);
//
const formRef = ref<FormInstance | null>(null);
const { currentRoute } = useRouter();
const themeStore = useThemeStore();
const { screenWidth, styleResponsive } = storeToRefs(themeStore);
const formId = ref<number>(268);
const total = ref();
const formInfo = ref<any>();
//
const timeItem = ref<Dayjs>();
//
const imageItem = ref<ItemType[]>([]);
const currentItemIndex = ref<number>(0);
const formRules = ref<any>({});
//
const { form,assignFields,resetFields } = useFormData<any>({});
const onTimeItem = (index: number) => {
formData.value[index].value = toDateString(timeItem.value, 'HH:mm');
};
const onClickItem = (index: number) => {
currentItemIndex.value = index;
};
const reload = () => {
getForm(formId.value).then(data => {
data.layout = JSON.parse(data.layout)
formInfo.value = data;
resetFields();
//
data.layout.map(d => {
console.log(d)
let item2 = {}
item2[d.name] = ''
Object.assign(form,item2)
})
//
data.layout.filter(d => d.checked == true).map(d => {
let item = {}
item[d.name] = [{
"required": true,
"type": 'string',
"message": `请填写${d.name}`,
"trigger": 'blur'
}]
Object.assign(rules,item)
})
})
}
const rules = reactive({});
const onSelect = (item: any, index: number) => {
if (item.type == 'checkbox') {
formData.value[index].checkedList = ['aaa'];
formData.value[index].options = ['aaa', 'bbb'];
}
if (item.type == 'radio') {
formData.value[index].radio = 'A';
formData.value[index].options = ['A', 'B', 'C', 'D'];
}
};
/* 上传事件 */
const uploadHandlerItem = (file: File) => {
const item: ItemType = {
file,
uid: (file as any).uid,
name: file.name
};
if (file.type.startsWith('video')) {
if (file.size / 1024 / 1024 > 200) {
message.error('大小不能超过 200MB');
return;
}
}
if (file.type.startsWith('image')) {
if (file.size / 1024 / 1024 > 5) {
message.error('大小不能超过 5MB');
return;
}
}
onUploadItem(item);
};
const onUploadItem = (item: any) => {
const { file } = item;
if (currentItemIndex.value > 0) {
uploadFile(file)
.then((data) => {
// console.log(data);
// console.log(formData.value[currentItemIndex.value])
formData.value[currentItemIndex.value].image = data.path;
imageItem.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
currentItemIndex.value = 0;
})
.catch((e) => {
message.error(e.message);
});
}
};
const objToArray = (obj) => {
return Object.keys(obj).map((key) => {
return {
name: key,
value: obj[key]
}
});
}
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
const saveData = {
formId: 268,
formData: JSON.stringify(objToArray(form))
};
addFormRecord(saveData)
.then((msg) => {
message.success(msg);
reload();
})
.catch((e) => {
message.error(e.message);
});
})
.catch(() => {});
};
watch(
currentRoute,
(route) => {
console.log(route)
const { params } = unref(route);
const { id } = params;
if (id) {
formId.value = Number(id);
}
reload();
},
{ immediate: true }
);
</script>
<style scoped lang="less">
.news-list-bg{
padding-bottom: 70px;
.news-list{
.breadcrumb{
padding-bottom: 20px;
padding-left: 5px;
}
margin: 0 auto;
padding: 20px 0;
.title{
margin-top: 10px;
padding-bottom: 20px;
display: flex;
align-items: center;
h4{
color: #ffffff;
font-weight: 500;
font-size: 26px;
}
.title-bar{
width: 6px;
height: 28px;
margin-right: 14px;
background: linear-gradient(to bottom, #b7a381, #e5cc8c);
}
}
.img-list{
display: flex;
justify-content: space-between;
padding-bottom: 30px;
}
}
.title-box{
display: flex;
flex-direction: column;
justify-content: space-around;
height: 120px;
.title-text {
font-weight: bold; font-size: 16px;
color: #333333;
}
.title-text:hover{
color: #ff0000;
}
.desc{
font-size: 14px;
}
.actions{
font-size: 12px;
}
}
}
.pagination{
text-align: center;
padding: 20px;
}
.ele-text-secondary{
font-size: 16px;
}
.ele-text-heading{
line-height: 1.6em;
font-size: 16px;
}
.line-height-d5{
p{
line-height: .7;
}
}
</style>

24
src/views/form/index.vue

@ -0,0 +1,24 @@
<template>
<a-layout>
<Banner />
<Header />
<a-layout-content>
<Detail />
</a-layout-content>
<Footer />
</a-layout>
</template>
<script lang="ts" setup>
import Banner from "@/components/Banner/index.vue";
import Detail from "./components/detail.vue";
</script>
<script lang="ts">
export default {
name: 'Form'
};
</script>
<style lang="less">
</style>

2
src/views/index/index.vue

@ -175,7 +175,7 @@
</a-card> </a-card>
<a-card title="Paper Submission" :bordered="false" hoverable :head-style="{ backgroundColor: '#e0f0fd', color: '#0096FF', fontSize: '24px'}" :style="{ boxShadow: '2px 0px 10px #ccc', marginBottom: '20px'}"> <a-card title="Paper Submission" :bordered="false" hoverable :head-style="{ backgroundColor: '#e0f0fd', color: '#0096FF', fontSize: '24px'}" :style="{ boxShadow: '2px 0px 10px #ccc', marginBottom: '20px'}">
<div class="ele-text-heading"> <div class="ele-text-heading">
<p>Authors are invited to submit papers through the conference Submission System since January 1, 2024.
<p>Authors are invited to submit papers through the conference Submission System since April 1, 2024.
Submissions must be original and should not have been published previously or be under consideration for publication while being evaluated for this conference.</p> Submissions must be original and should not have been published previously or be under consideration for publication while being evaluated for this conference.</p>

Loading…
Cancel
Save