Files
medical-mall/pages/mall/admin/marketing/member/right.uvue
2026-02-15 16:37:37 +08:00

195 lines
5.0 KiB
Plaintext

<template>
<view class="marketing-member-right">
<view class="table-card border-shadow">
<view class="table-container">
<!-- Loading 遮罩 -->
<view v-if="isLoading" class="loading-mask">
<text class="loading-text">加载中...</text>
</view>
<view class="table-head">
<view class="th cell-id">ID</view>
<view class="th cell-icon">权益图标</view>
<view class="th cell-name">权益名称</view>
<view class="th cell-desc">权益简介</view>
<view class="th cell-status">是否展示</view>
<view class="th cell-sort">排序</view>
<view class="th cell-op">操作</view>
</view>
<view class="table-body">
<view v-if="memberRights.length === 0 && !isLoading" class="empty-row">
<text>暂无权益配置</text>
</view>
<view v-for="item in memberRights" :key="item.id" class="table-row">
<view class="td cell-id"><text class="td-txt">{{ item.id }}</text></view>
<view class="td cell-icon">
<image class="right-icon" :src="item.icon_url || '/static/logo.png'" mode="aspectFit"></image>
</view>
<view class="td cell-name"><text class="td-txt">{{ item.name }}</text></view>
<view class="td cell-desc"><text class="td-txt">{{ item.description || '-' }}</text></view>
<view class="td cell-status">
<view class="switch-mock" :class="{ active: item.is_show }" @click="toggleStatus(item)">
<view class="switch-dot"></view>
<text class="switch-txt">{{ item.is_show ? '显示' : '隐藏' }}</text>
</view>
</view>
<view class="td cell-sort"><text class="td-txt">{{ item.sort_order }}</text></view>
<view class="td cell-op">
<text class="op-link" @click="handleEdit(item)">编辑</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, onMounted } from 'vue'
import { fetchMemberRights, saveMemberRight, MemberRight } from '@/services/admin/marketingService.uts'
const memberRights = ref<MemberRight[]>([])
const isLoading = ref(false)
onMounted(() => {
loadData()
})
async function loadData() {
isLoading.value = true
try {
const res = await fetchMemberRights()
memberRights.value = res
} catch (e) {
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
isLoading.value = false
}
}
async function toggleStatus(item : MemberRight) {
if (item.id == null) return
const nextStatus = !item.is_show
const success = await saveMemberRight({ ...item, is_show: nextStatus } as MemberRight)
if (success) {
item.is_show = nextStatus
uni.showToast({ title: '修改成功', icon: 'success' })
}
}
const handleEdit = (item: MemberRight) => {
uni.showToast({ title: '编辑功能开发中', icon: 'none' })
}
</script>
<style scoped lang="scss">
.marketing-member-right {
padding: 16px;
background: #f0f2f5;
min-height: 100vh;
}
.border-shadow {
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
}
.table-card { padding: 24px; }
.table-head {
display: flex;
flex-direction: row;
background-color: #f8f8f9;
border-bottom: 1px solid #e8eaec;
}
.th {
padding: 12px 8px;
font-size: 13px;
color: #515a6e;
font-weight: bold;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #e8eaec;
align-items: center;
}
.td { padding: 16px 8px; }
.td-txt { font-size: 13px; color: #515a6e; }
.cell-id { width: 80px; }
.cell-icon { width: 100px; text-align: center; }
.cell-name { width: 150px; }
.cell-desc { flex: 1; }
.cell-status { width: 120px; text-align: center; }
.cell-sort { width: 100px; text-align: center; }
.cell-op { width: 80px; text-align: right; }
.right-icon {
width: 40px;
height: 40px;
}
.switch-mock {
width: 50px;
height: 24px;
background-color: #bfbfbf;
border-radius: 12px;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 4px;
position: relative;
transition: background-color 0.3s;
cursor: pointer;
}
.switch-mock.active { background-color: #1890ff; }
.switch-dot {
width: 16px;
height: 16px;
background-color: #fff;
border-radius: 50%;
position: absolute;
left: 4px;
transition: left 0.3s;
}
.switch-mock.active .switch-dot { left: 30px; }
.switch-txt { font-size: 11px; color: #fff; margin-left: 20px; }
.switch-mock.active .switch-txt { margin-left: 4px; }
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
/* Loading & Empty Styles */
.table-container {
position: relative;
min-height: 300px;
}
.loading-mask {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-color: rgba(255, 255, 255, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.loading-text { color: #1890ff; font-size: 14px; }
.empty-row {
padding: 60px 0;
text-align: center;
color: #999;
font-size: 14px;
}
</style>