完善页面

This commit is contained in:
2026-02-04 09:14:37 +08:00
parent 3476c006e6
commit df642813c3
16 changed files with 3376 additions and 322 deletions

View File

@@ -1,27 +1,203 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
<view class="admin-marketing-lottery-config">
<view class="config-card box-shadow">
<view class="config-header">
<text class="title">抽奖配置</text>
</view>
<view class="config-body">
<!-- 积分抽奖 -->
<view class="form-item">
<view class="item-label">
<text class="label-text">积分抽奖:</text>
</view>
<view class="item-content">
<picker :range="lotteryOptions" range-key="name" @change="onIntegralLotteryChange">
<view class="picker-value">
<text :class="form.integralLotteryId > 0 ? '' : 'placeholder'">
{{ getLotteryName(form.integralLotteryId) }}
</text>
<text class="iconfont icon-arrow-down"></text>
</view>
</picker>
</view>
</view>
<!-- 支付抽奖 -->
<view class="form-item">
<view class="item-label">
<text class="label-text">支付抽奖:</text>
</view>
<view class="item-content">
<picker :range="lotteryOptions" range-key="name" @change="onPayLotteryChange">
<view class="picker-value">
<text :class="form.payLotteryId > 0 ? '' : 'placeholder'">
{{ getLotteryName(form.payLotteryId) }}
</text>
<text class="iconfont icon-arrow-down"></text>
</view>
</picker>
</view>
</view>
<!-- 评价抽奖 -->
<view class="form-item">
<view class="item-label">
<text class="label-text">评价抽奖:</text>
</view>
<view class="item-content">
<picker :range="lotteryOptions" range-key="name" @change="onReplyLotteryChange">
<view class="picker-value">
<text :class="form.replyLotteryId > 0 ? '' : 'placeholder'">
{{ getLotteryName(form.replyLotteryId) }}
</text>
<text class="iconfont icon-arrow-down"></text>
</view>
</picker>
</view>
</view>
<!-- 保存按钮 -->
<view class="form-ops">
<button class="admin-btn admin-btn-primary save-btn" @click="handleSave">保存</button>
</view>
</view>
</view>
</AdminLayout>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('lottery-config')
const title = ref<string>('config')
<script uts>
export default {
data() {
return {
form: {
integralLotteryId: 87,
payLotteryId: 82,
replyLotteryId: 86
},
lotteryOptions: [
{ id: 0, name: '请选择' },
{ id: 87, name: '积分抽奖' },
{ id: 86, name: '评价抽奖' },
{ id: 82, name: '订单抽奖' },
{ id: 75, name: '积分' }
] as any[]
}
},
methods: {
getLotteryName(id: number): string {
const found = this.lotteryOptions.find((item: any): boolean => item.id == id)
return found != null ? (found['name'] as string) : '请选择'
},
onIntegralLotteryChange(e: any) {
this.form.integralLotteryId = this.lotteryOptions[e.detail.value].id
},
onPayLotteryChange(e: any) {
this.form.payLotteryId = this.lotteryOptions[e.detail.value].id
},
onReplyLotteryChange(e: any) {
this.form.replyLotteryId = this.lotteryOptions[e.detail.value].id
},
handleSave() {
uni.showLoading({ title: '正在保存...' })
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '保存成功', icon: 'success' })
}, 800)
}
}
}
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
<style scoped>
.admin-marketing-lottery-config {
padding: 16px;
background-color: #f5f7f9;
min-height: 100vh;
}
.box-shadow {
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.05);
}
.config-card {
max-width: 800px;
margin: 0 auto;
}
.config-header {
padding: 20px;
border-bottom: 1px solid #e8eaec;
}
.title {
font-size: 16px;
font-weight: bold;
color: #17233d;
}
.config-body {
padding: 40px 60px;
}
.form-item {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 30px;
}
.item-label {
width: 120px;
text-align: right;
margin-right: 20px;
}
.label-text {
font-size: 14px;
color: #515a6e;
}
.item-content {
flex: 1;
}
.picker-value {
width: 320px;
height: 36px;
padding: 0 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
font-size: 14px;
transition: border-color 0.2s;
}
.picker-value:active {
border-color: #2d8cf0;
}
.placeholder {
color: #c5c8ce;
}
.form-ops {
padding-left: 140px;
margin-top: 40px;
}
.save-btn {
width: 100px;
height: 36px;
line-height: 36px;
}
.iconfont {
font-size: 12px;
color: #808695;
}
</style>

View File

@@ -1,27 +1,445 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
<view class="admin-marketing-lottery-list">
<!-- 筛选区域 -->
<view class="filter-card box-shadow">
<view class="filter-row">
<view class="filter-item">
<text class="label">活动时间:</text>
<view class="date-range-mock">
<text class="date-text">开始日期 - 结束日期</text>
<text class="iconfont icon-calendar"></text>
</view>
</view>
<view class="filter-item">
<text class="label">活动状态:</text>
<picker :range="statusOptions" range-key="label" @change="statusChange">
<view class="picker-value">{{ getStatusLabel(currentStatus) }} <text class="iconfont icon-arrow-down"></text></view>
</picker>
</view>
<view class="filter-item">
<text class="label">活动类型:</text>
<picker :range="typeOptions" range-key="label" @change="typeChange">
<view class="picker-value">{{ getTypeLabel(currentType) }} <text class="iconfont icon-arrow-down"></text></view>
</picker>
</view>
</view>
<view class="filter-row second-row">
<view class="filter-item">
<text class="label">搜索抽奖:</text>
<input class="admin-input" placeholder="请输入活动名称" v-model="searchQuery" style="width: 200px;" />
</view>
<view class="filter-item">
<button class="admin-btn admin-btn-primary search-btn" @click="handleSearch">搜索</button>
</view>
</view>
</view>
</AdminLayout>
<!-- 操作工具栏 -->
<view class="table-toolbar">
<button class="admin-btn admin-btn-primary">创建抽奖活动</button>
</view>
<!-- 表格区域 -->
<view class="table-card box-shadow">
<view class="table-header">
<text class="col-60 center">ID</text>
<text class="col-120">活动名称</text>
<text class="col-100">活动类型</text>
<text class="col-80 center">抽奖人数</text>
<text class="col-80 center">中奖人数</text>
<text class="col-80 center">抽奖次数</text>
<text class="col-80 center">中奖次数</text>
<text class="col-80 center">活动状态</text>
<text class="col-80 center">开启状态</text>
<text class="col-180">活动时间</text>
<text class="col-120 center">操作</text>
</view>
<view class="table-body">
<view v-for="(item, index) in tableData" :key="index" class="table-row">
<view class="col-60 center"><text class="id-text">{{ item.id }}</text></view>
<view class="col-120"><text class="cell-text">{{ item.name }}</text></view>
<view class="col-100"><text class="cell-text">{{ item.typeName }}</text></view>
<view class="col-80 center"><text class="cell-text">{{ item.memberCount }}</text></view>
<view class="col-80 center"><text class="cell-text">{{ item.winningMemberCount }}</text></view>
<view class="col-80 center"><text class="cell-text">{{ item.lotteryTimes }}</text></view>
<view class="col-80 center"><text class="cell-text">{{ item.winningTimes }}</text></view>
<view class="col-80 center">
<text class="status-text">{{ item.statusText }}</text>
</view>
<view class="col-80 center">
<view class="switch-box" :class="item.isOpen ? 'active' : ''" @click="toggleStatus(item)">
<view class="switch-dot"></view>
</view>
</view>
<view class="col-180">
<view class="time-range">
<text class="time-label">开始: {{ item.startTime }}</text>
<text class="time-label">结束: {{ item.endTime }}</text>
</view>
</view>
<view class="col-120 center ops-cell">
<text class="op-link">编辑</text>
<text class="op-link">抽奖记录</text>
<text class="op-link">更多 <text class="iconfont icon-arrow-down" style="font-size: 10px;"></text></text>
</view>
</view>
</view>
<!-- 分页区域 -->
<view class="table-pagination">
<text class="total-text">共 {{ total }} 条</text>
<view class="page-ops">
<picker :range="pageSizes" @change="pageSizeChange">
<view class="size-picker">{{ currentSize }}条/页 <text class="iconfont icon-arrow-down"></text></view>
</picker>
<button class="page-btn" disabled>上一页</button>
<text class="current-page">1</text>
<button class="page-btn" disabled>下一页</button>
<text class="jump-text">前往</text>
<input class="jump-input" value="1" />
<text class="jump-text">页</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('lottery-list')
const title = ref<string>('list')
<script uts>
export default {
data() {
return {
searchQuery: '',
currentStatus: 0,
currentType: 0,
total: 4,
currentSize: 15,
pageSizes: ['15条/页', '30条/页', '50条/页'],
statusOptions: [
{ label: '全部', value: 0 },
{ label: '未开始', value: 1 },
{ label: '进行中', value: 2 },
{ label: '已结束', value: 3 }
] as any[],
typeOptions: [
{ label: '全部', value: 0 },
{ label: '积分抽奖', value: 1 },
{ label: '订单评价', value: 2 },
{ label: '订单支付', value: 3 }
] as any[],
tableData: [
{
id: 87,
name: '积分抽奖',
typeName: '积分抽取',
memberCount: 166,
winningMemberCount: 0,
lotteryTimes: 329,
winningTimes: 0,
statusText: '已结束',
isOpen: true,
startTime: '2025-12-04 00:00:00',
endTime: '2026-01-31 23:59:59'
},
{
id: 86,
name: '评价抽奖',
typeName: '订单评价',
memberCount: 3,
winningMemberCount: 3,
lotteryTimes: 4,
winningTimes: 3,
statusText: '已结束',
isOpen: true,
startTime: '2023-12-12 00:00:00',
endTime: '2024-01-16 23:59:59'
},
{
id: 82,
name: '订单抽奖',
typeName: '订单支付',
memberCount: 100,
winningMemberCount: 5,
lotteryTimes: 124,
winningTimes: 6,
statusText: '已结束',
isOpen: true,
startTime: '2023-08-16 00:00:00',
endTime: '2024-01-31 23:59:59'
},
{
id: 75,
name: '积分',
typeName: '积分抽取',
memberCount: 1288,
winningMemberCount: 1130,
lotteryTimes: 3413,
winningTimes: 2628,
statusText: '已结束',
isOpen: true,
startTime: '2025-10-01 00:00:00',
endTime: '2025-11-30 23:59:59'
}
] as any[]
}
},
methods: {
getStatusLabel(val : number) : string {
const found = this.statusOptions.find((item : any) : boolean => item.value == val)
return found != null ? (found['label'] as string) : '全部'
},
getTypeLabel(val : number) : string {
const found = this.typeOptions.find((item : any) : boolean => item.value == val)
return found != null ? (found['label'] as string) : '全部'
},
statusChange(e : any) {
this.currentStatus = this.statusOptions[e.detail.value].value
},
typeChange(e : any) {
this.currentType = this.typeOptions[e.detail.value].value
},
pageSizeChange(e : any) {
const val = this.pageSizes[e.detail.value]
this.currentSize = parseInt(val.replace('条/页', ''))
},
handleSearch() {
uni.showToast({ title: '搜索', icon: 'none' })
},
toggleStatus(item : any) {
item.isOpen = !item.isOpen
uni.showToast({ title: '状态已变更', icon: 'none' })
}
}
}
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
<style scoped>
.admin-marketing-lottery-list {
padding: 16px;
background-color: #f5f7f9;
min-height: 100vh;
}
.box-shadow {
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.05);
margin-bottom: 16px;
}
/* 筛选区域 */
.filter-card {
padding: 15px 20px;
}
.filter-row {
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
}
.second-row {
margin-top: 5px;
}
.filter-item {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 24px;
margin-bottom: 15px;
}
.label {
font-size: 14px;
color: #606266;
white-space: nowrap;
}
.date-range-mock {
width: 280px;
height: 32px;
padding: 0 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.date-text { font-size: 13px; color: #c0c4cc; }
.picker-value {
width: 160px;
height: 32px;
padding: 0 10px;
border: 1px solid #dcdfe6;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
font-size: 14px;
}
.admin-input {
height: 32px;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 0 12px;
font-size: 14px;
}
.search-btn {
height: 32px;
line-height: 32px;
padding: 0 15px;
margin-left: 10px;
}
/* 表格工具栏 */
.table-toolbar {
margin-bottom: 16px;
}
/* 表格主体 */
.table-card {
overflow: hidden;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #f8f8f9;
border-bottom: 1px solid #e8eaec;
padding: 12px 0;
}
.table-header text {
font-size: 14px;
font-weight: bold;
color: #515a6e;
}
.table-row {
display: flex;
flex-direction: row;
align-items: center;
border-bottom: 1px solid #e8eaec;
padding: 12px 0;
}
/* 列定义 */
.col-60 { width: 60px; }
.col-80 { width: 80px; }
.col-100 { width: 100px; }
.col-120 { width: 120px; }
.col-180 { width: 180px; }
.center { text-align: center; justify-content: center; }
.cell-text { font-size: 13px; color: #515a6e; }
.id-text { font-size: 13px; color: #808695; }
.status-text { font-size: 13px; color: #515a6e; }
/* 开关组件 */
.switch-box {
width: 40px;
height: 20px;
background-color: #ccc;
border-radius: 10px;
position: relative;
transition: background-color 0.3s;
cursor: pointer;
}
.switch-box.active {
background-color: #2d8cf0;
}
.switch-dot {
width: 16px;
height: 16px;
background-color: #fff;
border-radius: 50%;
position: absolute;
top: 2px;
left: 2px;
transition: transform 0.3s;
}
.switch-box.active .switch-dot {
transform: translateX(20px);
}
.time-range {
display: flex;
flex-direction: column;
}
.time-label {
font-size: 12px;
color: #808695;
line-height: 1.4;
}
.ops-cell {
display: flex;
flex-direction: row;
}
.op-link {
font-size: 13px;
color: #2d8cf0;
margin: 0 4px;
cursor: pointer;
}
/* 分页 */
.table-pagination {
padding: 16px 20px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
}
.total-text {
font-size: 14px;
color: #515a6e;
margin-right: 15px;
}
.page-ops {
display: flex;
flex-direction: row;
align-items: center;
}
.size-picker {
font-size: 14px;
color: #515a6e;
border: 1px solid #dcdfe6;
padding: 2px 8px;
border-radius: 4px;
margin-right: 15px;
display: flex;
flex-direction: row;
align-items: center;
}
.page-btn {
height: 28px;
line-height: 28px;
padding: 0 8px;
font-size: 12px;
border: 1px solid #dcdfe6;
background-color: #fff;
}
.current-page {
width: 28px;
height: 28px;
line-height: 28px;
text-align: center;
background-color: #2d8cf0;
color: #fff;
font-size: 14px;
border-radius: 2px;
margin: 0 8px;
}
.jump-text { font-size: 14px; color: #515a6e; margin: 0 5px; }
.jump-input {
width: 40px;
height: 28px;
border: 1px solid #dcdfe6;
border-radius: 4px;
text-align: center;
font-size: 14px;
}
</style>