diff --git a/packages/omi-templates/package-lock.json b/packages/omi-templates/package-lock.json index 8f05b9cb07..d4fb2a6f61 100644 --- a/packages/omi-templates/package-lock.json +++ b/packages/omi-templates/package-lock.json @@ -19,6 +19,7 @@ "omi-router": "latest", "omi-suspense": "latest", "prismjs": "^1.29.0", + "swiper": "^11.1.4", "tailwind-merge": "^2.2.1" }, "devDependencies": { @@ -2924,6 +2925,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swiper": { + "version": "11.1.4", + "resolved": "https://registry.npmmirror.com/swiper/-/swiper-11.1.4.tgz", + "integrity": "sha512-1n7kbYJB2dFEpUHRFszq7gys/ofIBrMNibwTiMvPHwneKND/t9kImnHt6CfGPScMHgI+dWMbGTycCKGMoOO1KA==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "engines": { + "node": ">= 4.7.0" + } + }, "node_modules/tailwind-merge": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.1.tgz", @@ -5349,6 +5368,11 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "swiper": { + "version": "11.1.4", + "resolved": "https://registry.npmmirror.com/swiper/-/swiper-11.1.4.tgz", + "integrity": "sha512-1n7kbYJB2dFEpUHRFszq7gys/ofIBrMNibwTiMvPHwneKND/t9kImnHt6CfGPScMHgI+dWMbGTycCKGMoOO1KA==" + }, "tailwind-merge": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.1.tgz", diff --git a/packages/omi-templates/package.json b/packages/omi-templates/package.json index 7504d7766b..9d1fd1faa9 100644 --- a/packages/omi-templates/package.json +++ b/packages/omi-templates/package.json @@ -23,6 +23,7 @@ "omi-router": "latest", "omi-suspense": "latest", "prismjs": "^1.29.0", + "swiper": "^11.1.4", "tailwind-merge": "^2.2.1" }, "devDependencies": { diff --git a/packages/omi-templates/src/components/omiu/swiper/api.md b/packages/omi-templates/src/components/omiu/swiper/api.md new file mode 100644 index 0000000000..75e9b09b67 --- /dev/null +++ b/packages/omi-templates/src/components/omiu/swiper/api.md @@ -0,0 +1,34 @@ +## Swiper Props + +| Name | Type | Default | Description | +| ---------- | ------- | ------------ | ------------------------------------------------ | +| direction | string | 'horizontal' | Direction of the swiper (vertical or horizontal) | +| loop | boolean | true | Whether the swiper should loop | +| index | number | 0 | Initial index of the swiper | +| navigation | boolean | true | Whether to show navigation buttons | +| pagination | boolean | true | Whether to show pagination | +| slidesPerView | string or number | "auto" | Number of slides to show per view. "auto" will automatically adjust the number of slides based on the viewport size. | +| spaceBetween | number | 30 | Space between each slide in pixels. | +| autoPlay | boolean | true | Whether to enable auto play. | + +## Swiper Events + +| Name | Parameters | Description | +| ------ | ------------------------ | ---------------------------------- | +| change | evt: CustomEvent | Event triggered when slide changes | + +## Swiper 属性 + +| Name | Type | Default | Description | +| ---------- | ------ | ------------ | --------------------------- | +| direction | 字符串 | 'horizontal' | Swiper 的方向(垂直或水平) | +| loop | 布尔值 | true | 是否启用 Swiper 循环播放 | +| index | 数字 | 0 | Swiper 的初始索引 | +| navigation | 布尔值 | true | 是否显示导航按钮 | +| pagination | 布尔值 | true | 是否显示分页器 | + +## Swiper 事件 + +| Name | Parameter | Description | +| ------ | ------------------------ | ---------------------- | +| change | evt: CustomEvent | 幻灯片更改时触发的事件 | diff --git a/packages/omi-templates/src/components/omiu/swiper/swiper.tsx b/packages/omi-templates/src/components/omiu/swiper/swiper.tsx new file mode 100644 index 0000000000..d1f55bb955 --- /dev/null +++ b/packages/omi-templates/src/components/omiu/swiper/swiper.tsx @@ -0,0 +1,212 @@ +import { tag, Component, classNames, OmiProps, bind, VNode } from 'omi' +import { tailwind } from '@/tailwind' +// import '../collapse/collapse' + + +/** + * copy from omiu/src/components/swiper + * add Props . slidesPreView 、spaceBetween 、autoPlay + */ + + + +// https://swiperjs.com/get-started +// import Swiper JS +import Swiper from 'swiper' +// import Swiper styles +import swiperStyle from 'swiper/css?raw' + +interface Props { + direction?: 'vertical' | 'horizontal' + loop?: boolean + index: number + navigation?: boolean + slidesPerView?: "auto" | number + spaceBetween?: number, + autoplay?: boolean +} + +const theme = { + paginationButton: + 'mx-[3px] box-content h-[3px] w-[30px] flex-initial cursor-pointer border-0 border-y-[10px] border-solid border-transparent bg-white bg-clip-padding p-0 -indent-[999px] transition-opacity duration-[600ms] ease-[cubic-bezier(0.25,0.1,0.25,1.0)] motion-reduce:transition-none', +} + +@tag('o-swiper') +export class SwiperComponent extends Component { + static css = [ + tailwind, + swiperStyle, + `:host { + display: block; + } + `, + ] + paginationDiv: any + + @bind + onPreviusClick(evt: Event) { + this.swiper?.slidePrev() + this.fire('change', { + index: this.swiper!.realIndex, + nativeEvent: evt, + }) + } + + @bind + onNextClick(evt: Event) { + this.swiper?.slideNext() + this.fire('change', { + index: this.swiper!.realIndex, + nativeEvent: evt, + }) + } + + @bind + onPaginationClick(index: number, evt: Event) { + // https://www.swiper.com.cn/api/methods/417.html + this.swiper?.slideToLoop(index) + this.fire('change', { + index, + nativeEvent: evt, + }) + } + + static defaultProps = { + direction: 'horizontal', // vertical | horizontal + loop: true, + index: 0, + navigation: true, + pagination: true, + slidesPerView: 1, + spaceBetween: 30, + autoplay: 1000 + } + + swiper: Swiper | null = null + + installed(): void { + this.swiper = new Swiper(this.rootElement!, { + // Optional parameters + direction: this.props.direction, + loop: this.props.loop, + initialSlide: this.props.index, + slidesPerView: this.props.slidesPerView, + spaceBetween:this.props.spaceBetween, + autoplay:this.props.autoplay + // And if we need scrollbar + // scrollbar: { + // el: '.swiper-scrollbar', + // }, + }) + // try { + // console.log('swiper',this.swiper) + // this.swiper.slidesPerViewDynamic() // 动态设置slidesPerView + // this.swiper.autoplay.start() // 自动轮播 + // this.swiper.loopCreate() // 循环模式 + // }catch(e){ + // console.log(e) + // } + + this.swiper.on('slideChange', () => { + this.setActiveButton(this.swiper!.realIndex) + }) + } + + setActiveButton(index: number) { + const buttons = this.paginationDiv.querySelectorAll('button') + + for (let i = 0; i < buttons.length; i++) { + if (i === index) { + buttons[i].classList.remove('opacity-50') + } else { + buttons[i].classList.add('opacity-50') + } + } + } + + // 不需要更新,不然状态不一致 + receiveProps() { + return false + } + + render(props: OmiProps) { + return ( +
+
+ {(props.children as VNode[])?.map((child) => { + return
{child}
+ })} +
+ + {props.navigation && ( + + )} + + {props.navigation && ( + + )} + + {props.pagination && ( +
{ + this.paginationDiv = e + }} + class="absolute bottom-0 left-0 right-0 z-[2] mx-[15%] mb-4 flex list-none justify-center p-0" + > + {props.children ? props.children.map((child, index) => { + return ( + + ) + }) : null } +
+ )} +
+ ) + } +} diff --git a/packages/omi-templates/src/pages/education.tsx b/packages/omi-templates/src/pages/education.tsx index 287c1cc587..1bc01ac4b8 100644 --- a/packages/omi-templates/src/pages/education.tsx +++ b/packages/omi-templates/src/pages/education.tsx @@ -1,5 +1,6 @@ -import { render, signal, tag, Component } from 'omi'; -import '../components/omiu/button'; +import { render, signal, tag, Component } from 'omi' +import '../components/omiu/button' +import '../components/omiu/swiper/swiper' // 分类数据 const categories = signal([ @@ -8,7 +9,7 @@ const categories = signal([ { name: 'AI开发' }, { name: '数据科学' }, { name: '移动开发' }, -]); +]) // 课程数据 const allCourses = [ @@ -67,93 +68,188 @@ const allCourses = [ authorImage: 'https://omi.cdn-go.cn/admin/latest/home/omi.svg', category: '前端开发', }, +] + +const courses = signal(allCourses) + +// 名师风采 数据 +let teachers = [ + { + name: '李明', + title: 'Python编程专家', + description: 'Python编程领域的专家,拥有多年的开发和教学经验。', + image: 'http://yywebsite.cn/assets/img/edu/teacher1.jpg', + }, + { + name: '王华', + title: 'Web开发教授', + description: '在Web开发方面有着丰富的经验,专注于HTML、CSS和JavaScript教学。', + image: 'http://yywebsite.cn/assets/img/edu/teacher3.jpg', + }, + { + name: '赵倩', + title: '机器学习研究员', + description: '机器学习领域的资深研究员,擅长Python和数据分析。', + image: 'http://yywebsite.cn/assets/img/edu/teacher2.jpg', + }, + { + name: '陈浩', + title: '数据科学家', + description: '数据分析与可视化方面有着多年的实践经验,精通Pandas和Matplotlib。', + image: 'https://omi.cdn-go.cn/admin/latest/home/omi.svg', + }, + { + name: '刘静', + title: '前端开发总监', + description: '前端开发的专家,特别擅长Vue.js框架的应用与开发。', + image: 'https://omi.cdn-go.cn/admin/latest/home/omi.svg', + }, ]; -const courses = signal(allCourses); + +// 学生评价 数据 +const studentReviews = [ + { + name: '学生A', + title: '前端开发工程师', + description: '这门《Python编程基础》课程为我打开了新世界的大门,让我更深入地了解了编程的奥秘。', + image: 'http://yywebsite.cn/assets/img/edu/student1.jpg', + }, + { + name: '学生B', + title: '欧米大学大学生', + description: '《Web开发入门》课程让我掌握了许多实用的技能,我对Web开发的热情更加高涨了。', + image: 'http://yywebsite.cn/assets/img/edu/student2.jpg', + }, + { + name: '学生C', + title: '欧米大学小学生', + description: '《机器学习基础》课程让我对AI有了更深刻的认识,对未来的工作充满期待。', + image: 'http://yywebsite.cn/assets/img/edu/student3.jpg', + }, + { + name: '学生D', + title: '后端开发工程师', + description: '《数据分析与可视化》课程让我学习到了如何将数据转化为有价值的见解,对我的工作非常有帮助。', + image: 'http://yywebsite.cn/assets/img/edu/student1.jpg', + }, +]; @tag('education-template') class EducationTemplate extends Component { - - activeCategory: string = "全部"; + activeCategory: string = '全部' + // 筛选课程 - filterCourses(category:string) { + filterCourses(category: string) { if (category === '全部') { - courses.value = allCourses; + courses.value = allCourses } else { - courses.value = allCourses.filter((course) => course.category === category); + courses.value = allCourses.filter((course) => course.category === category) } } render() { return ( - // 分类和课程卡片列表 -
-
- {/* 分类section */} -
- {['全部', ...categories.value.map((category) => category.name)].map((category, index) => ( - { - this.activeCategory = category; - this.filterCourses(category); - }} - > - {category} - - ))} -
+
+
+
+ {/* 分类section */} +
+ {['全部', ...categories.value.map((category) => category.name)].map((category, index) => ( + { + this.activeCategory = category + this.filterCourses(category) + }} + > + {category} + + ))} +
- {/* 课程section*/} -
- {courses.value.length === 0 ? ( -
- 暂无相关课程 -
- ) : ( - courses.value.map((course, index) => ( -
- Course Image -
-
- - {course.date} - - - {course.duration} - -
-
-

- {course.title} -

-

{course.description}

-
-
-
- Author Image - {course.author} + {/* 课程section */} +
+ {courses.value.length === 0 ? ( +
暂无相关课程
+ ) : ( + courses.value.map((course, index) => ( +
+ Course Image +
+
+ + {course.date} + + + {course.duration} + +
+
+

+ {course.title} +

+

{course.description}

-
- - {course.price} - +
+
+ Author Image + {course.author} +
+
-
- )) - )} + )) + )} +
-
-
- ); +
+ + {/** 名师风采 */} +
+

名师风采

+

+ 我们骄傲地介绍我们的优秀教师团队,他们在各自的领域拥有丰富的经验和卓越的成就。 +

+ + {teachers.map((teacher) => ( +
+ {teacher.name} +

{teacher.name}

+

{teacher.title}

+

{teacher.description}

+
+ ))} +
+
+ + {/** 学生课程评价 */} +
+

学生课程评价

+ + {studentReviews.map((student) => ( +
+ {student.name} +

{student.name}

+

{student.title}

+

{student.description}

+
+ ))} +
+
+ + ) } } // 渲染组件到页面 export function EducationPage() { - return ; + return }