Files
medical-mall/pages/mall/admin/marketing/newcomer/index.uvue
2026-02-25 11:39:54 +08:00

528 lines
10 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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">新用户奖励金额必须大于等于00为不赠送</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">新用户奖励积分必须大于等于00为不赠送</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)
// 使用 reactive 并显式指定初始对象,后续通过类型断言或接口约束保证类型安全
const editingCoupon = reactive({
id: 0,
name: '',
desc: ''
}) as Coupon
const couponOptions = reactive([
{ id: 1, name: '满100减10元券', desc: '全场通用' },
{ id: 2, name: '新人5元无门槛', desc: '仅限新人使用' },
{ id: 3, name: '满200减50元券', desc: '限特定商品' }
] as Coupon[])
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: 0;
background-color: transparent;
min-height: auto;
}
.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>