大致完成页面
This commit is contained in:
198
pages/mall/admin/marketing/checkin/config.uvue
Normal file
198
pages/mall/admin/marketing/checkin/config.uvue
Normal file
@@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<view class="marketing-checkin-config">
|
||||
<view class="config-card border-shadow">
|
||||
<view class="config-header">
|
||||
<text class="config-title">用户签到配置</text>
|
||||
</view>
|
||||
|
||||
<view class="config-body">
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">签到开关:</text>
|
||||
<text class="label-desc">签到开关,商城是否开启签到功能,关闭后隐藏签到入口</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="radio-group">
|
||||
<view class="radio-item" @click="config.is_open = true">
|
||||
<view class="radio-circle" :class="{ checked: config.is_open }"></view>
|
||||
<text class="radio-txt">开启</text>
|
||||
</view>
|
||||
<view class="radio-item ml-20" @click="config.is_open = false">
|
||||
<view class="radio-circle" :class="{ checked: !config.is_open }"></view>
|
||||
<text class="radio-txt">关闭</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">签到模式:</text>
|
||||
<text class="label-desc">无限制,累积和连续签到不会清零;周循环,每周一会清理累积和连续的记录为0,重新开始计算;月循环,每月一号会清理累积和连续的记录为0,重新开始计算</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="radio-group">
|
||||
<view class="radio-item" @click="config.mode = 'none'">
|
||||
<view class="radio-circle" :class="{ checked: config.mode === 'none' }"></view>
|
||||
<text class="radio-txt">无限制</text>
|
||||
</view>
|
||||
<view class="radio-item ml-20" @click="config.mode = 'week'">
|
||||
<view class="radio-circle" :class="{ checked: config.mode === 'week' }"></view>
|
||||
<text class="radio-txt">周循环</text>
|
||||
</view>
|
||||
<view class="radio-item ml-20" @click="config.mode = 'month'">
|
||||
<view class="radio-circle" :class="{ checked: config.mode === 'month' }"></view>
|
||||
<text class="radio-txt">月循环</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">签到提醒:</text>
|
||||
<text class="label-desc">是否开启签到提醒,提醒方式为短信以及站内信</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="radio-group">
|
||||
<view class="radio-item" @click="config.notice_enabled = true">
|
||||
<view class="radio-circle" :class="{ checked: config.notice_enabled }"></view>
|
||||
<text class="radio-txt">开启</text>
|
||||
</view>
|
||||
<view class="radio-item ml-20" @click="config.notice_enabled = false">
|
||||
<view class="radio-circle" :class="{ checked: !config.notice_enabled }"></view>
|
||||
<text class="radio-txt">关闭</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">签到赠送积分:</text>
|
||||
<text class="label-desc">签到赠送积分,每日签到赠送的积分值</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<input class="config-input" type="number" v-model="config.integral" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">签到赠送经验:</text>
|
||||
<text class="label-desc">签到赠送用户经验值</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<input class="config-input" type="number" v-model="config.exp" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-footer">
|
||||
<button class="btn-submit" @click="handleSave">提交</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const config = reactive({
|
||||
is_open: true,
|
||||
mode: 'none',
|
||||
notice_enabled: false,
|
||||
integral: 10,
|
||||
exp: 1
|
||||
})
|
||||
|
||||
const handleSave = () => {
|
||||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.marketing-checkin-config {
|
||||
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);
|
||||
}
|
||||
|
||||
.config-card { padding: 24px; }
|
||||
|
||||
.config-header {
|
||||
border-bottom: 1px solid #e8eaec;
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.config-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #17233d;
|
||||
position: relative;
|
||||
padding-left: 12px;
|
||||
}
|
||||
.config-title::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 4px;
|
||||
bottom: 4px;
|
||||
width: 3px;
|
||||
background: #1890ff;
|
||||
}
|
||||
|
||||
.config-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 30px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.item-label { width: 220px; display: flex; flex-direction: column; }
|
||||
.label-txt { font-size: 14px; color: #333; margin-bottom: 4px; }
|
||||
.label-desc { font-size: 12px; color: #999; line-height: 1.5; padding-right: 20px; }
|
||||
|
||||
.item-content { flex: 1; }
|
||||
|
||||
.radio-group { display: flex; flex-direction: row; padding-top: 4px; flex-wrap: wrap; }
|
||||
.radio-item { display: flex; flex-direction: row; align-items: center; cursor: pointer; margin-bottom: 10px; }
|
||||
.radio-circle { width: 14px; height: 14px; border: 1px solid #dcdfe6; border-radius: 50%; margin-right: 6px; position: relative; }
|
||||
.radio-circle.checked { border-color: #1890ff; }
|
||||
.radio-circle.checked::after { content: ''; position: absolute; width: 8px; height: 8px; background: #1890ff; border-radius: 50%; top: 2px; left: 2px; }
|
||||
.radio-txt { font-size: 14px; color: #606266; }
|
||||
.ml-20 { margin-left: 20px; }
|
||||
|
||||
.config-input {
|
||||
width: 400px;
|
||||
height: 36px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.config-footer {
|
||||
margin-top: 40px;
|
||||
padding-left: 220px;
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
width: 80px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
271
pages/mall/admin/marketing/checkin/reward.uvue
Normal file
271
pages/mall/admin/marketing/checkin/reward.uvue
Normal file
@@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<view class="marketing-checkin-reward">
|
||||
<view class="reward-card border-shadow">
|
||||
<!-- Tabs -->
|
||||
<view class="reward-tabs">
|
||||
<view class="tab-item" :class="{ active: currentTab === 'continuous' }" @click="currentTab = 'continuous'">
|
||||
<text class="tab-txt">连续签到奖励</text>
|
||||
</view>
|
||||
<view class="tab-item" :class="{ active: currentTab === 'cumulative' }" @click="currentTab = 'cumulative'">
|
||||
<text class="tab-txt">累积签到奖励</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="action-row">
|
||||
<button v-if="currentTab === 'continuous'" class="btn-primary" @click="openModal('continuous')">添加连续签到奖励</button>
|
||||
<button v-else class="btn-primary" @click="openModal('cumulative')">添加累积签到奖励</button>
|
||||
</view>
|
||||
|
||||
<!-- Table -->
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">编号</view>
|
||||
<view class="th cell-type">类型</view>
|
||||
<view class="th cell-days">签到天数</view>
|
||||
<view class="th cell-reward">奖励内容</view>
|
||||
<view class="th cell-status">是否可用</view>
|
||||
<view class="th cell-op">操作</view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-for="item in displayList" :key="item.id" class="table-row">
|
||||
<view class="td cell-id"><text class="td-txt">{{ item.id }}</text></view>
|
||||
<view class="td cell-type"><text class="td-txt">{{ item.type === 'continuous' ? '连续签到' : '累积签到' }}</text></view>
|
||||
<view class="td cell-days"><text class="td-txt">{{ item.days }}天</text></view>
|
||||
<view class="td cell-reward">
|
||||
<text class="td-txt">积分+{{ item.integral }}, 经验+{{ item.exp }}</text>
|
||||
</view>
|
||||
<view class="td cell-status">
|
||||
<view class="switch-mock" :class="{ active: item.is_open }" @click="toggleStatus(item)">
|
||||
<view class="switch-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="td cell-op">
|
||||
<text class="op-link" @click="handleEdit(item)">编辑</text>
|
||||
<text class="op-link del ml-10" @click="handleDelete(item)">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 奖励设置弹窗 -->
|
||||
<view v-if="showModal" class="modal-mask">
|
||||
<view class="modal-content">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">{{ modalType === 'continuous' ? '连续签到奖励' : '累积签到奖励' }}</text>
|
||||
<text class="modal-close" @click="showModal = false">×</text>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<view class="form-item">
|
||||
<text class="form-label">{{ modalType === 'continuous' ? '连续签到天数' : '累积签到天数' }}:</text>
|
||||
<input class="form-input" v-model="formData.days" type="number" placeholder="0" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">赠送积分:</text>
|
||||
<input class="form-input" v-model="formData.integral" type="number" placeholder="0" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">赠送经验:</text>
|
||||
<input class="form-input" v-model="formData.exp" type="number" placeholder="0" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<button class="btn-cancel" @click="showModal = false">取消</button>
|
||||
<button class="btn-submit" @click="handleSubmit">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
|
||||
const currentTab = ref('continuous')
|
||||
const showModal = ref(false)
|
||||
const modalType = ref('continuous')
|
||||
|
||||
const formData = reactive({
|
||||
days: '',
|
||||
integral: '',
|
||||
exp: ''
|
||||
})
|
||||
|
||||
const continuousList = ref([
|
||||
{ id: 1, type: 'continuous', days: 3, integral: 20, exp: 2, is_open: true },
|
||||
{ id: 2, type: 'continuous', days: 7, integral: 50, exp: 5, is_open: true }
|
||||
])
|
||||
|
||||
const cumulativeList = ref([
|
||||
{ id: 3, type: 'cumulative', days: 15, integral: 100, exp: 10, is_open: true },
|
||||
{ id: 4, type: 'cumulative', days: 30, integral: 200, exp: 20, is_open: true }
|
||||
])
|
||||
|
||||
const displayList = computed(() => {
|
||||
return currentTab.value === 'continuous' ? continuousList.value : cumulativeList.value
|
||||
})
|
||||
|
||||
const openModal = (type: string) => {
|
||||
modalType.value = type
|
||||
formData.days = ''
|
||||
formData.integral = ''
|
||||
formData.exp = ''
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
const toggleStatus = (item: any) => {
|
||||
item.is_open = !item.is_open
|
||||
uni.showToast({ title: '修改成功', icon: 'success' })
|
||||
}
|
||||
|
||||
const handleEdit = (item: any) => {
|
||||
uni.showToast({ title: '编辑功能暂未对接', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleDelete = (item: any) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认删除该奖励配置吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
if (currentTab.value === 'continuous') {
|
||||
continuousList.value = continuousList.value.filter(i => i.id !== item.id)
|
||||
} else {
|
||||
cumulativeList.value = cumulativeList.value.filter(i => i.id !== item.id)
|
||||
}
|
||||
uni.showToast({ title: '已删除', icon: 'success' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!formData.days) {
|
||||
uni.showToast({ title: '请输入天数', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const newItem = {
|
||||
id: Date.now(),
|
||||
type: modalType.value,
|
||||
days: parseInt(formData.days.toString()),
|
||||
integral: parseInt(formData.integral.toString() || '0'),
|
||||
exp: parseInt(formData.exp.toString() || '0'),
|
||||
is_open: true
|
||||
}
|
||||
|
||||
if (modalType.value === 'continuous') {
|
||||
continuousList.value.push(newItem)
|
||||
} else {
|
||||
cumulativeList.value.push(newItem)
|
||||
}
|
||||
|
||||
showModal.value = false
|
||||
uni.showToast({ title: '添加成功', icon: 'success' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.marketing-checkin-reward {
|
||||
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);
|
||||
}
|
||||
|
||||
.reward-card { padding: 24px; }
|
||||
|
||||
.reward-tabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1px solid #e8eaec;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tab-txt { font-size: 14px; color: #515a6e; }
|
||||
|
||||
.tab-item.active .tab-txt { color: #1890ff; font-weight: bold; }
|
||||
.tab-item.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: #1890ff;
|
||||
}
|
||||
|
||||
.action-row { margin-bottom: 24px; }
|
||||
|
||||
.btn-primary {
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.table-head { display: flex; flex-direction: row; background: #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-type { width: 120px; }
|
||||
.cell-days { width: 120px; }
|
||||
.cell-reward { flex: 1; }
|
||||
.cell-status { width: 100px; text-align: center; }
|
||||
.cell-op { width: 150px; text-align: right; }
|
||||
|
||||
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
|
||||
.op-link.del { color: #ff4d4f; }
|
||||
.ml-10 { margin-left: 10px; }
|
||||
|
||||
.switch-mock {
|
||||
width: 44px; height: 22px; background-color: #bfbfbf; border-radius: 11px;
|
||||
display: flex; align-items: center; padding: 0 4px; position: relative;
|
||||
transition: background-color 0.3s; cursor: pointer;
|
||||
}
|
||||
.switch-mock.active { background-color: #1890ff; }
|
||||
.switch-dot {
|
||||
width: 14px; height: 14px; background-color: #fff; border-radius: 50%;
|
||||
position: absolute; left: 4px; transition: left 0.3s;
|
||||
}
|
||||
.switch-mock.active .switch-dot { left: 26px; }
|
||||
|
||||
/* Modal */
|
||||
.modal-mask {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000;
|
||||
}
|
||||
.modal-content { width: 500px; background: #fff; border-radius: 4px; }
|
||||
.modal-header { padding: 16px 24px; border-bottom: 1px solid #e8eaec; display: flex; justify-content: space-between; align-items: center; }
|
||||
.modal-title { font-size: 16px; font-weight: bold; }
|
||||
.modal-close { font-size: 24px; color: #999; cursor: pointer; }
|
||||
.modal-body { padding: 24px; }
|
||||
.modal-footer { padding: 12px 24px; border-top: 1px solid #e8eaec; display: flex; justify-content: flex-end; }
|
||||
|
||||
.form-item { display: flex; flex-direction: row; margin-bottom: 20px; align-items: center; }
|
||||
.form-label { width: 120px; font-size: 14px; color: #606266; }
|
||||
.form-input { flex: 1; height: 32px; border: 1px solid #dcdfe6; border-radius: 4px; padding: 0 12px; }
|
||||
|
||||
.btn-cancel { margin-right: 8px; height: 32px; line-height: 32px; padding: 0 16px; font-size: 14px; border-radius: 4px; border: 1px solid #dcdfe6; background: #fff; }
|
||||
.btn-submit { height: 32px; line-height: 32px; padding: 0 16px; font-size: 14px; border-radius: 4px; background: #1890ff; color: #fff; border: none; }
|
||||
</style>
|
||||
@@ -1,27 +1,364 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
<view class="marketing-live-anchor">
|
||||
<view class="action-bar">
|
||||
<button class="btn-add" @click="showModal = true">添加主播</button>
|
||||
</view>
|
||||
|
||||
<view class="table-card border-shadow">
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">ID</view>
|
||||
<view class="th cell-name">名称</view>
|
||||
<view class="th cell-phone">电话</view>
|
||||
<view class="th cell-wechat">微信号</view>
|
||||
<view class="th cell-op">操作</view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-for="item in anchorList" :key="item.id" class="table-row">
|
||||
<view class="td cell-id"><text class="td-txt">{{ item.id }}</text></view>
|
||||
<view class="td cell-name"><text class="td-txt">{{ item.name }}</text></view>
|
||||
<view class="td cell-phone"><text class="td-txt">{{ item.phone }}</text></view>
|
||||
<view class="td cell-wechat"><text class="td-txt">{{ item.wechat }}</text></view>
|
||||
<view class="td cell-op">
|
||||
<view class="op-links">
|
||||
<text class="op-link" @click="handleEdit(item)">修改</text>
|
||||
<text class="op-split">|</text>
|
||||
<text class="op-link" @click="handleDelete(item)">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="pagination-footer">
|
||||
<view class="page-total"><text class="total-txt">共 {{ anchorList.length }} 条</text></view>
|
||||
<view class="page-select">
|
||||
<view class="select-mock mini">
|
||||
<text class="select-val">15条/页</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="page-btns">
|
||||
<text class="p-btn disabled">‹</text>
|
||||
<text class="p-btn active">1</text>
|
||||
<text class="p-btn disabled">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
|
||||
<!-- Modal Overlay -->
|
||||
<view v-if="showModal" class="modal-mask" @click="showModal = false"></view>
|
||||
|
||||
<!-- Modal Panel -->
|
||||
<view v-if="showModal" class="modal-panel">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">添加主播</text>
|
||||
<text class="modal-close" @click="showModal = false">×</text>
|
||||
</view>
|
||||
<view class="modal-content">
|
||||
<view class="form-item">
|
||||
<text class="form-label required">主播名称:</text>
|
||||
<input class="form-input" placeholder="请输入主播名称" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label required">主播微信号:</text>
|
||||
<input class="form-input" placeholder="请输入主播微信号" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label required">主播手机号:</text>
|
||||
<input class="form-input" v-model="formData.phone" placeholder="请输入主播手机号" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">主播图像:</text>
|
||||
<view class="upload-mock" @click="handleUpload">
|
||||
<image v-if="formData.avatar" :src="formData.avatar" mode="aspectFill" class="avatar-preview" />
|
||||
<text v-else class="upload-ic">🖼️</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<button class="btn-cancel" @click="showModal = false">取消</button>
|
||||
<button class="btn-confirm" @click="handleSubmit">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
const currentPage = ref<string>('live-anchor')
|
||||
const title = ref<string>('anchor')
|
||||
|
||||
const showModal = ref(false)
|
||||
const formData = ref({
|
||||
id: 0,
|
||||
name: '',
|
||||
wechat: '',
|
||||
phone: '',
|
||||
avatar: ''
|
||||
})
|
||||
|
||||
const anchorList = ref([
|
||||
{
|
||||
id: 11,
|
||||
name: '万万',
|
||||
phone: '15012341234',
|
||||
wechat: 'xiao112032014'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: '打羽毛球',
|
||||
phone: '13333333333',
|
||||
wechat: 'evoxwht'
|
||||
}
|
||||
])
|
||||
|
||||
const handleEdit = (item: any) => {
|
||||
formData.value = { ...item, avatar: '' }
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
const handleDelete = (item: any) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除该主播吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
anchorList.value = anchorList.value.filter(i => i.id !== item.id)
|
||||
uni.showToast({ title: '删除成功' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleUpload = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: (res) => {
|
||||
formData.value.avatar = res.tempFilePaths[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
uni.showToast({ title: '操作成功', icon: 'success' })
|
||||
showModal.value = false
|
||||
}
|
||||
</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; }
|
||||
.marketing-live-anchor {
|
||||
min-height: 100vh;
|
||||
background: #f0f2f5;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.border-shadow {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 操作栏 */
|
||||
.action-bar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.btn-add {
|
||||
width: auto;
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 表格区域 */
|
||||
.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-name { flex: 1; min-width: 150px; }
|
||||
.cell-phone { width: 180px; }
|
||||
.cell-wechat { width: 180px; }
|
||||
.cell-op { width: 120px; text-align: right; }
|
||||
|
||||
.op-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
|
||||
.op-split { color: #e8eaec; margin: 0 8px; }
|
||||
|
||||
/* Pagination */
|
||||
.pagination-footer {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
.total-txt { font-size: 13px; color: #606266; }
|
||||
.select-mock.mini {
|
||||
width: 100px;
|
||||
height: 28px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 12px;
|
||||
}
|
||||
.select-val { font-size: 12px; color: #606266; }
|
||||
.arrow { font-size: 10px; color: #c0c4cc; }
|
||||
|
||||
/* Modal Styles */
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.45);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-panel {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 520px;
|
||||
background-color: #fff;
|
||||
z-index: 1001;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.modal-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
}
|
||||
.modal-close {
|
||||
font-size: 24px;
|
||||
color: #bfbfbf;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #262626;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.required::before {
|
||||
content: '*';
|
||||
color: #ff4d4f;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.form-input {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.upload-mock {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.upload-ic { font-size: 24px; color: #bfbfbf; }
|
||||
|
||||
.modal-footer {
|
||||
padding: 10px 16px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn-cancel, .btn-confirm {
|
||||
width: auto;
|
||||
padding: 0 15px;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-cancel {
|
||||
background-color: #fff;
|
||||
border: 1px solid #d9d9d9;
|
||||
color: #595959;
|
||||
}
|
||||
.btn-confirm {
|
||||
background-color: #1890ff;
|
||||
border: 1px solid #1890ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
const currentPage = ref<string>('live-goods')
|
||||
const title = ref<string>('goods')
|
||||
</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>
|
||||
|
||||
|
||||
488
pages/mall/admin/marketing/live/product.uvue
Normal file
488
pages/mall/admin/marketing/live/product.uvue
Normal file
@@ -0,0 +1,488 @@
|
||||
<template>
|
||||
<view class="marketing-live-product">
|
||||
<!-- List View -->
|
||||
<template v-if="!isAdding">
|
||||
<view class="filter-card border-shadow">
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="label">审核状态:</text>
|
||||
<view class="select-mock">
|
||||
<text class="select-val">全部</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-item ml-24">
|
||||
<text class="label">搜索:</text>
|
||||
<input class="input-mock" placeholder="请输入商品名称/ID" />
|
||||
</view>
|
||||
<button class="btn-query ml-16">查询</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="action-bar">
|
||||
<button class="btn-add" @click="isAdding = true">添加商品</button>
|
||||
</view>
|
||||
|
||||
<view class="table-card border-shadow">
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">商品ID</view>
|
||||
<view class="th cell-info">商品名称</view>
|
||||
<view class="th cell-price">直播价</view>
|
||||
<view class="th cell-price">原价</view>
|
||||
<view class="th cell-stock">库存</view>
|
||||
<view class="th cell-audit">审核状态</view>
|
||||
<view class="th cell-status">是否显示</view>
|
||||
<view class="th cell-op">操作</view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-for="item in productList" :key="item.id" class="table-row">
|
||||
<view class="td cell-id"><text class="td-txt">{{ item.id }}</text></view>
|
||||
<view class="td cell-info">
|
||||
<view class="info-wrap">
|
||||
<image class="thumb" :src="item.image" mode="aspectFill"></image>
|
||||
<text class="p-title line-clamp-2">{{ item.title }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="td cell-price"><text class="td-txt">{{ item.live_price.toFixed(2) }}</text></view>
|
||||
<view class="td cell-price"><text class="td-txt">{{ item.price.toFixed(2) }}</text></view>
|
||||
<view class="td cell-stock"><text class="td-txt">{{ item.stock }}</text></view>
|
||||
<view class="td cell-audit"><text class="td-txt">{{ item.audit_status }}</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-op">
|
||||
<view class="op-links">
|
||||
<text class="op-link" @click="handleEdit(item)">详情</text>
|
||||
<text class="op-split">|</text>
|
||||
<text class="op-link" @click="handleDelete(item)">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="pagination-footer">
|
||||
<view class="page-total"><text class="total-txt">共 {{ productList.length }} 条</text></view>
|
||||
<view class="page-select">
|
||||
<view class="select-mock mini">
|
||||
<text class="select-val">20条/页</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="page-btns">
|
||||
<text class="p-btn disabled">‹</text>
|
||||
<text class="p-btn active">1</text>
|
||||
<text class="p-btn disabled">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- Adding View -->
|
||||
<template v-else>
|
||||
<view class="breadcrumb">
|
||||
<text class="back-link" @click="isAdding = false">‹ 返回</text>
|
||||
<text class="current-path">直播商品管理</text>
|
||||
</view>
|
||||
|
||||
<view class="select-card border-shadow">
|
||||
<view class="form-row">
|
||||
<text class="f-label">选择商品:</text>
|
||||
<view class="selected-list">
|
||||
<view class="p-box" v-for="(p, index) in selectedList" :key="index">
|
||||
<image class="p-thumb" :src="p.image" mode="aspectFill"></image>
|
||||
<view class="remove-btn" @click="removeSelected(index)">×</view>
|
||||
</view>
|
||||
<view class="add-box" @click="handleAddProduct">
|
||||
<text class="add-ic">👜</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-row mt-24">
|
||||
<button class="btn-generate" @click="handleGenerate">生成直播商品</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const isAdding = ref(false)
|
||||
const selectedList = ref([
|
||||
{ image: 'https://img0.baidu.com/it/u=3033502919,1657850259&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500' }
|
||||
])
|
||||
|
||||
const productList = ref([
|
||||
{
|
||||
id: 92,
|
||||
image: 'https://img0.baidu.com/it/u=3023224345,1529124233&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
|
||||
title: '绣球永生花网红干花花束大',
|
||||
live_price: 149.00,
|
||||
price: 149.00,
|
||||
stock: 10617,
|
||||
audit_status: '审核通过',
|
||||
is_show: false
|
||||
},
|
||||
{
|
||||
id: 89,
|
||||
image: 'https://img1.baidu.com/it/u=3175865615,2002599723&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
|
||||
title: '家居梵高系列款软版盒袋',
|
||||
live_price: 350.00,
|
||||
price: 350.00,
|
||||
stock: 8625,
|
||||
audit_status: '审核通过',
|
||||
is_show: false
|
||||
},
|
||||
{
|
||||
id: 93,
|
||||
image: 'https://img2.baidu.com/it/u=2719717192,3826027113&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
|
||||
title: '【LESHUCANGHU',
|
||||
live_price: 300.00,
|
||||
price: 300.00,
|
||||
stock: 164,
|
||||
audit_status: '审核通过',
|
||||
is_show: false
|
||||
},
|
||||
{
|
||||
id: 116,
|
||||
image: 'https://img0.baidu.com/it/u=2257917711,1359654032&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
|
||||
title: '爱奇艺智能 奇遇LT01',
|
||||
live_price: 1199.00,
|
||||
price: 1199.00,
|
||||
stock: 6287,
|
||||
audit_status: '审核通过',
|
||||
is_show: false
|
||||
}
|
||||
])
|
||||
|
||||
const toggleStatus = (item: any) => {
|
||||
item.is_show = !item.is_show
|
||||
uni.showToast({ title: '状态修改成功', icon: 'success' })
|
||||
}
|
||||
|
||||
const handleEdit = (item: any) => {
|
||||
uni.showToast({ title: '详情查看中', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleDelete = (item: any) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除该商品吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
productList.value = productList.value.filter(i => i.id !== item.id)
|
||||
uni.showToast({ title: '删除成功' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleAddProduct = () => {
|
||||
uni.showToast({ title: '选择商品功能开发中', icon: 'none' })
|
||||
}
|
||||
|
||||
const removeSelected = (index: number) => {
|
||||
selectedList.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const handleGenerate = () => {
|
||||
uni.showToast({ title: '生成成功', icon: 'success' })
|
||||
isAdding.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.marketing-live-product {
|
||||
min-height: 100vh;
|
||||
background: #f0f2f5;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.border-shadow {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.ml-16 { margin-left: 16px; }
|
||||
.ml-24 { margin-left: 24px; }
|
||||
.mt-24 { margin-top: 24px; }
|
||||
|
||||
/* 过滤栏 */
|
||||
.filter-card {
|
||||
padding: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.input-mock, .select-mock {
|
||||
width: 200px;
|
||||
height: 32px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.input-mock { width: 300px; }
|
||||
.select-mock { width: 160px; justify-content: space-between; }
|
||||
.select-mock.mini { width: 100px; height: 28px; }
|
||||
|
||||
.select-val { font-size: 13px; color: #606266; }
|
||||
.arrow { font-size: 10px; color: #c0c4cc; }
|
||||
|
||||
.btn-query {
|
||||
width: 64px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 操作栏 */
|
||||
.action-bar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.btn-add {
|
||||
width: auto;
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 表格区域 */
|
||||
.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: 70px; }
|
||||
.cell-info { flex: 1; min-width: 250px; }
|
||||
.cell-price { width: 100px; text-align: center; }
|
||||
.cell-stock { width: 100px; text-align: center; }
|
||||
.cell-audit { width: 120px; text-align: center; }
|
||||
.cell-status { width: 100px; text-align: center; }
|
||||
.cell-op { width: 120px; text-align: right; }
|
||||
|
||||
.info-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.thumb {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.p-title {
|
||||
font-size: 13px;
|
||||
color: #515a6e;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
.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-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
|
||||
.op-split { color: #e8eaec; margin: 0 8px; }
|
||||
|
||||
/* Adding Template Styles */
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.back-link { font-size: 14px; color: #8c8c8c; cursor: pointer; }
|
||||
.current-path { font-size: 14px; color: #262626; font-weight: 600; }
|
||||
|
||||
.select-card {
|
||||
padding: 48px;
|
||||
}
|
||||
.form-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.f-label {
|
||||
width: 100px;
|
||||
font-size: 14px;
|
||||
color: #262626;
|
||||
}
|
||||
.selected-list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
}
|
||||
.p-box {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
position: relative;
|
||||
}
|
||||
.p-thumb {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
.remove-btn {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-color: #bfbfbf;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
.add-box {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fafafa;
|
||||
cursor: pointer;
|
||||
}
|
||||
.add-ic { font-size: 24px; color: #bfbfbf; }
|
||||
|
||||
.btn-generate {
|
||||
width: auto;
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
margin-left: 100px;
|
||||
}
|
||||
|
||||
/* 分页 */
|
||||
.pagination-footer {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
.total-txt { font-size: 13px; color: #606266; }
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,27 +1,744 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
<view class="marketing-live-room">
|
||||
<view class="filter-card border-shadow">
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="label">直播状态:</text>
|
||||
<view class="select-mock">
|
||||
<text class="select-val">全部</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-item ml-24">
|
||||
<text class="label">搜索:</text>
|
||||
<input class="input-mock" placeholder="请输入直播间名称/ID/主播昵称/微信号" />
|
||||
</view>
|
||||
<button class="btn-query ml-16">查询</button>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
|
||||
<view class="action-bar">
|
||||
<button class="btn-add" @click="showDrawer = true">添加直播间</button>
|
||||
<button class="btn-sync ml-16">同步直播间</button>
|
||||
</view>
|
||||
|
||||
<view class="table-card border-shadow">
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">直播间ID</view>
|
||||
<view class="th cell-name">直播间名称</view>
|
||||
<view class="th cell-nick">主播昵称</view>
|
||||
<view class="th cell-wechat">主播微信号</view>
|
||||
<view class="th cell-time">直播开始时间</view>
|
||||
<view class="th cell-time">计划结束时间</view>
|
||||
<view class="th cell-time">创建时间</view>
|
||||
<view class="th cell-status">显示状态</view>
|
||||
<view class="th cell-live-status">直播状态</view>
|
||||
<view class="th cell-sort">排序</view>
|
||||
<view class="th cell-op">操作</view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-for="item in roomList" :key="item.id" class="table-row">
|
||||
<view class="td cell-id"><text class="td-txt">{{ item.id }}</text></view>
|
||||
<view class="td cell-name"><text class="td-txt">{{ item.name }}</text></view>
|
||||
<view class="td cell-nick"><text class="td-txt">{{ item.anchor_nick }}</text></view>
|
||||
<view class="td cell-wechat"><text class="td-txt">{{ item.anchor_wechat }}</text></view>
|
||||
<view class="td cell-time"><text class="td-txt-small">{{ item.start_time }}</text></view>
|
||||
<view class="td cell-time"><text class="td-txt-small">{{ item.end_time }}</text></view>
|
||||
<view class="td cell-time"><text class="td-txt-small">{{ item.create_time }}</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-live-status"><text class="td-txt">{{ item.live_status }}</text></view>
|
||||
<view class="td cell-sort"><text class="td-txt">{{ item.sort }}</text></view>
|
||||
<view class="td cell-op">
|
||||
<view class="op-links">
|
||||
<text class="op-link" @click="handleEdit(item)">详情</text>
|
||||
<text class="op-split">|</text>
|
||||
<text class="op-link" @click="handleDelete(item)">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="pagination-footer">
|
||||
<view class="page-total"><text class="total-txt">共 {{ roomList.length }} 条</text></view>
|
||||
<view class="page-select">
|
||||
<view class="select-mock mini">
|
||||
<text class="select-val">20条/页</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="page-btns">
|
||||
<text class="p-btn disabled">‹</text>
|
||||
<text class="p-btn active">1</text>
|
||||
<text class="p-btn disabled">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Drawer Overlay -->
|
||||
<view v-if="showDrawer || isAnimating" class="drawer-mask" :class="{ active: showDrawer }" @click="closeDrawer"></view>
|
||||
|
||||
<!-- Drawer Panel -->
|
||||
<view class="drawer-panel" :class="{ active: showDrawer }">
|
||||
<view class="drawer-header">
|
||||
<view class="header-left">
|
||||
<text class="back-btn" @click="closeDrawer">‹ 返回</text>
|
||||
<text class="drawer-title">直播间管理</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="drawer-content">
|
||||
<view class="alert-info">
|
||||
<text class="alert-txt">提示:必须前往微信小程序官方后台开通直播权限,关注【小程序直播】获知直播状态</text>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="form-label required">选择主播:</view>
|
||||
<view class="select-mock full" @click="handleSelectAnchor">
|
||||
<text class="select-val">{{ formData.anchor_nick || '请选择' }}</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="form-label required">直播间名称:</view>
|
||||
<view class="input-wrap">
|
||||
<input class="form-input" v-model="formData.name" placeholder="请输入直播间名称" />
|
||||
<text class="char-count">{{ formData.name.length }}/80</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="form-label required">背景图:</view>
|
||||
<view class="upload-box" @click="handleUpload('background')">
|
||||
<view class="upload-placeholder" v-if="!formData.background">
|
||||
<text class="up-ic">🖼️</text>
|
||||
</view>
|
||||
<image v-else :src="formData.background" class="upload-preview" mode="aspectFill" />
|
||||
<text class="up-tip blue-bg">尺寸:1080*1920px</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="form-label required">分享图:</view>
|
||||
<view class="upload-box" @click="handleUpload('share')">
|
||||
<view class="upload-placeholder" v-if="!formData.share_img">
|
||||
<text class="up-ic">🖼️</text>
|
||||
</view>
|
||||
<image v-else :src="formData.share_img" class="upload-preview" mode="aspectFill" />
|
||||
<text class="up-tip">尺寸:800*640px</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="form-label">联系电话:</view>
|
||||
<view class="input-wrap">
|
||||
<input class="form-input" v-model="formData.phone" placeholder="请输入主播联系电话" />
|
||||
<text class="char-count">{{ formData.phone.length }}/11</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="form-label required">直播时间:</view>
|
||||
<view class="date-range-mock" @click="handleOpenDatePicker">
|
||||
<text class="calendar-ic">📅</text>
|
||||
<text class="date-val">{{ formData.start_time || '开始日期' }} - {{ formData.end_time || '结束日期' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="form-label">排序:</view>
|
||||
<input class="form-input w-extra-small" type="number" v-model="formData.sort" />
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="form-label">直播间类型:</view>
|
||||
<view class="radio-group">
|
||||
<view class="radio-item" @click="formData.type = 'phone'">
|
||||
<view class="radio-circle" :class="{ active: formData.type === 'phone' }"></view>
|
||||
<text class="radio-txt">手机直播</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item flex-row">
|
||||
<view class="form-label">直播间点赞:</view>
|
||||
<view class="switch-mock" :class="{ active: formData.like_enabled }" @click="formData.like_enabled = !formData.like_enabled">
|
||||
<view class="switch-dot"></view>
|
||||
<text class="switch-txt">{{ formData.like_enabled ? '开启' : '关闭' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item flex-row">
|
||||
<view class="form-label">直播卖货:</view>
|
||||
<view class="switch-mock" :class="{ active: formData.sale_enabled }" @click="formData.sale_enabled = !formData.sale_enabled">
|
||||
<view class="switch-dot"></view>
|
||||
<text class="switch-txt">{{ formData.sale_enabled ? '开启' : '关闭' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item flex-row">
|
||||
<view class="form-label">直播间评论:</view>
|
||||
<view class="switch-mock" :class="{ active: formData.comment_enabled }" @click="formData.comment_enabled = !formData.comment_enabled">
|
||||
<view class="switch-dot"></view>
|
||||
<text class="switch-txt">{{ formData.comment_enabled ? '开启' : '关闭' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-actions-bottom">
|
||||
<button class="btn-submit" @click="handleSubmit">提交</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
const currentPage = ref<string>('live-room')
|
||||
const title = ref<string>('room')
|
||||
|
||||
const showDrawer = ref(false)
|
||||
const isAnimating = ref(false)
|
||||
|
||||
const formData = ref({
|
||||
anchor_nick: '',
|
||||
name: '',
|
||||
background: '',
|
||||
share_img: '',
|
||||
phone: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
sort: 0,
|
||||
type: 'phone',
|
||||
like_enabled: true,
|
||||
sale_enabled: true,
|
||||
comment_enabled: true
|
||||
})
|
||||
|
||||
const roomList = ref([
|
||||
{
|
||||
id: 88,
|
||||
name: 'CRMEB 年中618活动开始',
|
||||
anchor_nick: '打羽毛球',
|
||||
anchor_wechat: 'evoxwht',
|
||||
start_time: '2025-06-17 00:00:00',
|
||||
end_time: '2025-06-18 00:00:00',
|
||||
create_time: '2025-06-16 14:56:53',
|
||||
is_show: true,
|
||||
live_status: '已结束',
|
||||
sort: 1
|
||||
},
|
||||
{
|
||||
id: 90,
|
||||
name: '123456789',
|
||||
anchor_nick: '万万',
|
||||
anchor_wechat: 'xiao112032014',
|
||||
start_time: '2025-07-07 10:20:00',
|
||||
end_time: '2025-07-07 12:00:00',
|
||||
create_time: '2025-07-07 10:05:43',
|
||||
is_show: true,
|
||||
live_status: '已结束',
|
||||
sort: 0
|
||||
},
|
||||
{
|
||||
id: 89,
|
||||
name: '测试1111111',
|
||||
anchor_nick: '打羽毛球',
|
||||
anchor_wechat: '',
|
||||
start_time: '2025-05-20 14:50:00',
|
||||
end_time: '2025-05-20 15:22:00',
|
||||
create_time: '2025-06-17 10:03:08',
|
||||
is_show: true,
|
||||
live_status: '已结束',
|
||||
sort: 0
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: '开学季,最后一天',
|
||||
anchor_nick: '等风来',
|
||||
anchor_wechat: 'welalnidaobel',
|
||||
start_time: '2021-09-01 19:00:00',
|
||||
end_time: '2021-09-01 20:00:00',
|
||||
create_time: '2021-08-30 11:53:01',
|
||||
is_show: false,
|
||||
live_status: '已结束',
|
||||
sort: 0
|
||||
}
|
||||
])
|
||||
|
||||
const toggleStatus = (item: any) => {
|
||||
item.is_show = !item.is_show
|
||||
uni.showToast({ title: '状态修改成功', icon: 'success' })
|
||||
}
|
||||
|
||||
const handleEdit = (item: any) => {
|
||||
formData.value = { ...item, like_enabled: true, sale_enabled: true, comment_enabled: true }
|
||||
showDrawer.value = true
|
||||
}
|
||||
|
||||
const handleDelete = (item: any) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除该直播间吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
roomList.value = roomList.value.filter(i => i.id !== item.id)
|
||||
uni.showToast({ title: '删除成功' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleSelectAnchor = () => {
|
||||
uni.showToast({ title: '功能开发中', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleUpload = (type: string) => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: (res) => {
|
||||
if (type === 'background') {
|
||||
formData.value.background = res.tempFilePaths[0]
|
||||
} else {
|
||||
formData.value.share_img = res.tempFilePaths[0]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleOpenDatePicker = () => {
|
||||
uni.showToast({ title: '日期选择功能开发中', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
uni.showToast({ title: '提交成功', icon: 'success' })
|
||||
closeDrawer()
|
||||
}
|
||||
|
||||
const closeDrawer = () => {
|
||||
showDrawer.value = false
|
||||
isAnimating.value = true
|
||||
setTimeout(() => {
|
||||
isAnimating.value = false
|
||||
}, 300)
|
||||
}
|
||||
</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; }
|
||||
.marketing-live-room {
|
||||
min-height: 100vh;
|
||||
background: #f0f2f5;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.border-shadow {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.ml-16 { margin-left: 16px; }
|
||||
.ml-24 { margin-left: 24px; }
|
||||
.mt-16 { margin-top: 16px; }
|
||||
|
||||
/* 过滤栏 */
|
||||
.filter-card {
|
||||
padding: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.input-mock, .select-mock {
|
||||
width: 200px;
|
||||
height: 32px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.input-mock { width: 300px; }
|
||||
.select-mock { width: 160px; justify-content: space-between; }
|
||||
.select-mock.mini { width: 100px; height: 28px; }
|
||||
.select-mock.full { width: 100%; }
|
||||
|
||||
.select-val { font-size: 13px; color: #606266; }
|
||||
.arrow { font-size: 10px; color: #c0c4cc; }
|
||||
|
||||
.btn-query {
|
||||
width: 64px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 操作栏 */
|
||||
.action-bar {
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.btn-add {
|
||||
width: auto;
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-sync {
|
||||
width: auto;
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
background-color: #fff;
|
||||
color: #1890ff;
|
||||
border: 1px solid #1890ff;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 表格区域 */
|
||||
.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; }
|
||||
.td-txt-small { font-size: 12px; color: #808695; display: block; }
|
||||
|
||||
/* 各列宽度 */
|
||||
.cell-id { width: 70px; }
|
||||
.cell-name { flex: 1; min-width: 150px; }
|
||||
.cell-nick { width: 120px; }
|
||||
.cell-wechat { width: 120px; }
|
||||
.cell-time { width: 150px; }
|
||||
.cell-status { width: 100px; text-align: center; }
|
||||
.cell-live-status { width: 100px; text-align: center; }
|
||||
.cell-sort { width: 60px; text-align: center; }
|
||||
.cell-op { width: 120px; text-align: right; }
|
||||
|
||||
.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;
|
||||
}
|
||||
.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-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
|
||||
.op-split { color: #e8eaec; margin: 0 8px; }
|
||||
|
||||
/* 分页 */
|
||||
.pagination-footer {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
.total-txt { font-size: 13px; color: #606266; }
|
||||
|
||||
/* Drawer Styles */
|
||||
.drawer-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.45);
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
pointer-events: none;
|
||||
}
|
||||
.drawer-mask.active {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.drawer-panel {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: -50%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
z-index: 1001;
|
||||
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: right 0.3s ease-out;
|
||||
}
|
||||
.drawer-panel.active {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.drawer-header {
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
.header-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.back-btn {
|
||||
font-size: 14px;
|
||||
color: #8c8c8c;
|
||||
cursor: pointer;
|
||||
}
|
||||
.drawer-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.drawer-content {
|
||||
flex: 1;
|
||||
padding: 24px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background-color: #fff7e6;
|
||||
border: 1px solid #ffe7ba;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 24px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.alert-txt {
|
||||
font-size: 13px;
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #262626;
|
||||
margin-bottom: 8px;
|
||||
width: 120px;
|
||||
}
|
||||
.flex-row .form-label { margin-bottom: 0; }
|
||||
|
||||
.required::before {
|
||||
content: '*';
|
||||
color: #ff4d4f;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.form-input {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
padding: 0 40px 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.w-extra-small { width: 80px; }
|
||||
.char-count {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 6px;
|
||||
font-size: 12px;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.upload-box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
.upload-placeholder {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.up-ic { font-size: 24px; color: #bfbfbf; }
|
||||
.up-tip {
|
||||
font-size: 12px;
|
||||
color: #1890ff;
|
||||
background-color: #e6f7ff;
|
||||
border: 1px solid #91d5ff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.up-tip.blue-bg {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.date-range-mock {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
.calendar-ic { font-size: 14px; color: #bfbfbf; }
|
||||
.date-val { font-size: 14px; color: #bfbfbf; }
|
||||
|
||||
.radio-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 24px;
|
||||
}
|
||||
.radio-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.radio-circle {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
}
|
||||
.radio-circle.active { border-color: #1890ff; }
|
||||
.radio-circle.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #1890ff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.radio-txt { font-size: 14px; color: #262626; }
|
||||
|
||||
.form-actions-bottom {
|
||||
margin-top: 40px;
|
||||
}
|
||||
.btn-submit {
|
||||
width: 64px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,343 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
<view class="marketing-member-card">
|
||||
<view class="filter-card border-shadow">
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="label">批次搜索:</text>
|
||||
<input class="input-mock" placeholder="请输入批次名" />
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="label">是否开启:</text>
|
||||
<view class="select-mock">
|
||||
<text class="select-val">全部</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-group">
|
||||
<button class="btn btn-search">查询</button>
|
||||
<button class="btn btn-reset">重置</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="action-row">
|
||||
<button class="btn btn-primary" @click="showAddBatch = true">+ 添加批次</button>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
|
||||
<view class="table-card border-shadow">
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">ID</view>
|
||||
<view class="th cell-name">批次名称</view>
|
||||
<view class="th cell-num">体验卡数量</view>
|
||||
<view class="th cell-type">会员类型</view>
|
||||
<view class="th cell-time">生效时间</view>
|
||||
<view class="th cell-status">是否启用</view>
|
||||
<view class="th cell-op">操作</view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-for="item in cards" :key="item.id" class="table-row">
|
||||
<view class="td cell-id"><text class="td-txt">{{ item.id }}</text></view>
|
||||
<view class="td cell-name"><text class="td-txt">{{ item.title }}</text></view>
|
||||
<view class="td cell-num"><text class="td-txt">{{ item.use_num }}/{{ item.total_num }}</text></view>
|
||||
<view class="td cell-type"><text class="td-txt">{{ item.member_type }}</text></view>
|
||||
<view class="td cell-time"><text class="td-txt">{{ item.create_time }}</text></view>
|
||||
<view class="td cell-status">
|
||||
<view class="switch-mock" :class="{ active: item.status }" @click="toggleStatus(item)">
|
||||
<view class="switch-dot"></view>
|
||||
<text class="switch-txt">{{ item.status ? '开启' : '关闭' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="td cell-op">
|
||||
<text class="op-link" @click="showQrCode(item)">二维码</text>
|
||||
<text class="op-link ml-10" @click="viewDetails(item)">详情</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 添加批次弹窗 -->
|
||||
<view v-if="showAddBatch" class="modal-mask">
|
||||
<view class="modal-content">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">添加批次</text>
|
||||
<text class="modal-close" @click="showAddBatch = false">×</text>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<view class="form-item">
|
||||
<text class="form-label">批次名称:</text>
|
||||
<input class="form-input" placeholder="请输入批次名称" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">导入数量:</text>
|
||||
<input class="form-input" type="number" placeholder="请输入生成数量" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">会员类型:</text>
|
||||
<view class="form-select">
|
||||
<text>请选择会员类型</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">备注:</text>
|
||||
<textarea class="form-textarea" placeholder="请输入备注"></textarea>
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<button class="btn btn-cancel" @click="showAddBatch = false">取消</button>
|
||||
<button class="btn btn-submit" @click="handleAddSubmit">提交</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 二维码弹窗 -->
|
||||
<view v-if="showQrModal" class="modal-mask">
|
||||
<view class="modal-content qr-modal">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">预览体验卡二维码</text>
|
||||
<text class="modal-close" @click="showQrModal = false">×</text>
|
||||
</view>
|
||||
<view class="modal-body qr-body">
|
||||
<image class="qr-img" src="https://demo26.crmeb.net/uploads/attach/2021/11/20211115/a6f3b06e9d6d5a1b3c9d6d5a1b3c9d6d.png" mode="aspectFit"></image>
|
||||
<text class="qr-tips">请扫码体验会员卡</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>('member-card')
|
||||
const title = ref<string>('card')
|
||||
|
||||
const showAddBatch = ref(false)
|
||||
const showQrModal = ref(false)
|
||||
|
||||
const cards = ref([
|
||||
{ id: 4, title: '双11体验卡', use_num: 1, total_num: 100, member_type: '年卡会员', create_time: '2023-11-01 12:00:00', status: true },
|
||||
{ id: 3, title: '新人体验卷', use_num: 50, total_num: 200, member_type: '月卡会员', create_time: '2023-10-25 09:30:00', status: true },
|
||||
{ id: 2, title: '测试批次', use_num: 0, total_num: 10, member_type: '季卡会员', create_time: '2023-10-20 15:45:00', status: false }
|
||||
])
|
||||
|
||||
const toggleStatus = (item: any) => {
|
||||
item.status = !item.status
|
||||
uni.showToast({ title: '操作成功', icon: 'success' })
|
||||
}
|
||||
|
||||
const showQrCode = (item: any) => {
|
||||
showQrModal.value = true
|
||||
}
|
||||
|
||||
const viewDetails = (item: any) => {
|
||||
uni.showToast({ title: '查看详情: ' + item.title, icon: 'none' })
|
||||
}
|
||||
|
||||
const handleAddSubmit = () => {
|
||||
showAddBatch.value = false
|
||||
uni.showToast({ title: '添加成功', icon: 'success' })
|
||||
}
|
||||
</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; }
|
||||
.marketing-member-card {
|
||||
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);
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
padding: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-right: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.input-mock {
|
||||
width: 200px;
|
||||
height: 32px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.select-mock {
|
||||
width: 200px;
|
||||
height: 32px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.select-val { font-size: 14px; color: #606266; }
|
||||
.arrow { font-size: 10px; color: #c0c4cc; }
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 20px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-right: 8px;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.btn-primary { background: #1890ff; color: #fff; border: none; }
|
||||
.btn-search { background: #1890ff; color: #fff; border: none; }
|
||||
.btn-reset { margin-left: 8px; }
|
||||
|
||||
.action-row {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.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: 60px; }
|
||||
.cell-name { flex: 1; }
|
||||
.cell-num { width: 120px; }
|
||||
.cell-type { width: 120px; }
|
||||
.cell-time { width: 160px; }
|
||||
.cell-status { width: 100px; }
|
||||
.cell-op { width: 120px; text-align: right; }
|
||||
|
||||
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
|
||||
.ml-10 { margin-left: 10px; }
|
||||
|
||||
.switch-mock {
|
||||
width: 44px;
|
||||
height: 22px;
|
||||
background-color: #bfbfbf;
|
||||
border-radius: 11px;
|
||||
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: 14px;
|
||||
height: 14px;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
transition: left 0.3s;
|
||||
}
|
||||
.switch-mock.active .switch-dot { left: 26px; }
|
||||
.switch-txt { font-size: 10px; color: #fff; margin-left: 18px; }
|
||||
.switch-mock.active .switch-txt { margin-left: 4px; }
|
||||
|
||||
/* Modal Styles */
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
.modal-content {
|
||||
width: 500px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.modal-header {
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid #e8eaec;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.modal-title { font-size: 16px; font-weight: bold; color: #17233d; }
|
||||
.modal-close { font-size: 24px; color: #909399; cursor: pointer; }
|
||||
.modal-body { padding: 24px; }
|
||||
.modal-footer {
|
||||
padding: 12px 24px;
|
||||
border-top: 1px solid #e8eaec;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.form-label { width: 100px; font-size: 14px; color: #606266; padding-top: 6px; }
|
||||
.form-input { flex: 1; height: 32px; border: 1px solid #dcdfe6; border-radius: 4px; padding: 0 12px; }
|
||||
.form-select { flex: 1; height: 32px; border: 1px solid #dcdfe6; border-radius: 4px; padding: 0 12px; display: flex; flex-direction: row; align-items: center; justify-content: space-between; color: #c0c4cc; font-size: 14px; }
|
||||
.form-textarea { flex: 1; height: 80px; border: 1px solid #dcdfe6; border-radius: 4px; padding: 8px 12px; }
|
||||
|
||||
.btn-cancel { margin-right: 8px; }
|
||||
.btn-submit { background: #1890ff; color: #fff; border: none; }
|
||||
|
||||
.qr-modal { width: 300px; }
|
||||
.qr-body { display: flex; flex-direction: column; align-items: center; }
|
||||
.qr-img { width: 200px; height: 200px; margin-bottom: 16px; }
|
||||
.qr-tips { font-size: 14px; color: #666; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,225 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
<view class="marketing-member-config">
|
||||
<view class="config-card border-shadow">
|
||||
<view class="config-header">
|
||||
<text class="config-title">会员基础配置</text>
|
||||
</view>
|
||||
<view class="config-body">
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">是否开启付费会员:</text>
|
||||
<text class="label-desc">关闭之后,商城将不再展示付费会员相关功能</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="switch-mock" :class="{ active: config.is_open }" @click="config.is_open = !config.is_open">
|
||||
<view class="switch-dot"></view>
|
||||
<text class="switch-txt">{{ config.is_open ? '开启' : '关闭' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">会员期内背景图:</text>
|
||||
<text class="label-desc">建议尺寸: 700*320px</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="upload-box" @click="handleUpload('bg')">
|
||||
<image v-if="config.bg_img" :src="config.bg_img" class="preview-img"></image>
|
||||
<view v-else class="upload-placeholder">
|
||||
<text class="plus">+</text>
|
||||
<text class="upload-txt">选择图片</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">会员到期背景图:</text>
|
||||
<text class="label-desc">建议尺寸: 700*320px</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="upload-box" @click="handleUpload('expire_bg')">
|
||||
<image v-if="config.expire_bg_img" :src="config.expire_bg_img" class="preview-img"></image>
|
||||
<view v-else class="upload-placeholder">
|
||||
<text class="plus">+</text>
|
||||
<text class="upload-txt">选择图片</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">会员规则说明:</text>
|
||||
<text class="label-desc">在会员中心页面展示的规则说明</text>
|
||||
</view>
|
||||
<view class="item-content flex-1">
|
||||
<textarea class="config-textarea" v-model="config.rules" placeholder="请输入会员规则说明"></textarea>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-footer">
|
||||
<button class="btn btn-primary" @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>('member-config')
|
||||
const title = ref<string>('config')
|
||||
import { ref, reactive } from 'vue'
|
||||
|
||||
const config = reactive({
|
||||
is_open: true,
|
||||
bg_img: 'https://demo26.crmeb.net/uploads/attach/2021/11/20211115/a6f3b06e9d6d5a1b3c9d6d5a1b3c9d6d.png',
|
||||
expire_bg_img: '',
|
||||
rules: '1. 会员有效期自购买之日起计算\n2. 会员权益仅限本人使用\n3. 会员卡一经售出,概不退换'
|
||||
})
|
||||
|
||||
const handleUpload = (type: string) => {
|
||||
uni.showToast({ title: '文件管理器暂未开启', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||||
}
|
||||
</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; }
|
||||
.marketing-member-config {
|
||||
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);
|
||||
}
|
||||
|
||||
.config-card {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.config-header {
|
||||
border-bottom: 1px solid #e8eaec;
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.config-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #17233d;
|
||||
}
|
||||
|
||||
.config-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 30px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
width: 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.label-txt {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.label-desc {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.switch-mock {
|
||||
width: 44px;
|
||||
height: 22px;
|
||||
background-color: #bfbfbf;
|
||||
border-radius: 11px;
|
||||
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: 14px;
|
||||
height: 14px;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
transition: left 0.3s;
|
||||
}
|
||||
.switch-mock.active .switch-dot { left: 26px; }
|
||||
.switch-txt { font-size: 10px; color: #fff; margin-left: 18px; }
|
||||
.switch-mock.active .switch-txt { margin-left: 4px; }
|
||||
|
||||
.upload-box {
|
||||
width: 120px;
|
||||
height: 80px;
|
||||
border: 1px dashed #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.upload-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.plus { font-size: 24px; color: #999; }
|
||||
.upload-txt { font-size: 12px; color: #999; }
|
||||
.preview-img { width: 100%; height: 100%; object-fit: cover; }
|
||||
|
||||
.config-textarea {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
height: 120px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.config-footer {
|
||||
margin-top: 40px;
|
||||
padding-left: 200px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
width: 120px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.flex-1 { flex: 1; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,187 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
<view class="marketing-member-record">
|
||||
<view class="filter-card border-shadow">
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="label">会员名:</text>
|
||||
<input class="input-mock" placeholder="请输入会员名" />
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="label">支付方式:</text>
|
||||
<view class="select-mock">
|
||||
<text class="select-val">全部</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-group">
|
||||
<button class="btn btn-search">查询</button>
|
||||
<button class="btn btn-reset">重置</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
|
||||
<view class="table-card border-shadow">
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">ID</view>
|
||||
<view class="th cell-user">用户信息</view>
|
||||
<view class="th cell-type">会员类型</view>
|
||||
<view class="th cell-price">支付金额</view>
|
||||
<view class="th cell-pay">支付方式</view>
|
||||
<view class="th cell-time">购买时间</view>
|
||||
<view class="th cell-expire">过期时间</view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-for="item in records" :key="item.id" class="table-row">
|
||||
<view class="td cell-id"><text class="td-txt">{{ item.id }}</text></view>
|
||||
<view class="td cell-user">
|
||||
<view class="user-box">
|
||||
<image class="avatar" :src="item.avatar"></image>
|
||||
<text class="nickname">{{ item.nickname }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="td cell-type"><text class="td-txt">{{ item.member_type }}</text></view>
|
||||
<view class="td cell-price"><text class="td-txt">¥{{ item.price.toFixed(2) }}</text></view>
|
||||
<view class="td cell-pay"><text class="td-txt">{{ item.pay_type }}</text></view>
|
||||
<view class="td cell-time"><text class="td-txt">{{ item.create_time }}</text></view>
|
||||
<view class="td cell-expire"><text class="td-txt">{{ item.expire_time }}</text></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
const currentPage = ref<string>('member-record')
|
||||
const title = ref<string>('record')
|
||||
|
||||
const records = ref([
|
||||
{ id: 10, avatar: 'https://demo26.crmeb.net/uploads/attach/2021/11/20211115/a6f3b06e9d6d5a1b3c9d6d5a1b3c9d6d.png', nickname: '张三', member_type: '年卡会员', price: 99.00, pay_type: '微信支付', create_time: '2023-11-20 10:00:00', expire_time: '2024-11-20 10:00:00' },
|
||||
{ id: 9, avatar: 'https://demo26.crmeb.net/uploads/attach/2021/11/20211115/a6f3b06e9d6d5a1b3c9d6d5a1b3c9d6d.png', nickname: '李四', member_type: '月卡会员', price: 9.90, pay_type: '余额支付', create_time: '2023-11-19 15:30:00', expire_time: '2023-12-19 15:30:00' },
|
||||
{ id: 8, avatar: 'https://demo26.crmeb.net/uploads/attach/2021/11/20211115/a6f3b06e9d6d5a1b3c9d6d5a1b3c9d6d.png', nickname: '王五', member_type: '体验会员', price: 0.00, pay_type: '激活码', create_time: '2023-11-18 09:20:00', expire_time: '2023-11-25 09:20:00' }
|
||||
])
|
||||
</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; }
|
||||
.marketing-member-record {
|
||||
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);
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
padding: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.input-mock {
|
||||
width: 180px;
|
||||
height: 32px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.select-mock {
|
||||
width: 180px;
|
||||
height: 32px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.select-val { font-size: 14px; color: #606266; }
|
||||
.arrow { font-size: 10px; color: #c0c4cc; }
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 20px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-right: 8px;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.btn-search { background: #1890ff; color: #fff; border: none; }
|
||||
|
||||
.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: 60px; }
|
||||
.cell-user { flex: 1; }
|
||||
.cell-type { width: 120px; }
|
||||
.cell-price { width: 100px; }
|
||||
.cell-pay { width: 100px; }
|
||||
.cell-time { width: 160px; }
|
||||
.cell-expire { width: 160px; }
|
||||
|
||||
.user-box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.nickname { font-size: 13px; color: #515a6e; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
142
pages/mall/admin/marketing/member/right.uvue
Normal file
142
pages/mall/admin/marketing/member/right.uvue
Normal file
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<view class="marketing-member-right">
|
||||
<view class="table-card border-shadow">
|
||||
<view class="table-container">
|
||||
<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-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" 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.desc }}</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 }}</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 } from 'vue'
|
||||
|
||||
const memberRights = ref([
|
||||
{ id: 9, icon: 'https://demo26.crmeb.net/uploads/attach/2021/11/20211115/a6f3b06e9d6d5a1b3c9d6d5a1b3c9d6d.png', name: '运费券', desc: '每月领取运费券', is_show: true, sort: 10 },
|
||||
{ id: 8, icon: 'https://demo26.crmeb.net/uploads/attach/2021/11/20211115/a6f3b06e9d6d5a1b3c9d6d5a1b3c9d6d.png', name: '充值优惠', desc: '充值立减优惠', is_show: true, sort: 8 },
|
||||
{ id: 7, icon: 'https://demo26.crmeb.net/uploads/attach/2021/11/20211115/a6f3b06e9d6d5a1b3c9d6d5a1b3c9d6d.png', name: '积分翻倍', desc: '购物获取双倍积分', is_show: true, sort: 7 }
|
||||
])
|
||||
|
||||
const toggleStatus = (item: any) => {
|
||||
item.is_show = !item.is_show
|
||||
uni.showToast({ title: '修改成功', icon: 'success' })
|
||||
}
|
||||
|
||||
const handleEdit = (item: any) => {
|
||||
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; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
const currentPage = ref<string>('member-rights')
|
||||
const title = ref<string>('rights')
|
||||
</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>
|
||||
|
||||
|
||||
@@ -1,27 +1,147 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
<view class="marketing-member-type">
|
||||
<view class="table-card border-shadow">
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">ID</view>
|
||||
<view class="th cell-name">会员名</view>
|
||||
<view class="th cell-days">有效期(天)</view>
|
||||
<view class="th cell-price">原价</view>
|
||||
<view class="th cell-discount">优惠价</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-for="item in memberTypes" :key="item.id" class="table-row">
|
||||
<view class="td cell-id"><text class="td-txt">{{ item.id }}</text></view>
|
||||
<view class="td cell-name"><text class="td-txt">{{ item.name }}</text></view>
|
||||
<view class="td cell-days"><text class="td-txt">{{ item.days }}</text></view>
|
||||
<view class="td cell-price"><text class="td-txt">¥{{ item.price.toFixed(2) }}</text></view>
|
||||
<view class="td cell-discount"><text class="td-txt">¥{{ item.discount.toFixed(2) }}</text></view>
|
||||
<view class="td cell-status">
|
||||
<view class="switch-mock" :class="{ active: item.is_open }" @click="toggleStatus(item)">
|
||||
<view class="switch-dot"></view>
|
||||
<text class="switch-txt">{{ item.is_open ? '开启' : '关闭' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="td cell-sort"><text class="td-txt">{{ item.sort }}</text></view>
|
||||
<view class="td cell-op">
|
||||
<text class="op-link" @click="handleEdit(item)">编辑</text>
|
||||
</view>
|
||||
</view>
|
||||
</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>('member-type')
|
||||
const title = ref<string>('type')
|
||||
|
||||
const memberTypes = ref([
|
||||
{ id: 5, name: '44555', days: '12', price: 69.00, discount: 0.00, is_open: true, sort: 55 },
|
||||
{ id: 4, name: '5566', days: '永久', price: 1080.00, discount: 1080.00, is_open: true, sort: 5 },
|
||||
{ id: 3, name: '年卡', days: '365', price: 99.00, discount: 0.01, is_open: true, sort: 5 },
|
||||
{ id: 2, name: '55', days: '90', price: 699.00, discount: 499.00, is_open: true, sort: 5 },
|
||||
{ id: 1, name: '55', days: '30', price: 699.00, discount: 499.00, is_open: true, sort: 5 }
|
||||
])
|
||||
|
||||
const toggleStatus = (item: any) => {
|
||||
item.is_open = !item.is_open
|
||||
uni.showToast({ title: '修改成功', icon: 'success' })
|
||||
}
|
||||
|
||||
const handleEdit = (item: any) => {
|
||||
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; }
|
||||
.marketing-member-type {
|
||||
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-name { flex: 1; min-width: 150px; }
|
||||
.cell-days { width: 120px; }
|
||||
.cell-price { width: 120px; }
|
||||
.cell-discount { width: 120px; }
|
||||
.cell-status { width: 120px; text-align: center; }
|
||||
.cell-sort { width: 100px; text-align: center; }
|
||||
.cell-op { width: 80px; text-align: right; }
|
||||
|
||||
.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; }
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
const currentPage = ref<string>('newcomer')
|
||||
const title = ref<string>('newcomer')
|
||||
</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>
|
||||
|
||||
|
||||
521
pages/mall/admin/marketing/newcomer/index.uvue
Normal file
521
pages/mall/admin/marketing/newcomer/index.uvue
Normal file
@@ -0,0 +1,521 @@
|
||||
<template>
|
||||
<view class="admin-main">
|
||||
<view class="card-container">
|
||||
<view class="card-header">
|
||||
<text class="card-header-title">新人礼设置</text>
|
||||
</view>
|
||||
|
||||
<view class="form-content">
|
||||
<!-- 赠送余额 -->
|
||||
<view class="form-item">
|
||||
<view class="label-col">
|
||||
<text class="form-label">赠送余额(元):</text>
|
||||
</view>
|
||||
<view class="input-col">
|
||||
<input type="number" class="form-input" v-model="formData.balance" />
|
||||
<text class="form-tip">新用户奖励金额,必须大于等于0,0为不赠送</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 赠送积分 -->
|
||||
<view class="form-item">
|
||||
<view class="label-col">
|
||||
<text class="form-label">赠送积分:</text>
|
||||
</view>
|
||||
<view class="input-col">
|
||||
<input type="number" class="form-input" v-model="formData.integral" />
|
||||
<text class="form-tip">新用户奖励积分,必须大于等于0,0为不赠送</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 赠送优惠券 -->
|
||||
<view class="form-item row-center">
|
||||
<view class="label-col">
|
||||
<text class="form-label">赠送优惠券:</text>
|
||||
</view>
|
||||
<view class="input-col row-layout">
|
||||
<view class="coupon-display-area" v-if="formData.coupons.length > 0">
|
||||
<view v-for="(coupon, index) in formData.coupons" :key="index" class="coupon-tag-group">
|
||||
<view class="coupon-tag-main">
|
||||
<text class="coupon-tag-name">{{ coupon.name }}</text>
|
||||
<text class="coupon-tag-del" @click="removeCoupon(index)">×</text>
|
||||
</view>
|
||||
<view class="explicit-edit-btn" @click="editCoupon(index)">
|
||||
<text class="edit-btn-text">修改设置</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<button class="btn-select-action" @click="showCouponModal = true">选择优惠券</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 确认按钮 -->
|
||||
<view class="form-submit-bar">
|
||||
<button class="btn-primary-confirm" @click="handleSubmit">确认</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 优惠券选择与详情配置弹窗 -->
|
||||
<view class="modal-mask" v-if="showCouponModal" @click="closeModal">
|
||||
<view class="modal-box" @click.stop>
|
||||
<view class="modal-head">
|
||||
<text class="modal-head-title">{{ isEditing ? '配置赠送详情' : '选择优惠券' }}</text>
|
||||
<text class="modal-head-close" @click="closeModal">×</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<!-- 编辑/设置模式:当用户由于点击“修改设置”时触发 -->
|
||||
<view v-if="isEditing" class="setting-form">
|
||||
<view class="setting-row">
|
||||
<text class="setting-label">显示名称:</text>
|
||||
<input class="setting-input" v-model="editingCoupon.name" placeholder="请输入页面显示的名称" />
|
||||
</view>
|
||||
<view class="setting-row">
|
||||
<text class="setting-label">发放描述:</text>
|
||||
<input class="setting-input" v-model="editingCoupon.desc" placeholder="请输入发放时的描述" />
|
||||
</view>
|
||||
<view class="setting-tip">
|
||||
<text class="tip-text">* 此处的修改仅影响“新人礼”活动中的展示,不影响优惠券自身配置</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选择模式 -->
|
||||
<view v-else class="selection-list">
|
||||
<view v-for="(item, index) in couponOptions" :key="index"
|
||||
class="selection-card" :class="{'selected-card': isSelected(item)}"
|
||||
@click="toggleCoupon(item)">
|
||||
<view class="card-left">
|
||||
<text class="card-name">{{ item.name }}</text>
|
||||
<text class="card-desc">{{ item.desc }}</text>
|
||||
</view>
|
||||
<view class="card-right">
|
||||
<view class="check-circle" :class="{'checked-circle': isSelected(item)}">
|
||||
<text class="check-mark" v-if="isSelected(item)">✓</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-foot">
|
||||
<button class="foot-btn-cancel" @click="closeModal">取消</button>
|
||||
<button class="foot-btn-ok" @click="confirmModal">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, reactive } from 'vue'
|
||||
|
||||
interface Coupon {
|
||||
id: number;
|
||||
name: string;
|
||||
desc: string;
|
||||
}
|
||||
|
||||
const formData = reactive({
|
||||
balance: '88888',
|
||||
integral: '88888',
|
||||
coupons: [] as Coupon[]
|
||||
})
|
||||
|
||||
const showCouponModal = ref(false)
|
||||
const isEditing = ref(false)
|
||||
const editingIndex = ref(-1)
|
||||
const editingCoupon = reactive<Coupon>({ id: 0, name: '', desc: '' })
|
||||
|
||||
const couponOptions = reactive<Coupon[]>([
|
||||
{ id: 1, name: '满100减10元券', desc: '全场通用' },
|
||||
{ id: 2, name: '新人5元无门槛', desc: '仅限新人使用' },
|
||||
{ id: 3, name: '满200减50元券', desc: '限特定商品' }
|
||||
])
|
||||
|
||||
function isSelected(item: Coupon): boolean {
|
||||
return formData.coupons.some(c => c.id === item.id)
|
||||
}
|
||||
|
||||
function toggleCoupon(item: Coupon) {
|
||||
const index = formData.coupons.findIndex(c => c.id === item.id)
|
||||
if (index > -1) {
|
||||
formData.coupons.splice(index, 1)
|
||||
} else {
|
||||
formData.coupons.push({ ...item })
|
||||
}
|
||||
}
|
||||
|
||||
function removeCoupon(index: number) {
|
||||
formData.coupons.splice(index, 1)
|
||||
}
|
||||
|
||||
function editCoupon(index: number) {
|
||||
editingIndex.value = index
|
||||
const coupon = formData.coupons[index]
|
||||
editingCoupon.id = coupon.id
|
||||
editingCoupon.name = coupon.name
|
||||
editingCoupon.desc = coupon.desc
|
||||
isEditing.value = true
|
||||
showCouponModal.value = true
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showCouponModal.value = false
|
||||
isEditing.value = false
|
||||
}
|
||||
|
||||
function confirmModal() {
|
||||
if (isEditing.value && editingIndex.value > -1) {
|
||||
formData.coupons[editingIndex.value].name = editingCoupon.name
|
||||
formData.coupons[editingIndex.value].desc = editingCoupon.desc
|
||||
}
|
||||
closeModal()
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
uni.showLoading({ title: '保存中...' })
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '设置已生效',
|
||||
icon: 'success'
|
||||
})
|
||||
}, 500)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.admin-main {
|
||||
padding: 24px;
|
||||
background-color: #f0f2f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.card-container {
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.card-header-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
|
||||
.form-content {
|
||||
padding: 40px 60px;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.row-center {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.label-col {
|
||||
width: 140px;
|
||||
padding-right: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.input-col {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.row-layout {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 320px;
|
||||
height: 40px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.coupon-display-area {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.coupon-tag-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-right: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.coupon-tag-main {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #d9d9d9;
|
||||
padding: 0 10px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.coupon-tag-name {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.coupon-tag-del {
|
||||
margin-left: 8px;
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.explicit-edit-btn {
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.edit-btn-text {
|
||||
font-size: 13px;
|
||||
color: #1890ff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.btn-select-action {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
color: #666;
|
||||
margin-left: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-submit-bar {
|
||||
margin-top: 32px;
|
||||
padding-left: 140px;
|
||||
}
|
||||
|
||||
.btn-primary-confirm {
|
||||
width: 88px;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
margin-left: 0;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-box {
|
||||
width: 560px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-head {
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-head-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.modal-head-close {
|
||||
font-size: 24px;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 24px;
|
||||
max-height: 450px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.setting-form {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.setting-row {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.setting-label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.setting-input {
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.setting-tip {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
font-size: 12px;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.selection-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.selection-card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border: 1px solid #f0f0f0;
|
||||
margin-bottom: 14px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selection-card:hover {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.selected-card {
|
||||
border-color: #1890ff;
|
||||
background-color: #f0faff;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 13px;
|
||||
color: #888;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.check-circle {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.checked-circle {
|
||||
background-color: #1890ff;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.check-mark {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.modal-foot {
|
||||
padding: 16px 24px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.foot-btn-cancel, .foot-btn-ok {
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
padding: 0 20px;
|
||||
margin-left: 12px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.foot-btn-cancel {
|
||||
background-color: #fff;
|
||||
border: 1px solid #d9d9d9;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.foot-btn-ok {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
const currentPage = ref<string>('recharge-amount')
|
||||
const title = ref<string>('amount')
|
||||
</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>
|
||||
|
||||
|
||||
@@ -1,27 +1,187 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
<view class="marketing-recharge-config">
|
||||
<view class="config-card border-shadow">
|
||||
<view class="config-header">
|
||||
<text class="config-title">用户充值配置</text>
|
||||
</view>
|
||||
|
||||
<view class="config-body">
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">余额功能启用:</text>
|
||||
<text class="label-desc">商城余额功能启用或者关闭</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="radio-group">
|
||||
<view class="radio-item" @click="config.balance_enabled = true">
|
||||
<view class="radio-circle" :class="{ checked: config.balance_enabled }"></view>
|
||||
<text class="radio-txt">开启</text>
|
||||
</view>
|
||||
<view class="radio-item ml-20" @click="config.balance_enabled = false">
|
||||
<view class="radio-circle" :class="{ checked: !config.balance_enabled }"></view>
|
||||
<text class="radio-txt">关闭</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">充值注意事项:</text>
|
||||
<text class="label-desc">充值注意事项</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<textarea class="config-textarea" v-model="config.notice" placeholder="请输入充值注意事项"></textarea>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">小程序充值开关:</text>
|
||||
<text class="label-desc">仅小程序端的充值开关,小程序提交审核前,需要关闭此功能</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="radio-group">
|
||||
<view class="radio-item" @click="config.mp_recharge = true">
|
||||
<view class="radio-circle" :class="{ checked: config.mp_recharge }"></view>
|
||||
<text class="radio-txt">开启</text>
|
||||
</view>
|
||||
<view class="radio-item ml-20" @click="config.mp_recharge = false">
|
||||
<view class="radio-circle" :class="{ checked: !config.mp_recharge }"></view>
|
||||
<text class="radio-txt">关闭</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-item">
|
||||
<view class="item-label">
|
||||
<text class="label-txt">最低充值金额:</text>
|
||||
<text class="label-desc">用户单次最低充值金额</text>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<input class="config-input" type="number" v-model="config.min_amount" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="config-footer">
|
||||
<button class="btn-submit" @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>('recharge-config')
|
||||
const title = ref<string>('config')
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const config = reactive({
|
||||
balance_enabled: true,
|
||||
notice: '充值后账户的金额不能提现,可用于商城消费使用\n佣金导入账户之后不能再次导出、不可提现\n账户充值出现问题可联系商城客服',
|
||||
mp_recharge: false,
|
||||
min_amount: 0.01
|
||||
})
|
||||
|
||||
const handleSave = () => {
|
||||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||||
}
|
||||
</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; }
|
||||
.marketing-recharge-config {
|
||||
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);
|
||||
}
|
||||
|
||||
.config-card { padding: 24px; }
|
||||
|
||||
.config-header {
|
||||
border-bottom: 1px solid #e8eaec;
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.config-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #17233d;
|
||||
position: relative;
|
||||
padding-left: 12px;
|
||||
}
|
||||
.config-title::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 4px;
|
||||
bottom: 4px;
|
||||
width: 3px;
|
||||
background: #1890ff;
|
||||
}
|
||||
|
||||
.config-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 30px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.item-label { width: 220px; display: flex; flex-direction: column; }
|
||||
.label-txt { font-size: 14px; color: #333; margin-bottom: 4px; }
|
||||
.label-desc { font-size: 12px; color: #999; }
|
||||
|
||||
.item-content { flex: 1; }
|
||||
|
||||
.radio-group { display: flex; flex-direction: row; padding-top: 4px; }
|
||||
.radio-item { display: flex; flex-direction: row; align-items: center; cursor: pointer; }
|
||||
.radio-circle { width: 14px; height: 14px; border: 1px solid #dcdfe6; border-radius: 50%; margin-right: 6px; position: relative; }
|
||||
.radio-circle.checked { border-color: #1890ff; }
|
||||
.radio-circle.checked::after { content: ''; position: absolute; width: 8px; height: 8px; background: #1890ff; border-radius: 50%; top: 2px; left: 2px; }
|
||||
.radio-txt { font-size: 14px; color: #606266; }
|
||||
.ml-20 { margin-left: 20px; }
|
||||
|
||||
.config-textarea {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
height: 120px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.config-input {
|
||||
width: 300px;
|
||||
height: 32px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.config-footer {
|
||||
margin-top: 40px;
|
||||
padding-left: 220px;
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
width: 80px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
383
pages/mall/admin/marketing/recharge/quota.uvue
Normal file
383
pages/mall/admin/marketing/recharge/quota.uvue
Normal file
@@ -0,0 +1,383 @@
|
||||
<template>
|
||||
<view class="marketing-recharge-quota">
|
||||
<view class="content-layout">
|
||||
<!-- 左侧预览 -->
|
||||
<view class="preview-side">
|
||||
<view class="phone-mock">
|
||||
<view class="phone-header">
|
||||
<text class="balance-label">我的余额</text>
|
||||
<view class="balance-val">
|
||||
<text class="symbol">¥</text>
|
||||
<text class="num">0.00</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="recharge-tabs">
|
||||
<view class="tab active"><text class="tab-txt">账户充值</text></view>
|
||||
<view class="tab"><text class="tab-txt">佣金导入</text></view>
|
||||
</view>
|
||||
|
||||
<view class="quota-grid">
|
||||
<view v-for="(item, index) in list" :key="index" class="quota-item" :class="{ active: index === 0 }">
|
||||
<text class="price">{{ item.price }}元</text>
|
||||
<text class="bonus">赠送{{ item.bonus }}元</text>
|
||||
</view>
|
||||
<view class="quota-item other">
|
||||
<text class="other-txt">其他</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="notice-section">
|
||||
<text class="notice-title">注意事项:</text>
|
||||
<text class="notice-content">充值后账户的金额不能提现,可用于商城消费使用。佣金导入账户之后不能再次导出、不可提现。账户充值出现问题可联系商城客服,也可拨打商城客服热线:4008888888。</text>
|
||||
</view>
|
||||
|
||||
<button class="recharge-btn">立即充值</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧设置 -->
|
||||
<view class="table-side border-shadow">
|
||||
<view class="side-header">
|
||||
<text class="side-title">充值金额设置</text>
|
||||
</view>
|
||||
<view class="action-row">
|
||||
<button class="btn-primary" @click="showAddModal = true">添加数据</button>
|
||||
</view>
|
||||
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">编号</view>
|
||||
<view class="th cell-price">售价</view>
|
||||
<view class="th cell-bonus">赠送</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-for="item in list" :key="item.id" class="table-row">
|
||||
<view class="td cell-id"><text class="td-txt">{{ item.id }}</text></view>
|
||||
<view class="td cell-price"><text class="td-txt">{{ item.price }}</text></view>
|
||||
<view class="td cell-bonus"><text class="td-txt">{{ item.bonus }}</text></view>
|
||||
<view class="td cell-status">
|
||||
<view class="switch-mock" :class="{ active: item.is_open }" @click="toggleStatus(item)">
|
||||
<view class="switch-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="td cell-sort"><text class="td-txt">{{ item.sort }}</text></view>
|
||||
<view class="td cell-op">
|
||||
<text class="op-link" @click="handleEdit(item)">编辑</text>
|
||||
<text class="op-link del ml-10" @click="handleDelete(item)">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 添加数据弹窗 -->
|
||||
<view v-if="showAddModal" class="modal-mask">
|
||||
<view class="modal-content">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">添加数据</text>
|
||||
<text class="modal-close" @click="showAddModal = false">×</text>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<view class="form-item">
|
||||
<text class="form-label">售价:</text>
|
||||
<input class="form-input" v-model="formData.price" type="number" placeholder="请输入售价" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">赠送:</text>
|
||||
<input class="form-input" v-model="formData.bonus" type="number" placeholder="请输入赠送" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">排序:</text>
|
||||
<input class="form-input" v-model="formData.sort" type="number" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">状态:</text>
|
||||
<view class="radio-group">
|
||||
<view class="radio-item" @click="formData.is_open = true">
|
||||
<view class="radio-circle" :class="{ checked: formData.is_open }"></view>
|
||||
<text class="radio-txt">显示</text>
|
||||
</view>
|
||||
<view class="radio-item ml-20" @click="formData.is_open = false">
|
||||
<view class="radio-circle" :class="{ checked: !formData.is_open }"></view>
|
||||
<text class="radio-txt">隐藏</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<button class="btn-cancel" @click="showAddModal = false">取消</button>
|
||||
<button class="btn-submit" @click="handleSubmit">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, reactive } from 'vue'
|
||||
|
||||
const showAddModal = ref(false)
|
||||
const formData = reactive({
|
||||
price: '',
|
||||
bonus: '',
|
||||
sort: 1,
|
||||
is_open: true
|
||||
})
|
||||
|
||||
const list = ref([
|
||||
{ id: 640, price: 10, bonus: 2, is_open: true, sort: 6 },
|
||||
{ id: 641, price: 20, bonus: 8, is_open: true, sort: 5 },
|
||||
{ id: 642, price: 50, bonus: 20, is_open: true, sort: 4 },
|
||||
{ id: 643, price: 100, bonus: 50, is_open: true, sort: 3 },
|
||||
{ id: 644, price: 200, bonus: 110, is_open: true, sort: 2 },
|
||||
{ id: 645, price: 300, bonus: 200, is_open: true, sort: 1 }
|
||||
])
|
||||
|
||||
const toggleStatus = (item: any) => {
|
||||
item.is_open = !item.is_open
|
||||
uni.showToast({ title: '操作成功', icon: 'success' })
|
||||
}
|
||||
|
||||
const handleEdit = (item: any) => {
|
||||
uni.showToast({ title: '编辑功能', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleDelete = (item: any) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认删除该项吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
const index = list.value.findIndex(i => i.id === item.id)
|
||||
if (index > -1) {
|
||||
list.value.splice(index, 1)
|
||||
uni.showToast({ title: '已删除', icon: 'success' })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!formData.price) {
|
||||
uni.showToast({ title: '请输入售价', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const newItem = {
|
||||
id: Math.floor(Math.random() * 1000) + 700,
|
||||
price: parseFloat(formData.price),
|
||||
bonus: parseFloat(formData.bonus || '0'),
|
||||
sort: parseInt(formData.sort.toString()),
|
||||
is_open: formData.is_open
|
||||
}
|
||||
|
||||
list.value.push(newItem)
|
||||
|
||||
// 重置表单
|
||||
formData.price = ''
|
||||
formData.bonus = ''
|
||||
formData.sort = 1
|
||||
formData.is_open = true
|
||||
|
||||
showAddModal.value = false
|
||||
uni.showToast({ title: '添加成功', icon: 'success' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.marketing-recharge-quota {
|
||||
padding: 16px;
|
||||
background: #f0f2f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.content-layout {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
/* 左侧预览 */
|
||||
.preview-side {
|
||||
width: 320px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.phone-mock {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.phone-header {
|
||||
height: 120px;
|
||||
background: #e74c3c;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.balance-label { font-size: 13px; opacity: 0.9; margin-bottom: 8px; }
|
||||
.balance-val { display: flex; flex-direction: row; align-items: baseline; }
|
||||
.symbol { font-size: 18px; margin-right: 4px; }
|
||||
.num { font-size: 32px; font-weight: bold; }
|
||||
|
||||
.recharge-tabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 44px;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
.tab { flex: 1; display: flex; align-items: center; justify-content: center; position: relative; }
|
||||
.tab.active::after { content: ''; position: absolute; bottom: 0; width: 40px; height: 2px; background: #e74c3c; }
|
||||
.tab-txt { font-size: 14px; color: #666; font-weight: bold; }
|
||||
.tab.active .tab-txt { color: #333; }
|
||||
|
||||
.quota-grid {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
padding: 16px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.quota-item {
|
||||
width: 90px;
|
||||
height: 60px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.quota-item.active {
|
||||
border-color: #e74c3c;
|
||||
background: #fff;
|
||||
position: relative;
|
||||
}
|
||||
.quota-item.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
border: 1px solid #e74c3c;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.price { font-size: 14px; color: #333; font-weight: bold; margin-bottom: 4px; }
|
||||
.bonus { font-size: 11px; color: #999; }
|
||||
.quota-item.active .price { color: #e74c3c; }
|
||||
.quota-item.active .bonus { color: #e74c3c; opacity: 0.8; }
|
||||
|
||||
.other { background: #f0f0f0; border: none; }
|
||||
.other-txt { font-size: 14px; color: #666; }
|
||||
|
||||
.notice-section {
|
||||
padding: 0 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.notice-title { font-size: 13px; color: #333; font-weight: bold; margin-bottom: 8px; display: block; }
|
||||
.notice-content { font-size: 12px; color: #999; line-height: 1.6; }
|
||||
|
||||
.recharge-btn {
|
||||
margin: 24px 16px 0;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
background: #e74c3c;
|
||||
color: #fff;
|
||||
border-radius: 20px;
|
||||
font-size: 15px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* 右侧设置 */
|
||||
.table-side {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.border-shadow { border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); }
|
||||
|
||||
.side-header { border-left: 4px solid #1890ff; padding-left: 12px; margin-bottom: 24px; }
|
||||
.side-title { font-size: 16px; font-weight: bold; color: #333; }
|
||||
|
||||
.action-row { margin-bottom: 16px; }
|
||||
|
||||
.btn-primary {
|
||||
background: #1890ff; color: #fff; border: none;
|
||||
height: 32px; line-height: 32px; padding: 0 16px; font-size: 14px; border-radius: 4px; cursor: pointer;
|
||||
}
|
||||
|
||||
.table-head { display: flex; flex-direction: row; background: #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-price { width: 120px; }
|
||||
.cell-bonus { width: 120px; }
|
||||
.cell-status { width: 100px; text-align: center; }
|
||||
.cell-sort { width: 100px; text-align: center; }
|
||||
.cell-op { flex: 1; text-align: right; }
|
||||
|
||||
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
|
||||
.op-link.del { color: #ff4d4f; }
|
||||
.ml-10 { margin-left: 10px; }
|
||||
|
||||
.switch-mock {
|
||||
width: 44px; height: 22px; background-color: #bfbfbf; border-radius: 11px;
|
||||
display: flex; align-items: center; padding: 0 4px; position: relative;
|
||||
transition: background-color 0.3s; cursor: pointer;
|
||||
}
|
||||
.switch-mock.active { background-color: #1890ff; }
|
||||
.switch-dot {
|
||||
width: 14px; height: 14px; background-color: #fff; border-radius: 50%;
|
||||
position: absolute; left: 4px; transition: left 0.3s;
|
||||
}
|
||||
.switch-mock.active .switch-dot { left: 26px; }
|
||||
|
||||
/* Modal */
|
||||
.modal-mask {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000;
|
||||
}
|
||||
.modal-content { width: 500px; background: #fff; border-radius: 4px; }
|
||||
.modal-header { padding: 16px 24px; border-bottom: 1px solid #e8eaec; display: flex; justify-content: space-between; align-items: center; }
|
||||
.modal-title { font-size: 16px; font-weight: bold; }
|
||||
.modal-close { font-size: 24px; color: #999; cursor: pointer; }
|
||||
.modal-body { padding: 24px; }
|
||||
.modal-footer { padding: 12px 24px; border-top: 1px solid #e8eaec; display: flex; justify-content: flex-end; }
|
||||
|
||||
.form-item { display: flex; flex-direction: row; margin-bottom: 20px; align-items: center; }
|
||||
.form-label { width: 80px; font-size: 14px; color: #606266; }
|
||||
.form-input { flex: 1; height: 32px; border: 1px solid #dcdfe6; border-radius: 4px; padding: 0 12px; }
|
||||
|
||||
.radio-group { display: flex; flex-direction: row; flex: 1; }
|
||||
.radio-item { display: flex; flex-direction: row; align-items: center; cursor: pointer; }
|
||||
.radio-circle { width: 14px; height: 14px; border: 1px solid #dcdfe6; border-radius: 50%; margin-right: 6px; position: relative; }
|
||||
.radio-circle.checked { border-color: #1890ff; }
|
||||
.radio-circle.checked::after { content: ''; position: absolute; width: 8px; height: 8px; background: #1890ff; border-radius: 50%; top: 2px; left: 2px; }
|
||||
.radio-txt { font-size: 14px; color: #606266; }
|
||||
.ml-20 { margin-left: 20px; }
|
||||
|
||||
.btn-cancel { margin-right: 8px; height: 32px; line-height: 32px; padding: 0 16px; font-size: 14px; border-radius: 4px; border: 1px solid #dcdfe6; background: #fff; }
|
||||
.btn-submit { height: 32px; line-height: 32px; padding: 0 16px; font-size: 14px; border-radius: 4px; background: #1890ff; color: #fff; border: none; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,528 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
<view class="marketing-seckill-config">
|
||||
<view class="filter-card border-shadow">
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="label">是否显示:</text>
|
||||
<view class="select-mock">
|
||||
<text class="select-val">请选择</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
|
||||
<view class="action-bar">
|
||||
<button class="btn-add" @click="showDrawer = true">添加数据</button>
|
||||
</view>
|
||||
|
||||
<view class="table-card border-shadow">
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">编号</view>
|
||||
<view class="th cell-hour">开启时间(整点)</view>
|
||||
<view class="th cell-duration">持续时间(整数小时)</view>
|
||||
<view class="th cell-img">幻灯片</view>
|
||||
<view class="th cell-sort">排序</view>
|
||||
<view class="th cell-status">状态</view>
|
||||
<view class="th cell-op">操作</view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-for="item in configList" :key="item.id" class="table-row">
|
||||
<view class="td cell-id">
|
||||
<text class="td-txt">{{ item.id }}</text>
|
||||
</view>
|
||||
<view class="td cell-hour">
|
||||
<text class="td-txt">{{ item.start_hour }}</text>
|
||||
</view>
|
||||
<view class="td cell-duration">
|
||||
<text class="td-txt">{{ item.duration }}</text>
|
||||
</view>
|
||||
<view class="td cell-img">
|
||||
<image v-if="item.image" class="thumb" :src="item.image" mode="aspectFill"></image>
|
||||
<text v-else class="td-txt">-</text>
|
||||
</view>
|
||||
<view class="td cell-sort">
|
||||
<text class="td-txt">{{ item.sort }}</text>
|
||||
</view>
|
||||
<view class="td cell-status">
|
||||
<view class="switch-mock" :class="{ active: item.status }" @click="toggleStatus(item)">
|
||||
<view class="switch-dot"></view>
|
||||
<text class="switch-txt">{{ item.status ? '开启' : '关闭' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="td cell-op">
|
||||
<view class="op-links">
|
||||
<text class="op-link" @click="handleEdit(item)">编辑</text>
|
||||
<text class="op-split">|</text>
|
||||
<text class="op-link" @click="handleDelete(item)">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="pagination-footer">
|
||||
<view class="page-total">
|
||||
<text class="total-txt">共 {{ configList.length }} 条</text>
|
||||
</view>
|
||||
<view class="page-select">
|
||||
<view class="select-mock mini">
|
||||
<text class="select-val">20条/页</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Drawer Overlay -->
|
||||
<view v-if="showDrawer || isAnimating" class="drawer-mask" :class="{ active: showDrawer }" @click="closeDrawer"></view>
|
||||
|
||||
<!-- Drawer Panel -->
|
||||
<view class="drawer-panel" :class="{ active: showDrawer }">
|
||||
<view class="drawer-header">
|
||||
<text class="drawer-title">添加数据</text>
|
||||
<text class="drawer-close" @click="closeDrawer">×</text>
|
||||
</view>
|
||||
<view class="drawer-content">
|
||||
<view class="form-item">
|
||||
<text class="form-label required">开启时间(整点):</text>
|
||||
<input class="form-input" placeholder="请输入开启时间(整点)" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label required">持续时间(整数小时):</text>
|
||||
<input class="form-input" placeholder="请输入持续时间(整数小时)" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">幻灯片:</text>
|
||||
<view class="upload-mock" @click="handleUpload">
|
||||
<image v-if="formData.image" :src="formData.image" mode="aspectFill" class="thumb" />
|
||||
<text v-else class="upload-ic">🖼️</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">排序:</text>
|
||||
<input class="form-input" type="number" v-model="formData.sort" placeholder="1" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">状态:</text>
|
||||
<view class="radio-group">
|
||||
<view class="radio-item" @click="formData.status = true">
|
||||
<view class="radio-circle" :class="{ active: formData.status }"></view>
|
||||
<text class="radio-txt">显示</text>
|
||||
</view>
|
||||
<view class="radio-item" @click="formData.status = false">
|
||||
<view class="radio-circle" :class="{ active: !formData.status }"></view>
|
||||
<text class="radio-txt">隐藏</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="drawer-footer">
|
||||
<button class="btn-cancel" @click="closeDrawer">取消</button>
|
||||
<button class="btn-confirm" @click="handleSubmit">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
const currentPage = ref<string>('seckill-config')
|
||||
const title = ref<string>('config')
|
||||
|
||||
const showDrawer = ref(false)
|
||||
const isAnimating = ref(false)
|
||||
|
||||
const formData = ref({
|
||||
id: 0,
|
||||
start_hour: 0,
|
||||
duration: 1,
|
||||
image: '',
|
||||
sort: 1,
|
||||
status: true
|
||||
})
|
||||
|
||||
const configList = ref([
|
||||
{
|
||||
id: 2268,
|
||||
start_hour: 6,
|
||||
duration: 18,
|
||||
image: '',
|
||||
sort: 1,
|
||||
status: true
|
||||
}
|
||||
])
|
||||
|
||||
const toggleStatus = (item: any) => {
|
||||
item.status = !item.status
|
||||
uni.showToast({ title: '修改成功', icon: 'success' })
|
||||
}
|
||||
|
||||
const handleEdit = (item: any) => {
|
||||
formData.value = { ...item }
|
||||
showDrawer.value = true
|
||||
}
|
||||
|
||||
const handleDelete = (item: any) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除此项配置吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
configList.value = configList.value.filter(i => i.id !== item.id)
|
||||
uni.showToast({ title: '删除成功' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleUpload = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: (res) => {
|
||||
formData.value.image = res.tempFilePaths[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
uni.showToast({ title: '提交成功', icon: 'success' })
|
||||
closeDrawer()
|
||||
}
|
||||
|
||||
const closeDrawer = () => {
|
||||
showDrawer.value = false
|
||||
isAnimating.value = true
|
||||
setTimeout(() => {
|
||||
isAnimating.value = false
|
||||
}, 300)
|
||||
}
|
||||
</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; }
|
||||
.marketing-seckill-config {
|
||||
min-height: 100vh;
|
||||
background: #f0f2f5;
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.border-shadow {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 过滤栏 */
|
||||
.filter-card {
|
||||
padding: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.select-mock {
|
||||
width: 200px;
|
||||
height: 32px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.select-mock.mini { width: 100px; height: 28px; }
|
||||
|
||||
.select-val { font-size: 13px; color: #606266; }
|
||||
.arrow { font-size: 10px; color: #c0c4cc; }
|
||||
|
||||
/* 操作栏 */
|
||||
.action-bar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.btn-add {
|
||||
width: auto;
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 表格区域 */
|
||||
.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-hour { width: 150px; text-align: center; }
|
||||
.cell-duration { width: 150px; text-align: center; }
|
||||
.cell-img { flex: 1; min-width: 150px; }
|
||||
.cell-sort { width: 100px; text-align: center; }
|
||||
.cell-status { width: 100px; text-align: center; }
|
||||
.cell-op { width: 120px; text-align: right; }
|
||||
|
||||
.thumb {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
.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-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
|
||||
.op-split { color: #e8eaec; margin: 0 8px; }
|
||||
|
||||
/* 分页 */
|
||||
.pagination-footer {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
.total-txt { font-size: 13px; color: #606266; }
|
||||
|
||||
/* Drawer Styles */
|
||||
.drawer-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.45);
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
pointer-events: none;
|
||||
}
|
||||
.drawer-mask.active {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.drawer-panel {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: -50%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
z-index: 1001;
|
||||
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: right 0.3s ease-out;
|
||||
}
|
||||
.drawer-panel.active {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.drawer-header {
|
||||
padding: 16px 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: 600;
|
||||
color: #262626;
|
||||
}
|
||||
.drawer-close {
|
||||
font-size: 24px;
|
||||
color: #bfbfbf;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.drawer-content {
|
||||
flex: 1;
|
||||
padding: 24px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #262626;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.required::before {
|
||||
content: '*';
|
||||
color: #ff4d4f;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.form-input {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.upload-mock {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fafafa;
|
||||
cursor: pointer;
|
||||
}
|
||||
.upload-ic { font-size: 24px; color: #bfbfbf; }
|
||||
|
||||
.radio-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 24px;
|
||||
}
|
||||
.radio-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.radio-circle {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
}
|
||||
.radio-circle.active {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
.radio-circle.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #1890ff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.radio-txt { font-size: 14px; color: #262626; }
|
||||
|
||||
.drawer-footer {
|
||||
padding: 10px 16px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn-cancel, .btn-confirm {
|
||||
width: auto;
|
||||
padding: 0 15px;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-cancel {
|
||||
background-color: #fff;
|
||||
border: 1px solid #d9d9d9;
|
||||
color: #595959;
|
||||
}
|
||||
.btn-confirm {
|
||||
background-color: #1890ff;
|
||||
border: 1px solid #1890ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面占位 (自动生成)</text>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
const currentPage = ref<string>('seckill-goods')
|
||||
const title = ref<string>('goods')
|
||||
</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>
|
||||
|
||||
|
||||
@@ -1,15 +1,119 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<view class="page-header">
|
||||
<text class="page-title">秒杀列表</text>
|
||||
<text class="page-subtitle">Component: MarketingSeckill</text>
|
||||
<view class="marketing-seckill-list">
|
||||
<view class="filter-card border-shadow">
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="label">活动搜索:</text>
|
||||
<input class="input-mock" placeholder="请输入活动名称, ID" />
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="label">活动状态:</text>
|
||||
<view class="select-mock">
|
||||
<text class="select-val">请选择</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="label">活动时段:</text>
|
||||
<view class="select-mock">
|
||||
<text class="select-val">请选择</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-row mt-16">
|
||||
<view class="filter-item">
|
||||
<text class="label">活动时间:</text>
|
||||
<view class="date-picker-mock">
|
||||
<text class="calendar-ic">📅</text>
|
||||
<text class="date-placeholder">开始日期 - 结束日期</text>
|
||||
</view>
|
||||
</view>
|
||||
<button class="btn-query">查询</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="page-content">
|
||||
<view class="placeholder-card">
|
||||
<text class="placeholder-title">页面占位</text>
|
||||
<text class="placeholder-desc">该功能模块正在开发中</text>
|
||||
<text class="placeholder-info">当前采用 CRMEB 路由体系 1:1 映射</text>
|
||||
|
||||
<view class="action-bar">
|
||||
<button class="btn-add" @click="handleAdd">添加秒杀活动</button>
|
||||
</view>
|
||||
|
||||
<view class="table-card border-shadow">
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">ID</view>
|
||||
<view class="th cell-title">活动标题</view>
|
||||
<view class="th cell-limit">单次限购</view>
|
||||
<view class="th cell-total">总购买数量限制</view>
|
||||
<view class="th cell-count">商品数量</view>
|
||||
<view class="th cell-period">活动时段</view>
|
||||
<view class="th cell-time">活动时间</view>
|
||||
<view class="th cell-status">状态</view>
|
||||
<view class="th cell-op">操作</view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-for="item in seckillList" :key="item.id" class="table-row">
|
||||
<view class="td cell-id">
|
||||
<text class="td-txt">{{ item.id }}</text>
|
||||
</view>
|
||||
<view class="td cell-title">
|
||||
<text class="td-txt">{{ item.title }}</text>
|
||||
</view>
|
||||
<view class="td cell-limit">
|
||||
<text class="td-txt">{{ item.single_limit }}</text>
|
||||
</view>
|
||||
<view class="td cell-total">
|
||||
<text class="td-txt">{{ item.total_limit }}</text>
|
||||
</view>
|
||||
<view class="td cell-count">
|
||||
<text class="td-txt">{{ item.product_count }}</text>
|
||||
</view>
|
||||
<view class="td cell-period">
|
||||
<view class="period-tag">
|
||||
<text class="period-txt">{{ item.time_range }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="td cell-time">
|
||||
<text class="td-txt-small">开始: {{ item.start_date }}</text>
|
||||
<text class="td-txt-small">结束: {{ item.end_date }}</text>
|
||||
</view>
|
||||
<view class="td cell-status">
|
||||
<view class="switch-mock" :class="{ active: item.status }" @click="toggleStatus(item)">
|
||||
<view class="switch-dot"></view>
|
||||
<text class="switch-txt">{{ item.status ? '开启' : '关闭' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="td cell-op">
|
||||
<view class="op-links">
|
||||
<text class="op-link" @click="handleEdit(item)">编辑</text>
|
||||
<text class="op-split">|</text>
|
||||
<text class="op-link" @click="handleDelete(item)">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="pagination-footer">
|
||||
<view class="page-total">
|
||||
<text class="total-txt">共 {{ seckillList.length }} 条</text>
|
||||
</view>
|
||||
<view class="page-select">
|
||||
<view class="select-mock mini">
|
||||
<text class="select-val">15条/页</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="page-btns">
|
||||
<text class="p-btn disabled">‹</text>
|
||||
<text class="p-btn active">1</text>
|
||||
<text class="p-btn disabled">›</text>
|
||||
</view>
|
||||
<view class="page-jump">
|
||||
<text class="jump-txt">前往</text>
|
||||
<input class="jump-input" placeholder="1" />
|
||||
<text class="jump-txt">页</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -18,70 +122,258 @@
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const pageTitle = ref<string>('秒杀列表')
|
||||
const seckillList = ref([
|
||||
{
|
||||
id: 91,
|
||||
title: '秒杀活动',
|
||||
single_limit: 1,
|
||||
total_limit: 10,
|
||||
product_count: 5,
|
||||
time_range: '06:00-24:00',
|
||||
start_date: '2025-07-01 00:00:00',
|
||||
end_date: '2028-08-22 23:59:59',
|
||||
status: true
|
||||
}
|
||||
])
|
||||
|
||||
const toggleStatus = (item: any) => {
|
||||
item.status = !item.status
|
||||
uni.showToast({ title: '修改成功', icon: 'success' })
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
uni.showToast({ title: '添加活动功能开发中', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleEdit = (item: any) => {
|
||||
uni.showToast({ title: '编辑活动功能开发中', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleDelete = (item: any) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除该活动吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
seckillList.value = seckillList.value.filter(i => i.id !== item.id)
|
||||
uni.showToast({ title: '删除成功' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
padding: 24px;
|
||||
background-color: #f0f2f5;
|
||||
<style scoped lang="scss">
|
||||
.marketing-seckill-list {
|
||||
min-height: 100vh;
|
||||
background: #f0f2f5;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
background-color: #ffffff;
|
||||
.border-shadow {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.mt-16 { margin-top: 16px; }
|
||||
|
||||
/* 过滤栏 */
|
||||
.filter-card {
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
margin-bottom: 8px;
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
display: block;
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #8c8c8c;
|
||||
color: #606266;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
background-color: #ffffff;
|
||||
.input-mock, .date-picker-mock, .select-mock {
|
||||
width: 240px;
|
||||
height: 32px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.input-mock { width: 200px; }
|
||||
.select-mock { width: 160px; justify-content: space-between; }
|
||||
.select-mock.mini { width: 100px; height: 28px; }
|
||||
|
||||
.calendar-ic { font-size: 14px; color: #c0c4cc; margin-right: 8px; }
|
||||
.date-placeholder { font-size: 13px; color: #c0c4cc; }
|
||||
.select-val { font-size: 13px; color: #606266; }
|
||||
.arrow { font-size: 10px; color: #c0c4cc; }
|
||||
|
||||
.btn-query {
|
||||
width: 64px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* 操作栏 */
|
||||
.action-bar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.btn-add {
|
||||
width: auto;
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 表格区域 */
|
||||
.table-card {
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.placeholder-card {
|
||||
text-align: center;
|
||||
padding: 48px 24px;
|
||||
.table-head {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: #f8f8f9;
|
||||
border-bottom: 1px solid #e8eaec;
|
||||
}
|
||||
|
||||
.placeholder-title {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
margin-bottom: 12px;
|
||||
.th {
|
||||
padding: 12px 8px;
|
||||
font-size: 13px;
|
||||
color: #515a6e;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.placeholder-desc {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #8c8c8c;
|
||||
margin-bottom: 8px;
|
||||
.table-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1px solid #e8eaec;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.placeholder-info {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #bfbfbf;
|
||||
.td {
|
||||
padding: 16px 8px;
|
||||
}
|
||||
|
||||
.td-txt { font-size: 13px; color: #515a6e; }
|
||||
.td-txt-small { font-size: 12px; color: #808695; display: block; }
|
||||
|
||||
/* 各列宽度 */
|
||||
.cell-id { width: 60px; }
|
||||
.cell-title { flex: 1; min-width: 150px; }
|
||||
.cell-limit { width: 100px; text-align: center; }
|
||||
.cell-total { width: 150px; text-align: center; }
|
||||
.cell-count { width: 100px; text-align: center; }
|
||||
.cell-period { width: 120px; text-align: center; }
|
||||
.cell-time { width: 220px; }
|
||||
.cell-status { width: 100px; text-align: center; }
|
||||
.cell-op { width: 120px; text-align: right; }
|
||||
|
||||
.period-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border: 1px solid #1890ff;
|
||||
border-radius: 4px;
|
||||
background-color: #f0f7ff;
|
||||
}
|
||||
.period-txt { color: #1890ff; font-size: 12px; }
|
||||
|
||||
.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;
|
||||
}
|
||||
.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-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
|
||||
.op-split { color: #e8eaec; margin: 0 8px; }
|
||||
|
||||
/* 分页 */
|
||||
.pagination-footer {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
.total-txt { font-size: 13px; color: #606266; }
|
||||
.page-btns { display: flex; flex-direction: row; gap: 8px; }
|
||||
.p-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
.p-btn.active { background-color: #1890ff; border-color: #1890ff; color: #fff; }
|
||||
.p-btn.disabled { color: #c0c4cc; cursor: not-allowed; }
|
||||
|
||||
.page-jump { display: flex; flex-direction: row; align-items: center; gap: 8px; }
|
||||
.jump-txt { font-size: 13px; color: #606266; }
|
||||
.jump-input { width: 40px; height: 28px; border: 1px solid #dcdfe6; border-radius: 4px; text-align: center; }
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
430
pages/mall/admin/marketing/seckill/product.uvue
Normal file
430
pages/mall/admin/marketing/seckill/product.uvue
Normal file
@@ -0,0 +1,430 @@
|
||||
<template>
|
||||
<view class="marketing-seckill-product">
|
||||
<view class="filter-card border-shadow">
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="label">商品搜索:</text>
|
||||
<input class="input-mock" placeholder="请输入商品名称, ID" />
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="label">活动搜索:</text>
|
||||
<input class="input-mock" placeholder="请输入活动名称" />
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="label">活动状态:</text>
|
||||
<view class="select-mock">
|
||||
<text class="select-val">请选择</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-row mt-16">
|
||||
<view class="filter-item">
|
||||
<text class="label">活动时间:</text>
|
||||
<view class="date-picker-mock">
|
||||
<text class="calendar-ic">📅</text>
|
||||
<text class="date-placeholder">开始日期 - 结束日期</text>
|
||||
</view>
|
||||
</view>
|
||||
<button class="btn-query">查询</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="action-bar">
|
||||
<button class="btn-export">导出</button>
|
||||
</view>
|
||||
|
||||
<view class="table-card border-shadow">
|
||||
<view class="table-container">
|
||||
<view class="table-head">
|
||||
<view class="th cell-id">ID</view>
|
||||
<view class="th cell-img">商品图片</view>
|
||||
<view class="th cell-title">商品标题</view>
|
||||
<view class="th cell-intro">商品简介</view>
|
||||
<view class="th cell-activity">活动名称</view>
|
||||
<view class="th cell-price">售价</view>
|
||||
<view class="th cell-price">秒杀价</view>
|
||||
<view class="th cell-num">限量</view>
|
||||
<view class="th cell-num">限量剩余</view>
|
||||
<view class="th cell-status-txt">秒杀状态</view>
|
||||
<view class="th cell-time">活动时间</view>
|
||||
<view class="th cell-status">状态</view>
|
||||
<view class="th cell-op">操作</view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-for="item in productList" :key="item.id" class="table-row">
|
||||
<view class="td cell-id">
|
||||
<text class="td-txt">{{ item.id }}</text>
|
||||
</view>
|
||||
<view class="td cell-img">
|
||||
<image class="thumb" :src="item.image" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="td cell-title">
|
||||
<text class="product-title line-clamp-2">{{ item.title }}</text>
|
||||
</view>
|
||||
<view class="td cell-intro">
|
||||
<text class="product-title line-clamp-2">{{ item.intro }}</text>
|
||||
</view>
|
||||
<view class="td cell-activity">
|
||||
<text class="td-txt">{{ item.activity_name }}</text>
|
||||
</view>
|
||||
<view class="td cell-price">
|
||||
<text class="td-txt">{{ item.price.toFixed(2) }}</text>
|
||||
</view>
|
||||
<view class="td cell-price">
|
||||
<text class="td-txt-price">{{ item.seckill_price.toFixed(2) }}</text>
|
||||
</view>
|
||||
<view class="td cell-num">
|
||||
<text class="td-txt">{{ item.limit }}</text>
|
||||
</view>
|
||||
<view class="td cell-num">
|
||||
<text class="td-txt">{{ item.limit_remaining }}</text>
|
||||
</view>
|
||||
<view class="td cell-status-txt">
|
||||
<text class="status-text green">进行中</text>
|
||||
</view>
|
||||
<view class="td cell-time">
|
||||
<text class="td-txt-small">起: {{ item.start_date }}</text>
|
||||
<text class="td-txt-small">止: {{ item.end_date }}</text>
|
||||
</view>
|
||||
<view class="td cell-status">
|
||||
<view class="switch-mock" :class="{ active: item.status }" @click="toggleStatus(item)">
|
||||
<view class="switch-dot"></view>
|
||||
<text class="switch-txt">{{ item.status ? '开启' : '关闭' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="td cell-op">
|
||||
<view class="op-links">
|
||||
<text class="op-link" @click="handleDelete(item)">删除</text>
|
||||
<text class="op-split">|</text>
|
||||
<text class="op-link" @click="handleStats(item)">统计</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="pagination-footer">
|
||||
<view class="page-total">
|
||||
<text class="total-txt">共 {{ productList.length }} 条</text>
|
||||
</view>
|
||||
<view class="page-select">
|
||||
<view class="select-mock mini">
|
||||
<text class="select-val">15条/页</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="page-btns">
|
||||
<text class="p-btn disabled">‹</text>
|
||||
<text class="p-btn active">1</text>
|
||||
<text class="p-btn disabled">›</text>
|
||||
</view>
|
||||
<view class="page-jump">
|
||||
<text class="jump-txt">前往</text>
|
||||
<input class="jump-input" placeholder="1" />
|
||||
<text class="jump-txt">页</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const productList = ref([
|
||||
{
|
||||
id: 599,
|
||||
image: 'https://img0.baidu.com/it/u=2257917711,1359654032&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
|
||||
title: '爱奇艺智能 奇遇LT01 投影仪 家用卧室超...',
|
||||
intro: '爱奇艺智能 奇遇LT01 投影仪...',
|
||||
activity_name: '秒杀活动',
|
||||
price: 44.00,
|
||||
seckill_price: 1.00,
|
||||
limit: 100,
|
||||
limit_remaining: 55,
|
||||
status: true,
|
||||
start_date: '2025-07-01 00:00:00',
|
||||
end_date: '2028-08-22 23:59:59'
|
||||
},
|
||||
{
|
||||
id: 598,
|
||||
image: 'https://img1.baidu.com/it/u=3175865615,2002599723&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
|
||||
title: '美式复古阔腿牛仔裤女夏季2024新款高...',
|
||||
intro: '',
|
||||
activity_name: '秒杀活动',
|
||||
price: 49.98,
|
||||
seckill_price: 20.00,
|
||||
limit: 750,
|
||||
limit_remaining: 736,
|
||||
status: true,
|
||||
start_date: '2025-07-01 00:00:00',
|
||||
end_date: '2028-08-22 23:59:59'
|
||||
},
|
||||
{
|
||||
id: 596,
|
||||
image: 'https://img0.baidu.com/it/u=3023224345,1529124233&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
|
||||
title: '真力时 (ZENITH) 瑞士手表DEFY系列C...',
|
||||
intro: '真力时 (ZENITH) 瑞士手表...',
|
||||
activity_name: '秒杀活动',
|
||||
price: 61000.00,
|
||||
seckill_price: 20.00,
|
||||
limit: 50,
|
||||
limit_remaining: 46,
|
||||
status: true,
|
||||
start_date: '2025-07-01 00:00:00',
|
||||
end_date: '2028-08-22 23:59:59'
|
||||
}
|
||||
])
|
||||
|
||||
const toggleStatus = (item: any) => {
|
||||
item.status = !item.status
|
||||
uni.showToast({ title: '修改成功', icon: 'success' })
|
||||
}
|
||||
|
||||
const handleDelete = (item: any) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除该秒杀商品吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
productList.value = productList.value.filter(i => i.id !== item.id)
|
||||
uni.showToast({ title: '删除成功' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleStats = (item: any) => {
|
||||
uni.showToast({ title: '统计查看中', icon: 'none' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.marketing-seckill-product {
|
||||
min-height: 100vh;
|
||||
background: #f0f2f5;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.border-shadow {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.mt-16 { margin-top: 16px; }
|
||||
|
||||
/* 过滤栏 */
|
||||
.filter-card {
|
||||
padding: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.input-mock, .date-picker-mock, .select-mock {
|
||||
width: 220px;
|
||||
height: 32px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.select-mock { width: 160px; justify-content: space-between; }
|
||||
.select-mock.mini { width: 100px; height: 28px; }
|
||||
|
||||
.calendar-ic { font-size: 14px; color: #c0c4cc; margin-right: 8px; }
|
||||
.date-placeholder { font-size: 13px; color: #c0c4cc; }
|
||||
.select-val { font-size: 13px; color: #606266; }
|
||||
.arrow { font-size: 10px; color: #c0c4cc; }
|
||||
|
||||
.btn-query {
|
||||
width: 64px;
|
||||
height: 32px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 操作栏 */
|
||||
.action-bar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.btn-export {
|
||||
width: auto;
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
background-color: #fff;
|
||||
color: #606266;
|
||||
border: 1px solid #dcdfe6;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 表格区域 */
|
||||
.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; }
|
||||
.td-txt-price { font-size: 13px; color: #ff4d4f; font-weight: bold; }
|
||||
.td-txt-small { font-size: 12px; color: #808695; display: block; }
|
||||
|
||||
/* 各列宽度 */
|
||||
.cell-id { width: 60px; }
|
||||
.cell-img { width: 80px; }
|
||||
.cell-title { width: 200px; }
|
||||
.cell-intro { width: 180px; }
|
||||
.cell-activity { width: 120px; }
|
||||
.cell-price { width: 80px; text-align: center; }
|
||||
.cell-num { width: 80px; text-align: center; }
|
||||
.cell-status-txt { width: 100px; text-align: center; }
|
||||
.cell-time { width: 220px; }
|
||||
.cell-status { width: 100px; text-align: center; }
|
||||
.cell-op { width: 120px; text-align: right; }
|
||||
|
||||
.thumb {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.product-title {
|
||||
font-size: 13px;
|
||||
color: #515a6e;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.status-text { font-size: 12px; }
|
||||
.status-text.green { color: #52c41a; }
|
||||
|
||||
.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;
|
||||
}
|
||||
.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-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
|
||||
.op-split { color: #e8eaec; margin: 0 8px; }
|
||||
|
||||
/* 分页 */
|
||||
.pagination-footer {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
.total-txt { font-size: 13px; color: #606266; }
|
||||
.page-btns { display: flex; flex-direction: row; gap: 8px; }
|
||||
.p-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
.p-btn.active { background-color: #1890ff; border-color: #1890ff; color: #fff; }
|
||||
.p-btn.disabled { color: #c0c4cc; cursor: not-allowed; }
|
||||
|
||||
.page-jump { display: flex; flex-direction: row; align-items: center; gap: 8px; }
|
||||
.jump-txt { font-size: 13px; color: #606266; }
|
||||
.jump-input { width: 40px; height: 28px; border: 1px solid #dcdfe6; border-radius: 4px; text-align: center; }
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user