mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-11 03:18:56 +08:00
feat(coupon): 重构优惠券类型为满减、满折、立减三种分类
- 新增立减优惠券类型,支持无门槛直接抵扣 - 优惠券列表页统一使用行对象判断类型并显示完整类型名称 - 发布页增加优惠券类型选择组件,切换时自动联动字段显隐 - 买家端购物车满减门槛为零时显示"无门槛使用" - 卖家端发布页新增券面实时预览面板 - 卖家端发布页重构为分步布局并优化表单交互样式 - 面额与消费门槛校验增加大于零的数值约束 - 移除列表页冗余的标签颜色映射函数,简化展示逻辑
This commit is contained in:
@@ -111,7 +111,7 @@
|
||||
|
||||
<!-- 权益编码 -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1585,3 +1585,6 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
47
manager/src/views/member/paid/records.vue
Normal file
47
manager/src/views/member/paid/records.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card shadow="never"><el-form inline :model="query" class="search-form" @submit.prevent @keyup.enter="search">
|
||||
<el-form-item label="客户名称"><el-input v-model="query.keyword" placeholder="请输入客户昵称" clearable /></el-form-item><el-form-item label="会员ID"><el-input v-model="query.memberId" placeholder="请输入会员ID" clearable /></el-form-item>
|
||||
<el-form-item label="支付状态"><el-select v-model="query.status" clearable placeholder="全部状态" style="width:160px"><el-option label="未支付" value="UNPAID" /><el-option label="已支付" value="PAID" /><el-option label="已取消" value="CANCELLED" /></el-select></el-form-item>
|
||||
<el-form-item><el-button type="primary" @click="search">搜索</el-button><el-button @click="reset">重置</el-button></el-form-item>
|
||||
</el-form></el-card>
|
||||
<el-card shadow="never" class="mt_10"><div class="actions"><el-button :loading="exporting" @click="exportRecords">导出记录</el-button><el-button @click="router.push({name:'paid-member-settings'})">付费会员</el-button></div>
|
||||
<el-table v-loading="loading" :data="rows" border>
|
||||
<el-table-column prop="sn" label="会员费订单号" min-width="200" /><el-table-column prop="memberName" label="客户" width="130" /><el-table-column prop="memberId" label="会员ID" min-width="180" />
|
||||
<el-table-column label="会员名称" min-width="140"><template #default="{row}">{{ name(row) }}</template></el-table-column>
|
||||
<el-table-column prop="amount" label="付费金额(元)" width="115" /><el-table-column prop="months" label="有效月数" width="90" /><el-table-column label="支付状态" width="110"><template #default="{row}">{{ status(row) }}</template></el-table-column>
|
||||
<el-table-column label="会员状态" width="110"><template #default="{row}">{{ membership(row) }}</template></el-table-column><el-table-column prop="payTime" label="支付时间" width="175" /><el-table-column prop="startTime" label="生效时间" width="175" /><el-table-column prop="endTime" label="到期时间" width="175" />
|
||||
<el-table-column prop="paymentMethod" label="支付方式" width="115" /><el-table-column prop="createTime" label="创建时间" width="175" />
|
||||
</el-table>
|
||||
<el-pagination v-model:current-page="query.pageNumber" :page-size="20" :total="total" layout="total, prev, pager, next" @current-change="load" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { getPaidOrders } from '@/api/paid-member';
|
||||
defineOptions({name:'paidMemberRecords'});
|
||||
const router=useRouter(), query=reactive({keyword:'',memberId:'',status:'',pageNumber:1,pageSize:20});
|
||||
const rows=ref([]), total=ref(0), loading=ref(false), exporting=ref(false);
|
||||
let request=0;
|
||||
const time=value => value ? new Date(String(value).replace(/-/g,'/')).getTime() : 0;
|
||||
function status(row) { return row.status === 'UNPAID' && time(row.payDeadline)<Date.now() ? '已超时' : ({UNPAID:'未支付',PAID:'已支付',CANCELLED:'已取消'}[row.status] || row.status); }
|
||||
function membership(row) { return row.status !== 'PAID' ? '未开通' : time(row.startTime)>Date.now() ? '待生效' : time(row.endTime)<=Date.now() ? '已到期' : '有效'; }
|
||||
function name(row) { try { return JSON.parse(row.configSnapshot).name; } catch { return '付费会员'; } }
|
||||
async function load() { const id=++request;loading.value=true;try {const res=await getPaidOrders({...query});if(id===request && res.success){rows.value=res.result.records||[];total.value=res.result.total||0;}} finally {if(id===request)loading.value=false;} }
|
||||
function search(){query.pageNumber=1;load();} function reset(){query.keyword='';query.memberId='';query.status='';search();}
|
||||
async function exportRecords() {
|
||||
exporting.value=true;
|
||||
try {
|
||||
const list=[], filters={...query}; let page=1;
|
||||
while(true){const res=await getPaidOrders({...filters,pageNumber:page,pageSize:100});if(!res.success)return;const records=res.result.records||[];if(!records.length)break;list.push(...records);if(list.length>=res.result.total)break;if(list.length>=10000){ElMessage.warning('一次最多导出10000条,请缩小筛选范围');return;}page++;}
|
||||
const quote=value=>{let text=String(value??'');if(/^[=+\-@]/.test(text))text="'"+text;return '"'+text.replace(/"/g,'""')+'"';};
|
||||
const csv=[['会员费订单号','客户','会员ID','会员名称','付费金额','有效月数','支付状态','会员状态','支付时间','生效时间','到期时间','支付方式'],...list.map(r=>[r.sn,r.memberName,r.memberId,name(r),r.amount,r.months,status(r),membership(r),r.payTime,r.startTime,r.endTime,r.paymentMethod])].map(row=>row.map(quote).join(',')).join('\r\n');
|
||||
const url=URL.createObjectURL(new Blob(['\uFEFF'+csv],{type:'text/csv;charset=utf-8;'}));const a=document.createElement('a');a.href=url;a.download='付费记录.csv';a.click();URL.revokeObjectURL(url);
|
||||
} finally {exporting.value=false;}
|
||||
}
|
||||
onMounted(load);
|
||||
</script>
|
||||
<style scoped>.actions { display:flex; gap:12px; margin-bottom:18px; }.el-pagination { margin-top:20px;justify-content:flex-end; }</style>
|
||||
159
manager/src/views/member/paid/settings.vue
Normal file
159
manager/src/views/member/paid/settings.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div class="paid-settings" v-loading="loading">
|
||||
<el-alert v-if="loadError" title="设置加载失败,请点击重试" type="error" :closable="false"><el-button link @click="loadSettings">重新加载</el-button></el-alert>
|
||||
<el-form :model="form" label-width="150px" @submit.prevent>
|
||||
<el-card shadow="never"><template #header>基本信息</template>
|
||||
<el-form-item label="开放购买/续费"><el-switch v-model="form.enabled" /><span class="hint inline">关闭后,已支付会员仍有效至到期</span></el-form-item>
|
||||
<el-form-item label="会员名称" required><el-input v-model="form.name" maxlength="50" style="width:360px" /></el-form-item>
|
||||
<el-form-item label="会员图标"><UploadPicInput v-model="form.icon" /><div class="hint">推荐100 × 100像素,透明PNG</div></el-form-item>
|
||||
<el-form-item label="会员背景"><el-radio-group v-model="form.backgroundType"><el-radio value="COLOR">背景色</el-radio><el-radio value="IMAGE">背景图</el-radio></el-radio-group><el-color-picker v-if="form.backgroundType === 'COLOR'" v-model="form.backgroundColor" class="inline" /></el-form-item>
|
||||
<el-form-item v-if="form.backgroundType === 'IMAGE'" label="背景图"><UploadPicInput v-model="form.backgroundImage" /></el-form-item>
|
||||
<el-form-item label="字体颜色"><el-color-picker v-model="form.fontColor" /></el-form-item>
|
||||
<el-form-item label="开通价格" required><el-input-number v-model="form.price" :min="0.01" :max="1000000" :precision="2" :controls="false" /><span class="inline">元</span></el-form-item>
|
||||
<el-form-item label="会员有效期" required><el-input-number v-model="form.months" :min="1" :max="120" :precision="0" :controls="false" /><span class="inline">个月</span><div class="hint">按自然月计算;未到期续费从原到期时间顺延。</div></el-form-item>
|
||||
<el-form-item label="关联客户权益"><el-select v-model="form.benefitIds" multiple filterable style="width:480px;max-width:100%" placeholder="选择权益"><el-option v-for="b in benefitOptions" :key="b.id" :label="b.benefitName" :value="String(b.id)" /></el-select></el-form-item>
|
||||
</el-card>
|
||||
<el-card shadow="never"><template #header>商家设置</template>
|
||||
<el-form-item label="商家范围"><el-radio-group v-model="form.storeScope"><el-radio value="SELF">自营商家</el-radio><el-radio value="CUSTOM">自定义选择</el-radio></el-radio-group></el-form-item>
|
||||
<el-form-item v-if="form.storeScope === 'CUSTOM'" label="选择商家"><el-button @click="openPicker('stores')">添加商家</el-button><div class="picked-tags"><el-tag v-for="id in form.storeIds" :key="id" closable @close="remove('storeIds', id)">{{ stores[id]?.storeName || id }}</el-tag></div></el-form-item>
|
||||
</el-card>
|
||||
<el-card shadow="never"><template #header>商品折扣</template>
|
||||
<el-form-item label="商品会员折扣"><el-radio-group v-model="form.discountMode"><el-radio value="ALL">所有适用商品统一折扣</el-radio><el-radio value="CUSTOM">自定义商品折扣</el-radio></el-radio-group></el-form-item>
|
||||
<el-form-item v-if="form.discountMode === 'ALL'" label="统一折扣"><el-input-number v-model="form.discount" :min="0.01" :max="10" :precision="2" :controls="false" /><span class="inline">折</span></el-form-item>
|
||||
<el-form-item v-else label="折扣商品"><div class="full"><el-button @click="openPicker('discount')">添加商品</el-button><el-table :data="filteredDiscountRules" border class="mt_10"><el-table-column label="商品" min-width="230"><template #default="{row}">{{ goods[row.skuId]?.goodsName || row.skuId }}</template></el-table-column><el-table-column label="SKU编码" width="180"><template #default="{row}">{{ goods[row.skuId]?.sn || row.skuId }}</template></el-table-column><el-table-column label="规格" min-width="160" show-overflow-tooltip><template #default="{row}">{{ goods[row.skuId]?.simpleSpecs?.trim() || '—' }}</template></el-table-column><el-table-column label="分类" min-width="180" show-overflow-tooltip><template #default="{row}">{{ goods[row.skuId]?.categoryName || '—' }}</template></el-table-column><el-table-column label="品牌" min-width="120" show-overflow-tooltip><template #default="{row}">{{ goods[row.skuId]?.brandName || '—' }}</template></el-table-column><el-table-column label="售价" width="120" align="right"><template #default="{row}">{{ goods[row.skuId]?.price == null ? '—' : '¥' + Number(goods[row.skuId].price).toFixed(2) }}</template></el-table-column><el-table-column label="折扣" width="190"><template #default="{row}"><el-input-number v-model="row.discount" :min="0.01" :max="10" :precision="2" style="width:140px" :controls="false" /> 折</template></el-table-column><el-table-column label="操作" width="80"><template #default="{row}"><el-button link type="danger" @click="form.goodsRules = form.goodsRules.filter(r => r.skuId !== row.skuId)">移除</el-button></template></el-table-column></el-table></div></el-form-item>
|
||||
</el-card>
|
||||
<el-card shadow="never"><template #header>会员中心推荐商品</template>
|
||||
<el-button @click="openPicker('recommended')">添加商品</el-button>
|
||||
<el-table :data="form.recommendedSkuIds.map(id => goods[id] || {id})" border class="mt_10"><el-table-column prop="sn" label="SKU编码" width="180" /><el-table-column label="商品名称" min-width="240"><template #default="{row}">{{ row.goodsName || row.id }}</template></el-table-column><el-table-column label="规格" min-width="160" show-overflow-tooltip><template #default="{row}">{{ row.simpleSpecs?.trim() || '—' }}</template></el-table-column><el-table-column label="分类" min-width="180" show-overflow-tooltip><template #default="{row}">{{ row.categoryName || '—' }}</template></el-table-column><el-table-column label="品牌" min-width="120" show-overflow-tooltip><template #default="{row}">{{ row.brandName || '—' }}</template></el-table-column><el-table-column label="售价" width="120" align="right"><template #default="{row}">{{ row.price == null ? '—' : '¥' + Number(row.price).toFixed(2) }}</template></el-table-column><el-table-column label="操作" width="90"><template #default="{row}"><el-button link type="danger" @click="remove('recommendedSkuIds', String(row.id))">移除</el-button></template></el-table-column></el-table>
|
||||
</el-card>
|
||||
<el-card shadow="never">
|
||||
<template #header>展示设置</template>
|
||||
<div class="entry-settings-grid">
|
||||
<section class="entry-settings-block">
|
||||
<h4>未开通会员入口</h4>
|
||||
<el-form-item label="入口文案" required><el-input v-model="form.unopenedText" maxlength="50" show-word-limit class="entry-text-input" /></el-form-item>
|
||||
<el-form-item label="字体颜色" required><el-color-picker v-model="form.unopenedFontColor" /><span class="entry-color-value">{{ form.unopenedFontColor }}</span></el-form-item>
|
||||
<el-form-item label="背景图"><UploadPicInput v-model="form.unopenedBackground" /><div class="hint">用于个人中心未开通及已到期入口,推荐 750 × 160 像素,支持 PNG/JPG;不设置时使用默认渐变背景。</div></el-form-item>
|
||||
<el-form-item label="效果预览"><div class="entry-preview" :style="{color: form.unopenedFontColor}"><img v-if="form.unopenedBackground" :src="form.unopenedBackground" alt="未开通入口背景" /><div class="entry-preview-content"><div><strong>{{ form.unopenedText || '开通付费会员,享专属权益' }}</strong><small>开通享商品优惠与专属权益</small></div><span>›</span></div></div></el-form-item>
|
||||
</section>
|
||||
<section class="entry-settings-block">
|
||||
<h4>已开通会员入口</h4>
|
||||
<el-form-item label="入口文案" required><el-input v-model="form.openedText" maxlength="50" show-word-limit class="entry-text-input" /></el-form-item>
|
||||
<el-form-item label="字体颜色" required><el-color-picker v-model="form.openedFontColor" /><span class="entry-color-value">{{ form.openedFontColor }}</span></el-form-item>
|
||||
<el-form-item label="背景图"><UploadPicInput v-model="form.openedBackground" /><div class="hint">用于个人中心有效会员入口,推荐 750 × 160 像素,支持 PNG/JPG;不设置时使用默认渐变背景。</div></el-form-item>
|
||||
<el-form-item label="效果预览"><div class="entry-preview" :style="{color: form.openedFontColor}"><img v-if="form.openedBackground" :src="form.openedBackground" alt="已开通入口背景" /><div class="entry-preview-content"><div><strong>{{ form.openedText || '我的付费会员' }}</strong><small>有效期至 2027-11-05(示例)</small></div><span>›</span></div></div></el-form-item>
|
||||
</section>
|
||||
</div>
|
||||
<el-form-item label="会员价格标签"><UploadPicInput v-model="form.priceLabel" /><div class="hint">展示在商品列表、商品详情的会员价前,推荐 48 × 48 像素、透明 PNG;不设置时只显示会员价文字。</div></el-form-item>
|
||||
<el-form-item label="标签预览"><div class="price-label-preview"><img v-if="form.priceLabel" :src="form.priceLabel" alt="会员价格标签" /><span>会员价 ¥88.00</span></div></el-form-item>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="document-card">
|
||||
<template #header><div class="document-heading"><div><span>购买说明</span><div class="document-hint">展示在移动端付费会员页面,可填写权益介绍和使用说明。</div></div><el-button @click="fullscreenEditor(descriptionEditor)">全屏编辑</el-button></div></template>
|
||||
<el-form-item label-width="0" class="document-field"><RichEditor ref="descriptionEditor" v-model="form.description" image-picker height="520px" class="full" /></el-form-item>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="document-card">
|
||||
<template #header><div class="document-heading"><div><span>会员协议</span><div class="document-hint">用户购买前阅读并同意的协议,开启购买前请完善内容。</div></div><el-button @click="fullscreenEditor(agreementEditor)">全屏编辑</el-button></div></template>
|
||||
<el-form-item label-width="0" class="document-field"><RichEditor ref="agreementEditor" v-model="form.agreement" image-picker height="600px" class="full" /></el-form-item>
|
||||
</el-card>
|
||||
<div class="save-bar"><el-button type="primary" :loading="saving" :disabled="loading || !loaded" @click="save">保存设置</el-button></div>
|
||||
</el-form>
|
||||
<StoreSelector ref="picker" @selected="selected" />
|
||||
<GoodsSelector ref="goodsPicker" :goods-loader="discountPicking ? getPaidDiscountSkus : undefined" :query-params="discountQuery" @selectedGoodsData="selected" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted, watch, computed } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import UploadPicInput from '@/components/lili/upload-pic-input.vue';
|
||||
import RichEditor from '@/components/editor/index.vue';
|
||||
import StoreSelector from '@/components/store-selector/index.vue';
|
||||
import GoodsSelector from '@/components/lili-dialog/index.vue';
|
||||
import { getPaidConfig, savePaidConfig, getPaidResources, getPaidDiscountSkus } from '@/api/paid-member';
|
||||
import { getMemberBenefitByPage } from '@/api/member';
|
||||
defineOptions({name:'paidMemberSettings'});
|
||||
const form = reactive({ goodsRules: [], storeIds: [], recommendedSkuIds: [], benefitIds: [] });
|
||||
const loading = ref(false), loaded = ref(false), loadError = ref(false), saving = ref(false), picker = ref(), benefitOptions = ref([]), stores = reactive({}), goods = reactive({});
|
||||
const descriptionEditor = ref(), agreementEditor = ref(), goodsPicker = ref(), discountPicking = ref(false);
|
||||
const discountQuery = computed(() => discountPicking.value ? { storeScope: form.storeScope, storeIds: form.storeIds.join(',') } : {});
|
||||
const filteredDiscountRules = computed(() => form.goodsRules.filter(rule => storeAllowed(goods[rule.skuId])));
|
||||
function fullscreenEditor(editor) { editor?.getEditor()?.execCommand('mceFullScreen'); }
|
||||
let target = '';
|
||||
function remember(rows, type) { rows.forEach(row => { const cache = type === 'STORE' ? stores : goods; cache[String(row.id)] = { ...cache[String(row.id)], ...row }; }); }
|
||||
function remove(field, id) { form[field] = form[field].filter(value => value !== id); }
|
||||
async function openPicker(value) {
|
||||
target = value;
|
||||
if (value === 'stores') {
|
||||
picker.value.open(form.storeIds.map(id => ({ ...stores[id], id: String(id) })));
|
||||
return;
|
||||
}
|
||||
discountPicking.value = value === 'discount';
|
||||
if (discountPicking.value) {
|
||||
if (form.storeScope === 'CUSTOM' && !form.storeIds.length) { ElMessage.warning('请先选择参与商家'); return; }
|
||||
try { await reconcileDiscounts(); } catch { ElMessage.error('商品商家信息加载失败,请重试'); return; }
|
||||
}
|
||||
const ids = value === 'discount' ? form.goodsRules.map(rule => rule.skuId) : form.recommendedSkuIds;
|
||||
goodsPicker.value.goodsData = ids.map(id => ({ ...goods[id], id: String(id) }));
|
||||
goodsPicker.value.open('goods');
|
||||
}
|
||||
async function selected(rows) {
|
||||
remember(rows, target === 'stores' ? 'STORE' : 'SKU');
|
||||
if (target === 'stores') { form.storeIds = rows.map(row => String(row.id)); return; }
|
||||
if (target === 'discount') {
|
||||
try { await hydrate('SKU', rows.map(row => String(row.id))); } catch { ElMessage.error('商品商家信息加载失败,请重试'); return; }
|
||||
const validRows = rows.filter(row => storeAllowed(goods[String(row.id)]));
|
||||
if (validRows.length !== rows.length) ElMessage.warning('已排除不属于参与商家的商品');
|
||||
const discounts = new Map(form.goodsRules.map(rule => [String(rule.skuId), rule.discount]));
|
||||
form.goodsRules = validRows.map(row => ({ skuId: String(row.id), discount: discounts.get(String(row.id)) ?? form.discount ?? 9.5 }));
|
||||
} else { form.recommendedSkuIds = rows.map(row => String(row.id)); }
|
||||
const missing = rows.filter(row => !Object.hasOwn(row, 'categoryName') || !Object.hasOwn(row, 'brandName'));
|
||||
if (missing.length) { try { await hydrate('SKU', missing.map(row => String(row.id))); } catch { ElMessage.warning('商品分类和品牌加载失败,可刷新页面重试'); } }
|
||||
}
|
||||
async function save() { saving.value = true; try { await reconcileDiscounts(); const res = await savePaidConfig({...form}); if (res.success) ElMessage.success('付费会员设置已保存'); } finally { saving.value = false; } }
|
||||
function storeAllowed(sku) {
|
||||
return !!sku && (form.storeScope === 'SELF' ? sku.storeSelfOperated === true : form.storeIds.map(String).includes(String(sku.storeId)));
|
||||
}
|
||||
async function reconcileDiscounts() {
|
||||
const missing = form.goodsRules.filter(rule => !Object.hasOwn(goods[rule.skuId] || {}, 'storeSelfOperated'));
|
||||
if (missing.length) await hydrate('SKU', missing.map(rule => rule.skuId));
|
||||
const remaining = form.goodsRules.filter(rule => storeAllowed(goods[rule.skuId]));
|
||||
if (remaining.length !== form.goodsRules.length) { form.goodsRules = remaining; ElMessage.warning('已移除不属于当前参与商家的折扣商品'); }
|
||||
}
|
||||
watch(() => [form.storeScope, ...form.storeIds], () => {
|
||||
if (loading.value || !loaded.value) return;
|
||||
reconcileDiscounts().catch(() => ElMessage.error('商品商家信息加载失败,请重试'));
|
||||
}, { flush: 'sync' });
|
||||
async function hydrate(type, ids) {
|
||||
for (let i=0; i<ids.length; i+=100) { const res = await getPaidResources(type, ids.slice(i,i+100)); if (!res.success) throw new Error('resources'); remember(res.result, type); }
|
||||
}
|
||||
async function loadSettings() {
|
||||
loading.value = true; loaded.value = false; loadError.value = false;
|
||||
try {
|
||||
const [config, rights] = await Promise.all([getPaidConfig(), getMemberBenefitByPage({pageNumber:1,pageSize:200})]);
|
||||
if (!config.success) { loadError.value = true; return; }
|
||||
Object.assign(form, config.result); loaded.value = true; benefitOptions.value = rights.result?.records || [];
|
||||
const ids = [...new Set([...form.recommendedSkuIds, ...form.goodsRules.map(r => r.skuId)])];
|
||||
await Promise.all([hydrate('SKU',ids), hydrate('STORE',form.storeIds)]);
|
||||
} catch { loadError.value = true; } finally { loading.value = false; }
|
||||
}
|
||||
onMounted(loadSettings);
|
||||
</script>
|
||||
<style scoped>
|
||||
.entry-settings-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; margin-bottom: 24px; }
|
||||
.entry-settings-block { min-width: 0; border: 1px solid #ebeef5; border-radius: 6px; padding: 20px 16px 0; }
|
||||
.entry-settings-block h4 { margin: 0 0 24px; color: #303133; font-size: 14px; }
|
||||
.entry-text-input { width: 480px; max-width: 100%; }
|
||||
.entry-color-value { margin-left: 12px; color: #909399; font-size: 12px; }
|
||||
.entry-preview { position: relative; overflow: hidden; width: 375px; max-width: 100%; min-height: 80px; border-radius: 10px; background: linear-gradient(110deg, #423c36, #756149); }
|
||||
.entry-preview > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
|
||||
.entry-preview-content { position: relative; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px; min-height: 80px; box-sizing: border-box; line-height: 1.5; }
|
||||
.entry-preview-content strong { font-size: 14px; }.entry-preview-content small { display: block; margin-top: 4px; opacity: .75; font-size: 11px; }.entry-preview-content > span { font-size: 24px; }
|
||||
.price-label-preview { display: flex; align-items: center; gap: 6px; color: #a77c3e; font-size: 13px; }.price-label-preview img { width: 24px; height: 24px; object-fit: contain; }
|
||||
@media (max-width: 1400px) { .entry-settings-grid { grid-template-columns: minmax(0, 1fr); } }
|
||||
.document-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; }
|
||||
.document-heading > div { min-width: 0; }
|
||||
.document-hint { margin-top: 8px; color: #909399; font-size: 12px; font-weight: normal; line-height: 20px; }
|
||||
.document-field { margin-bottom: 0; }
|
||||
.document-field :deep(.el-form-item__content) { min-width: 0; }
|
||||
.document-card :deep(.tox-tinymce) { border-color: #dcdfe6; }
|
||||
.paid-settings { padding-bottom: 70px; }.el-card { margin-top: 16px; }.hint { width: 100%; font-size: 12px; line-height: 20px; margin-top: 6px; color: #909399; }.inline { margin-left: 12px; }.hint.inline { width: auto; margin-top: 0; }.full { width: 100%; min-width: 0; }.picked-tags { width: 100%; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }.save-bar { position: sticky; bottom: 0; padding: 16px; background: #fff; border-top: 1px solid #ebeef5; display: flex; justify-content: flex-end; z-index: 10; }
|
||||
</style>
|
||||
@@ -80,7 +80,7 @@
|
||||
<div class="ctrl-row">
|
||||
<div class="ctrl-label">选择店铺</div>
|
||||
<div class="ctrl-body">
|
||||
<el-button plain type="primary" @click="shopDialogVisible = true">选择店铺</el-button>
|
||||
<el-button plain type="primary" @click="openShops">选择店铺</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shop-chips" v-if="firstItem.shopList && firstItem.shopList.length">
|
||||
@@ -176,38 +176,7 @@
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="shopDialogVisible"
|
||||
title="选择店铺"
|
||||
width="720px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@open="loadShops"
|
||||
>
|
||||
<el-table
|
||||
:data="shopOptions"
|
||||
v-loading="shopLoading"
|
||||
max-height="400"
|
||||
@selection-change="onShopSelection"
|
||||
>
|
||||
<el-table-column type="selection" width="50" />
|
||||
<el-table-column label="店铺" min-width="220">
|
||||
<template #default="{ row }">
|
||||
<div class="shop-cell">
|
||||
<img :src="row.storeLogo" class="shop-cell-logo" alt="" />
|
||||
<span>{{ row.storeName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" width="100">
|
||||
<template #default="{ row }">{{ row.selfOperated ? "自营" : "旗舰店" }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<el-button @click="shopDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmShops">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<StoreSelector ref="storeSelector" :query-params="{ storeDisable: 'OPEN' }" @selected="confirmShops" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -217,7 +186,7 @@ import ColorField from "../controls/ColorField.vue";
|
||||
import SliderField from "../controls/SliderField.vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
import ways from "@/components/lili-dialog/wap.js";
|
||||
import { getShopListData } from "@/api/shops";
|
||||
import StoreSelector from "@/components/store-selector/index.vue";
|
||||
|
||||
const DEFAULTS = {
|
||||
bgColor1: "#FFFFFF",
|
||||
@@ -247,7 +216,7 @@ const DEFAULT_ITEM = {
|
||||
export default {
|
||||
name: "PanelShop",
|
||||
hideDecorateTitle: true,
|
||||
components: { GradientField, ColorField, SliderField, ossManage },
|
||||
components: { GradientField, ColorField, SliderField, ossManage, StoreSelector },
|
||||
props: {
|
||||
res: { type: Object, required: true },
|
||||
},
|
||||
@@ -260,10 +229,6 @@ export default {
|
||||
{ key: "style", title: "样式" },
|
||||
],
|
||||
picModelFlag: false,
|
||||
shopDialogVisible: false,
|
||||
shopLoading: false,
|
||||
shopOptions: [],
|
||||
selectedShops: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -284,30 +249,15 @@ export default {
|
||||
if (!Array.isArray(item.shopList)) item.shopList = [];
|
||||
},
|
||||
methods: {
|
||||
// 加载店铺列表供选择
|
||||
loadShops() {
|
||||
this.shopLoading = true;
|
||||
this.selectedShops = [];
|
||||
getShopListData({ pageNumber: 1, pageSize: 100, status: "OPEN" })
|
||||
.then((res) => {
|
||||
this.shopOptions = (res && res.result && res.result.records) || [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.shopLoading = false;
|
||||
});
|
||||
},
|
||||
onShopSelection(rows) {
|
||||
this.selectedShops = rows;
|
||||
},
|
||||
confirmShops() {
|
||||
this.firstItem.shopList = this.selectedShops.map((s) => ({
|
||||
openShops() { this.$refs.storeSelector.open(this.firstItem.shopList); },
|
||||
confirmShops(rows) {
|
||||
this.firstItem.shopList = rows.map((s) => ({
|
||||
id: s.id,
|
||||
storeName: s.storeName,
|
||||
storeLogo: s.storeLogo,
|
||||
selfOperated: s.selfOperated,
|
||||
storeBanner: s.storeBanner || "",
|
||||
}));
|
||||
this.shopDialogVisible = false;
|
||||
},
|
||||
removeShop(index) {
|
||||
this.firstItem.shopList.splice(index, 1);
|
||||
|
||||
Reference in New Issue
Block a user