Compare commits

...

4 Commits

6 changed files with 229 additions and 50 deletions

View File

@@ -3,7 +3,8 @@
<TopNavbar />
<main class="flex-grow pt-16">
<div class="mx-auto max-w-screen-xl py-8 flex gap-8">
<!-- Creator Sidebar (Dark Theme) --> <aside class="w-[260px] flex-shrink-0 hidden lg:block">
<!-- Creator Sidebar (Dark Theme) -->
<aside class="w-[280px] flex-shrink-0 hidden lg:block">
<div
class="bg-slate-900 rounded-2xl shadow-sm overflow-hidden sticky top-24 text-slate-300 min-h-[600px] flex flex-col">
<!-- Header -->

View File

@@ -28,7 +28,7 @@
<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"
class="flex-1 block w-full rounded-r-lg border border-slate-300 focus:border-primary-500 focus:ring-primary-500 sm:text-lg py-3 px-4"
placeholder="请输入手机号"
required
>
@@ -74,12 +74,14 @@
<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">
<div class="mb-8">
<input
v-for="i in 6" :key="i"
v-model="otpCode"
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"
maxlength="4"
class="w-full h-14 px-4 text-center text-3xl font-bold border border-slate-300 rounded-lg focus:border-primary-500 focus:ring-4 focus:ring-primary-100 outline-none tracking-[1.5em] pl-[1.5em]"
placeholder="0000"
@input="otpCode = otpCode.replace(/\D/g, '')"
>
</div>
@@ -105,6 +107,7 @@ import { useRouter } from 'vue-router';
const router = useRouter();
const step = ref(1);
const phone = ref('');
const otpCode = ref('');
const agreed = ref(false);
const getOTP = () => {
@@ -117,8 +120,10 @@ const getOTP = () => {
const login = () => {
// Simulate Login
if (otpCode.value.length >= 4) {
setTimeout(() => {
router.push('/');
}, 800);
}
};
</script>

View File

@@ -13,7 +13,7 @@
<!-- Key Metrics -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div v-for="metric in metrics" :key="metric.label"
class="bg-white p-6 rounded-xl shadow-sm border border-slate-100 hover:shadow-md transition-shadow">
class="bg-white p-6 rounded-xl shadow-sm border border-slate-100 hover:shadow-md transition-shadow relative">
<div class="text-sm text-slate-500 mb-2">{{ metric.label }}</div>
<div class="flex items-baseline gap-2">
<span class="text-3xl font-bold text-slate-900">{{ metric.value }}</span>
@@ -24,6 +24,12 @@
</span>
</div>
<div class="text-xs text-slate-400 mt-2">{{ metric.subtext }}</div>
<!-- Withdraw Button for Revenue Card -->
<button v-if="metric.label === '累计总收益'" @click="showWithdraw = true"
class="absolute top-6 right-6 px-3 py-1.5 bg-green-50 text-green-700 text-xs font-bold rounded-lg hover:bg-green-100 transition-colors">
提现
</button>
</div>
</div>
@@ -58,14 +64,90 @@
</div>
</div>
</div>
<!-- Withdraw Dialog -->
<Dialog v-model:visible="showWithdraw" modal header="收益提现" :style="{ width: '35rem' }">
<div class="space-y-6 pt-2">
<div>
<label class="block text-sm font-bold text-slate-700 mb-3">提现方式</label>
<div class="grid grid-cols-1 gap-3">
<label
class="flex items-center gap-4 p-4 border rounded-xl cursor-pointer hover:border-primary-500 hover:bg-slate-50 transition-all"
:class="withdrawMethod === 'external' ? 'border-primary-500 bg-primary-50 ring-1 ring-primary-500' : 'border-slate-200'">
<input type="radio" v-model="withdrawMethod" value="external" class="hidden">
<div class="w-10 h-10 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center"><i
class="pi pi-credit-card text-xl"></i></div>
<div class="flex-1">
<div class="font-bold text-slate-900">提现至银行卡/支付宝</div>
<div class="text-xs text-slate-500" v-if="hasPayoutAccount">已绑定招商银行 (8888)</div>
<div class="text-xs text-orange-600 font-bold flex items-center gap-1" v-else>
<i class="pi pi-exclamation-circle"></i> 未配置收款账户
<router-link to="/creator/settings"
class="underline hover:text-orange-800 ml-1">去配置</router-link>
</div>
</div>
<i v-if="withdrawMethod === 'external'" class="pi pi-check-circle text-primary-600 text-xl"></i>
</label>
<label
class="flex items-center gap-4 p-4 border rounded-xl cursor-pointer hover:border-primary-500 hover:bg-slate-50 transition-all"
:class="withdrawMethod === 'wallet' ? 'border-primary-500 bg-primary-50 ring-1 ring-primary-500' : 'border-slate-200'">
<input type="radio" v-model="withdrawMethod" value="wallet" class="hidden">
<div class="w-10 h-10 rounded-full bg-green-100 text-green-600 flex items-center justify-center"><i
class="pi pi-wallet text-xl"></i></div>
<div class="flex-1">
<div class="font-bold text-slate-900">转入个人钱包余额</div>
<div class="text-xs text-slate-500">实时到账可用于平台消费</div>
</div>
<i v-if="withdrawMethod === 'wallet'" class="pi pi-check-circle text-primary-600 text-xl"></i>
</label>
</div>
</div>
<div>
<label class="block text-sm font-bold text-slate-700 mb-2">提现金额 (¥)</label>
<input type="number" placeholder="请输入金额"
class="w-full h-12 px-4 border border-slate-200 rounded-lg text-lg font-bold outline-none focus:border-primary-500 transition-colors">
<div class="text-xs text-slate-400 mt-2 flex justify-between">
<span>可提现收益: ¥ 8,920.50</span>
<button class="text-primary-600 hover:underline">全部提现</button>
</div>
</div>
</div>
<template #footer>
<button @click="showWithdraw = false"
class="px-4 py-2 text-slate-500 hover:text-slate-700 text-sm font-bold">取消</button>
<button @click="handleWithdraw"
class="px-6 py-2 bg-primary-600 text-white rounded-lg text-sm font-bold hover:bg-primary-700 shadow-sm"
:disabled="withdrawMethod === 'external' && !hasPayoutAccount"
:class="{ 'opacity-50 cursor-not-allowed': withdrawMethod === 'external' && !hasPayoutAccount }">确认提现</button>
</template>
</Dialog>
<Toast />
</div>
</template>
<script setup>
import Dialog from 'primevue/dialog';
import Toast from 'primevue/toast';
import { useToast } from 'primevue/usetoast';
import { ref } from 'vue';
const toast = useToast();
const showWithdraw = ref(false);
const withdrawMethod = ref('wallet');
const hasPayoutAccount = ref(false); // Mock state
const metrics = ref([
{ label: '总关注用户', value: '12,548', trend: 1.2, subtext: '昨日 +125' },
{ label: '累计总收益', value: '¥ 8,920.50', trend: 5.4, subtext: '近30天 +2,300' },
]);
const handleWithdraw = () => {
showWithdraw.value = false;
const msg = withdrawMethod.value === 'wallet' ? '资金已转入您的个人钱包' : '提现申请已提交至银行处理';
toast.add({ severity: 'success', summary: '提现成功', detail: msg, life: 3000 });
};
</script>

View File

@@ -1,14 +1,8 @@
<template>
<div class="max-w-5xl mx-auto">
<div class="flex items-center justify-between mb-8">
<h1 class="text-2xl font-bold text-slate-900">频道设置</h1>
<button @click="saveSettings"
class="px-8 py-2.5 bg-primary-600 text-white rounded-lg font-bold hover:bg-primary-700 shadow-sm shadow-primary-200 cursor-pointer active:scale-95 flex items-center gap-2">
<i class="pi pi-check"></i> 保存修改
</button>
</div>
<div>
<h1 class="text-2xl font-bold text-slate-900 mb-8">频道设置</h1>
<div class="max-w-5xl mx-auto space-y-8">
<div class="space-y-8">
<!-- 0. Tips -->
<div class="bg-blue-50 p-6 rounded-xl border border-blue-100 text-sm text-blue-700 flex items-start gap-4">
<i class="pi pi-info-circle text-xl mt-0.5"></i>
@@ -17,16 +11,15 @@
<ul class="list-disc list-inside space-y-1 opacity-90">
<li>建议封面图尺寸 1280x320px重点内容居中</li>
<li>频道名称修改后需人工审核审核期间原名称仍可见</li>
<li>优质的频道介绍和精美的视觉设计有助于大幅提升关注转化率</li>
</ul>
</div>
</div>
<!-- 1. Basic Info -->
<!-- 1. Basic Info & Visuals -->
<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-8">
<h2 class="text-lg font-bold text-slate-900 mb-6 pb-4 border-b border-slate-100">基本信息</h2>
<div class="space-y-6">
<div class="space-y-8">
<!-- Avatar & Name -->
<div class="flex items-start gap-6">
<div class="relative group cursor-pointer flex-shrink-0" @click="triggerUpload('avatar')">
@@ -55,20 +48,7 @@
</div>
</div>
<!-- Intro Detail -->
<div>
<label class="block text-sm font-bold text-slate-700 mb-2">详细介绍</label>
<textarea v-model="form.description" rows="4"
class="w-full p-4 border border-slate-200 rounded-lg focus:border-primary-500 outline-none transition-colors resize-none"
placeholder="详细描述您的履历、师承或频道内容规划..."></textarea>
</div>
</div>
</div>
<!-- 2. Visual Style -->
<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-8">
<h2 class="text-lg font-bold text-slate-900 mb-6 pb-4 border-b border-slate-100">视觉风格</h2>
<!-- Cover Image (Moved Here) -->
<div>
<label class="block text-sm font-bold text-slate-700 mb-3">频道头图 (Cover)</label>
<div
@@ -81,22 +61,118 @@
<i class="pi pi-image text-3xl mb-2"></i>
<span class="text-sm font-medium">点击上传 (建议尺寸 1280x320)</span>
</div>
<!-- Hover Overlay for replace -->
<!-- Hover Overlay -->
<div v-if="form.cover"
class="absolute inset-0 bg-black/40 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
<span class="text-white font-bold"><i class="pi pi-refresh mr-2"></i>更换封面</span>
</div>
</div>
</div>
<!-- Intro Detail -->
<div>
<label class="block text-sm font-bold text-slate-700 mb-2">详细介绍</label>
<textarea v-model="form.description" rows="4"
class="w-full p-4 border border-slate-200 rounded-lg focus:border-primary-500 outline-none transition-colors resize-none"
placeholder="详细描述您的履历、师承或频道内容规划..."></textarea>
</div>
<!-- Save Button -->
<div class="pt-6 border-t border-slate-100 flex justify-end">
<button @click="saveSettings"
class="px-8 py-3 bg-primary-600 text-white rounded-lg font-bold hover:bg-primary-700 shadow-lg shadow-primary-200 cursor-pointer active:scale-95 flex items-center gap-2">
<i class="pi pi-check"></i> 保存修改
</button>
</div>
</div>
</div>
<!-- 2. Payout Settings -->
<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-8">
<div class="flex items-center justify-between mb-6 pb-4 border-b border-slate-100">
<h2 class="text-lg font-bold text-slate-900">收款账户</h2>
<button @click="showAddAccount = true"
class="text-sm font-bold text-primary-600 hover:text-primary-700 cursor-pointer flex items-center gap-1 px-3 py-1.5 rounded hover:bg-primary-50 transition-colors">
<i class="pi pi-plus"></i> 添加账户
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Mock Account Card -->
<div
class="p-4 border border-slate-200 rounded-xl flex items-center gap-4 bg-white relative group hover:border-primary-200 transition-colors">
<div class="w-10 h-10 rounded-full bg-red-50 text-red-600 flex items-center justify-center"><i
class="pi pi-briefcase"></i></div>
<div>
<div class="font-bold text-slate-900">招商银行</div>
<div class="text-sm text-slate-500">储蓄卡 (8888)</div>
</div>
<button
class="absolute top-4 right-4 text-slate-300 hover:text-red-500 cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity p-2"><i
class="pi pi-trash"></i></button>
</div>
</div>
</div>
</div>
<input type="file" ref="fileInput" class="hidden" @change="handleFileChange">
<!-- Add Account Dialog -->
<Dialog v-model:visible="showAddAccount" modal header="添加收款账户" :style="{ width: '25rem' }">
<div class="space-y-4">
<div>
<label class="block text-sm font-bold text-slate-700 mb-2">账户类型</label>
<div class="flex gap-4">
<label
class="flex items-center gap-2 cursor-pointer p-2 border border-transparent rounded hover:bg-slate-50">
<RadioButton v-model="newAccount.type" value="bank" />
<span>银行卡</span>
</label>
<label
class="flex items-center gap-2 cursor-pointer p-2 border border-transparent rounded hover:bg-slate-50">
<RadioButton v-model="newAccount.type" value="alipay" />
<span>支付宝</span>
</label>
</div>
</div>
<div v-if="newAccount.type === 'bank'">
<label class="block text-sm font-bold text-slate-700 mb-2">银行名称</label>
<input type="text"
class="w-full h-10 px-3 border border-slate-200 rounded-lg outline-none focus:border-primary-500"
placeholder="如:招商银行">
</div>
<div>
<label class="block text-sm font-bold text-slate-700 mb-2">{{ newAccount.type === 'bank' ? '银行卡号' :
'支付宝账号' }}</label>
<input type="text"
class="w-full h-10 px-3 border border-slate-200 rounded-lg outline-none focus:border-primary-500"
placeholder="请输入账号">
</div>
<div>
<label class="block text-sm font-bold text-slate-700 mb-2">真实姓名</label>
<input type="text"
class="w-full h-10 px-3 border border-slate-200 rounded-lg outline-none focus:border-primary-500"
placeholder="需与实名认证一致">
</div>
</div>
<template #footer>
<button @click="showAddAccount = false"
class="px-4 py-2 text-slate-500 hover:text-slate-700 text-sm font-bold cursor-pointer">取消</button>
<button @click="handleAddAccount"
class="px-6 py-2 bg-primary-600 text-white rounded-lg text-sm font-bold hover:bg-primary-700 cursor-pointer shadow-sm">保存</button>
</template>
</Dialog>
<Toast />
</div>
</template>
<script setup>
import Dialog from 'primevue/dialog';
import RadioButton from 'primevue/radiobutton';
import Toast from 'primevue/toast';
import { useToast } from 'primevue/usetoast';
import { reactive, ref } from 'vue';
@@ -104,6 +180,14 @@ import { reactive, ref } from 'vue';
const toast = useToast();
const fileInput = ref(null);
const currentUploadType = ref('');
const showAddAccount = ref(false);
const newAccount = reactive({
type: 'bank',
name: '',
account: '',
realname: ''
});
const form = reactive({
name: '梅派传人小林',
@@ -133,6 +217,11 @@ const handleFileChange = (event) => {
}
};
const handleAddAccount = () => {
showAddAccount.value = false;
toast.add({ severity: 'success', summary: '添加成功', detail: '收款账户已添加', life: 3000 });
};
const saveSettings = () => {
if (!form.name) {
toast.add({ severity: 'error', summary: '错误', detail: '频道名称不能为空', life: 3000 });

View File

@@ -7,14 +7,14 @@
v-for="tab in tabs"
:key="tab.value"
@click="currentTab = tab.value"
class="pb-4 text-sm font-medium transition-colors border-b-2 cursor-pointer focus:outline-none relative"
class="pb-4 text-base font-bold transition-colors border-b-2 cursor-pointer focus:outline-none relative"
:class="currentTab === tab.value ? 'text-primary-600 border-primary-600' : 'text-slate-500 border-transparent hover:text-slate-700'"
>
{{ tab.label }}
<span v-if="tab.count > 0" class="absolute -top-1 -right-3 min-w-[1.25rem] h-5 px-1 bg-red-500 text-white text-xs rounded-full flex items-center justify-center scale-75">{{ tab.count }}</span>
<span v-if="tab.count > 0" class="absolute -top-1 -right-4 min-w-[1.25rem] h-5 px-1.5 bg-red-500 text-white text-[10px] rounded-full flex items-center justify-center">{{ tab.count }}</span>
</button>
</div>
<button class="mb-4 text-sm text-slate-500 hover:text-primary-600 cursor-pointer flex items-center gap-1">
<button class="mb-4 text-base font-medium text-slate-500 hover:text-primary-600 cursor-pointer flex items-center gap-1">
<i class="pi pi-check-circle"></i> 全部已读
</button>
</div>
@@ -37,13 +37,13 @@
<!-- Content -->
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between mb-1">
<h3 class="font-bold text-slate-900 text-sm group-hover:text-primary-600 transition-colors flex items-center gap-2">
<h3 class="font-bold text-slate-900 text-lg group-hover:text-primary-600 transition-colors flex items-center gap-2">
<span v-if="!item.read" class="w-2 h-2 bg-blue-600 rounded-full inline-block"></span>
{{ item.title }}
</h3>
<span class="text-xs text-slate-400 whitespace-nowrap">{{ item.time }}</span>
<span class="text-sm text-slate-400 whitespace-nowrap">{{ item.time }}</span>
</div>
<p class="text-sm text-slate-600 line-clamp-2">{{ item.content }}</p>
<p class="text-base text-slate-600 line-clamp-2">{{ item.content }}</p>
</div>
</div>
</div>

View File

@@ -79,16 +79,18 @@
</div>
</div>
</div>
<!-- Empty State (Hidden for mock) -->
<!-- <div class="text-center py-12 text-slate-400">暂无交易记录</div> -->
</div>
<Toast />
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import Toast from 'primevue/toast';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const showRecharge = ref(false);
const selectedAmount = ref(50);
const customAmount = ref('');