国外MOSE官网
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
12 KiB

  1. <script setup lang="ts">
  2. import { ref, onMounted, h } from 'vue';
  3. import { useI18n } from 'vue-i18n';
  4. const { t } = useI18n();
  5. // Resource categories
  6. const categories = ref([
  7. { id: 'all', name: t('resources.categories.all') },
  8. { id: 'docs', name: t('resources.categories.docs') },
  9. { id: 'tutorials', name: t('resources.categories.tutorials') },
  10. { id: 'sdks', name: t('resources.categories.sdks') },
  11. { id: 'tools', name: t('resources.categories.tools') },
  12. { id: 'community', name: t('resources.categories.community') }
  13. ]);
  14. // Active category
  15. const activeCategory = ref('all');
  16. // Resources list
  17. const resources = ref([
  18. {
  19. id: 1,
  20. title: t('resources.items.docs_title'),
  21. description: t('resources.items.docs_description'),
  22. link: 'https://docs.mose.io',
  23. category: 'docs',
  24. icon: 'document'
  25. },
  26. {
  27. id: 2,
  28. title: t('resources.items.api_title'),
  29. description: t('resources.items.api_description'),
  30. link: 'https://api.mose.io',
  31. category: 'docs',
  32. icon: 'code'
  33. },
  34. {
  35. id: 3,
  36. title: t('resources.items.tutorial_beginner_title'),
  37. description: t('resources.items.tutorial_beginner_description'),
  38. link: 'https://learn.mose.io/beginner',
  39. category: 'tutorials',
  40. icon: 'academic-cap'
  41. },
  42. {
  43. id: 4,
  44. title: t('resources.items.tutorial_advanced_title'),
  45. description: t('resources.items.tutorial_advanced_description'),
  46. link: 'https://learn.mose.io/advanced',
  47. category: 'tutorials',
  48. icon: 'academic-cap'
  49. },
  50. {
  51. id: 5,
  52. title: t('resources.items.sdk_js_title'),
  53. description: t('resources.items.sdk_js_description'),
  54. link: 'https://github.com/mose/js-sdk',
  55. category: 'sdks',
  56. icon: 'code-bracket'
  57. },
  58. {
  59. id: 6,
  60. title: t('resources.items.sdk_python_title'),
  61. description: t('resources.items.sdk_python_description'),
  62. link: 'https://github.com/mose/python-sdk',
  63. category: 'sdks',
  64. icon: 'code-bracket'
  65. },
  66. {
  67. id: 7,
  68. title: t('resources.items.explorer_title'),
  69. description: t('resources.items.explorer_description'),
  70. link: 'https://explorer.mose.io',
  71. category: 'tools',
  72. icon: 'magnifying-glass'
  73. },
  74. {
  75. id: 8,
  76. title: t('resources.items.wallet_title'),
  77. description: t('resources.items.wallet_description'),
  78. link: 'https://wallet.mose.io',
  79. category: 'tools',
  80. icon: 'wallet'
  81. },
  82. {
  83. id: 9,
  84. title: t('resources.items.forum_title'),
  85. description: t('resources.items.forum_description'),
  86. link: 'https://forum.mose.io',
  87. category: 'community',
  88. icon: 'chat-bubble-left-right'
  89. },
  90. {
  91. id: 10,
  92. title: t('resources.items.github_title'),
  93. description: t('resources.items.github_description'),
  94. link: 'https://github.com/mose',
  95. category: 'community',
  96. icon: 'code-bracket'
  97. }
  98. ]);
  99. // Filtered resources based on active category
  100. const filteredResources = ref([]);
  101. // Filter resources based on active category
  102. const filterResources = () => {
  103. if (activeCategory.value === 'all') {
  104. filteredResources.value = resources.value;
  105. } else {
  106. filteredResources.value = resources.value.filter(resource => resource.category === activeCategory.value);
  107. }
  108. };
  109. // Set active category
  110. const setActiveCategory = (category: string) => {
  111. activeCategory.value = category;
  112. filterResources();
  113. };
  114. // Initialize with all resources
  115. onMounted(() => {
  116. filterResources();
  117. });
  118. // Icon components as Vue render functions
  119. const DocumentIcon = {
  120. render() {
  121. return h('svg', {
  122. xmlns: 'http://www.w3.org/2000/svg',
  123. fill: 'none',
  124. viewBox: '0 0 24 24',
  125. 'stroke-width': '1.5',
  126. stroke: 'currentColor',
  127. class: 'w-6 h-6'
  128. }, [
  129. h('path', {
  130. 'stroke-linecap': 'round',
  131. 'stroke-linejoin': 'round',
  132. d: 'M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z'
  133. })
  134. ]);
  135. }
  136. };
  137. const CodeIcon = {
  138. render() {
  139. return h('svg', {
  140. xmlns: 'http://www.w3.org/2000/svg',
  141. fill: 'none',
  142. viewBox: '0 0 24 24',
  143. 'stroke-width': '1.5',
  144. stroke: 'currentColor',
  145. class: 'w-6 h-6'
  146. }, [
  147. h('path', {
  148. 'stroke-linecap': 'round',
  149. 'stroke-linejoin': 'round',
  150. d: 'M14.25 9.75 16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0 0 20.25 18V6A2.25 2.25 0 0 0 18 3.75H6A2.25 2.25 0 0 0 3.75 6v12A2.25 2.25 0 0 0 6 20.25Z'
  151. })
  152. ]);
  153. }
  154. };
  155. const AcademicCapIcon = {
  156. render() {
  157. return h('svg', {
  158. xmlns: 'http://www.w3.org/2000/svg',
  159. fill: 'none',
  160. viewBox: '0 0 24 24',
  161. 'stroke-width': '1.5',
  162. stroke: 'currentColor',
  163. class: 'w-6 h-6'
  164. }, [
  165. h('path', {
  166. 'stroke-linecap': 'round',
  167. 'stroke-linejoin': 'round',
  168. d: 'M4.26 10.147a60.438 60.438 0 0 0-.491 6.347A48.62 48.62 0 0 1 12 20.904a48.62 48.62 0 0 1 8.232-4.41 60.46 60.46 0 0 0-.491-6.347m-15.482 0a50.636 50.636 0 0 0-2.658-.813A59.906 59.906 0 0 1 12 3.493a59.903 59.903 0 0 1 10.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.717 50.717 0 0 1 12 13.489a50.702 50.702 0 0 1 7.74-3.342M6.75 15a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm0 0v-3.675A55.378 55.378 0 0 1 12 8.443m-7.007 11.55A5.981 5.981 0 0 0 6.75 15.75v-1.5'
  169. })
  170. ]);
  171. }
  172. };
  173. const CodeBracketIcon = {
  174. render() {
  175. return h('svg', {
  176. xmlns: 'http://www.w3.org/2000/svg',
  177. fill: 'none',
  178. viewBox: '0 0 24 24',
  179. 'stroke-width': '1.5',
  180. stroke: 'currentColor',
  181. class: 'w-6 h-6'
  182. }, [
  183. h('path', {
  184. 'stroke-linecap': 'round',
  185. 'stroke-linejoin': 'round',
  186. d: 'M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5'
  187. })
  188. ]);
  189. }
  190. };
  191. const MagnifyingGlassIcon = {
  192. render() {
  193. return h('svg', {
  194. xmlns: 'http://www.w3.org/2000/svg',
  195. fill: 'none',
  196. viewBox: '0 0 24 24',
  197. 'stroke-width': '1.5',
  198. stroke: 'currentColor',
  199. class: 'w-6 h-6'
  200. }, [
  201. h('path', {
  202. 'stroke-linecap': 'round',
  203. 'stroke-linejoin': 'round',
  204. d: 'm21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z'
  205. })
  206. ]);
  207. }
  208. };
  209. const WalletIcon = {
  210. render() {
  211. return h('svg', {
  212. xmlns: 'http://www.w3.org/2000/svg',
  213. fill: 'none',
  214. viewBox: '0 0 24 24',
  215. 'stroke-width': '1.5',
  216. stroke: 'currentColor',
  217. class: 'w-6 h-6'
  218. }, [
  219. h('path', {
  220. 'stroke-linecap': 'round',
  221. 'stroke-linejoin': 'round',
  222. d: 'M21 12a2.25 2.25 0 0 0-2.25-2.25H15a3 3 0 1 1-6 0H5.25A2.25 2.25 0 0 0 3 12m18 0v6a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 18v-6m18 0V9M3 12V9m18 0a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 9m18 0V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v3'
  223. })
  224. ]);
  225. }
  226. };
  227. const ChatBubbleLeftRightIcon = {
  228. render() {
  229. return h('svg', {
  230. xmlns: 'http://www.w3.org/2000/svg',
  231. fill: 'none',
  232. viewBox: '0 0 24 24',
  233. 'stroke-width': '1.5',
  234. stroke: 'currentColor',
  235. class: 'w-6 h-6'
  236. }, [
  237. h('path', {
  238. 'stroke-linecap': 'round',
  239. 'stroke-linejoin': 'round',
  240. d: 'M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155'
  241. })
  242. ]);
  243. }
  244. };
  245. // Icon component mapping
  246. const getIconComponent = (iconName: string) => {
  247. switch (iconName) {
  248. case 'document':
  249. return DocumentIcon;
  250. case 'code':
  251. return CodeIcon;
  252. case 'academic-cap':
  253. return AcademicCapIcon;
  254. case 'code-bracket':
  255. return CodeBracketIcon;
  256. case 'magnifying-glass':
  257. return MagnifyingGlassIcon;
  258. case 'wallet':
  259. return WalletIcon;
  260. case 'chat-bubble-left-right':
  261. return ChatBubbleLeftRightIcon;
  262. default:
  263. return DocumentIcon;
  264. }
  265. };
  266. </script>
  267. <template>
  268. <div class="py-12 px-6 md:px-12 lg:px-24">
  269. <!-- Hero Section -->
  270. <div class="text-center mb-12 animate__animated animate__fadeIn">
  271. <h1 class="text-4xl md:text-5xl font-bold mb-4 text-text">{{ t('resources.hero.title') }}</h1>
  272. <p class="text-xl text-text-secondary max-w-3xl mx-auto">{{ t('resources.hero.subtitle') }}</p>
  273. </div>
  274. <!-- Categories Filter -->
  275. <div class="flex flex-wrap justify-center gap-4 mb-12 animate__animated animate__fadeInUp animate__delay-xs">
  276. <button
  277. v-for="category in categories"
  278. :key="category.id"
  279. @click="setActiveCategory(category.id)"
  280. class="px-4 py-2 rounded-full transition-all duration-300 hover:scale-105"
  281. :class="activeCategory === category.id
  282. ? 'bg-primary text-background font-medium shadow-lg'
  283. : 'bg-background-light text-text-secondary hover:bg-background hover:text-text'"
  284. >
  285. {{ category.name }}
  286. </button>
  287. </div>
  288. <!-- Resources Grid -->
  289. <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
  290. <div
  291. v-for="resource in filteredResources"
  292. :key="resource.id"
  293. class="bg-background-light rounded-lg p-6 shadow-md transition-all duration-300 hover:shadow-xl hover:translate-y-[-4px] animate__animated animate__fadeIn"
  294. >
  295. <div class="flex items-start mb-4">
  296. <div class="p-3 rounded-full bg-primary/10 text-primary mr-4">
  297. <component :is="getIconComponent(resource.icon)" />
  298. </div>
  299. <div>
  300. <h3 class="text-xl font-semibold text-text mb-2">{{ resource.title }}</h3>
  301. <p class="text-text-secondary mb-4">{{ resource.description }}</p>
  302. </div>
  303. </div>
  304. <a
  305. :href="resource.link"
  306. target="_blank"
  307. rel="noopener noreferrer"
  308. class="inline-flex items-center text-primary hover:text-primary-light transition-colors"
  309. >
  310. {{ t('resources.explore') }}
  311. <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  312. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
  313. </svg>
  314. </a>
  315. </div>
  316. </div>
  317. <!-- Newsletter Section -->
  318. <div class="mt-16 bg-background-dark rounded-xl p-8 md:p-12 animate__animated animate__fadeIn">
  319. <div class="max-w-4xl mx-auto">
  320. <h2 class="text-2xl md:text-3xl font-bold mb-4 text-text">{{ t('resources.newsletter.title') }}</h2>
  321. <p class="text-text-secondary mb-6">{{ t('resources.newsletter.subtitle') }}</p>
  322. <form class="flex flex-col md:flex-row gap-4">
  323. <input
  324. type="email"
  325. :placeholder="t('resources.newsletter.placeholder')"
  326. class="flex-grow px-4 py-3 rounded-lg bg-background border border-background-light focus:outline-none focus:ring-2 focus:ring-primary"
  327. />
  328. <button
  329. type="submit"
  330. class="px-6 py-3 bg-primary text-background font-medium rounded-lg hover:bg-primary-light transition-colors duration-300"
  331. >
  332. {{ t('resources.newsletter.button') }}
  333. </button>
  334. </form>
  335. </div>
  336. </div>
  337. </div>
  338. </template>