Files
medical-mall/pages/mall/admin/product/labels/index.uvue
2026-03-09 15:49:16 +08:00

464 lines
10 KiB
Plaintext

<template>
<view class="admin-main">
<view class="label-layout">
<!-- 左侧标签组 -->
<view class="label-group-aside">
<view class="aside-header" @click="openGroupModal()">
<text class="btn-group-add">+ 添加分组</text>
</view>
<scroll-view class="aside-list" :scroll-y="true">
<view
v-for="(group, gIndex) in groups"
:key="gIndex"
class="group-item"
:class="activeGroupIndex === gIndex ? 'active' : ''"
@click="activeGroupIndex = gIndex"
>
<view class="group-left">
<image src="/static/logo.png" class="folder-icon" />
<text class="group-name">{{ group.name }}</text>
</view>
<view class="group-ops">
<text class="op-more">...</text>
</view>
</view>
</scroll-view>
</view>
<!-- 右侧标签列表 -->
<view class="label-content-main">
<view class="table-card-full">
<view class="table-toolbar">
<button class="btn-add-label" @click="openLabelDrawer()">添加标签</button>
</view>
<view class="table-header-row">
<text class="th-cell flex-1">ID</text>
<text class="th-cell flex-3">标签名称</text>
<text class="th-cell flex-3">分类名称</text>
<text class="th-cell flex-2 text-center">状态</text>
<text class="th-cell flex-2 text-center">移动端展示</text>
<text class="th-cell flex-2 text-center">操作</text>
</view>
<view class="table-body-scroll">
<view v-if="filteredLabels.length === 0" class="empty-box">
<text class="empty-text">该分组下暂无标签</text>
</view>
<view v-for="(label, lIndex) in filteredLabels" :key="lIndex" class="table-row-line">
<text class="td-cell flex-1 color-9">{{ label.id }}</text>
<view class="td-cell flex-3">
<text class="label-tag-box">{{ label.name }}</text>
</view>
<text class="td-cell flex-3">{{ groups[activeGroupIndex]?.name }}</text>
<view class="td-cell flex-2 row-center">
<StatusSwitch v-model="label.status" />
</view>
<view class="td-cell flex-2 row-center">
<StatusSwitch v-model="label.showInMobile" activeText="显示" inactiveText="隐藏" />
</view>
<view class="td-cell flex-2 row-center">
<text class="btn-op-blue" @click="openLabelDrawer(label)">修改</text>
<view class="v-line"></view>
<text class="btn-op-red" @click="deleteLabel(label)">删除</text>
</view>
</view>
</view>
<!-- 分页模拟 -->
<view class="table-pagination">
<text class="page-total">共 {{ filteredLabels.length }} 条</text>
<view class="page-size-selector">
<text>15条/页</text>
<text class="arrow-down">v</text>
</view>
<view class="page-numbers">
<text class="page-btn active">1</text>
</view>
<view class="page-jump">
<text>前往</text>
<input class="jump-input" :value="'1'" />
<text>页</text>
</view>
</view>
</view>
</view>
</view>
<!-- 标签抽屉 (右侧展示) -->
<view class="drawer-mask" v-if="showDrawerMask" @click="closeLabelDrawer">
<view class="drawer-content" @click.stop="" :class="{ 'drawer-show': showDrawer }">
<view class="drawer-header">
<text class="drawer-title">添加标签</text>
<text class="drawer-close" @click="closeLabelDrawer"></text>
</view>
<view class="drawer-body">
<view class="form-item">
<text class="form-label">标签名称:</text>
<input class="drawer-input" v-model="labelForm.name" placeholder="请输入标签名称" />
</view>
<view class="form-item">
<text class="form-label">所属分组:</text>
<text class="form-value">{{ groups[activeGroupIndex]?.name }}</text>
</view>
</view>
<view class="drawer-footer">
<button class="btn-footer-cancel" @click="closeLabelDrawer">取消</button>
<button class="btn-footer-submit" @click="closeLabelDrawer">确定</button>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, reactive, computed } from 'vue'
import StatusSwitch from '@/components/StatusSwitch.uvue'
interface Label {
id: number;
name: string;
groupId: number;
status: boolean;
showInMobile: boolean;
}
interface Group {
id: number;
name: string;
}
const groups = reactive<Group[]>([
{ id: 0, name: '全部' } as Group,
{ id: 1, name: '商务礼品专题' } as Group,
{ id: 2, name: '员工福利' } as Group,
{ id: 3, name: '主题' } as Group
])
const labels = reactive<Label[]>([
{ id: 1, name: '外事礼品', groupId: 1, status: true, showInMobile: true } as Label,
{ id: 2, name: '会议庆典', groupId: 1, status: true, showInMobile: true } as Label,
{ id: 3, name: '入职纪念', groupId: 2, status: true, showInMobile: true } as Label,
{ id: 4, name: '员工激励', groupId: 2, status: true, showInMobile: true } as Label,
{ id: 5, name: '员工生日', groupId: 2, status: true, showInMobile: true } as Label,
{ id: 6, name: '三八妇女节', groupId: 3, status: true, showInMobile: true } as Label,
{ id: 7, name: '新春快乐', groupId: 3, status: true, showInMobile: true } as Label
])
const activeGroupIndex = ref(0)
const filteredLabels = computed((): Label[] => {
const activeGroup = groups[activeGroupIndex.value]
if (activeGroupIndex.value === 0) return labels
return labels.filter((l: Label): boolean => l.groupId === activeGroup.id)
})
// Drawer logic
const showDrawerMask = ref(false)
const showDrawer = ref(false)
const labelForm = reactive({ name: '' })
function openLabelDrawer(label: Label | null = null) {
if (label) { labelForm.name = label.name } else { labelForm.name = '' }
showDrawerMask.value = true
setTimeout(() => {
showDrawer.value = true
}, 50)
}
function closeLabelDrawer() {
showDrawer.value = false
setTimeout(() => {
showDrawerMask.value = false
}, 300)
}
function openGroupModal() {
uni.showToast({ title: '添加分组功能已模拟', icon: 'none' })
}
function deleteLabel(label: Label) {
const idx = labels.indexOf(label)
if (idx > -1) { labels.splice(idx, 1) }
}
</script>
<style scoped lang="scss">
.admin-main {
padding: 0;
background-color: transparent;
min-height: auto;
}
.label-layout {
display: flex;
flex-direction: row;
height: 100%;
gap: 20px;
}
/* 左侧 */
.label-group-aside {
width: 200px;
background-color: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
}
.aside-header {
padding: 16px;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
}
.btn-group-add { font-size: 14px; color: #999; }
.aside-list { flex: 1; }
.group-item {
padding: 12px 16px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
cursor: pointer;
border-left: 3px solid transparent;
}
.group-item.active {
background-color: #e6f7ff;
color: #1890ff;
border-left-color: #1890ff;
}
.group-left {
display: flex;
flex-direction: row;
align-items: center;
}
.folder-icon {
width: 16px;
height: 16px;
margin-right: 8px;
}
.group-name { font-size: 14px; }
.op-more { color: #ccc; }
/* 右侧 */
.label-content-main {
flex: 1;
background-color: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
}
.table-card-full {
display: flex;
flex-direction: column;
height: 100%;
padding: 24px;
}
.table-toolbar { margin-bottom: 20px; }
.btn-add-label {
background-color: #1890ff;
color: #fff;
height: 32px;
line-height: 32px;
padding: 0 16px;
font-size: 14px;
border-radius: 4px;
border: none;
margin: 0;
}
.table-header-row {
display: flex;
flex-direction: row;
background-color: #fafafa;
height: 44px;
align-items: center;
border: 1px solid #f0f0f0;
}
.th-cell { font-size: 14px; font-weight: bold; padding: 0 12px; }
.table-body-scroll { flex: 1; overflow-y: auto; }
.table-row-line {
display: flex;
flex-direction: row;
min-height: 54px;
align-items: center;
border-bottom: 1px solid #f0f0f0;
border-left: 1px solid #f0f0f0;
border-right: 1px solid #f0f0f0;
}
.td-cell { padding: 0 12px; font-size: 14px; color: #666; }
.color-9 { color: #999; }
.label-tag-box {
background-color: #f0f9eb;
color: #67c23a;
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
}
.btn-op-blue { color: #1890ff; font-size: 14px; cursor: pointer; }
.btn-op-red { color: #ff4d4f; font-size: 14px; cursor: pointer; }
.v-line { width: 1px; height: 12px; background-color: #eee; margin: 0 10px; }
/* 分页 */
.table-pagination {
padding-top: 20px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
}
.page-total { font-size: 13px; color: #666; margin-right: 12px; }
.page-size-selector {
display: flex;
flex-direction: row;
align-items: center;
border: 1px solid #dcdfe6;
padding: 0 8px;
height: 28px;
border-radius: 4px;
margin-right: 12px;
}
.page-size-selector text { font-size: 12px; }
.arrow-down { margin-left: 5px; color: #999; }
.page-numbers { display: flex; flex-direction: row; margin-right: 12px; }
.page-btn {
width: 28px;
height: 28px;
line-height: 28px;
text-align: center;
border: 1px solid #dcdfe6;
border-radius: 4px;
font-size: 13px;
margin: 0 2px;
}
.page-btn.active { background-color: #1890ff; color: #fff; border-color: #1890ff; }
.page-jump {
display: flex;
flex-direction: row;
align-items: center;
font-size: 13px;
color: #666;
}
.jump-input {
width: 40px;
height: 28px;
border: 1px solid #dcdfe6;
border-radius: 4px;
text-align: center;
margin: 0 8px;
}
/* Drawer styles */
.drawer-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
background-color: rgba(0,0,0,0.5);
}
.drawer-content {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 50%;
background-color: #fff;
transform: translateX(100%);
transition: transform 0.3s ease-in-out;
display: flex;
flex-direction: column;
}
.drawer-show {
transform: translateX(0);
}
.drawer-header {
padding: 20px 24px;
border-bottom: 1px solid #f0f0f0;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.drawer-title { font-size: 16px; font-weight: bold; }
.drawer-close { font-size: 20px; color: #999; cursor: pointer; }
.drawer-body { flex: 1; padding: 24px; }
.form-item {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 16px;
}
.form-label { width: 100px; font-size: 14px; color: #666; }
.form-value { font-size: 14px; color: #333; }
.drawer-input {
flex: 1;
height: 36px;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 0 12px;
font-size: 14px;
}
.drawer-footer {
padding: 16px 24px;
border-top: 1px solid #f0f0f0;
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.btn-footer-cancel, .btn-footer-submit {
height: 32px;
line-height: 32px;
padding: 0 20px;
border-radius: 4px;
font-size: 14px;
margin-left: 12px;
}
.btn-footer-cancel { background-color: #fff; border: 1px solid #dcdfe6; color: #666; }
.btn-footer-submit { background-color: #1890ff; color: #fff; border: none; }
.empty-box { padding: 40px 0; text-align: center; }
.empty-text { font-size: 13px; color: #999; }
.row-center { display: flex; flex-direction: row; justify-content: center; align-items: center; }
.flex-1 { flex: 1; }
.flex-2 { flex: 2; }
.flex-3 { flex: 3; }
</style>