feat: add initial styling and layout for portal views
- Created a global CSS file for consistent styling across the application. - Implemented the Explore, Home, Topics, and various user views with responsive design. - Added a Login view with OTP functionality and a Checkout view for order processing. - Developed a NotFound view for handling 404 errors. - Established a configuration file for Vite with Tailwind CSS integration.
This commit is contained in:
24
frontend/portal/.gitignore
vendored
Normal file
24
frontend/portal/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
5
frontend/portal/README.md
Normal file
5
frontend/portal/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
||||
13
frontend/portal/index.html
Normal file
13
frontend/portal/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>portal</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
2580
frontend/portal/package-lock.json
generated
Normal file
2580
frontend/portal/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
frontend/portal/package.json
Normal file
27
frontend/portal/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "portal",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@primevue/themes": "^4.5.4",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"pinia": "^3.0.4",
|
||||
"primeicons": "^7.0.0",
|
||||
"primevue": "^4.5.4",
|
||||
"sass": "^1.97.1",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vue": "^3.5.24",
|
||||
"vue-router": "^4.6.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"vite": "^7.2.4"
|
||||
}
|
||||
}
|
||||
1
frontend/portal/public/vite.svg
Normal file
1
frontend/portal/public/vite.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
3
frontend/portal/src/App.vue
Normal file
3
frontend/portal/src/App.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
30
frontend/portal/src/assets/main.css
Normal file
30
frontend/portal/src/assets/main.css
Normal file
@@ -0,0 +1,30 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "tailwindcss-animate";
|
||||
|
||||
:root {
|
||||
--color-primary-50: #eff6ff;
|
||||
--color-primary-100: #dbeafe;
|
||||
--color-primary-200: #bfdbfe;
|
||||
--color-primary-300: #93c5fd;
|
||||
--color-primary-400: #60a5fa;
|
||||
--color-primary-500: #3b82f6;
|
||||
--color-primary-600: #2563eb;
|
||||
--color-primary-700: #1d4ed8;
|
||||
--color-primary-800: #1e40af;
|
||||
--color-primary-900: #1e3a8a;
|
||||
--color-primary-950: #172554;
|
||||
}
|
||||
|
||||
@theme {
|
||||
--color-primary-50: var(--color-primary-50);
|
||||
--color-primary-100: var(--color-primary-100);
|
||||
--color-primary-200: var(--color-primary-200);
|
||||
--color-primary-300: var(--color-primary-300);
|
||||
--color-primary-400: var(--color-primary-400);
|
||||
--color-primary-500: var(--color-primary-500);
|
||||
--color-primary-600: var(--color-primary-600);
|
||||
--color-primary-700: var(--color-primary-700);
|
||||
--color-primary-800: var(--color-primary-800);
|
||||
--color-primary-900: var(--color-primary-900);
|
||||
--color-primary-950: var(--color-primary-950);
|
||||
}
|
||||
1
frontend/portal/src/assets/vue.svg
Normal file
1
frontend/portal/src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 496 B |
57
frontend/portal/src/components/AppFooter.vue
Normal file
57
frontend/portal/src/components/AppFooter.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<footer class="bg-slate-900 text-slate-400 mt-auto">
|
||||
<div class="mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
|
||||
<!-- Brand -->
|
||||
<div class="col-span-1">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<div class="w-8 h-8 bg-white/10 rounded-lg flex items-center justify-center text-white font-bold text-xl">Q</div>
|
||||
<span class="text-xl font-bold text-white">Quyun</span>
|
||||
</div>
|
||||
<p class="text-sm leading-relaxed mb-6">专业的租户管理与内容交付平台,连接创作者与用户,探索内容的无限可能。</p>
|
||||
<div class="flex gap-4">
|
||||
<a href="#" class="w-8 h-8 rounded-full bg-white/5 flex items-center justify-center hover:bg-white/20 hover:text-white transition-all"><i class="pi pi-twitter"></i></a>
|
||||
<a href="#" class="w-8 h-8 rounded-full bg-white/5 flex items-center justify-center hover:bg-white/20 hover:text-white transition-all"><i class="pi pi-github"></i></a>
|
||||
<a href="#" class="w-8 h-8 rounded-full bg-white/5 flex items-center justify-center hover:bg-white/20 hover:text-white transition-all"><i class="pi pi-discord"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Links -->
|
||||
<div>
|
||||
<h3 class="text-white font-bold mb-4">关于我们</h3>
|
||||
<ul class="space-y-3 text-sm">
|
||||
<li><a href="#" class="hover:text-white transition-colors">平台介绍</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">加入我们</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">联系方式</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">合作伙伴</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-white font-bold mb-4">帮助中心</h3>
|
||||
<ul class="space-y-3 text-sm">
|
||||
<li><a href="#" class="hover:text-white transition-colors">用户指南</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">创作者手册</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">常见问题</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">反馈建议</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-white font-bold mb-4">法律条款</h3>
|
||||
<ul class="space-y-3 text-sm">
|
||||
<li><a href="#" class="hover:text-white transition-colors">用户协议</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">隐私政策</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">知识产权</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">社区规范</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-slate-800 mt-12 pt-8 flex flex-col md:flex-row justify-between items-center text-xs">
|
||||
<p>© 2025 Quyun Platform. All rights reserved.</p>
|
||||
<p class="mt-2 md:mt-0">ICP 备 88888888 号-1 | 公安网备 11010102000000 号</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
43
frontend/portal/src/components/HelloWorld.vue
Normal file
43
frontend/portal/src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
msg: String,
|
||||
})
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="card">
|
||||
<button type="button" @click="count++">count is {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test HMR
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out
|
||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||
>create-vue</a
|
||||
>, the official Vue + Vite starter
|
||||
</p>
|
||||
<p>
|
||||
Learn more about IDE Support for Vue in the
|
||||
<a
|
||||
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
|
||||
target="_blank"
|
||||
>Vue Docs Scaling up Guide</a
|
||||
>.
|
||||
</p>
|
||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
||||
88
frontend/portal/src/components/TopNavbar.vue
Normal file
88
frontend/portal/src/components/TopNavbar.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<nav class="fixed top-0 w-full z-50 bg-white border-b border-slate-200 h-16">
|
||||
<div class="mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8 h-full flex items-center justify-between">
|
||||
<!-- Left: Logo -->
|
||||
<router-link to="/" class="flex items-center gap-2">
|
||||
<div class="w-8 h-8 bg-primary-600 rounded-lg flex items-center justify-center text-white font-bold text-xl">Q</div>
|
||||
<span class="text-xl font-bold text-slate-900 hidden sm:block">Quyun</span>
|
||||
</router-link>
|
||||
|
||||
<!-- Center-Left: Nav Links (Desktop) -->
|
||||
<div class="hidden md:flex items-center space-x-8">
|
||||
<router-link to="/" class="text-slate-600 font-medium hover:text-primary-600" active-class="text-primary-600">首页</router-link>
|
||||
<router-link to="/explore" class="text-slate-600 font-medium hover:text-primary-600" active-class="text-primary-600">发现</router-link>
|
||||
<router-link to="/topics" class="text-slate-600 font-medium hover:text-primary-600" active-class="text-primary-600">专题</router-link>
|
||||
</div>
|
||||
|
||||
<!-- Center-Right: Global Search -->
|
||||
<div class="hidden sm:flex flex-1 max-w-md mx-8">
|
||||
<div class="relative w-full">
|
||||
<i class="pi pi-search absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索感兴趣的内容..."
|
||||
class="w-full h-10 pl-10 pr-4 rounded-full bg-slate-100 border-none focus:bg-white focus:ring-2 focus:ring-primary-100 text-sm transition-all"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: User Actions -->
|
||||
<div class="flex items-center gap-4">
|
||||
<template v-if="isLoggedIn">
|
||||
<!-- Notification -->
|
||||
<router-link to="/me/notifications" class="relative w-10 h-10 flex items-center justify-center rounded-full hover:bg-slate-50 text-slate-600">
|
||||
<i class="pi pi-bell text-xl"></i>
|
||||
<span class="absolute top-2 right-2 w-2 h-2 bg-red-500 rounded-full border border-white"></span>
|
||||
</router-link>
|
||||
|
||||
<!-- Creator Entry -->
|
||||
<router-link to="/creator/apply" class="hidden sm:flex items-center gap-1 px-3 py-1.5 text-sm font-medium text-slate-600 hover:bg-slate-50 rounded-lg border border-slate-200">
|
||||
<i class="pi pi-pencil"></i>
|
||||
<span>创作</span>
|
||||
</router-link>
|
||||
|
||||
<!-- Avatar Dropdown -->
|
||||
<div class="relative group">
|
||||
<button class="w-9 h-9 rounded-full overflow-hidden border border-slate-200 focus:ring-2 ring-primary-100">
|
||||
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Felix" alt="User" class="w-full h-full object-cover">
|
||||
</button>
|
||||
<!-- Dropdown Menu (Mock) -->
|
||||
<div class="absolute right-0 top-full mt-2 w-48 bg-white rounded-xl shadow-lg border border-slate-100 py-1 hidden group-hover:block">
|
||||
<div class="px-4 py-3 border-b border-slate-50">
|
||||
<p class="text-sm font-bold text-slate-900">Felix Demo</p>
|
||||
<p class="text-xs text-slate-500 truncate">felix@example.com</p>
|
||||
</div>
|
||||
<router-link to="/me" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50">个人中心</router-link>
|
||||
<router-link to="/creator" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50">创作者中心</router-link>
|
||||
<div class="border-t border-slate-50 mt-1"></div>
|
||||
<button @click="logout" class="block w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-red-50">退出登录</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<router-link to="/auth/login" class="text-slate-600 font-medium hover:text-primary-600 px-3 py-2">登录</router-link>
|
||||
<router-link to="/auth/login" class="bg-primary-600 text-white px-5 py-2 rounded-full font-medium hover:bg-primary-700 transition-colors">注册</router-link>
|
||||
</template>
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
<button class="md:hidden w-10 h-10 flex items-center justify-center text-slate-600">
|
||||
<i class="pi pi-bars text-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const isLoggedIn = ref(true); // Mock login state
|
||||
const router = useRouter();
|
||||
|
||||
const logout = () => {
|
||||
isLoggedIn.value = false;
|
||||
router.push('/');
|
||||
};
|
||||
</script>
|
||||
5
frontend/portal/src/layout/LayoutAuth.vue
Normal file
5
frontend/portal/src/layout/LayoutAuth.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-slate-50 flex items-center justify-center p-4">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
14
frontend/portal/src/layout/LayoutMain.vue
Normal file
14
frontend/portal/src/layout/LayoutMain.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex flex-col bg-slate-50">
|
||||
<TopNavbar />
|
||||
<main class="flex-grow pt-16">
|
||||
<router-view />
|
||||
</main>
|
||||
<AppFooter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import TopNavbar from '../components/TopNavbar.vue';
|
||||
import AppFooter from '../components/AppFooter.vue';
|
||||
</script>
|
||||
68
frontend/portal/src/layout/LayoutUser.vue
Normal file
68
frontend/portal/src/layout/LayoutUser.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex flex-col bg-slate-50">
|
||||
<TopNavbar />
|
||||
<main class="flex-grow pt-16">
|
||||
<div class="mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8 py-8 flex gap-8">
|
||||
<!-- Sidebar -->
|
||||
<aside class="w-[280px] flex-shrink-0 hidden lg:block">
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-100 overflow-hidden sticky top-24">
|
||||
<!-- User Brief -->
|
||||
<div class="p-6 border-b border-slate-100 bg-slate-50/50">
|
||||
<div class="flex items-center gap-4">
|
||||
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Felix" class="w-12 h-12 rounded-full border-2 border-white shadow-sm" />
|
||||
<div class="overflow-hidden">
|
||||
<div class="font-bold text-slate-900 truncate">Felix Demo</div>
|
||||
<div class="text-xs text-slate-500">ID: 9527330</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Menus -->
|
||||
<nav class="p-4 space-y-1">
|
||||
<router-link to="/me" exact-active-class="bg-primary-50 text-primary-600 font-semibold" class="flex items-center gap-3 px-4 py-3 rounded-lg text-slate-600 hover:bg-slate-50 transition-colors">
|
||||
<i class="pi pi-home text-lg"></i>
|
||||
<span>概览</span>
|
||||
</router-link>
|
||||
<router-link to="/me/orders" active-class="bg-primary-50 text-primary-600 font-semibold" class="flex items-center gap-3 px-4 py-3 rounded-lg text-slate-600 hover:bg-slate-50 transition-colors">
|
||||
<i class="pi pi-shopping-bag text-lg"></i>
|
||||
<span>我的订单</span>
|
||||
</router-link>
|
||||
<router-link to="/me/wallet" active-class="bg-primary-50 text-primary-600 font-semibold" class="flex items-center gap-3 px-4 py-3 rounded-lg text-slate-600 hover:bg-slate-50 transition-colors">
|
||||
<i class="pi pi-wallet text-lg"></i>
|
||||
<span>我的钱包</span>
|
||||
</router-link>
|
||||
<router-link to="/me/library" active-class="bg-primary-50 text-primary-600 font-semibold" class="flex items-center gap-3 px-4 py-3 rounded-lg text-slate-600 hover:bg-slate-50 transition-colors">
|
||||
<i class="pi pi-book text-lg"></i>
|
||||
<span>已购内容</span>
|
||||
</router-link>
|
||||
<router-link to="/me/notifications" active-class="bg-primary-50 text-primary-600 font-semibold" class="flex items-center gap-3 px-4 py-3 rounded-lg text-slate-600 hover:bg-slate-50 transition-colors">
|
||||
<i class="pi pi-bell text-lg"></i>
|
||||
<span>消息中心</span>
|
||||
</router-link>
|
||||
<div class="my-2 border-t border-slate-100"></div>
|
||||
<router-link to="/me/profile" active-class="bg-primary-50 text-primary-600 font-semibold" class="flex items-center gap-3 px-4 py-3 rounded-lg text-slate-600 hover:bg-slate-50 transition-colors">
|
||||
<i class="pi pi-user text-lg"></i>
|
||||
<span>个人资料</span>
|
||||
</router-link>
|
||||
<router-link to="/me/security" active-class="bg-primary-50 text-primary-600 font-semibold" class="flex items-center gap-3 px-4 py-3 rounded-lg text-slate-600 hover:bg-slate-50 transition-colors">
|
||||
<i class="pi pi-shield text-lg"></i>
|
||||
<span>账号安全</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="flex-grow min-w-0">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<AppFooter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import TopNavbar from '../components/TopNavbar.vue';
|
||||
import AppFooter from '../components/AppFooter.vue';
|
||||
</script>
|
||||
26
frontend/portal/src/main.js
Normal file
26
frontend/portal/src/main.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import PrimeVue from 'primevue/config';
|
||||
import Aura from '@primevue/themes/aura';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
|
||||
import './assets/main.css';
|
||||
import 'primeicons/primeicons.css';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(createPinia());
|
||||
app.use(router);
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
preset: Aura,
|
||||
options: {
|
||||
prefix: 'p',
|
||||
darkModeSelector: '.my-app-dark',
|
||||
cssLayer: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.mount('#app');
|
||||
161
frontend/portal/src/router/index.js
Normal file
161
frontend/portal/src/router/index.js
Normal file
@@ -0,0 +1,161 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import LayoutMain from '../layout/LayoutMain.vue'
|
||||
import LayoutAuth from '../layout/LayoutAuth.vue'
|
||||
import LayoutUser from '../layout/LayoutUser.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
component: LayoutMain,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'home',
|
||||
component: () => import('../views/HomeView.vue')
|
||||
},
|
||||
{
|
||||
path: 'contents/:id',
|
||||
name: 'content-detail',
|
||||
component: () => import('../views/content/DetailView.vue')
|
||||
},
|
||||
{
|
||||
path: 't/:id',
|
||||
name: 'tenant-home',
|
||||
component: () => import('../views/tenant/HomeView.vue')
|
||||
},
|
||||
{
|
||||
path: 'explore',
|
||||
name: 'explore',
|
||||
component: () => import('../views/ExploreView.vue') // Placeholder
|
||||
},
|
||||
{
|
||||
path: 'topics',
|
||||
name: 'topics',
|
||||
component: () => import('../views/TopicsView.vue') // Placeholder
|
||||
},
|
||||
{
|
||||
path: 'creator/apply',
|
||||
name: 'creator-apply',
|
||||
component: () => import('../views/creator/ApplyView.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/auth',
|
||||
component: LayoutAuth,
|
||||
children: [
|
||||
{
|
||||
path: 'login',
|
||||
name: 'login',
|
||||
component: () => import('../views/auth/LoginView.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/me',
|
||||
component: LayoutUser,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'user-dashboard',
|
||||
component: () => import('../views/user/DashboardView.vue')
|
||||
},
|
||||
{
|
||||
path: 'orders',
|
||||
name: 'user-orders',
|
||||
component: () => import('../views/user/OrdersView.vue') // Placeholder
|
||||
},
|
||||
{
|
||||
path: 'wallet',
|
||||
name: 'user-wallet',
|
||||
component: () => import('../views/user/WalletView.vue') // Placeholder
|
||||
},
|
||||
{
|
||||
path: 'library',
|
||||
name: 'user-library',
|
||||
component: () => import('../views/user/LibraryView.vue') // Placeholder
|
||||
},
|
||||
{
|
||||
path: 'notifications',
|
||||
name: 'user-notifications',
|
||||
component: () => import('../views/user/NotificationsView.vue') // Placeholder
|
||||
},
|
||||
{
|
||||
path: 'profile',
|
||||
name: 'user-profile',
|
||||
component: () => import('../views/user/ProfileView.vue') // Placeholder
|
||||
},
|
||||
{
|
||||
path: 'security',
|
||||
name: 'user-security',
|
||||
component: () => import('../views/user/SecurityView.vue') // Placeholder
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/creator',
|
||||
component: LayoutUser, // Initially use LayoutUser, later maybe specialized LayoutCreator
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'creator-dashboard',
|
||||
component: () => import('../views/creator/DashboardView.vue')
|
||||
},
|
||||
{
|
||||
path: 'contents',
|
||||
name: 'creator-contents',
|
||||
component: () => import('../views/creator/ContentsView.vue')
|
||||
},
|
||||
{
|
||||
path: 'orders',
|
||||
name: 'creator-orders',
|
||||
component: () => import('../views/creator/OrdersView.vue')
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
name: 'creator-settings',
|
||||
component: () => import('../views/creator/SettingsView.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/checkout',
|
||||
component: LayoutMain, // Or a simplified checkout layout
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'checkout',
|
||||
component: () => import('../views/order/CheckoutView.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/payment/:id',
|
||||
component: LayoutMain,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'payment',
|
||||
component: () => import('../views/order/PaymentView.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
// Fallback
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'not-found',
|
||||
component: () => import('../views/misc/NotFoundView.vue')
|
||||
}
|
||||
],
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
} else {
|
||||
return { top: 0 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
79
frontend/portal/src/style.css
Normal file
79
frontend/portal/src/style.css
Normal file
@@ -0,0 +1,79 @@
|
||||
:root {
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
6
frontend/portal/src/views/ExploreView.vue
Normal file
6
frontend/portal/src/views/ExploreView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="mx-auto max-w-screen-xl my-8 p-8 bg-white rounded-xl shadow-sm">
|
||||
<h1 class="text-2xl font-bold mb-4">Explore</h1>
|
||||
<p class="text-slate-400">(Explore content)</p>
|
||||
</div>
|
||||
</template>
|
||||
224
frontend/portal/src/views/HomeView.vue
Normal file
224
frontend/portal/src/views/HomeView.vue
Normal file
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<div class="mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8 py-8">
|
||||
<!-- Hero Banner -->
|
||||
<div class="relative w-full h-[400px] rounded-2xl overflow-hidden bg-slate-900 mb-8 group">
|
||||
<!-- Mock Carousel Image -->
|
||||
<img src="https://images.unsplash.com/photo-1514306191717-452ec28c7f31?ixlib=rb-1.2.1&auto=format&fit=crop&w=1950&q=80" class="w-full h-full object-cover opacity-80 transition-transform duration-700 group-hover:scale-105" alt="Banner">
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent"></div>
|
||||
<div class="absolute bottom-0 left-0 p-10 max-w-2xl text-white">
|
||||
<div class="inline-block px-3 py-1 bg-red-600 text-white text-xs font-bold rounded mb-3">置顶推荐</div>
|
||||
<h2 class="text-4xl font-bold mb-4 leading-tight">京剧《霸王别姬》全本实录:程派艺术的巅峰演绎</h2>
|
||||
<p class="text-lg text-slate-200 line-clamp-2">梅兰芳大师经典之作,高清修复版独家上线。感受国粹魅力,重温梨园风华。</p>
|
||||
</div>
|
||||
<!-- Arrows (Always visible as per spec) -->
|
||||
<button class="absolute left-4 top-1/2 -translate-y-1/2 w-12 h-12 bg-black/30 hover:bg-black/50 text-white rounded-full flex items-center justify-center backdrop-blur-sm transition-all"><i class="pi pi-chevron-left text-xl"></i></button>
|
||||
<button class="absolute right-4 top-1/2 -translate-y-1/2 w-12 h-12 bg-black/30 hover:bg-black/50 text-white rounded-full flex items-center justify-center backdrop-blur-sm transition-all"><i class="pi pi-chevron-right text-xl"></i></button>
|
||||
<!-- Indicators -->
|
||||
<div class="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2">
|
||||
<span class="w-2 h-2 rounded-full bg-white"></span>
|
||||
<span class="w-2 h-2 rounded-full bg-white/50"></span>
|
||||
<span class="w-2 h-2 rounded-full bg-white/50"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter Bar -->
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center gap-8 border-b border-slate-200 pb-4 mb-4">
|
||||
<button class="text-lg font-bold text-primary-600 border-b-2 border-primary-600 -mb-4.5 pb-4 px-2">推荐</button>
|
||||
<button class="text-lg font-medium text-slate-500 hover:text-slate-800 -mb-4.5 pb-4 px-2 transition-colors">最新</button>
|
||||
<button class="text-lg font-medium text-slate-500 hover:text-slate-800 -mb-4.5 pb-4 px-2 transition-colors">热门</button>
|
||||
</div>
|
||||
<!-- Tags -->
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<button class="px-4 py-1.5 rounded-full bg-slate-900 text-white text-sm font-medium">全部</button>
|
||||
<button class="px-4 py-1.5 rounded-full bg-slate-100 text-slate-600 hover:bg-slate-200 text-sm font-medium transition-colors">京剧</button>
|
||||
<button class="px-4 py-1.5 rounded-full bg-slate-100 text-slate-600 hover:bg-slate-200 text-sm font-medium transition-colors">昆曲</button>
|
||||
<button class="px-4 py-1.5 rounded-full bg-slate-100 text-slate-600 hover:bg-slate-200 text-sm font-medium transition-colors">越剧</button>
|
||||
<button class="px-4 py-1.5 rounded-full bg-slate-100 text-slate-600 hover:bg-slate-200 text-sm font-medium transition-colors">名家名段</button>
|
||||
<button class="px-4 py-1.5 rounded-full bg-slate-100 text-slate-600 hover:bg-slate-200 text-sm font-medium transition-colors">戏曲教学</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Layout: Grid 9:3 -->
|
||||
<div class="grid grid-cols-12 gap-8">
|
||||
<!-- Main Feed (Left 9) -->
|
||||
<div class="col-span-12 lg:col-span-8 xl:col-span-9 space-y-6">
|
||||
|
||||
<!-- Card Variant 1: Single Image (Right) -->
|
||||
<router-link to="/contents/1" class="block bg-white rounded-xl shadow-sm border border-slate-100 p-5 hover:shadow-md transition-shadow group">
|
||||
<div class="flex gap-6">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="px-1.5 py-0.5 rounded text-xs font-medium bg-red-50 text-red-600 border border-red-100">置顶</span>
|
||||
<span class="text-xs text-slate-500 border border-slate-200 px-1 rounded">[京剧]</span>
|
||||
</div>
|
||||
<h3 class="text-lg font-bold text-slate-900 mb-2 leading-snug group-hover:text-primary-600 transition-colors">《锁麟囊》选段:春秋亭外风雨暴 (张火丁亲授版)</h3>
|
||||
<p class="text-base text-slate-500 line-clamp-2 mb-4 leading-relaxed">张火丁教授亲自讲解程派发音技巧,深度剖析《锁麟囊》中春秋亭一折的唱腔设计与情感表达。包含完整示范与逐句拆解。</p>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3 text-sm text-slate-500">
|
||||
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Zhang" class="w-6 h-6 rounded-full">
|
||||
<span>张火丁工作室</span>
|
||||
<span class="w-1 h-1 bg-slate-300 rounded-full"></span>
|
||||
<span>行当:青衣</span>
|
||||
<span class="w-1 h-1 bg-slate-300 rounded-full"></span>
|
||||
<span>2小时前</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<span class="text-sm text-slate-400"><i class="pi pi-eye mr-1"></i> 1.2万</span>
|
||||
<span class="text-lg font-bold text-red-600">¥ 9.90</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-[240px] h-[135px] flex-shrink-0 rounded-lg overflow-hidden relative bg-slate-100 hidden sm:block">
|
||||
<img src="https://images.unsplash.com/photo-1576014131795-d44019d02374?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60" class="w-full h-full object-cover">
|
||||
<div class="absolute inset-0 bg-black/20 flex items-center justify-center">
|
||||
<i class="pi pi-play-circle text-4xl text-white opacity-80"></i>
|
||||
</div>
|
||||
<span class="absolute bottom-2 right-2 px-1.5 py-0.5 bg-black/60 text-white text-xs rounded">15:30</span>
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
|
||||
<!-- Card Variant 2: No Image (Text Only) -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-5 hover:shadow-md transition-shadow group">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="px-1.5 py-0.5 rounded text-xs font-medium bg-green-50 text-green-600 border border-green-100">限免</span>
|
||||
<span class="text-xs text-slate-500 border border-slate-200 px-1 rounded">[昆曲]</span>
|
||||
</div>
|
||||
<h3 class="text-lg font-bold text-slate-900 mb-3 group-hover:text-primary-600 transition-colors">浅谈昆曲《牡丹亭》中的水磨腔艺术特点</h3>
|
||||
<p class="text-base text-slate-500 line-clamp-3 mb-4 leading-relaxed">昆曲之所以被称为“百戏之祖”,其细腻婉转的水磨腔功不可没。本文将从发音、吐字、行腔三个维度,带您领略昆曲的声韵之美。对于初学者来说,掌握“字头、字腹、字尾”的处理是关键...</p>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3 text-sm text-slate-500">
|
||||
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Li" class="w-6 h-6 rounded-full">
|
||||
<span>梨园小生</span>
|
||||
<span class="w-1 h-1 bg-slate-300 rounded-full"></span>
|
||||
<span>昨天</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs text-slate-400 line-through">¥ 5.00</span>
|
||||
<span class="text-sm font-bold text-green-600 border border-green-200 px-2 py-0.5 rounded">限时免费</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card Variant 3: 3 Images -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-5 hover:shadow-md transition-shadow group">
|
||||
<h3 class="text-lg font-bold text-slate-900 mb-3 group-hover:text-primary-600 transition-colors">[图集] 2024 新年京剧晚会后台探班:名角云集</h3>
|
||||
<div class="grid grid-cols-3 gap-2 mb-4">
|
||||
<div class="aspect-[4/3] rounded-lg overflow-hidden bg-slate-100">
|
||||
<img src="https://images.unsplash.com/photo-1469571486292-0ba58a3f068b?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60" class="w-full h-full object-cover hover:scale-105 transition-transform duration-500">
|
||||
</div>
|
||||
<div class="aspect-[4/3] rounded-lg overflow-hidden bg-slate-100">
|
||||
<img src="https://images.unsplash.com/photo-1533174072545-e8d4aa97edf9?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60" class="w-full h-full object-cover hover:scale-105 transition-transform duration-500">
|
||||
</div>
|
||||
<div class="aspect-[4/3] rounded-lg overflow-hidden bg-slate-100 relative">
|
||||
<img src="https://images.unsplash.com/photo-1516450360452-9312f5e86fc7?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60" class="w-full h-full object-cover hover:scale-105 transition-transform duration-500">
|
||||
<div class="absolute bottom-2 right-2 px-1.5 py-0.5 bg-black/60 text-white text-xs rounded">+9</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3 text-sm text-slate-500">
|
||||
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Photo" class="w-6 h-6 rounded-full">
|
||||
<span>戏曲摄影师老王</span>
|
||||
<span class="w-1 h-1 bg-slate-300 rounded-full"></span>
|
||||
<span>3天前</span>
|
||||
</div>
|
||||
<span class="text-sm text-slate-400"><i class="pi pi-eye mr-1"></i> 8.5k</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Load More -->
|
||||
<div class="pt-4 text-center">
|
||||
<button class="px-8 py-3 bg-white border border-slate-200 rounded-full text-slate-600 hover:bg-slate-50 hover:text-primary-600 font-medium transition-all shadow-sm">
|
||||
点击加载更多内容
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar (Right 3) -->
|
||||
<div class="hidden lg:block lg:col-span-4 xl:col-span-3 space-y-6">
|
||||
<!-- Announcement -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-5">
|
||||
<h3 class="font-bold text-slate-900 mb-4 flex items-center gap-2">
|
||||
<i class="pi pi-megaphone text-orange-500"></i> 公告
|
||||
</h3>
|
||||
<ul class="space-y-3 text-sm text-slate-600">
|
||||
<li class="line-clamp-1 hover:text-primary-600 cursor-pointer">• 关于调整创作者收益结算周期的通知</li>
|
||||
<li class="line-clamp-1 hover:text-primary-600 cursor-pointer">• “国粹传承”戏曲短视频大赛开启!</li>
|
||||
<li class="line-clamp-1 hover:text-primary-600 cursor-pointer">• 平台系统维护升级公告 (12.30)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Recommended Tenants -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-5">
|
||||
<h3 class="font-bold text-slate-900 mb-4">推荐名家</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Master1" class="w-10 h-10 rounded-full">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-bold text-slate-900 text-sm truncate">梅派传人小林</div>
|
||||
<div class="text-xs text-slate-500 truncate">粉丝 12.5万</div>
|
||||
</div>
|
||||
<button class="px-3 py-1 bg-primary-50 text-primary-600 text-xs font-bold rounded-full hover:bg-primary-100">关注</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Master2" class="w-10 h-10 rounded-full">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-bold text-slate-900 text-sm truncate">豫剧李大师</div>
|
||||
<div class="text-xs text-slate-500 truncate">粉丝 8.9万</div>
|
||||
</div>
|
||||
<button class="px-3 py-1 bg-primary-50 text-primary-600 text-xs font-bold rounded-full hover:bg-primary-100">关注</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Master3" class="w-10 h-10 rounded-full">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-bold text-slate-900 text-sm truncate">越剧小生阿强</div>
|
||||
<div class="text-xs text-slate-500 truncate">粉丝 5.2万</div>
|
||||
</div>
|
||||
<button class="px-3 py-1 bg-slate-100 text-slate-400 text-xs font-bold rounded-full">已关注</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trending List -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-5">
|
||||
<h3 class="font-bold text-slate-900 mb-4 flex items-center gap-2">
|
||||
<i class="pi pi-chart-line text-red-500"></i> 本周热门
|
||||
</h3>
|
||||
<ul class="space-y-4">
|
||||
<li class="flex gap-3 items-start">
|
||||
<span class="text-red-500 font-bold italic text-lg w-4">1</span>
|
||||
<div class="flex-1">
|
||||
<h4 class="text-sm font-medium text-slate-800 line-clamp-2 hover:text-primary-600 cursor-pointer">《智取威虎山》选段:今日痛饮庆功酒</h4>
|
||||
<span class="text-xs text-slate-400 mt-1 block">15.2万 阅读</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex gap-3 items-start">
|
||||
<span class="text-orange-500 font-bold italic text-lg w-4">2</span>
|
||||
<div class="flex-1">
|
||||
<h4 class="text-sm font-medium text-slate-800 line-clamp-2 hover:text-primary-600 cursor-pointer">【深度解析】京剧脸谱颜色的含义</h4>
|
||||
<span class="text-xs text-slate-400 mt-1 block">9.8万 阅读</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex gap-3 items-start">
|
||||
<span class="text-yellow-500 font-bold italic text-lg w-4">3</span>
|
||||
<div class="flex-1">
|
||||
<h4 class="text-sm font-medium text-slate-800 line-clamp-2 hover:text-primary-600 cursor-pointer">黄梅戏《女驸马》全场高清</h4>
|
||||
<span class="text-xs text-slate-400 mt-1 block">7.5万 阅读</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Ad / Promo -->
|
||||
<div class="rounded-xl overflow-hidden shadow-sm">
|
||||
<img src="https://images.unsplash.com/photo-1557683316-973673baf926?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60" class="w-full h-40 object-cover">
|
||||
<div class="bg-white p-3 flex justify-between items-center">
|
||||
<span class="text-xs text-slate-400 border border-slate-200 px-1 rounded">广告</span>
|
||||
<span class="text-sm font-medium text-slate-700">戏曲周边商城上线啦</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/TopicsView.vue
Normal file
6
frontend/portal/src/views/TopicsView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="mx-auto max-w-screen-xl my-8 p-8 bg-white rounded-xl shadow-sm">
|
||||
<h1 class="text-2xl font-bold mb-4">Topics</h1>
|
||||
<p class="text-slate-400">(Topic list)</p>
|
||||
</div>
|
||||
</template>
|
||||
124
frontend/portal/src/views/auth/LoginView.vue
Normal file
124
frontend/portal/src/views/auth/LoginView.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<div class="bg-white rounded-2xl shadow-xl w-full max-w-4xl overflow-hidden flex min-h-[550px]">
|
||||
<!-- Left Brand Area -->
|
||||
<div class="hidden md:flex w-1/2 bg-slate-900 relative p-12 flex-col justify-between text-white">
|
||||
<!-- Decor/Bg could be here -->
|
||||
<div class="z-10">
|
||||
<div class="flex items-center gap-2 mb-8">
|
||||
<div class="w-8 h-8 bg-white/20 rounded flex items-center justify-center font-bold">Q</div>
|
||||
<span class="text-xl font-bold">Quyun</span>
|
||||
</div>
|
||||
<h1 class="text-4xl font-bold leading-tight mb-4">探索戏曲的<br>无限可能</h1>
|
||||
<p class="text-slate-400">专业的租户管理与内容交付平台,连接创作者与用户。</p>
|
||||
</div>
|
||||
<div class="text-xs text-slate-500 z-10">© 2025 Quyun. All rights reserved.</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Form Area -->
|
||||
<div class="w-full md:w-1/2 p-8 sm:p-12 flex flex-col justify-center bg-white relative">
|
||||
<div v-if="step === 1">
|
||||
<h2 class="text-2xl font-bold text-slate-900 mb-2">欢迎回来</h2>
|
||||
<p class="text-sm text-slate-500 mb-8">未注册的手机号验证后将自动创建账号</p>
|
||||
|
||||
<form @submit.prevent="getOTP">
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium text-slate-700 mb-2">手机号码</label>
|
||||
<div class="flex">
|
||||
<span class="inline-flex items-center px-4 rounded-l-lg border border-r-0 border-slate-300 bg-slate-50 text-slate-500 text-sm">+86</span>
|
||||
<input
|
||||
v-model="phone"
|
||||
type="tel"
|
||||
class="flex-1 block w-full rounded-r-lg border-slate-300 focus:border-primary-500 focus:ring-primary-500 sm:text-lg py-3"
|
||||
placeholder="请输入手机号"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start mb-6">
|
||||
<div class="flex items-center h-5">
|
||||
<input id="terms" v-model="agreed" type="checkbox" class="w-5 h-5 rounded border-slate-300 text-primary-600 focus:ring-primary-500">
|
||||
</div>
|
||||
<label for="terms" class="ml-3 text-sm text-slate-500">
|
||||
我已阅读并同意 <a href="#" class="text-primary-600 hover:underline">用户协议</a> 和 <a href="#" class="text-primary-600 hover:underline">隐私政策</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-lg font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
:disabled="!agreed || !phone"
|
||||
>
|
||||
获取验证码
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-8">
|
||||
<div class="relative">
|
||||
<div class="absolute inset-0 flex items-center">
|
||||
<div class="w-full border-t border-slate-200"></div>
|
||||
</div>
|
||||
<div class="relative flex justify-center text-sm">
|
||||
<span class="px-2 bg-white text-slate-500">其他方式登录</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 flex justify-center gap-6">
|
||||
<button class="w-10 h-10 rounded-full bg-slate-50 border border-slate-200 flex items-center justify-center hover:bg-slate-100 text-green-600"><i class="pi pi-wechat text-xl"></i></button>
|
||||
<button class="w-10 h-10 rounded-full bg-slate-50 border border-slate-200 flex items-center justify-center hover:bg-slate-100 text-slate-800"><i class="pi pi-github text-xl"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="step === 2">
|
||||
<button @click="step = 1" class="absolute top-8 left-8 text-slate-400 hover:text-slate-600"><i class="pi pi-arrow-left mr-1"></i> 返回</button>
|
||||
<h2 class="text-2xl font-bold text-slate-900 mb-2">输入验证码</h2>
|
||||
<p class="text-sm text-slate-500 mb-8">验证码已发送至 +86 {{ phone }}</p>
|
||||
|
||||
<div class="flex gap-3 mb-8 justify-center">
|
||||
<input
|
||||
v-for="i in 6" :key="i"
|
||||
type="text"
|
||||
maxlength="1"
|
||||
class="w-12 h-14 text-center text-2xl font-bold border border-slate-300 rounded-lg focus:border-primary-500 focus:ring-2 focus:ring-primary-200"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-8">
|
||||
<button class="text-sm text-slate-500 hover:text-primary-600">59s 后重新获取</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@click="login"
|
||||
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-lg font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
|
||||
>
|
||||
登录 / 注册
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const step = ref(1);
|
||||
const phone = ref('');
|
||||
const agreed = ref(false);
|
||||
|
||||
const getOTP = () => {
|
||||
if(!agreed.value) return;
|
||||
// Simulate API call
|
||||
setTimeout(() => {
|
||||
step.value = 2;
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const login = () => {
|
||||
// Simulate Login
|
||||
setTimeout(() => {
|
||||
router.push('/');
|
||||
}, 800);
|
||||
};
|
||||
</script>
|
||||
10
frontend/portal/src/views/content/DetailView.vue
Normal file
10
frontend/portal/src/views/content/DetailView.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Placeholder for Content Detail View -->
|
||||
<div class="bg-white p-8 rounded-xl shadow-sm border border-slate-100 mx-auto max-w-screen-xl my-8 text-center">
|
||||
<h1 class="text-2xl font-bold mb-4">Content Detail Page</h1>
|
||||
<p class="text-slate-500">ID: {{ $route.params.id }}</p>
|
||||
<p class="text-slate-400 mt-2">(Implementation pending based on PAGE_CONTENT_DETAIL.md)</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
7
frontend/portal/src/views/creator/ApplyView.vue
Normal file
7
frontend/portal/src/views/creator/ApplyView.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div class="mx-auto max-w-3xl my-12 bg-white rounded-xl shadow-sm border border-slate-100 p-8">
|
||||
<h1 class="text-2xl font-bold mb-4 text-center">Creator Application</h1>
|
||||
<p class="text-slate-500 text-center mb-8">Join us and start your creative journey.</p>
|
||||
<p class="text-slate-400 text-center">(Implementation pending based on PAGE_TENANT_APPLY.md)</p>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/creator/ContentsView.vue
Normal file
6
frontend/portal/src/views/creator/ContentsView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">Creator Contents</h1>
|
||||
<p class="text-slate-400">(List of contents)</p>
|
||||
</div>
|
||||
</template>
|
||||
7
frontend/portal/src/views/creator/DashboardView.vue
Normal file
7
frontend/portal/src/views/creator/DashboardView.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">Creator Dashboard</h1>
|
||||
<p class="text-slate-500">Welcome to the Creator Center.</p>
|
||||
<p class="text-slate-400 mt-2">(Implementation pending based on PAGE_TENANT_MANAGEMENT.md)</p>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/creator/OrdersView.vue
Normal file
6
frontend/portal/src/views/creator/OrdersView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">Creator Orders</h1>
|
||||
<p class="text-slate-400">(List of orders)</p>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/creator/SettingsView.vue
Normal file
6
frontend/portal/src/views/creator/SettingsView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">Creator Settings</h1>
|
||||
<p class="text-slate-400">(Tenant settings)</p>
|
||||
</div>
|
||||
</template>
|
||||
16
frontend/portal/src/views/misc/NotFoundView.vue
Normal file
16
frontend/portal/src/views/misc/NotFoundView.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex items-center justify-center bg-slate-50">
|
||||
<div class="text-center">
|
||||
<div class="mb-6">
|
||||
<!-- Use an image or illustration here -->
|
||||
<i class="pi pi-compass text-6xl text-slate-300"></i>
|
||||
</div>
|
||||
<h1 class="text-4xl font-bold text-slate-900 mb-4">404</h1>
|
||||
<p class="text-xl text-slate-600 mb-8">抱歉,您访问的页面走丢了。</p>
|
||||
<div class="flex justify-center gap-4">
|
||||
<router-link to="/" class="px-6 py-3 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors">返回首页</router-link>
|
||||
<button @click="$router.back()" class="px-6 py-3 border border-slate-300 text-slate-700 rounded-lg hover:bg-slate-50 transition-colors">返回上一页</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/order/CheckoutView.vue
Normal file
6
frontend/portal/src/views/order/CheckoutView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="mx-auto max-w-screen-xl my-8 p-8 bg-white rounded-xl shadow-sm">
|
||||
<h1 class="text-2xl font-bold mb-4">Checkout</h1>
|
||||
<p class="text-slate-400">(Checkout flow)</p>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/order/PaymentView.vue
Normal file
6
frontend/portal/src/views/order/PaymentView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="mx-auto max-w-screen-xl my-8 p-8 bg-white rounded-xl shadow-sm">
|
||||
<h1 class="text-2xl font-bold mb-4">Payment Cashier</h1>
|
||||
<p class="text-slate-400">Order ID: {{ $route.params.id }}</p>
|
||||
</div>
|
||||
</template>
|
||||
10
frontend/portal/src/views/tenant/HomeView.vue
Normal file
10
frontend/portal/src/views/tenant/HomeView.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Placeholder for Tenant Home View -->
|
||||
<div class="bg-white p-8 rounded-xl shadow-sm border border-slate-100 mx-auto max-w-screen-xl my-8 text-center">
|
||||
<h1 class="text-2xl font-bold mb-4">Tenant Home Page</h1>
|
||||
<p class="text-slate-500">Tenant ID: {{ $route.params.id }}</p>
|
||||
<p class="text-slate-400 mt-2">(Implementation pending based on PAGE_TENANT_HOME.md)</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
72
frontend/portal/src/views/user/DashboardView.vue
Normal file
72
frontend/portal/src/views/user/DashboardView.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<!-- Stat Cards -->
|
||||
<div class="bg-white p-6 rounded-xl shadow-sm border border-slate-100 flex items-center gap-4">
|
||||
<div class="w-12 h-12 rounded-full bg-blue-50 text-blue-600 flex items-center justify-center text-xl"><i class="pi pi-wallet"></i></div>
|
||||
<div>
|
||||
<div class="text-sm text-slate-500">账户余额</div>
|
||||
<div class="text-2xl font-bold text-slate-900">¥ 128.50</div>
|
||||
</div>
|
||||
<!-- <button class="ml-auto text-sm font-medium text-primary-600 hover:bg-primary-50 px-3 py-1.5 rounded transition-colors">充值</button> -->
|
||||
</div>
|
||||
<div class="bg-white p-6 rounded-xl shadow-sm border border-slate-100 flex items-center gap-4">
|
||||
<div class="w-12 h-12 rounded-full bg-yellow-50 text-yellow-600 flex items-center justify-center text-xl"><i class="pi pi-star"></i></div>
|
||||
<div>
|
||||
<div class="text-sm text-slate-500">我的积分</div>
|
||||
<div class="text-2xl font-bold text-slate-900">2,450</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white p-6 rounded-xl shadow-sm border border-slate-100 flex items-center gap-4">
|
||||
<div class="w-12 h-12 rounded-full bg-red-50 text-red-600 flex items-center justify-center text-xl"><i class="pi pi-ticket"></i></div>
|
||||
<div>
|
||||
<div class="text-sm text-slate-500">优惠券</div>
|
||||
<div class="text-2xl font-bold text-slate-900">3 张</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Orders -->
|
||||
<div class="mt-8 bg-white rounded-xl shadow-sm border border-slate-100 p-6">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h2 class="text-xl font-bold text-slate-900">最近订单</h2>
|
||||
<router-link to="/me/orders" class="text-sm text-primary-600 hover:text-primary-700 font-medium">查看全部 <i class="pi pi-angle-right"></i></router-link>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-4 p-4 border border-slate-100 rounded-lg hover:border-slate-300 transition-colors cursor-pointer">
|
||||
<div class="w-16 h-16 bg-slate-100 rounded object-cover flex-shrink-0">
|
||||
<img src="https://images.unsplash.com/photo-1514306191717-452ec28c7f31?ixlib=rb-1.2.1&auto=format&fit=crop&w=100&q=60" class="w-full h-full object-cover rounded">
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-bold text-slate-900 truncate">《霸王别姬》全本实录珍藏版</h3>
|
||||
<div class="text-sm text-slate-500 mt-1">2025-12-24 14:30 · 订单号: 82934712</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="font-bold text-slate-900">¥ 9.90</div>
|
||||
<div class="text-sm text-green-600 mt-1">交易成功</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- More items... -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Views -->
|
||||
<div class="mt-8 bg-white rounded-xl shadow-sm border border-slate-100 p-6">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h2 class="text-xl font-bold text-slate-900">最近浏览</h2>
|
||||
<button class="text-sm text-slate-500 hover:text-slate-700"><i class="pi pi-trash"></i> 清空历史</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
|
||||
<div v-for="i in 5" :key="i" class="group cursor-pointer">
|
||||
<div class="aspect-[16/9] bg-slate-100 rounded-lg overflow-hidden mb-2 relative">
|
||||
<img :src="`https://images.unsplash.com/photo-1469571486292-0ba58a3f068b?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=60`" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500">
|
||||
<div class="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors"></div>
|
||||
</div>
|
||||
<h4 class="text-sm font-medium text-slate-800 line-clamp-2 group-hover:text-primary-600">京剧名家谈戏曲传承与创新发展的思考</h4>
|
||||
<div class="text-xs text-slate-400 mt-1">10分钟前</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/user/LibraryView.vue
Normal file
6
frontend/portal/src/views/user/LibraryView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">My Library</h1>
|
||||
<p class="text-slate-400">(Purchased content)</p>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/user/NotificationsView.vue
Normal file
6
frontend/portal/src/views/user/NotificationsView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">Notifications</h1>
|
||||
<p class="text-slate-400">(Message center)</p>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/user/OrdersView.vue
Normal file
6
frontend/portal/src/views/user/OrdersView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">My Orders</h1>
|
||||
<p class="text-slate-400">(Order list)</p>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/user/ProfileView.vue
Normal file
6
frontend/portal/src/views/user/ProfileView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">Profile Settings</h1>
|
||||
<p class="text-slate-400">(Edit profile)</p>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/user/SecurityView.vue
Normal file
6
frontend/portal/src/views/user/SecurityView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">Account Security</h1>
|
||||
<p class="text-slate-400">(Password and bindings)</p>
|
||||
</div>
|
||||
</template>
|
||||
6
frontend/portal/src/views/user/WalletView.vue
Normal file
6
frontend/portal/src/views/user/WalletView.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">My Wallet</h1>
|
||||
<p class="text-slate-400">(Balance and transactions)</p>
|
||||
</div>
|
||||
</template>
|
||||
22
frontend/portal/vite.config.js
Normal file
22
frontend/portal/vite.config.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
tailwindcss(),
|
||||
],
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 5174,
|
||||
strictPort: true,
|
||||
proxy: {
|
||||
'/v1': {
|
||||
target: 'http://localhost:8080',
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user