基于Taro.js开发的H5应用

This commit is contained in:
2025-07-07 13:24:48 +08:00
parent 3db95dbe9b
commit 59f771d542
26 changed files with 919 additions and 193 deletions

View File

@@ -8,9 +8,7 @@ import type { CmsNavigation, CmsNavigationParam } from './model';
export async function pageCmsNavigation(params: CmsNavigationParam) {
const res = await request.get<ApiResult<PageResult<CmsNavigation>>>(
'/cms/cms-navigation/page',
{
params
}
);
if (res.code === 0) {
return res.data;

View File

@@ -30,6 +30,8 @@ export interface CmsNavigation {
banner?: string;
// 图标颜色
color?: string;
// 栅格宽度
span?: string;
// 是否隐藏, 0否, 1是(仅注册路由不显示在左侧菜单)
hide?: number;
// 可见类型 0所有人 1登录可见 2密码可见

View File

@@ -3,7 +3,6 @@ export default defineAppConfig({
'pages/index/index',
'pages/kefu/kefu',
'pages/user/user',
'pages/article/article',
'pages/study/study'
],
"subpackages": [
@@ -27,6 +26,20 @@ export default defineAppConfig({
"userVerify/index",
"userVerify/admin"
]
},
{
"root": "article",
"pages": [
"index",
"detail"
]
},
{
"root": "photo",
"pages": [
"index",
"detail"
]
}
],
window: {

View File

@@ -1,3 +1,6 @@
// 引入手机版容器样式
//@use './styles/mobile-container.scss';
/* ./src/index.css */
@tailwind base;
@tailwind components;
@@ -10,6 +13,8 @@ page{
background-position: bottom;
}
// 在全局样式文件中添加
button {
&::after {

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '详情',
navigationBarBackgroundColor: '#ffe0e0'
})

128
src/article/detail.scss Normal file
View File

@@ -0,0 +1,128 @@
.content {
padding: 32px;
line-height: 2.4rem;
// 富文本内容样式
:global {
// 段落样式
p {
margin: 16px 0;
line-height: 1.8;
text-align: justify;
}
// 标题样式
h1, h2, h3, h4, h5, h6 {
margin: 24px 0 16px 0;
font-weight: bold;
line-height: 1.4;
}
h1 { font-size: 24px; }
h2 { font-size: 22px; }
h3 { font-size: 20px; }
h4 { font-size: 18px; }
h5 { font-size: 16px; }
h6 { font-size: 14px; }
// 图片样式
img {
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 16px 0;
display: block;
}
// 列表样式
ul, ol {
margin: 16px 0;
padding-left: 24px;
li {
margin: 8px 0;
line-height: 1.6;
}
}
// 引用样式
blockquote {
margin: 16px 0;
padding: 16px;
background-color: #f5f5f5;
border-left: 4px solid #ddd;
border-radius: 4px;
p {
margin: 0;
font-style: italic;
}
}
// 代码样式
code {
background-color: #f5f5f5;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 14px;
}
pre {
background-color: #f5f5f5;
padding: 16px;
border-radius: 8px;
overflow-x: auto;
margin: 16px 0;
code {
background: none;
padding: 0;
}
}
// 表格样式
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
th, td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
th {
background-color: #f5f5f5;
font-weight: bold;
}
}
// 链接样式
a {
color: #1890ff;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
// 分割线样式
hr {
margin: 24px 0;
border: none;
border-top: 1px solid #eee;
}
// 强调样式
strong, b {
font-weight: bold;
}
em, i {
font-style: italic;
}
}
}

52
src/article/detail.tsx Normal file
View File

@@ -0,0 +1,52 @@
import {useEffect, useState} from 'react'
import {Tag} from '@nutui/nutui-react-taro'
import {useRouter} from '@tarojs/taro'
import {Divider} from '@nutui/nutui-react-taro'
import {CmsArticle} from "@/api/cms/cmsArticle/model"
import {Eye} from '@nutui/icons-react-taro'
// 显示html富文本
import {View, RichText} from '@tarojs/components'
import './detail.scss'
import Line from "@/components/Gap";
import {getCmsArticle} from "@/api/cms/cmsArticle";
function Detail() {
const {params} = useRouter();
// 文章详情
const [item, setItem] = useState<CmsArticle>()
// 浏览量
const [views, setViews] = useState<number>()
const reload = () => {
getCmsArticle(Number(params.id)).then(data => {
if (data) {
setItem(data)
setViews(data.actualViews)
}
})
}
useEffect(() => {
reload();
}, []);
return (
<div className={'bg-white'}>
<div className={'p-3 font-bold text-lg'}>{item?.title}</div>
<div className={'flex justify-between px-3'}>
<Tag type={'success'}>{item?.categoryName}</Tag>
<div className={'flex items-center gap-2 text-sm text-gray-400'}><Eye size={14}/>{views}</div>
</div>
<Divider/>
<View className={'content text-gray-700 text-sm'}>
<RichText
nodes={item?.content || '暂无内容'}
space="nbsp"
/>
</View>
<Line height={44}/>
</div>
)
}
export default Detail

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '红色资源'
})

109
src/article/index.tsx Normal file
View File

@@ -0,0 +1,109 @@
import {useEffect, useState} from "react";
import {pageCmsArticle} from "@/api/cms/cmsArticle";
import {CmsArticle} from "@/api/cms/cmsArticle/model";
import Taro from '@tarojs/taro'
import {useRouter} from '@tarojs/taro'
import {Image} from '@nutui/nutui-react-taro'
import {getCmsNavigation, pageCmsNavigation} from "@/api/cms/cmsNavigation";
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
/**
* 文章终极列表
* @constructor
*/
const Index = () => {
const {params} = useRouter();
const [navigation, setNavigation] = useState<CmsNavigation>()
const [childCategory, setChildCategory] = useState<CmsNavigation[]>([])
const [list, setList] = useState<CmsArticle[]>([])
const reload = async () => {
// 获取栏目ID
const categoryId = Number(params.id);
// 当前栏目信息
const navs = await getCmsNavigation(categoryId);
// 二级栏目
const childCateogry = await pageCmsNavigation({parentId: categoryId});
// 终极新闻列表
const articles = await pageCmsArticle({categoryId});
// 当前栏目信息
if (navs) {
setNavigation(navs);
}
// 获取子级栏目
if (childCateogry) {
setChildCategory(childCateogry.list)
}
// 新闻列表
if (articles) {
setList(articles?.list || [])
}
}
useEffect(() => {
reload()
}, [])
return (
<>
<div style={{padding: navigation?.span + 'px'}}>
<Image src={navigation?.style} width={'100%'}
height={'auto'}/>
</div>
<div className={'bg-white rounded-lg py-3 px-3'}>
<div className={'grid grid-cols-2 gap-3'}>
{
// 子级栏目
childCategory.map((item, index) => {
return (
<div
key={index}
className={'flex flex-col justify-center items-center cursor-pointer'}
onClick={() => Taro.navigateTo({url: `./index?id=${item.navigationId}`})}
>
{/* 图片容器 */}
<div className={'w-full mb-2 flex justify-center'}>
<img
className={'object-cover rounded-lg'}
src={item.icon}
alt={item.title || ''}
/>
</div>
</div>
)
})
}
</div>
<div className={'grid grid-cols-3'}>
{
// 终极文章列表
list.map((item, index) => {
return (
<div
key={index}
className={'flex flex-col items-center cursor-pointer my-1'}
onClick={() => Taro.navigateTo({url: `./detail?id=${item.articleId}`})}
>
{/* 图片容器 */}
<div className={'w-full mb-2 flex justify-center'}>
<img
className={'object-cover rounded-lg'}
src={item.image}
alt={item.title || ''}
/>
</div>
{/* 标题 */}
<div className={'text-xs text-center text-gray-800 leading-tight px-1'}>
{item.title}
</div>
</div>
)
})
}
</div>
</div>
</>
)
}
export default Index

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '红色资源'
})

View File

@@ -0,0 +1,72 @@
import {useEffect, useState} from "react";
import {pageCmsArticle} from "@/api/cms/cmsArticle";
import {CmsArticle} from "@/api/cms/cmsArticle/model";
import Taro from '@tarojs/taro'
import {useRouter} from '@tarojs/taro'
import {Image} from '@nutui/nutui-react-taro'
import {getCmsNavigation} from "@/api/cms/cmsNavigation";
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
/**
* 文章终极列表
* @constructor
*/
const Article = () => {
const {params} = useRouter();
const [navigation, setNavigation] = useState<CmsNavigation>()
const [list, setList] = useState<CmsArticle[]>([])
const reload = async () => {
const categoryId = Number(params.id);
const nav = await getCmsNavigation(categoryId);
const articles = await pageCmsArticle({categoryId});
if(nav){
setNavigation(nav);
}
if(articles){
setList(articles?.list)
}
}
useEffect(() => {
reload()
}, [])
return (
<div className="mobile-container">
<Image src={navigation?.style} width={'100%'}
height={'auto'}/>
<div className={'bg-white rounded-lg py-3 px-2'}>
{/* 宫格布局容器 */}
<div className={'grid grid-cols-3'}>
{
list.map((item, index) => {
return (
<div
key={index}
className={'flex flex-col items-center cursor-pointer my-1'}
onClick={() => Taro.navigateTo({url: `/pages/article/detail?id=${item.articleId}`})}
>
{/* 图片容器 */}
<div className={'w-full mb-2 flex justify-center'}>
<img
className={'object-cover rounded-lg'}
src={item.image}
alt={item.title || ''}
/>
</div>
{/* 标题 */}
<div className={'text-xs text-center text-gray-800 leading-tight px-1'}>
{item.title}
</div>
</div>
)
})
}
</div>
</div>
</div>
)
}
export default Article

View File

@@ -1,50 +0,0 @@
import {useEffect, useState} from "react";
import {ArrowRight} from '@nutui/icons-react-taro'
import {pageCmsArticle} from "@/api/cms/cmsArticle";
import {CmsArticle} from "@/api/cms/cmsArticle/model";
import Taro from '@tarojs/taro'
/**
* 文章终极列表
* @constructor
*/
const Article = () => {
// const {params} = useRouter();
// const [categoryId, setCategoryId] = useState<number>(3494)
const [list, setList] = useState<CmsArticle[]>([])
const reload = () => {
// if (params.id) {
// setCategoryId(Number(params.id))
// }
pageCmsArticle({}).then(res => {
if (res?.list) {
setList(res?.list)
}
})
}
useEffect(() => {
reload()
}, [])
return (
<div className={'px-3 mt-4 mb-10'}>
<div className={'flex flex-col justify-between items-center bg-white rounded-lg p-4'}>
<div className={'bg-white w-full'}>
{
list.map((item, index) => {
return (
<div key={index} className={'flex justify-between items-center py-2'} onClick={() => Taro.navigateTo({url: `/cms/help?id=${item.articleId}`}) }>
<div className={'text-sm'}>{item.title}</div>
<ArrowRight color={'#cccccc'} size={18} />
</div>
)
})
}
</div>
</div>
</div>
)
}
export default Article

View File

@@ -0,0 +1,54 @@
import {useEffect, useState} from 'react'
import {Tag} from '@nutui/nutui-react-taro'
import {useRouter} from '@tarojs/taro'
import {Divider} from '@nutui/nutui-react-taro'
import {CmsArticle} from "@/api/cms/cmsArticle/model"
import {Eye} from '@nutui/icons-react-taro'
// 显示html富文本
import {View, RichText} from '@tarojs/components'
import './detail.scss'
import Line from "@/components/Gap";
import {getCmsArticle} from "@/api/cms/cmsArticle";
function Detail() {
const {params} = useRouter();
// 文章详情
const [item, setItem] = useState<CmsArticle>()
// 浏览量
const [views, setViews] = useState<number>()
const reload = () => {
getCmsArticle(Number(params.id)).then(data => {
if (data) {
setItem(data)
setViews(data.actualViews)
}
})
}
useEffect(() => {
reload();
}, []);
return (
<div className="mobile-container">
<div className={'bg-white'}>
<div className={'p-3 font-bold text-lg'}>{item?.title}</div>
<div className={'flex justify-between px-3'}>
<Tag type={'success'}>{item?.categoryName}</Tag>
<div className={'flex items-center gap-2 text-sm text-gray-400'}><Eye size={14}/>{views}</div>
</div>
<Divider/>
<View className={'content text-gray-700 text-sm'}>
<RichText
nodes={item?.content || '暂无内容'}
space="nbsp"
/>
</View>
<Line height={44}/>
</div>
</div>
)
}
export default Detail

View File

@@ -6,6 +6,7 @@ import {pageCmsAd} from "@/api/cms/cmsAd";
const MyPage = () => {
const [item, setItem] = useState<CmsAd>()
const [height, setHeight] = useState<string>()
const [width, setWidth] = useState<string>()
const reload = () => {
pageCmsAd({keywords: '幻灯片'}).then(data => {
@@ -13,6 +14,7 @@ const MyPage = () => {
if(data && data?.count > 0){
const cmsAd = data.list[0];
setHeight(cmsAd.height)
setWidth(cmsAd.width)
setItem(data.list[0])
}
})
@@ -23,15 +25,15 @@ const MyPage = () => {
}, [])
return (
<>
<Swiper defaultValue={0} height={height} indicator style={{ height: height + 'px' }}>
<div style={{width: width, height: height}} className={'px-3'}>
<Swiper defaultValue={0} height={height} indicator style={{ height: height + 'px', margin: '0 auto' }}>
{item?.imageList?.map((item) => (
<Swiper.Item key={item}>
<img width="100%" height="100%" src={item.url} alt="" style={{ height: height + 'px' }} />
</Swiper.Item>
))}
</Swiper>
</>
</div>
)
}
export default MyPage

36
src/pages/index/Image.tsx Normal file
View File

@@ -0,0 +1,36 @@
import {useEffect, useState} from 'react'
import {CmsAd} from "@/api/cms/cmsAd/model";
import {pageCmsAd} from "@/api/cms/cmsAd";
const MyPage = () => {
const [item, setItem] = useState<CmsAd>()
const [image, setImage] = useState()
const [height, setHeight] = useState<string>()
const [width, setWidth] = useState<string>()
const reload = () => {
pageCmsAd({keywords: 'TopBanner'}).then(data => {
if (data && data?.count > 0) {
const cmsAd = data.list[0];
setItem(cmsAd)
console.log(item,'ad')
setHeight(cmsAd.height)
setWidth(cmsAd.width)
if (cmsAd.imageList && cmsAd.imageList.length > 0) {
setImage(cmsAd.imageList[0].url)
}
}
})
}
useEffect(() => {
reload()
}, [])
return (
<>
<img src={image} alt="" style={{width: width + 'px', height: height + 'px'}}/>
</>
)
}
export default MyPage

View File

@@ -1,78 +1,19 @@
import {useEffect, useState} from 'react'
import {navigateTo} from '@tarojs/taro'
import Taro from '@tarojs/taro'
import {Button} from '@tarojs/components';
import {Image} from '@nutui/nutui-react-taro'
import {getSiteInfo} from "@/api/layout";
import {TenantId} from "@/utils/config";
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
const Page = () => {
const [loading, setLoading] = useState(true)
const [isLogin, setIsLogin] = useState<boolean>(false)
const [navItems, setNavItems] = useState<CmsNavigation[]>([])
/* 获取用户手机号 */
const handleGetPhoneNumber = ({detail}) => {
const {code, encryptedData, iv} = detail
Taro.login({
success: function () {
if (code) {
Taro.request({
url: 'https://server.gxwebsoft.com/api/wx-login/loginByMpWxPhone',
method: 'POST',
data: {
code,
encryptedData,
iv,
notVerifyPhone: true,
refereeId: 0,
sceneType: 'save_referee',
tenantId: TenantId
},
header: {
'content-type': 'application/json',
TenantId
},
success: function (res) {
Taro.setStorageSync('access_token', res.data.data.access_token)
Taro.setStorageSync('UserId', res.data.data.user.userId)
Taro.setStorageSync('Phone', res.data.data.user.phone)
setIsLogin(true)
Taro.showToast({
title: '登录成功',
icon: 'success'
});
}
})
} else {
console.log('登录失败!')
}
}
})
}
const onLogin = (item: any) => {
if(!isLogin){
return navigateTo({url: `/pages/category/category?id=${item.navigationId}`})
}else {
// 善款明细
if(item.navigationId == 4119){
return navigateTo({url: `/bszx/pay-record/pay-record`})
}
return navigateTo({url: `/pages/category/category?id=${item.navigationId}`})
}
}
const reload = () => {
getSiteInfo().then(res => {
console.log(res);
console.log(res.topNavs, 'top');
setNavItems(res.topNavs || []);
})
setLoading(false);
};
useEffect(() => {
@@ -80,34 +21,19 @@ const Page = () => {
}, [])
return (
<div className={'my-3'}>
<div>
<div className={'flex justify-between pb-2 px-1'}>
<div className={'py-2 my-3 mx-2'}>
<div className={'bg-white grid grid-cols-1 md:grid-cols-3 lg:grid-cols-3 gap-2'}>
{
navItems.map((item, index) => (
<div key={index} className={'text-center'}>
{
isLogin && !loading ?
<div className={'flex flex-col justify-center items-center'} onClick={() => {
onLogin(item)
}}>
<Image src={item.icon} height={28} width={28}/>
<div className={'mt-2'} style={{fontSize: '15px'}}>{item?.title}</div>
</div>
:
<Button className={'text-white'} open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
<div className={'flex flex-col justify-center items-center'}>
<Image src={item.icon} height={28} width={28}/>
navItems.map((item) => (
<div className={'flex flex-col justify-center items-center'} onClick={() => Taro.navigateTo({url: `/${item.model}/index?id=${item.navigationId}`})}>
<Image className={'shadow-xl rounded-lg'} style={{borderRadius: '8px'}} src={item.icon}
height={90} width={90}/>
<div className={'mt-2 text-gray-700'} style={{fontSize: '15px'}}>{item?.title}</div>
</div>
</Button>
}
</div>
))
}
</div>
</div>
</div>
)
}
export default Page

View File

@@ -1,33 +0,0 @@
import { useEffect, useState } from 'react'
import { Swiper } from '@nutui/nutui-react-taro'
import {CmsAd} from "@/api/cms/cmsAd/model";
import {pageCmsAd} from "@/api/cms/cmsAd";
const MyPage = () => {
const [item, setItem] = useState<CmsAd>()
const reload = () => {
pageCmsAd({keywords: 'TopBanner'}).then(data => {
console.log(data,'幻灯片')
if(data && data?.count > 0){
setItem(data.list[0])
}
})
}
useEffect(() => {
reload()
}, [])
return (
<>
<Swiper defaultValue={0} height={279} indicator style={{ height: '280px' }}>
{item?.imageList?.map((item) => (
<Swiper.Item key={item}>
<img width="100%" height="100%" src={item.url} alt="" style={{ height: '280px' }} />
</Swiper.Item>
))}
</Swiper>
</>
)
}
export default MyPage

View File

@@ -1,2 +1,16 @@
page {
}
.mobile-container {
width: 100%;
min-height: 100vh;
// PC端样式
//@media screen and (min-width: 768px) {
// max-width: 414px;
// margin: 0 auto;
// min-height: auto;
// background-color: #fff;
// position: relative;
//}
}

View File

@@ -1,13 +1,12 @@
import './index.scss'
import Taro from '@tarojs/taro';
// import {useShareAppMessage, useShareTimeline} from "@tarojs/taro"
import {useEffect, useState} from "react";
import {getSiteInfo} from "@/api/layout";
import Login from "./Login";
import Banner from "./Banner";
import Menu from "./Menu";
import TopBanner from "./TopBanner";
import TabBar from "@/components/TabBar";
import Image from "./Image";
export interface Market {
// 自增ID
@@ -123,14 +122,14 @@ function Home() {
}, []);
return (
<>
<div className="mobile-container">
{!IsLogin && search ? (<Login done={handleLogin}/>) : (<>
<TopBanner/>
<Image/>
<Menu/>
<Banner/>
<TabBar/>
</>)}
</>
</div>
)
}

View File

@@ -8,13 +8,13 @@ function User() {
useEffect(() => {
}, []);
return (
<>
<div className="mobile-container">
<div className={'fixed w-full'}>
<UserCard />
<UserCell />
<TabBar/>
</div>
</>
</div>
)
}

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '详情',
navigationBarBackgroundColor: '#ffe0e0'
})

128
src/photo/detail.scss Normal file
View File

@@ -0,0 +1,128 @@
.content {
padding: 32px;
line-height: 2.4rem;
// 富文本内容样式
:global {
// 段落样式
p {
margin: 16px 0;
line-height: 1.8;
text-align: justify;
}
// 标题样式
h1, h2, h3, h4, h5, h6 {
margin: 24px 0 16px 0;
font-weight: bold;
line-height: 1.4;
}
h1 { font-size: 24px; }
h2 { font-size: 22px; }
h3 { font-size: 20px; }
h4 { font-size: 18px; }
h5 { font-size: 16px; }
h6 { font-size: 14px; }
// 图片样式
img {
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 16px 0;
display: block;
}
// 列表样式
ul, ol {
margin: 16px 0;
padding-left: 24px;
li {
margin: 8px 0;
line-height: 1.6;
}
}
// 引用样式
blockquote {
margin: 16px 0;
padding: 16px;
background-color: #f5f5f5;
border-left: 4px solid #ddd;
border-radius: 4px;
p {
margin: 0;
font-style: italic;
}
}
// 代码样式
code {
background-color: #f5f5f5;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 14px;
}
pre {
background-color: #f5f5f5;
padding: 16px;
border-radius: 8px;
overflow-x: auto;
margin: 16px 0;
code {
background: none;
padding: 0;
}
}
// 表格样式
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
th, td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
th {
background-color: #f5f5f5;
font-weight: bold;
}
}
// 链接样式
a {
color: #1890ff;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
// 分割线样式
hr {
margin: 24px 0;
border: none;
border-top: 1px solid #eee;
}
// 强调样式
strong, b {
font-weight: bold;
}
em, i {
font-style: italic;
}
}
}

52
src/photo/detail.tsx Normal file
View File

@@ -0,0 +1,52 @@
import {useEffect, useState} from 'react'
import {Tag} from '@nutui/nutui-react-taro'
import {useRouter} from '@tarojs/taro'
import {Divider} from '@nutui/nutui-react-taro'
import {CmsArticle} from "@/api/cms/cmsArticle/model"
import {Eye} from '@nutui/icons-react-taro'
// 显示html富文本
import {View, RichText} from '@tarojs/components'
import './detail.scss'
import Line from "@/components/Gap";
import {getCmsArticle} from "@/api/cms/cmsArticle";
function Detail() {
const {params} = useRouter();
// 文章详情
const [item, setItem] = useState<CmsArticle>()
// 浏览量
const [views, setViews] = useState<number>()
const reload = () => {
getCmsArticle(Number(params.id)).then(data => {
if (data) {
setItem(data)
setViews(data.actualViews)
}
})
}
useEffect(() => {
reload();
}, []);
return (
<div className={'bg-white'}>
<div className={'p-3 font-bold text-lg'}>{item?.title}</div>
<div className={'flex justify-between px-3'}>
<Tag type={'success'}>{item?.categoryName}</Tag>
<div className={'flex items-center gap-2 text-sm text-gray-400'}><Eye size={14}/>{views}</div>
</div>
<Divider/>
<View className={'content text-gray-700 text-sm'}>
<RichText
nodes={item?.content || '暂无内容'}
space="nbsp"
/>
</View>
<Line height={44}/>
</div>
)
}
export default Detail

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '图文列表'
})

109
src/photo/index.tsx Normal file
View File

@@ -0,0 +1,109 @@
import {useEffect, useState} from "react";
import {pageCmsArticle} from "@/api/cms/cmsArticle";
import {CmsArticle} from "@/api/cms/cmsArticle/model";
import Taro from '@tarojs/taro'
import {useRouter} from '@tarojs/taro'
import {Image} from '@nutui/nutui-react-taro'
import {getCmsNavigation, pageCmsNavigation} from "@/api/cms/cmsNavigation";
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
/**
* 文章终极列表
* @constructor
*/
const Index = () => {
const {params} = useRouter();
const [navigation, setNavigation] = useState<CmsNavigation>()
const [childCategory, setChildCategory] = useState<CmsNavigation[]>([])
const [list, setList] = useState<CmsArticle[]>([])
const reload = async () => {
// 获取栏目ID
const categoryId = Number(params.id);
// 当前栏目信息
const navs = await getCmsNavigation(categoryId);
// 二级栏目
const childCateogry = await pageCmsNavigation({parentId: categoryId});
// 终极新闻列表
const articles = await pageCmsArticle({categoryId});
// 当前栏目信息
if (navs) {
setNavigation(navs);
}
// 获取子级栏目
if (childCateogry) {
setChildCategory(childCateogry.list)
}
// 新闻列表
if (articles) {
setList(articles?.list || [])
}
}
useEffect(() => {
reload()
}, [])
return (
<>
<div style={{padding: navigation?.span + 'px'}}>
<Image src={navigation?.style} width={'100%'}
height={'auto'}/>
</div>
<div className={'bg-white rounded-lg py-3 px-3'}>
<div className={'grid grid-cols-2 gap-3'}>
{
// 子级栏目
childCategory.map((item, index) => {
return (
<div
key={index}
className={'flex flex-col justify-center items-center cursor-pointer'}
onClick={() => Taro.navigateTo({url: `./index?id=${item.navigationId}`})}
>
{/* 图片容器 */}
<div className={'w-full mb-2 flex justify-center'}>
<img
className={'object-cover rounded-lg'}
src={item.icon}
alt={item.title || ''}
/>
</div>
</div>
)
})
}
</div>
<div className={'grid grid-cols-3'}>
{
// 终极文章列表
list.map((item, index) => {
return (
<div
key={index}
className={'flex flex-col items-center cursor-pointer my-1'}
onClick={() => Taro.navigateTo({url: `./detail?id=${item.articleId}`})}
>
{/* 图片容器 */}
<div className={'w-full mb-2 flex justify-center'}>
<img
className={'object-cover rounded-lg'}
src={item.image}
alt={item.title || ''}
/>
</div>
{/* 标题 */}
<div className={'text-xs text-center text-gray-800 leading-tight px-1'}>
{item.title}
</div>
</div>
)
})
}
</div>
</div>
</>
)
}
export default Index

View File

@@ -0,0 +1,93 @@
// 手机版容器样式 - 用于PC端显示手机版效果
.mobile-container {
width: 100%;
min-height: 100vh;
position: relative;
// PC端样式 - 768px以上显示手机版效果
@media screen and (min-width: 768px) {
max-width: 414px;
width: 414px !important;
margin: 0 auto;
min-height: auto;
background-color: #fff;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
border-radius: 12px;
overflow: hidden;
position: relative;
// 确保内容不超出容器
* {
max-width: 100%;
}
}
// 大屏幕设备 - 1024px以上
@media screen and (min-width: 1024px) {
max-width: 375px;
width: 375px !important;
}
// 超大屏幕设备 - 1440px以上
@media screen and (min-width: 1440px) {
max-width: 414px;
width: 414px !important;
}
}
// PC端页面背景
@media screen and (min-width: 768px) {
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: flex-start;
padding: 40px 20px;
}
// 主应用容器
#app {
max-width: 414px !important;
width: 414px !important;
margin: 0 auto;
background-color: transparent;
position: relative;
}
// 页面容器
page {
max-width: 414px !important;
width: 414px !important;
margin: 0 auto;
background-color: #f5f5f5;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.15);
}
// Taro页面容器
.taro_page {
max-width: 414px !important;
width: 414px !important;
margin: 0 auto;
border-radius: 12px;
overflow: hidden;
}
// 底部导航栏在PC端的样式调整
.nut-tabbar {
max-width: 414px !important;
width: 414px !important;
margin: 0 auto;
border-radius: 0 0 12px 12px;
}
}
// 移动端保持原有样式
@media screen and (max-width: 767px) {
.mobile-container {
width: 100%;
min-height: 100vh;
}
}