feat(user): 创作记录/生成日志/订单页统一页头与工具栏
页头改为标题+说明+圆形刷新按钮;类型/状态筛选改下拉; 全选/下载/删除/去画图等操作收进工具栏右侧,样式与后台一致。 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import { api, jsonBody } from '../api'
|
||||
import { openPayment } from '../payment'
|
||||
import { refreshMe } from '../auth'
|
||||
import Icon from '../components/Icon.vue'
|
||||
import SelectMenu from '../components/SelectMenu.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const items = ref([])
|
||||
@@ -34,6 +35,7 @@ function fmt(unix) {
|
||||
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
|
||||
}
|
||||
|
||||
const STATUS_OPTS = [{ value: '', label: '全部状态' }, { value: 'pending', label: '待支付' }, { value: 'paid', label: '已支付' }, { value: 'cancelled', label: '已取消' }]
|
||||
async function load() {
|
||||
loading.value = true
|
||||
const qs = new URLSearchParams({ limit: String(pageSize), offset: String((page.value - 1) * pageSize) })
|
||||
@@ -91,27 +93,24 @@ async function cont(o) {
|
||||
|
||||
<template>
|
||||
<section class="space-y-5 log-page">
|
||||
<div class="flex items-end justify-between flex-wrap gap-3">
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold tracking-tight text-slate-900">订单</h1>
|
||||
<p class="text-sm text-slate-500 mt-1">{{ total }} 笔充值订单 · 未支付可继续支付</p>
|
||||
<h2 class="page-title">订单</h2>
|
||||
<p class="page-sub">{{ total }} 笔充值订单 · 未支付可继续支付</p>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<button @click="load" class="refresh-btn" title="刷新" aria-label="刷新"><Icon name="refresh" :size="18" /></button>
|
||||
</div>
|
||||
<button @click="router.push('/settings')" class="btn-primary"><Icon name="spark" class="w-4 h-4" /> 去充值</button>
|
||||
</div>
|
||||
|
||||
<!-- Filter bar -->
|
||||
<div class="card toolbar">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<button v-for="s in [['','全部'],['pending','待支付'],['paid','已支付'],['cancelled','已取消']]" :key="s[0]"
|
||||
@click="setStatus(s[0])"
|
||||
class="text-xs rounded-lg px-2.5 py-1.5 transition-colors"
|
||||
:class="status === s[0] ? 'chip-on' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'">{{ s[1] }}</button>
|
||||
</div>
|
||||
<SelectMenu :model-value="status" compact :options="STATUS_OPTS" @update:model-value="setStatus" />
|
||||
<div class="flex-1 min-w-[180px]">
|
||||
<input v-model="search" @keyup.enter="doSearch" @change="doSearch"
|
||||
class="field !py-1.5 text-xs" placeholder="搜索 订单号 / 金额 / 方式…" />
|
||||
class="field" placeholder="搜索 订单号 / 金额 / 方式…" />
|
||||
</div>
|
||||
<button @click="load" class="btn-soft"><Icon name="refresh" class="w-3.5 h-3.5" /> 刷新</button>
|
||||
<button @click="router.push('/settings')" class="btn-primary ml-auto"><Icon name="spark" class="w-3.5 h-3.5" /> 去充值</button>
|
||||
</div>
|
||||
|
||||
<div v-if="loading && !items.length" class="card text-center text-sm text-slate-400 py-24">加载中…</div>
|
||||
|
||||
@@ -130,14 +130,14 @@ const params = (e) => {
|
||||
<template>
|
||||
<section class="space-y-5 log-page">
|
||||
<!-- Header -->
|
||||
<div class="flex items-end justify-between flex-wrap gap-3">
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold tracking-tight text-slate-900">生成日志</h1>
|
||||
<p class="text-sm text-slate-500 mt-1">{{ total }} 条记录 · 含失败原因</p>
|
||||
<h2 class="page-title">生成日志</h2>
|
||||
<p class="page-sub">{{ total }} 条记录 · 含失败原因</p>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<button @click="load" class="refresh-btn" title="刷新" aria-label="刷新"><Icon name="refresh" :size="18" /></button>
|
||||
</div>
|
||||
<button @click="router.push('/user')" class="btn-primary">
|
||||
<Icon name="spark" class="w-4 h-4" /> 去画图
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- KPI 统计(本人累计) -->
|
||||
@@ -168,7 +168,7 @@ const params = (e) => {
|
||||
<input v-model="search" @keyup.enter="doSearch" @change="doSearch"
|
||||
class="field !py-1.5 text-xs" placeholder="搜索 提示词 / 模型 / 错误…" />
|
||||
</div>
|
||||
<button @click="load" class="btn-soft"><Icon name="refresh" class="w-3.5 h-3.5" /> 刷新</button>
|
||||
<button @click="router.push('/user')" class="btn-primary ml-auto"><Icon name="spark" class="w-3.5 h-3.5" /> 去画图</button>
|
||||
</div>
|
||||
|
||||
<!-- States -->
|
||||
|
||||
@@ -6,6 +6,7 @@ import { fmtTs } from '../utils/format'
|
||||
import { copyText } from '../utils/clipboard'
|
||||
import { zipSync } from 'fflate'
|
||||
import Icon from '../components/Icon.vue'
|
||||
import SelectMenu from '../components/SelectMenu.vue'
|
||||
import MediaLightbox from '../components/MediaLightbox.vue'
|
||||
|
||||
const router = useRouter()
|
||||
@@ -46,6 +47,7 @@ const filtered = computed(() => items.value)
|
||||
function doSearch() { page.value = 1; load() }
|
||||
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize)))
|
||||
const KIND_OPTS = [{ value: '', label: '全部类型' }, { value: 'image', label: '图像' }, { value: 'video', label: '视频' }]
|
||||
function setKind(v) { kindFilter.value = v; page.value = 1; load() }
|
||||
function goPage(n) {
|
||||
const target = Math.max(1, Math.min(totalPages.value, n))
|
||||
@@ -231,44 +233,39 @@ onUnmounted(() => {
|
||||
<template>
|
||||
<section class="space-y-5">
|
||||
<!-- Header -->
|
||||
<div class="flex items-end justify-between flex-wrap gap-3">
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold tracking-tight text-slate-900">我的创作记录</h1>
|
||||
<p class="text-sm text-slate-500 mt-1">
|
||||
{{ total }} 条作品
|
||||
</p>
|
||||
<h2 class="page-title">我的创作记录</h2>
|
||||
<p class="page-sub">{{ total }} 条作品</p>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<button @click="load" class="refresh-btn" title="刷新" aria-label="刷新"><Icon name="refresh" :size="18" /></button>
|
||||
</div>
|
||||
<button @click="router.push('/user')" class="btn-primary">
|
||||
<Icon name="spark" class="w-4 h-4" /> 去画图
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Filter bar -->
|
||||
<div class="card toolbar">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<button @click="setKind('')" class="text-xs rounded-lg px-2.5 py-1.5 transition-colors"
|
||||
:class="kindFilter === '' ? 'chip-on' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'">全部</button>
|
||||
<button @click="setKind('image')" class="text-xs rounded-lg px-2.5 py-1.5 transition-colors"
|
||||
:class="kindFilter === 'image' ? 'chip-on' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'">图像</button>
|
||||
<button @click="setKind('video')" class="text-xs rounded-lg px-2.5 py-1.5 transition-colors"
|
||||
:class="kindFilter === 'video' ? 'chip-on' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'">视频</button>
|
||||
</div>
|
||||
<SelectMenu :model-value="kindFilter" compact :options="KIND_OPTS" @update:model-value="setKind" />
|
||||
<div class="flex-1 min-w-[180px]">
|
||||
<input v-model="search" @keyup.enter="doSearch" @change="doSearch"
|
||||
class="field !py-1.5 text-xs" placeholder="搜索提示词或模型…" />
|
||||
class="field" placeholder="搜索提示词或模型…" />
|
||||
</div>
|
||||
<button @click="togglePickAll" class="text-xs rounded-lg px-2.5 py-1.5 transition-colors inline-flex items-center gap-1"
|
||||
:class="pageAllPicked ? 'chip-on' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'">
|
||||
<Icon name="check" class="w-3.5 h-3.5" /> 全选本页
|
||||
</button>
|
||||
<template v-if="picked.size">
|
||||
<button @click="downloadPicked" :disabled="zipping" class="text-xs rounded-lg px-2.5 py-1.5 bg-slate-900 text-white hover:bg-slate-700 inline-flex items-center gap-1 disabled:opacity-50">
|
||||
<Icon name="download" class="w-3.5 h-3.5" /> {{ zipping ? '打包中…' : `下载选中 (${picked.size})` }}
|
||||
<div class="ml-auto flex items-center gap-2 flex-wrap">
|
||||
<template v-if="picked.size">
|
||||
<button @click="deletePicked" class="btn-soft danger">
|
||||
<Icon name="trash" class="w-3.5 h-3.5" /> 删除选中 ({{ picked.size }})
|
||||
</button>
|
||||
<button @click="downloadPicked" :disabled="zipping" class="btn-soft disabled:opacity-50">
|
||||
<Icon name="download" class="w-3.5 h-3.5" /> {{ zipping ? '打包中…' : `下载选中 (${picked.size})` }}
|
||||
</button>
|
||||
</template>
|
||||
<button @click="togglePickAll" class="btn-soft">
|
||||
<Icon name="check" class="w-3.5 h-3.5" /> {{ pageAllPicked ? '取消全选' : '全选本页' }}
|
||||
</button>
|
||||
<button @click="deletePicked" class="text-xs rounded-lg px-2.5 py-1.5 bg-rose-600 text-white hover:bg-rose-500 inline-flex items-center gap-1">
|
||||
<Icon name="trash" class="w-3.5 h-3.5" /> 删除选中 ({{ picked.size }})
|
||||
<button @click="router.push('/user')" class="btn-primary">
|
||||
<Icon name="spark" class="w-3.5 h-3.5" /> 去画图
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty -->
|
||||
|
||||
Reference in New Issue
Block a user