refactor: 重构组件逻辑与样式优化

- 在 App.vue 中重构站点信息的获取与应用逻辑,提升代码可读性与维护性。
- 更新 Search.vue 组件,优化搜索框的样式与结构,增强用户体验。
- 在 element.scss 中注释掉不再使用的样式,并新增确认收货按钮的样式。
- 改进多个页面的布局与样式,确保响应式设计的一致性与美观性。
- 更新订单相关组件的按钮样式,提升视觉一致性与可用性。
This commit is contained in:
田香琪
2026-06-30 21:09:28 +08:00
parent ec948b5496
commit 28a857249a
121 changed files with 5122 additions and 2182 deletions

View File

@@ -159,7 +159,7 @@ export default {
background-image: url("../assets/background.png");
background-repeat: no-repeat;
background-position: center bottom;
background-size: 100% auto;
background-size: cover;
display: flex;
align-items: center;
justify-content: center;

View File

@@ -69,6 +69,8 @@
<el-form-item label="支持电子面单">
<el-switch
v-model="form.standBy"
active-value="true"
inactive-value="false"
inline-prompt
active-text=""
inactive-text=""
@@ -106,10 +108,12 @@ import {
const defaultForm = () => ({
name: "",
code: "",
standBy: false,
standBy: "false",
disabled: "CLOSE",
});
const normalizeStandBy = (value) => (value === true || value === "true" ? "true" : "false");
export default {
name: "logistics",
data() {
@@ -159,7 +163,7 @@ export default {
const data = res.result.records;
data.forEach((e) => {
e.switch = e.disabled === "OPEN";
e.standBy = e.standBy == "null" || !e.standBy ? false : !!e.standBy;
e.standBy = normalizeStandBy(e.standBy);
});
this.data = data;
this.total = res.result.total;
@@ -167,11 +171,12 @@ export default {
});
},
changeSwitch(row) {
this.form.name = row.name;
this.form.code = row.code;
this.form.standBy = row.standBy;
this.form.disabled = row.disabled === "CLOSE" ? "OPEN" : "CLOSE";
updateLogistics(row.id, this.form).then((res) => {
updateLogistics(row.id, {
name: row.name,
code: row.code,
standBy: normalizeStandBy(row.standBy),
disabled: row.disabled === "CLOSE" ? "OPEN" : "CLOSE",
}).then((res) => {
if (res.success) {
this.$Message.success("操作成功");
this.getDataList();
@@ -186,7 +191,10 @@ export default {
this.submitLoading = true;
if (this.modalTitle === "添加") {
const payload = { ...this.form };
const payload = {
...this.form,
standBy: normalizeStandBy(this.form.standBy),
};
delete payload.id;
addLogistics(payload).then((res) => {
this.submitLoading = false;
@@ -197,7 +205,10 @@ export default {
}
});
} else {
updateLogistics(this.id, this.form).then((res) => {
updateLogistics(this.id, {
...this.form,
standBy: normalizeStandBy(this.form.standBy),
}).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("操作成功");
@@ -222,7 +233,7 @@ export default {
this.form = {
name: v.name,
code: v.code,
standBy: v.standBy == "null" || !v.standBy ? false : !!v.standBy,
standBy: normalizeStandBy(v.standBy),
disabled: v.disabled,
};
this.modalVisible = true;

View File

@@ -4,15 +4,15 @@
<div class="operation padding-row">
<el-button type="primary" @click="openAdd">添加权益设置</el-button>
</div>
<el-table
v-loading="loading"
stripe
:data="data"
empty-text="暂无数据"
class="mt_10 benefit-list-table benefit-list-table--no-vertical-borders"
style="width: 100%"
>
<el-table-column label="权益类型" width="132">
<div class="benefit-list-panel">
<el-table
v-loading="loading"
stripe
:data="data"
empty-text="暂无数据"
class="mt_10 benefit-list-table benefit-list-table--no-vertical-borders"
>
<el-table-column label="权益类型" min-width="132">
<template #default="{ row }">
<span
v-if="row"
@@ -22,7 +22,7 @@
{{ benefitTypeLabel(row.benefitType) }}</span>
</template>
</el-table-column>
<el-table-column label="权益名称" width="188">
<el-table-column label="权益名称" min-width="188">
<template #default="{ row }">
<span
v-if="row"
@@ -32,7 +32,7 @@
{{ row.benefitName || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="权益LOGO" width="116" align="center">
<el-table-column label="权益LOGO" min-width="116" align="center">
<template #default="{ row }">
<span v-if="row && !row.benefitLogo">-</span>
<div v-else-if="row" class="benefit-logo-thumb benefit-logo-thumb--table">
@@ -40,7 +40,7 @@
</div>
</template>
</el-table-column>
<el-table-column label="状态" width="118" align="center">
<el-table-column label="状态" min-width="118" align="center">
<template #default="{ row }">
<el-switch
v-if="row"
@@ -53,7 +53,7 @@
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="140">
<el-table-column label="操作" align="center" min-width="140">
<template #default="{ row }">
<div v-if="row" class="ops" style="display: flex; justify-content: center">
<a class="link-text" @click="openEdit(row)">编辑</a>
@@ -63,6 +63,7 @@
</template>
</el-table-column>
</el-table>
</div>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
@@ -81,18 +82,24 @@
v-model="couponPickerVisible"
title="选择优惠券"
width="80%"
:z-index="3000"
append-to-body
modal-class="member-benefit-coupon-picker-modal"
:close-on-click-modal="false"
destroy-on-close
@close="handleCouponPickerClose"
>
<couponTemplate
v-if="couponPickerVisible"
ref="couponPicker"
manualConfirm
:selectedList="couponPickerSelectedList"
getType="ACTIVITY"
promotionStatus="START"
@selected="onCouponTemplateSelected"
/>
<template #footer>
<el-button @click="couponPickerVisible = false">取消</el-button>
<el-button type="primary" @click="confirmCouponPicker">确定</el-button>
</template>
</el-dialog>
<el-drawer
@@ -140,21 +147,22 @@
</el-form-item>
<el-form-item
v-show="formAdd.benefitType === 'COUPON_PACKAGE'"
class="benefit-coupon-form-item"
label="赠送优惠券"
prop="couponPackageRows"
:required="formAdd.benefitType === 'COUPON_PACKAGE'"
>
<div>
<div class="benefit-coupon-field">
<el-button plain class="mb_10" @click="openCouponPicker('add')">添加优惠券</el-button>
<el-table
border
size="small"
class="benefit-coupon-table"
:data="formAdd.couponPackageRows"
empty-text="请添加优惠券"
style="width: 100%"
>
<el-table-column prop="couponName" label="优惠券名称" min-width="100" show-overflow-tooltip />
<el-table-column label="有效期" min-width="100">
<el-table-column prop="couponName" label="优惠券名称" min-width="180" show-overflow-tooltip />
<el-table-column label="有效期" min-width="280">
<template #default="{ row }">
<span v-if="row" v-html="row.validRange || '-'" />
</template>
@@ -259,21 +267,22 @@
</el-form-item>
<el-form-item
v-show="formEdit.benefitType === 'COUPON_PACKAGE'"
class="benefit-coupon-form-item"
label="赠送优惠券"
prop="couponPackageRows"
:required="formEdit.benefitType === 'COUPON_PACKAGE'"
>
<div>
<div class="benefit-coupon-field">
<el-button plain class="mb_10" @click="openCouponPicker('edit')">添加优惠券</el-button>
<el-table
border
size="small"
class="benefit-coupon-table"
:data="formEdit.couponPackageRows"
empty-text="请添加优惠券"
style="width: 100%"
>
<el-table-column prop="couponName" label="优惠券名称" min-width="100" show-overflow-tooltip />
<el-table-column label="有效期" min-width="100">
<el-table-column prop="couponName" label="优惠券名称" min-width="180" show-overflow-tooltip />
<el-table-column label="有效期" min-width="280">
<template #default="{ row }">
<span v-if="row" v-html="row.validRange || '-'" />
</template>
@@ -405,14 +414,20 @@ export default {
const opt = this.benefitTypeOptions.find((o) => o.value === v);
return opt ? opt.description : v;
},
touchFormField(refName, field) {
this.$nextTick(() => {
const ref = this.$refs[refName];
if (ref && typeof ref.validateField === "function") {
// validateField rejects on failure; catch to avoid dev-server [object Object] overlay
ref.validateField(field, () => {});
}
});
},
onCouponQuantityChange(which, index, val) {
const formKey = which === "add" ? "formAdd" : "formEdit";
const refName = which === "add" ? "addForm" : "editForm";
this[formKey].couponPackageRows[index].quantity = val;
this.$nextTick(() => {
const ref = this.$refs[refName];
if (ref) ref.validateField("couponPackageRows");
});
this.touchFormField(refName, "couponPackageRows");
},
buildFormRules(which) {
const formKey = which === "add" ? "formAdd" : "formEdit";
@@ -524,14 +539,12 @@ export default {
if (val !== COUPON_PACKAGE) {
this.formAdd.couponPackageRows = [];
}
this.$nextTick(() => {
if (this.$refs.addForm && val !== GIFT_POINT) {
this.$refs.addForm.validateField("giftPoint");
}
if (this.$refs.addForm && val !== COUPON_PACKAGE) {
this.$refs.addForm.validateField("couponPackageRows");
}
});
if (this.$refs.addForm && val !== GIFT_POINT) {
this.touchFormField("addForm", "giftPoint");
}
if (this.$refs.addForm && val !== COUPON_PACKAGE) {
this.touchFormField("addForm", "couponPackageRows");
}
},
onEditBenefitTypeChange(val) {
if (val !== GIFT_POINT) {
@@ -540,14 +553,12 @@ export default {
if (val !== COUPON_PACKAGE) {
this.formEdit.couponPackageRows = [];
}
this.$nextTick(() => {
if (this.$refs.editForm && val !== GIFT_POINT) {
this.$refs.editForm.validateField("giftPoint");
}
if (this.$refs.editForm && val !== COUPON_PACKAGE) {
this.$refs.editForm.validateField("couponPackageRows");
}
});
if (this.$refs.editForm && val !== GIFT_POINT) {
this.touchFormField("editForm", "giftPoint");
}
if (this.$refs.editForm && val !== COUPON_PACKAGE) {
this.touchFormField("editForm", "couponPackageRows");
}
},
parseCouponsFromConfig(str) {
if (!str) return [];
@@ -627,6 +638,15 @@ export default {
this.couponPickerWhich = which;
this.couponPickerVisible = true;
},
confirmCouponPicker() {
const list = this.$refs.couponPicker?.getSelection?.() || [];
if (!list.length) {
this.$Message.warning("请至少选择一张优惠券");
return;
}
this.onCouponTemplateSelected(list);
this.couponPickerVisible = false;
},
onCouponTemplateSelected(selectedRows) {
const which = this.couponPickerWhich;
const form = which === "add" ? this.formAdd : this.formEdit;
@@ -638,19 +658,13 @@ export default {
return this.buildCouponDisplayRow(row, qty);
});
form.couponPackageRows = rows;
this.$nextTick(() => {
const ref = this.$refs[refName];
if (ref) ref.validateField("couponPackageRows");
});
this.touchFormField(refName, "couponPackageRows");
},
removeCouponRow(which, index) {
const form = which === "add" ? this.formAdd : this.formEdit;
const refName = which === "add" ? "addForm" : "editForm";
form.couponPackageRows.splice(index, 1);
this.$nextTick(() => {
const ref = this.$refs[refName];
if (ref) ref.validateField("couponPackageRows");
});
this.touchFormField(refName, "couponPackageRows");
},
init() {
this.loadBenefitTypes();
@@ -902,6 +916,13 @@ export default {
margin: 0 auto;
}
.member-benefit .benefit-list-panel {
width: 50%;
max-width: 100%;
}
.member-benefit .benefit-list-panel .benefit-list-table {
width: 100%;
}
.member-benefit .benefit-list-table table {
table-layout: fixed;
}
@@ -924,6 +945,25 @@ export default {
.member-benefit .benefit-list-table--no-vertical-borders .el-table td {
border-right: none !important;
}
.member-benefit-coupon-picker-modal {
z-index: 2700 !important;
}
.benefit-form-drawer .benefit-coupon-form-item.el-form-item .el-form-item__content {
flex: 1;
min-width: 0;
max-width: none;
}
.benefit-form-drawer .benefit-coupon-field {
width: 100%;
}
.benefit-form-drawer .benefit-coupon-table {
width: 100%;
}
.benefit-form-drawer .benefit-coupon-table table {
width: 100% !important;
}
</style>
<style lang="scss" scoped>

View File

@@ -709,10 +709,8 @@ export default {
},
mounted() {
this.getData();
if (!this.selectedMember) {
this.loadMemberGroupList();
this.loadMemberGradeList();
}
this.loadMemberGroupList();
this.loadMemberGradeList();
},
};
</script>

View File

@@ -308,7 +308,7 @@ export const modelData = [{
bgImg: {
img: require('@/assets/nav/decorate.png'),
url: '',
size: "1300*586"
size: "1300*596"
},
classification: [{
img: require('@/assets/nav/decorate2.jpeg'),

View File

@@ -27,11 +27,12 @@
<div class="search-con">
<img :src="require('@/assets/logo.png')" class="logo" alt="" />
<div class="search">
<el-input size="large" placeholder="输入你想查找的商品">
<template #append>
<el-button>搜索</el-button>
</template>
</el-input>
<el-input
size="large"
class="search-input"
placeholder="输入你想查找的商品"
/>
<div class="search-btn">搜索</div>
</div>
</div>
<div class="nav-con" v-if="$route.query.pageType !== 'SPECIAL'">
@@ -73,17 +74,18 @@
:close-on-click-modal="false"
append-to-body
destroy-on-close
@close="handleCancelTopAdvertModal"
>
<div class="modal-top-advert">
<div class="modal-top-advert" v-if="draftTopAdvert">
<div>
<img class="show-image" width="600" height="40" :src="topAdvert.img" alt />
<img class="show-image" width="600" height="40" :src="draftTopAdvert.img" alt />
</div>
<div class="tips">建议尺寸<span>{{ topAdvert.size }}</span></div>
<div class="tips">建议尺寸<span>{{ draftTopAdvert.size }}</span></div>
<div>
图片链接<el-input
class="outsideUrl"
v-model="topAdvert.url"
:disabled="!!topAdvert.type && topAdvert.type !== 'link'"
v-model="draftTopAdvert.url"
:disabled="!!draftTopAdvert.type && draftTopAdvert.type !== 'link'"
placeholder="https://"
/>
<el-button size="small" type="primary" @click="handleSelectLink">选择链接</el-button>
@@ -91,8 +93,12 @@
<div>
选择图片<el-button size="small" type="primary" @click="handleSelectImg">选择图片</el-button>
</div>
<div>选择背景色<el-color-picker v-model="topAdvert.bgColor" /></div>
<div>选择背景色<el-color-picker v-model="draftTopAdvert.bgColor" /></div>
</div>
<template #footer>
<el-button @click="handleCancelTopAdvertModal">取消</el-button>
<el-button type="primary" @click="handleConfirmTopAdvertModal">确定</el-button>
</template>
</el-dialog>
<el-dialog
v-model="showModalNav"
@@ -101,6 +107,7 @@
:close-on-click-modal="false"
append-to-body
destroy-on-close
@close="handleNavDialogClose"
>
<div class="modal-tab-bar">
<el-button type="primary" size="small" @click="handleAddNav">添加分类</el-button>
@@ -129,6 +136,10 @@
</tbody>
</table>
</div>
<template #footer>
<el-button @click="handleCancelNavModal">取消</el-button>
<el-button type="primary" @click="handleConfirmNavModal">确定</el-button>
</template>
</el-dialog>
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
@@ -136,6 +147,7 @@
@callback="callbackSelected"
:is-component="true"
:initialize="picModelFlag"
:max-select="1"
ref="ossManage"
/>
</el-dialog>
@@ -160,6 +172,8 @@ export default {
picModelFlag: false,
showModal: false,
showModalNav: false,
navListBackup: null,
draftTopAdvert: null,
selectedNav: null,
promotionTags: ["买2免1", "领200神券", "199减100", "母婴5折抢", "充100送20"],
topAdvert: {
@@ -204,31 +218,54 @@ export default {
if (this.showModalNav) {
this.selectedNav.url = this.$filters.formatLinkType(val);
this.selectedNav.type = val.___type === "other" && val.url === "" ? "link" : "other";
} else {
this.topAdvert.url = this.$filters.formatLinkType(val);
this.topAdvert.type = val.___type === "other" && val.url === "" ? "link" : "other";
this.navList.list[this.currentIndex].title = val.title;
} else if (this.showModal && this.draftTopAdvert) {
this.draftTopAdvert.url = this.$filters.formatLinkType(val);
this.draftTopAdvert.type = val.___type === "other" && val.url === "" ? "link" : "other";
}
this.navList.list[this.currentIndex].title = val.title;
},
handleDelNav(index) {
this.navList.list.splice(index, 1);
},
handleAddNav() {
this.navList.list.push({ name: "", url: "", title: "" });
this.$nextTick(() => {
this.selectedNav.title = val.title;
});
},
handleMoveEnd({ newIndex, oldIndex }) {
console.log("index", newIndex, oldIndex);
},
handleModel(type) {
if (type == "topAdvert") {
this.draftTopAdvert = JSON.parse(JSON.stringify(this.topAdvert));
this.showModal = true;
} else {
this.navListBackup = JSON.parse(JSON.stringify(this.navList));
this.showModalNav = true;
}
},
handleCancelTopAdvertModal() {
this.draftTopAdvert = null;
this.showModal = false;
},
handleConfirmTopAdvertModal() {
if (this.draftTopAdvert) {
Object.assign(this.topAdvert, JSON.parse(JSON.stringify(this.draftTopAdvert)));
}
this.draftTopAdvert = null;
this.showModal = false;
},
handleCancelNavModal() {
this.showModalNav = false;
},
handleConfirmNavModal() {
this.navListBackup = null;
this.showModalNav = false;
},
handleNavDialogClose() {
if (this.navListBackup) {
this.navList = JSON.parse(JSON.stringify(this.navListBackup));
this.navListBackup = null;
}
},
handleSelectImg() {
this.picModelFlag = true;
this.$nextTick(() => {
@@ -239,7 +276,8 @@ export default {
},
callbackSelected(item) {
this.picModelFlag = false;
this.topAdvert.img = item.url;
if (!item?.url || !this.draftTopAdvert) return;
this.draftTopAdvert.img = item.url;
},
handleModelAdd(evt) {
const newIndex = evt.newIndex;
@@ -264,15 +302,18 @@ export default {
@import "./modelList/setup-box.scss";
.model-form {
width: 1500px;
max-width: 100%;
}
.model-content {
width: 1200px;
margin: 0 auto;
background: #fff;
min-height: 1200px;
overflow-x: visible;
}
.model-form-list {
min-height: 500px;
box-sizing: border-box;
}
.top-fixed-advert {
display: flex;
@@ -329,27 +370,48 @@ export default {
height: 50px;
}
.search {
$search-height: 34px;
$search-btn-width: 60px;
$btn-gap: 3px;
position: relative;
width: 460px;
height: $search-height;
margin: 0 auto;
:deep(.el-input--large .el-input__wrapper) {
border: 2px solid $theme_color;
font-size: 12px;
height: 34px;
box-shadow: none;
}
:deep(.el-input-group__append) {
border: 1px solid $theme_color;
border-left: none;
height: 30px;
background-color: $theme_color;
color: #ffffff;
.el-button {
font-size: 14px;
font-weight: 600;
line-height: 1;
color: #fff;
.search-input {
width: 100%;
:deep(.el-input__wrapper) {
border: 2px solid $theme_color;
border-radius: calc($search-height / 2);
box-sizing: border-box;
padding-right: calc($search-btn-width + $btn-gap * 2);
font-size: 12px;
height: $search-height;
box-shadow: none;
}
}
.search-btn {
position: absolute;
top: $btn-gap;
right: $btn-gap;
bottom: $btn-gap;
width: $search-btn-width;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
border-radius: calc(($search-height - $btn-gap * 2) / 2);
background-color: $theme_color;
color: #fff;
font-size: 14px;
font-weight: 600;
line-height: 1;
cursor: default;
user-select: none;
}
}
}
.nav-con {

View File

@@ -2,67 +2,69 @@
<div class="model-item" v-if="element && element.key">
<!-- 轮播图模块包括个人信息快捷导航模块 -->
<template v-if="element.type == 'carousel'">
<model-carousel :data="element"></model-carousel>
<div class="carousel-wrap mb_20 width_1200_auto">
<model-carousel :data="element"></model-carousel>
</div>
</template>
<!-- 轮播图模块100%宽度无个人信息栏 -->
<template v-if="element.type == 'carousel1'">
<model-carousel1 class="mb_20" :data="element"></model-carousel1>
<model-carousel1 class="mb_20 width_1200_auto" :data="element"></model-carousel1>
</template>
<!-- 轮播图模块包括个人信息两个轮播模块 -->
<template v-if="element.type == 'carousel2'">
<model-carousel2 class="mb_20" :data="element"></model-carousel2>
<model-carousel2 class="mb_20 width_1200_auto" :data="element"></model-carousel2>
</template>
<!-- 热门广告 -->
<template v-if="element.type == 'hotAdvert'">
<div class="setup-content">
<img
style="display: block"
:src="element.options.list[0].img"
@click="$router.push(element.options.list[0].url)"
width="1200"
alt=""
/>
<div class="setup-box">
<div>
<el-button
size="small"
@click.stop="handleSelectModel(element.options.list[0])"
>编辑</el-button
>
<div class="hot-advert mb_20 width_1200_auto">
<div class="hot-advert-banner setup-content">
<img
:src="element.options.list[0].img"
@click="$router.push(element.options.list[0].url)"
alt=""
/>
<div class="setup-box">
<div>
<el-button
size="small"
@click.stop="handleSelectModel(element.options.list[0])"
>编辑</el-button
>
</div>
</div>
</div>
</div>
<ul class="advert-list">
<template v-for="(item, index) in element.options.list">
<li
v-if="index !== 0"
@click="$router.push(item.url)"
class="setup-content"
:key="index"
>
<img :src="item.img" width="230" height="190" alt="" />
<div class="setup-box">
<div>
<el-button size="small" @click.stop="handleSelectModel(item)"
>编辑</el-button
>
<ul class="advert-list">
<template v-for="(item, index) in element.options.list">
<li
v-if="index !== 0"
@click="$router.push(item.url)"
class="setup-content"
:key="index"
>
<img :src="item.img" alt="" />
<div class="setup-box">
<div>
<el-button size="small" @click.stop="handleSelectModel(item)"
>编辑</el-button
>
</div>
</div>
</div>
</li>
</template>
</ul>
</li>
</template>
</ul>
</div>
</template>
<!-- 限时秒杀 待完善 -->
<template v-if="element.type == 'seckill'">
<seckill :data="element"></seckill>
<seckill :data="element" class="mb_20 width_1200_auto"></seckill>
</template>
<!-- 限时秒杀 待完善 -->
<template v-if="element.type == 'seckill-only-album'">
<seckill-only-album :data="element"></seckill-only-album>
<seckill-only-album :data="element" class="mb_20 width_1200_auto"></seckill-only-album>
</template>
<!-- 折扣广告 -->
<template v-if="element.type == 'discountAdvert'">
<div class="discountAdvert-wrap">
<div class="discountAdvert-wrap width_1200_auto mb_20">
<div
class="discountAdvert"
:style="{
@@ -107,15 +109,15 @@
</template>
<!-- 好货推荐 -->
<template v-if="element.type == 'recommend'">
<recommend :data="element"></recommend>
<recommend :data="element" class="mb_20 width_1200_auto"></recommend>
</template>
<!-- 新品排行 -->
<template v-if="element.type == 'newGoodsSort'">
<new-goods-sort :data="element"></new-goods-sort>
<new-goods-sort :data="element" class="mb_20 width_1200_auto"></new-goods-sort>
</template>
<!-- 首页广告 -->
<template v-if="element.type == 'firstAdvert'">
<first-page-advert :data="element"></first-page-advert>
<first-page-advert :data="element" class="mb_20 width_1200_auto"></first-page-advert>
</template>
<!-- 商品模块 -->
<template v-if="element.type == 'goodsType'">
@@ -139,10 +141,10 @@
</template>
<!-- 横幅广告 -->
<template v-if="element.type == 'bannerAdvert'">
<div class="horizontal-advert setup-content">
<div class="horizontal-advert setup-content width_1200_auto mb_20">
<img
v-if="element.options.img"
width="1200"
class="banner-ad-img"
:src="element.options.img"
alt=""
/>
@@ -162,7 +164,7 @@
</div>
</template>
<template v-if="element.type == 'notEnough'">
<not-enough :data="element"></not-enough>
<not-enough :data="element" class="mb_20 width_1200_auto"></not-enough>
</template>
<div class="del-btn">
<el-button size="small" type="danger" @click="handleModelDelete">删除</el-button>
@@ -176,13 +178,13 @@
destroy-on-close
>
<div class="modal-top-advert">
<div>
<div class="modal-form-item modal-form-preview">
<!-- 热门广告两种图片尺寸 -->
<img
class="show-image"
width="600"
height="40"
v-if="selected.size && selected.size.indexOf('1200') >= 0"
v-if="selected.size && selected.size.indexOf('1200') >= 0 && selected.img"
:src="selected.img"
alt
/>
@@ -198,7 +200,7 @@
<img
class="show-image"
width="600"
height="300"
height="275"
v-if="selected.size && selected.size.indexOf('1300') >= 0"
:src="selected.img"
alt
@@ -220,26 +222,32 @@
alt
/>
</div>
<div class="tips">
建议尺寸<span>{{ selected.size }}</span>
<div class="tips">建议尺寸{{ selected.size }}</div>
<div class="modal-form-item modal-form-link">
<span class="modal-label">图片链接</span>
<div class="modal-link-field">
<el-input
class="outsideUrl"
v-model="selected.url"
:disabled="!!selected.type && selected.type !== 'link'"
placeholder="https://"
/>
<el-button size="small" type="primary" @click="handleSelectLink"
>选择链接</el-button
>
</div>
</div>
<div>
图片链接<el-input
class="outsideUrl"
v-model="selected.url"
:disabled="!!selected.type && selected.type !== 'link'"
placeholder="https://"
/>
<el-button size="small" type="primary" @click="handleSelectLink"
>选择链接</el-button
>
</div>
<div>
选择图片<el-button size="small" type="primary" @click="handleSelectImg"
<div class="modal-form-item">
<span class="modal-label">选择图片</span>
<el-button size="small" type="primary" @click="handleSelectImg"
>选择图片</el-button
>
</div>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
@@ -249,6 +257,7 @@
@callback="callbackSelected"
:is-component="true"
:initialize="picModelFlag"
:max-select="1"
ref="ossManage"
/>
</el-dialog>
@@ -296,14 +305,19 @@ export default {
data() {
return {
showModal: false, // modal显隐
selected: {}, // 已选数据
selected: {}, // 弹窗编辑草稿
editTarget: null, // 确认后写回的原数据
picModelFlag: false, // 图片选择器
};
},
methods: {
cloneItem(item) {
return JSON.parse(JSON.stringify(item || {}));
},
// 编辑模块
handleSelectModel(item) {
this.selected = item;
this.editTarget = item;
this.selected = this.cloneItem(item);
this.showModal = true;
},
// 删除模块
@@ -341,8 +355,22 @@ export default {
// 回显图片
callbackSelected(val) {
this.picModelFlag = false;
if (!val?.url) return;
this.selected.img = val.url;
},
handleCancelModal() {
this.editTarget = null;
this.selected = {};
this.showModal = false;
},
handleConfirmModal() {
if (this.editTarget) {
Object.assign(this.editTarget, this.cloneItem(this.selected));
}
this.editTarget = null;
this.selected = {};
this.showModal = false;
},
},
};
</script>
@@ -351,12 +379,21 @@ export default {
.model-item {
position: relative;
margin-bottom: 20px;
width: 100%;
box-sizing: border-box;
&:hover {
.del-btn {
display: block;
}
}
}
.carousel-wrap {
width: 100%;
max-width: 1200px;
margin: 0 auto;
box-sizing: border-box;
overflow: hidden;
}
.del-btn {
width: 100px;
height: 100px;
@@ -364,6 +401,7 @@ export default {
position: absolute;
right: -100px;
top: 0;
z-index: 10;
&:hover {
display: block;
}
@@ -371,23 +409,59 @@ export default {
/** 横幅广告 */
.horizontal-advert {
width: 1200px;
height: auto;
max-width: 1200px;
overflow: hidden;
line-height: 0;
.banner-ad-img {
display: block;
width: 100%;
height: auto;
max-height: 166px;
object-fit: cover;
}
.default-con {
height: 100px;
padding-top: 30px;
text-align: center;
background: #ddd;
line-height: normal;
}
}
/** 热门广告 */
.hot-advert {
width: 1200px;
box-sizing: border-box;
overflow: hidden;
.hot-advert-banner {
line-height: 0;
img {
display: block;
width: 100%;
height: auto;
}
}
}
.advert-list {
width: 100%;
margin: 0;
padding: 5px 6px;
list-style: none;
box-sizing: border-box;
background: $theme_color;
height: 200px;
display: flex;
justify-content: space-around;
padding: 3px 10px;
gap: 6px;
align-items: center;
> li {
flex: 0 0 calc((100% - 24px) / 5);
img {
display: block;
width: 100%;
height: 190px;
cursor: pointer;
border-radius: 10px;
transition: all 150ms ease-in-out;
@@ -414,11 +488,11 @@ export default {
}
.discountAdvert {
width: 1300px;
height: 566px;
height: 596px;
margin-left: -100px;
box-sizing: border-box;
background-repeat: no-repeat;
background-size: 1300px 566px;
background-size: 1300px 596px;
position: relative;
> div {
padding-left: 295px;
@@ -428,8 +502,11 @@ export default {
&:nth-child(1) img {
margin: 10px 10px 0 0;
}
&:nth-child(2) img {
margin: 0 10px 0 0;
&:nth-child(2) {
margin-top: 10px;
img {
margin: 0 10px 0 0;
}
}
}
}
@@ -535,11 +612,61 @@ export default {
/** 装修模态框 内部样式start */
.modal-top-advert {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
> * {
margin-bottom: 10px;
align-items: stretch;
padding: 0 20px;
width: 100%;
box-sizing: border-box;
.modal-form-item {
display: flex;
align-items: center;
width: 100%;
margin-bottom: 16px;
.modal-label {
flex-shrink: 0;
width: 100px;
text-align: right;
padding-right: 12px;
}
.el-input {
flex: 1;
}
}
.modal-form-preview {
justify-content: center;
margin-bottom: 8px;
}
.modal-form-link {
align-items: flex-start;
.modal-label {
padding-top: 8px;
}
.modal-link-field {
flex: 1;
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
.outsideUrl {
flex: 1;
min-width: 0;
}
}
}
.tips {
padding-left: 112px;
margin-bottom: 16px;
color: #999;
font-size: 12px;
}
}
</style>

View File

@@ -52,11 +52,9 @@
<el-dialog
v-model="showModal"
title="快捷导航"
width="800"
:close-on-click-modal="false"
append-to-body destroy-on-close>
append-to-body destroy-on-close @close="handleCancelModal">
<div class="modal-tab-bar">
<el-button type="primary" size="small" @click="handleAdd">添加轮播</el-button>
&nbsp;
@@ -72,7 +70,7 @@
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in data.options.list" :key="index">
<tr v-for="(item, index) in draftList" :key="index">
<td>
<img
style="cursor: pointer"
@@ -113,6 +111,10 @@
</tbody>
</table>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
@@ -122,6 +124,7 @@
@callback="callbackSelected"
:is-component="true"
:initialize="picModelFlag"
:max-select="1"
ref="ossManage"
/>
</el-dialog>
@@ -140,6 +143,7 @@ export default {
return {
showModal: false, // modal显隐
selected: null, // 已选数据
draftList: [], // 弹窗编辑草稿
picModelFlag: false, // 选择图片modal
userInfo: {},
articleList: [
@@ -152,14 +156,29 @@ export default {
},
methods: {
cloneList(list) {
return JSON.parse(JSON.stringify(list || []));
},
handleSelectModel() {
// 编辑模块
this.draftList = this.cloneList(this.data.options.list);
this.showModal = true;
},
handleCancelModal() {
this.draftList = [];
this.showModal = false;
},
handleConfirmModal() {
this.data.options.list.splice(
0,
this.data.options.list.length,
...this.cloneList(this.draftList)
);
this.draftList = [];
this.showModal = false;
},
// 添加轮播图
handleAdd() {
this.data.options.list.push({ img: "", url: "", type: "" });
this.$forceUpdate();
this.draftList.push({ img: "", url: "", type: "" });
},
handleSelectLink(item) {
// 选择链接
@@ -169,11 +188,11 @@ export default {
callbackSelected(item) {
// 选择图片回调
this.picModelFlag = false;
if (!item?.url) return;
this.selected.img = item.url;
},
handleDel(index) {
// 删除图片
this.data.options.list.splice(index, 1);
this.draftList.splice(index, 1);
},
selectedLink(val) {
// 选择链接回调
@@ -268,18 +287,24 @@ export default {
}
}
.shop-msg {
width: 100%;
display: flex;
justify-content: center;
div {
width: 100%;
margin: 10px 27px;
margin: 10px 0;
text-align: center;
span {
display: block;
cursor: pointer;
text-align: center;
font-weight: bold;
margin-left: 5px;
margin-left: 0;
}
span:nth-child(1) {
color: $theme_color;
margin-left: 0;
}
span:nth-child(2) {
font-weight: normal;
@@ -290,11 +315,15 @@ export default {
}
ul {
margin: 0 30px;
margin: 0;
padding: 0;
list-style: none;
li {
cursor: pointer;
margin: 5px 0;
color: #999395;
text-align: center;
&:hover {
color: $theme_color;
}

View File

@@ -84,7 +84,7 @@
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
<!-- 选择图片 -->
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
<ossManage @callback="callbackSelected" :is-component="true" :initialize="picModelFlag" ref="ossManage" />
<ossManage @callback="callbackSelected" :is-component="true" :initialize="picModelFlag" :max-select="1" ref="ossManage" />
</el-dialog>
</div>
</template>
@@ -130,6 +130,7 @@ export default {
callbackSelected(item) {
// 选择图片回调
this.picModelFlag = false;
if (!item?.url) return;
this.selected.img = item.url;
},
// 删除图片

View File

@@ -204,7 +204,7 @@
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
<!-- 选择图片 -->
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
<ossManage @callback="callbackSelected" :is-component="true" :initialize="picModelFlag" ref="ossManage" />
<ossManage @callback="callbackSelected" :is-component="true" :initialize="picModelFlag" :max-select="1" ref="ossManage" />
</el-dialog>
</div>
</template>
@@ -262,6 +262,7 @@ export default {
callbackSelected(item) {
// 选择图片回调
this.picModelFlag = false;
if (!item?.url) return;
this.selected.img = item.url;
},
// 删除图片
@@ -367,18 +368,24 @@ export default {
}
}
.shop-msg {
width: 100%;
display: flex;
justify-content: center;
div {
width: 100%;
margin: 10px 27px;
margin: 10px 0;
text-align: center;
span {
display: block;
cursor: pointer;
text-align: center;
font-weight: bold;
margin-left: 5px;
margin-left: 0;
}
span:nth-child(1) {
color: $theme_color;
margin-left: 0;
}
span:nth-child(2) {
font-weight: normal;
@@ -387,13 +394,19 @@ export default {
color: $theme_color;
}
}
ul {
margin: 0;
padding: 0;
list-style: none;
li {
cursor: pointer;
margin: 5px 0;
color: #999395;
width: 150px;
width: auto;
font-size: 12px;
text-align: center;
&:hover {
color: $theme_color;
}

View File

@@ -26,13 +26,13 @@
<el-dialog
v-model="showModal"
title="装修"
width="800"
width="800px"
:close-on-click-modal="false"
append-to-body destroy-on-close>
append-to-body
destroy-on-close
>
<div class="modal-top-advert">
<div>
<div class="modal-form-item modal-form-preview">
<img
class="show-image"
width="170"
@@ -41,42 +41,68 @@
alt
/>
</div>
<div><span>图片主标题</span><el-input v-model="selected.name" /></div>
<div><span>图片描述</span><el-input v-model="selected.describe" /></div>
<div class="tips">
建议尺寸<span>{{ selected.size }}</span>
<div class="modal-form-item">
<span class="modal-label">图片主标题</span>
<el-input v-model="selected.name" />
</div>
<div>
图片链接<el-input
class="outsideUrl"
v-model="selected.url"
:disabled="!!selected.type && selected.type !== 'link'"
placeholder="https://"
/>
<el-button size="small" type="primary" @click="handleSelectLink"
>选择链接</el-button
<div class="modal-form-item">
<span class="modal-label">图片描述</span>
<el-input v-model="selected.describe" />
</div>
<div class="tips">建议尺寸{{ selected.size }}</div>
<div class="modal-form-item modal-form-link">
<span class="modal-label">图片链接</span>
<div class="modal-link-field">
<el-input
class="outsideUrl"
v-model="selected.url"
:disabled="!!selected.type && selected.type !== 'link'"
placeholder="https://"
/>
<el-button size="small" type="primary" @click="handleSelectLink"
>选择链接</el-button
>
</div>
</div>
<div class="modal-form-item">
<span class="modal-label">渐变背景色</span>
<div class="modal-color-field">
<el-input v-model="selected.fromColor" />
<el-color-picker
v-if="selected.fromColor"
v-model="selected.fromColor"
/>
</div>
</div>
<div class="modal-form-item">
<span class="modal-label">渐变背景色</span>
<div class="modal-color-field">
<el-input v-model="selected.toColor" />
<el-color-picker
v-if="selected.toColor"
v-model="selected.toColor"
/>
</div>
</div>
<div class="modal-form-item modal-form-exhibition">
<div
:style="{
backgroundImage: `linear-gradient(to right, ${selected.fromColor}, ${selected.toColor})`,
}"
class="exhibition"
></div>
</div>
<div class="modal-form-item">
<span class="modal-label">选择图片</span>
<el-button size="small" type="primary" @click="handleSelectImg"
>选择图片</el-button
>
</div>
<div>
<span>渐变背景色</span><el-input v-model="selected.fromColor" />
<el-color-picker v-if="selected.fromColor" v-model="selected.fromColor" />
</div>
<div>
<span>渐变背景色</span><el-input v-model="selected.toColor" />
<el-color-picker v-if="selected.toColor" v-model="selected.toColor" />
</div>
<div
:style="{
backgroundImage: `linear-gradient(to right, ${selected.fromColor}, ${selected.toColor})`,
}"
class="exhibition"
></div>
<div>
选择图片<el-button size="small" type="primary" @click="handleSelectImg"
>选择图片</el-button
>&nbsp;
</div>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
@@ -86,6 +112,7 @@
@callback="callbackSelected"
:is-component="true"
:initialize="picModelFlag"
:max-select="1"
ref="ossManage"
/>
</el-dialog>
@@ -105,14 +132,19 @@ export default {
return {
options: this.data.options, // 当前类型数据
showModal: false, // modal显隐
selected: {}, // 已选数据
selected: {}, // 弹窗编辑草稿
editTarget: null, // 确认后写回的原数据
picModelFlag: false, // 图片选择器
};
},
methods: {
cloneItem(item) {
return JSON.parse(JSON.stringify(item || {}));
},
// 打开装修modal
handleSelectModel(item, type) {
this.selected = item;
handleSelectModel(item) {
this.editTarget = item;
this.selected = this.cloneItem(item);
this.showModal = true;
},
handleSelectLink(item, index) {
@@ -137,8 +169,22 @@ export default {
// 选择图片回调
callbackSelected(val) {
this.picModelFlag = false;
if (!val?.url) return;
this.selected.img = val.url;
},
handleCancelModal() {
this.editTarget = null;
this.selected = {};
this.showModal = false;
},
handleConfirmModal() {
if (this.editTarget) {
Object.assign(this.editTarget, this.cloneItem(this.selected));
}
this.editTarget = null;
this.selected = {};
this.showModal = false;
},
},
};
</script>
@@ -148,7 +194,6 @@ export default {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
// margin-top: -10px;
.item {
width: 393px;
height: 170px;
@@ -192,12 +237,86 @@ export default {
width: 154px;
}
}
.modal-top-advert {
align-items: start;
padding: 0 30px;
.exhibition {
width: 300px;
height: 50px;
display: flex;
flex-direction: column;
align-items: stretch;
padding: 0 20px;
width: 100%;
box-sizing: border-box;
.modal-form-item {
display: flex;
align-items: center;
width: 100%;
margin-bottom: 16px;
.modal-label {
flex-shrink: 0;
width: 100px;
text-align: right;
padding-right: 12px;
}
.el-input {
flex: 1;
}
}
.modal-form-preview {
justify-content: center;
margin-bottom: 8px;
}
.modal-form-link {
align-items: flex-start;
.modal-label {
padding-top: 8px;
}
.modal-link-field {
flex: 1;
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
.outsideUrl {
flex: 1;
min-width: 0;
}
}
}
.modal-color-field {
flex: 1;
display: flex;
align-items: center;
gap: 10px;
.el-input {
flex: 1;
}
}
.modal-form-exhibition {
padding-left: 112px;
box-sizing: border-box;
.exhibition {
width: 100%;
height: 50px;
border-radius: 4px;
}
}
.tips {
padding-left: 112px;
margin-bottom: 16px;
color: #999;
font-size: 12px;
}
}
</style>

View File

@@ -55,23 +55,23 @@
:close-on-click-modal="false"
append-to-body destroy-on-close>
<div>
<div class="modal-tab-bar">
<div class="modal-tab-bar" v-if="draftOptions">
<div>
主标题:
<el-input style="width: 200px" v-model="data.options.title" />
<el-input style="width: 200px" v-model="draftOptions.title" />
</div>
<el-divider content-position="left">左侧商品部分</el-divider>
<div class="flex">
<div>图片 :</div>
<div>
<img class="image" :src="data.options.data.image.src" alt="" />
<img class="image" :src="draftOptions.data.image.src" alt="" />
<div>
<div>推荐尺寸{{ data.options.data.image.size }}</div>
<div>推荐尺寸{{ draftOptions.data.image.size }}</div>
<div class="link-src">
链接地址
<el-input
style="width: 200px"
v-model="data.options.data.image.url"
v-model="draftOptions.data.image.url"
disabled
/>
</div>
@@ -95,7 +95,7 @@
<el-divider content-position="left">中间商品部分</el-divider>
<div class="flex column-list">
<div
v-for="(item, index) in data.options.data.list"
v-for="(item, index) in draftOptions.data.list"
:key="index"
class="draggable"
>
@@ -129,12 +129,12 @@
<div>右侧标题 :</div>
<el-input
style="width: 200px"
v-model="data.options.data.hot.title"
v-model="draftOptions.data.hot.title"
/>
</div>
<div class="flex column-list">
<div
v-for="(item, index) in data.options.data.hot.list"
v-for="(item, index) in draftOptions.data.hot.list"
:key="index"
class="draggable"
>
@@ -154,6 +154,10 @@
</div>
</div>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
@@ -169,6 +173,7 @@
@callback="callbackSelected"
:is-component="true"
:initialize="picModelFlag"
:max-select="1"
ref="ossManage"
/>
</el-dialog>
@@ -183,13 +188,11 @@ export default {
data() {
return {
showModal: false,
draftOptions: null,
picModelFlag: false,
selectedData: "", //选中的数据
goodsIndex: 0, // 商品索引
align: "",
defaultCallbackImageType: "",
current: "",
};
},
props: {
@@ -204,6 +207,9 @@ export default {
mounted() {
},
methods: {
cloneOptions(options) {
return JSON.parse(JSON.stringify(options || {}));
},
handleSelectImg(type, index) {
this.defaultCallbackImageType = type;
this.goodsIndex = index;
@@ -232,45 +238,51 @@ export default {
// 回显图片
callbackSelected(val) {
this.picModelFlag = false;
if (!val?.url || !this.draftOptions) return;
if (this.defaultCallbackImageType == "image") {
this.data.options.data.image.src = val.url;
this.draftOptions.data.image.src = val.url;
} else {
this.data.options.data[this.defaultCallbackImageType][
this.draftOptions.data[this.defaultCallbackImageType][
this.goodsIndex
].img = val.url;
}
},
// 选择商品回调
selectedGoodsData(val) {
this.data.options.data.hot.list[this.goodsIndex].img = val[0].thumbnail;
this.data.options.data.hot.list[this.goodsIndex].title = val[0].goodsName;
this.data.options.data.hot.list[this.goodsIndex].price = val[0].price;
this.data.options.data.hot.list[this.goodsIndex].url =
this.$filters.formatLinkType(val[0]);
console.log(val, this.selectedData, this.goodsIndex);
if (!val?.length || !this.draftOptions) return;
const item = this.draftOptions.data.hot.list[this.goodsIndex];
item.img = val[0].thumbnail;
item.title = val[0].goodsName;
item.price = val[0].price;
item.url = this.$filters.formatLinkType(val[0]);
},
// 选择链接回调
selectedLink(val) {
console.log(val,this.selectedData)
if (!this.draftOptions) return;
if (this.selectedData == "image") {
this.data.options.data.image.url =
this.$filters.formatLinkType(val);
this.draftOptions.data.image.url = this.$filters.formatLinkType(val);
} else {
console.log(this.data.options.data.list,this.goodsIndex)
this.data.options.data.list[
this.goodsIndex
].url = this.$filters.formatLinkType(val);
this.draftOptions.data.list[this.goodsIndex].url =
this.$filters.formatLinkType(val);
}
},
/*
* 点击不同模块进行编辑
* 判断也很简单,就是第一个第二个模块进行判断
*
* */
handleSelectModel() {
this.draftOptions = this.cloneOptions(this.data.options);
this.showModal = true;
},
handleCancelModal() {
this.draftOptions = null;
this.showModal = false;
},
handleConfirmModal() {
if (this.draftOptions) {
const saved = this.cloneOptions(this.draftOptions);
this.data.options.title = saved.title;
this.data.options.data = saved.data;
}
this.draftOptions = null;
this.showModal = false;
},
},
};
</script>

View File

@@ -39,7 +39,7 @@
<div>
<el-button
size="small"
@click.stop="handleSelectModel(data.options.list)"
@click.stop="handleSelectModel"
>编辑</el-button
>
</div>
@@ -54,16 +54,16 @@
:close-on-click-modal="false"
append-to-body destroy-on-close>
<div class="modal-tab-bar">
<div class="modal-tab-bar" v-if="draftOptions">
<div class="tab-bar">
标题
<el-input style="width: 300px" v-model="data.options.title" />
<el-input style="width: 300px" v-model="draftOptions.title" />
</div>
<div class="tab-bar" v-if="data.options.labels[tabCurrentlyIndex]">
<div class="tab-bar" v-if="draftOptions.labels[tabCurrentlyIndex]">
标签
<el-input
style="width: 300px"
v-model="data.options.labels[tabCurrentlyIndex].label"
v-model="draftOptions.labels[tabCurrentlyIndex].label"
/>
</div>
<el-button @click="handleTabsAdd" size="small" class="mb_10">增加</el-button>
@@ -73,7 +73,7 @@
@tab-click="handleClickTab"
>
<el-tab-pane
v-for="(tab,tabIndex) in data.options.labels"
v-for="(tab,tabIndex) in draftOptions.labels"
:key="tabIndex"
:label="tab.label"
:name="tab.___index + ''"
@@ -86,7 +86,7 @@
</div>
<div
v-for="(item, index) in data.options.list.filter((subset) => {
v-for="(item, index) in draftOptions.list.filter((subset) => {
return subset.___index == tabCurrentlyIndex;
})"
:key="index"
@@ -112,6 +112,10 @@
</el-tab-pane>
</el-tabs>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品。链接 -->
@@ -136,11 +140,10 @@ export default {
},
data() {
return {
tabIndex:0,
tabIndex: 0,
current: 0,
showModal: false,
selected: {}, // 已选数据
picModelFlag: false,
draftOptions: null,
tabCurrentlyIndex: 0, // 选项卡索引
};
},
@@ -148,89 +151,111 @@ export default {
this.tabIndex = this.data.options.labels[0].___index
},
methods: {
cloneOptions(options) {
return JSON.parse(JSON.stringify(options || {}));
},
/**
* 算出最大的index 然后索引叠加
*/
handleTabsAdd() {
let findAllIndex = this.data.options.labels.map((item) => {
return item.___index;
});
let max = Math.max.apply(null, findAllIndex);
if (!this.draftOptions) return;
const findAllIndex = this.draftOptions.labels.map((item) => item.___index);
const max = Math.max.apply(null, findAllIndex);
this.data.options.labels.push({
this.draftOptions.labels.push({
label: "标签" + (max + 1),
___index: max + 1,
});
},
// 删除标签
handleContextMenuDelete(item,index) {
// 删除标签下关联的商品
this.data.options.list.forEach((lab,currently)=>{
if(lab.___index == item.___index){
this.data.options.list.splice(currently,1)
}
})
// 删除当前标签
this.data.options.labels.splice(index,1)
handleContextMenuDelete(item, index) {
if (!this.draftOptions) return;
this.draftOptions.list = this.draftOptions.list.filter(
(lab) => lab.___index != item.___index
);
this.draftOptions.labels.splice(index, 1);
},
// 删除商品
delGoods(val) {
this.data.options.list.forEach((item, i) => {
if (!this.draftOptions) return;
this.draftOptions.list.forEach((item, i) => {
if (
item.title == val.title &&
item.___index == this.tabCurrentlyIndex
) {
this.data.options.list.splice(i, 1);
this.draftOptions.list.splice(i, 1);
}
});
},
// 切换选项卡
handleClickTab(name) {
console.log(name)
this.tabCurrentlyIndex = name;
this.tabCurrentlyIndex = name.paneName ?? name;
},
// 添加当前选项卡中的商品
addCurrentGoods() {
this.$refs.liliDialog.clearGoodsSelected();
this.$refs.liliDialog.open("goods");
},
// 编辑模块
handleSelectModel(item) {
console.log(item);
this.selected = item;
handleSelectModel() {
this.draftOptions = this.cloneOptions({
title: this.data.options.title,
labels: this.data.options.labels,
list: this.data.options.list,
});
this.tabCurrentlyIndex = this.draftOptions.labels[0]?.___index ?? 0;
this.tabIndex = String(this.tabCurrentlyIndex);
this.showModal = true;
},
// 选择商品回调
selectedGoodsData(val) {
if (val.length) {
val.forEach((item) => {
const pushGoodsDetail = {
img: item.thumbnail,
price: item.price,
title: item.goodsName,
desc: "",
url: `/goodsDetail?skuId=${item.id}&goodsId=${item.goodsId}`,
___index: this.tabCurrentlyIndex,
};
this.data.options.list.push(pushGoodsDetail);
if (!val?.length || !this.draftOptions) return;
val.forEach((item) => {
this.draftOptions.list.push({
img: item.thumbnail,
price: item.price,
title: item.goodsName,
desc: "",
url: `/goodsDetail?skuId=${item.id}&goodsId=${item.goodsId}`,
___index: this.tabCurrentlyIndex,
});
}
});
},
// 选择链接回调
selectedLink(val) {
this.selected[this.current].url =
this.$filters.formatLinkType(val);
this.selected[this.current].type =
if (!this.draftOptions?.list?.length) return;
const item = this.draftOptions.list[this.current];
if (!item) return;
item.url = this.$filters.formatLinkType(val);
item.type =
val.___type === "other" && val.url === "" ? "link" : "other";
console.log(this.selected[this.current]);
},
handleSelectLink(index) {
// 调起选择链接弹窗
this.$refs.liliDialog.open("link");
this.current = index;
},
handleCancelModal() {
this.draftOptions = null;
this.showModal = false;
},
handleConfirmModal() {
if (this.draftOptions) {
const saved = this.cloneOptions(this.draftOptions);
this.data.options.title = saved.title;
this.data.options.labels.splice(
0,
this.data.options.labels.length,
...saved.labels
);
this.data.options.list.splice(
0,
this.data.options.list.length,
...saved.list
);
}
this.draftOptions = null;
this.showModal = false;
},
},
};
</script>

View File

@@ -138,6 +138,10 @@
</div>
</div>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
@@ -153,6 +157,7 @@
@callback="callbackSelected"
:is-component="true"
:initialize="picModelFlag"
:max-select="1"
ref="ossManage"
/>
</el-dialog>
@@ -169,7 +174,7 @@ export default {
data() {
return {
showModal: false,
current: "", //当前选择的模块
current: null, // 当前编辑的模块草稿
picModelFlag: false,
selectedData: "", //选中的数据
goodsIndex: 0, // 商品索引
@@ -190,6 +195,9 @@ export default {
},
mounted() {},
methods: {
cloneModule(module) {
return JSON.parse(JSON.stringify(module || {}));
},
handleSelectImg(type, index) {
this.defaultCallbackImageType = type;
this.goodsIndex = index;
@@ -217,54 +225,49 @@ export default {
// 回显图片
callbackSelected(val) {
this.picModelFlag = false;
if (!val?.url || !this.current) return;
if (this.defaultCallbackImageType == "default") {
console.log(this.current.data.image);
this.current.data.image.src = val.url;
} else {
console.log(this.goodsIndex, this.data.options[this.align].data.list);
this.data.options[this.align].data.list[this.goodsIndex].img = val.url;
// this.data.options[this.align].data[this.selectedData][this.goodsIndex].url = val.img
this.current.data.list[this.goodsIndex].img = val.url;
}
},
// 选择商品回调
selectedGoodsData(val) {
console.log(val);
this.$nextTick(() => {
this.data.options[this.align].data.list[this.goodsIndex].img =
val[0].thumbnail;
this.data.options[this.align].data.list[this.goodsIndex].title =
val[0].goodsName;
this.data.options[this.align].data.list[this.goodsIndex].price =
val[0].price;
this.data.options[this.align].data.list[this.goodsIndex].url =
val[0].url;
});
console.log(this.data.options[this.align].data.list[this.goodsIndex]);
this.$forceUpdate();
if (!val?.length || !this.current) return;
const item = this.current.data.list[this.goodsIndex];
item.img = val[0].thumbnail;
item.title = val[0].goodsName;
item.price = val[0].price;
item.url = val[0].url;
},
// 选择链接回调
selectedLink(val) {
if (!this.current) return;
const formattedUrl = this.$filters.formatLinkType(val);
if (this.selectedData != "list") {
this.data.options[this.align].data[this.selectedData].url = this.$filters.formatLinkType(val);
console.log(this.data.options[this.align].data[this.selectedData])
this.current.data[this.selectedData].url = formattedUrl;
}
if (this.selectedData == "list") {
this.data.options[this.align].data[this.selectedData][this.goodsIndex]
.url = this.$filters.formatLinkType(val);
this.current.data.list[this.goodsIndex].url = formattedUrl;
}
},
/*
* 点击不同模块进行编辑
* 判断也很简单,就是第一个第二个模块进行判断
*
* */
handleSelectModel(align) {
this.align = align;
this.current = this.data.options[align];
console.log(this.data.options[align]);
this.current = this.cloneModule(this.data.options[align]);
this.showModal = true;
},
handleCancelModal() {
this.current = null;
this.showModal = false;
},
handleConfirmModal() {
if (this.current && this.align) {
this.data.options[this.align] = this.cloneModule(this.current);
}
this.current = null;
this.showModal = false;
},
},
};
</script>

View File

@@ -121,13 +121,13 @@
<el-dialog
v-model="showModal"
title="装修"
width="800"
width="800px"
:close-on-click-modal="false"
append-to-body destroy-on-close>
append-to-body
destroy-on-close
>
<div class="modal-top-advert">
<div>
<div class="modal-form-item modal-form-preview">
<img
class="show-image"
width="160"
@@ -145,65 +145,87 @@
alt
/>
</div>
<div><span>图片主标题</span><el-input v-model="selected.name" /></div>
<div><span>图片描述</span><el-input v-model="selected.describe" /></div>
<div class="tips">
建议尺寸<span>{{ selected.size }}</span>
<div class="modal-form-item">
<span class="modal-label">图片主标题</span>
<el-input v-model="selected.name" />
</div>
<div>
图片链接<el-input
class="outsideUrl"
v-model="selected.url"
:disabled="!!selected.type && selected.type !== 'link'"
placeholder="https://"
/>
<el-button size="small" type="primary" @click="handleSelectLink"
>选择链接</el-button
>
<div class="modal-form-item">
<span class="modal-label">图片描述</span>
<el-input v-model="selected.describe" />
</div>
<div>
<div class="tips">建议尺寸{{ selected.size }}</div>
<div class="modal-form-item modal-form-link">
<span class="modal-label">图片链接</span>
<div class="modal-link-field">
<el-input
class="outsideUrl"
v-model="selected.url"
:disabled="!!selected.type && selected.type !== 'link'"
placeholder="https://"
/>
<el-button size="small" type="primary" @click="handleSelectLink"
>选择链接</el-button
>
</div>
</div>
<div class="modal-form-item modal-form-actions">
<el-button size="small" type="primary" @click="handleSelectImg"
>选择图片</el-button
>&nbsp;
>
<el-button size="small" type="primary" @click="handleSelectGoods('')"
>选择商品</el-button
>
</div>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<!-- 装修标题 -->
<el-dialog
v-model="showModal1"
title="装修"
width="800"
width="800px"
:close-on-click-modal="false"
append-to-body destroy-on-close>
append-to-body
destroy-on-close
>
<div class="modal-top-advert">
<div><span>主标题</span><el-input v-model="selected.title" /></div>
<div><span>副标题</span><el-input v-model="selected.secondTitle" /></div>
<div>
<span
>副标题链接<el-input
<div class="modal-form-item">
<span class="modal-label">主标题</span>
<el-input v-model="selected.title" />
</div>
<div class="modal-form-item">
<span class="modal-label">副标题</span>
<el-input v-model="selected.secondTitle" />
</div>
<div class="modal-form-item modal-form-link">
<span class="modal-label">副标题链接</span>
<div class="modal-link-field">
<el-input
class="outsideUrl"
v-model="selected.url"
:disabled="!!selected.type && selected.type !== 'link'"
placeholder="https://" /></span
><el-button
size="small"
class="ml_10"
type="primary"
@click="handleSelectLink"
>选择链接</el-button
>
placeholder="https://"
/>
<el-button size="small" type="primary" @click="handleSelectLink"
>选择链接</el-button
>
</div>
</div>
<div>
<span>背景色</span
><el-color-picker v-if="selected.bgColor" v-model="selected.bgColor" />
<div class="modal-form-item">
<span class="modal-label">背景色</span>
<div class="modal-color-field">
<el-input v-model="selected.bgColor" />
<el-color-picker v-if="selected.bgColor" v-model="selected.bgColor" />
</div>
</div>
</div>
<template #footer>
<el-button @click="handleCancelModal1">取消</el-button>
<el-button type="primary" @click="handleConfirmModal1">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
<liliDialog
@@ -217,6 +239,7 @@
@callback="callbackSelected"
:is-component="true"
:initialize="picModelFlag"
:max-select="1"
ref="ossManage"
/>
</el-dialog>
@@ -239,15 +262,19 @@ export default {
options: this.data.options, // 当前数据
showModal: false, // modal显隐
showModal1: false, // modal显隐
selected: {}, // 已选数据
selected: {}, // 弹窗编辑草稿
editTarget: null, // 确认后写回的原数据
picModelFlag: false, // 选择图片modal
};
},
methods: {
cloneItem(item) {
return JSON.parse(JSON.stringify(item || {}));
},
// 装修modal
handleSelectModel(item, type) {
this.selected = item;
console.warn(item);
this.editTarget = item;
this.selected = this.cloneItem(item);
if (type) {
this.showModal1 = true;
} else {
@@ -259,9 +286,10 @@ export default {
this.$refs.liliDialog.open("link");
},
handleSelectGoods(item) {
// 调起选择商品
console.warn(item);
if (item) this.selected = item;
if (item) {
this.editTarget = item;
this.selected = this.cloneItem(item);
}
this.$refs.liliDialog.open("goods", "single");
setTimeout(() => {
this.$refs.liliDialog.goodsData = [this.selected];
@@ -269,18 +297,24 @@ export default {
},
// 选择链接回调
selectedLink(val) {
if (!this.selected) return;
this.selected.url = this.$filters.formatLinkType(val);
this.selected.type =
val.___type === "other" && val.url === "" ? "link" : "other";
},
// 选择商品回调
selectedGoodsData(val) {
let goods = val[0];
console.log(this.selected);
if (!val?.length || !this.selected) return;
const goods = val[0];
this.selected.img = goods.thumbnail;
this.selected.price = goods.price;
this.selected.name = goods.goodsName;
this.selected.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
if (this.editTarget && !this.showModal && !this.showModal1) {
Object.assign(this.editTarget, this.cloneItem(this.selected));
this.editTarget = null;
this.selected = {};
}
},
handleSelectImg() {
// 选择图片
@@ -294,8 +328,35 @@ export default {
// 选择图片回显
callbackSelected(val) {
this.picModelFlag = false;
if (!val?.url) return;
this.selected.img = val.url;
},
handleCancelModal() {
this.editTarget = null;
this.selected = {};
this.showModal = false;
},
handleConfirmModal() {
if (this.editTarget) {
Object.assign(this.editTarget, this.cloneItem(this.selected));
}
this.editTarget = null;
this.selected = {};
this.showModal = false;
},
handleCancelModal1() {
this.editTarget = null;
this.selected = {};
this.showModal1 = false;
},
handleConfirmModal1() {
if (this.editTarget) {
Object.assign(this.editTarget, this.cloneItem(this.selected));
}
this.editTarget = null;
this.selected = {};
this.showModal1 = false;
},
},
};
</script>
@@ -573,7 +634,78 @@ export default {
}
}
.modal-top-advert {
align-items: start;
padding: 0 30px;
display: flex;
flex-direction: column;
align-items: stretch;
padding: 0 20px;
width: 100%;
box-sizing: border-box;
.modal-form-item {
display: flex;
align-items: center;
width: 100%;
margin-bottom: 16px;
.modal-label {
flex-shrink: 0;
width: 100px;
text-align: right;
padding-right: 12px;
}
.el-input {
flex: 1;
}
}
.modal-form-preview {
justify-content: center;
margin-bottom: 8px;
}
.modal-form-link {
align-items: flex-start;
.modal-label {
padding-top: 8px;
}
.modal-link-field {
flex: 1;
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
.outsideUrl {
flex: 1;
min-width: 0;
}
}
}
.modal-form-actions {
padding-left: 112px;
gap: 10px;
}
.modal-color-field {
flex: 1;
display: flex;
align-items: center;
gap: 10px;
.el-input {
flex: 1;
}
}
.tips {
padding-left: 112px;
margin-bottom: 16px;
color: #999;
font-size: 12px;
}
}
</style>

View File

@@ -1,23 +1,24 @@
<template>
<div class="not-enough">
<ul class="nav-bar setup-content">
<li v-for="(item, index) in conData.options.navList" :class="currentIndex===index?'curr':''" @click="changeCurr(index)" :key="index">
<p>{{item.title}}</p>
<p>{{item.desc}}</p>
</li>
<div class="setup-box" style="width:100px;left:1100px;">
<div class="nav-bar setup-content">
<ul class="nav-list">
<li v-for="(item, index) in conData.options.navList" :class="currentIndex===index?'curr':''" @click="changeCurr(index)" :key="index">
<p>{{item.title}}</p>
<p>{{item.desc}}</p>
</li>
</ul>
<div class="setup-box nav-setup-box">
<div>
<el-button size="small" @click.stop="handleSelectModel">编辑</el-button>
</div>
</div>
</ul>
</div>
<div class="content" v-if="showContent">
<div v-for="(item, index) in conData.options.list[currentIndex]" :key="index" class="setup-content">
<img :src="item.img" width="210" height="210" :alt="item.name">
<p>{{item.name}}</p>
<p>
<span>{{ $filters.unitPrice(item.price, '¥') }}</span>
<!-- <span>{{ $filters.unitPrice(item.price, '¥') }}</span> -->
</p>
<div class="setup-box">
<div>
@@ -29,12 +30,12 @@
<el-dialog
v-model="showModal"
title="装修"
width="800"
:close-on-click-modal="false"
append-to-body destroy-on-close>
<div class="modal-tab-bar">
append-to-body
destroy-on-close
@close="handleCancelModal">
<div class="modal-tab-bar" v-if="draftNavList.length">
<el-button type="primary" size='small' @click="handleAddNav">添加分类</el-button>
<table cellspacing="0">
<thead>
@@ -45,7 +46,7 @@
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in conData.options.navList" :key="index">
<tr v-for="(item, index) in draftNavList" :key="index">
<td><el-input v-model="item.title" /></td>
<td><el-input v-model="item.desc" /></td>
<td v-if="index!=0">
@@ -55,8 +56,40 @@
</tbody>
</table>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<el-dialog
v-model="showGoodsModal"
title="装修"
width="600"
:close-on-click-modal="false"
append-to-body
destroy-on-close
@close="handleCancelGoodsModal">
<div class="modal-top-advert">
<div class="modal-form-item modal-form-preview" v-if="selected.img">
<img class="show-image" width="210" height="210" :src="selected.img" alt />
</div>
<div class="modal-form-item" v-if="selected.name">
<span class="modal-label">商品名称</span>
<span>{{ selected.name }}</span>
</div>
<div class="modal-form-item" v-if="selected.price">
<span class="modal-label">商品价格</span>
<span>{{ $filters.unitPrice(selected.price, '¥') }}</span>
</div>
<div class="modal-form-item modal-form-actions">
<el-button size="small" type="primary" @click="handleOpenGoodsPicker">选择商品</el-button>
</div>
</div>
<template #footer>
<el-button @click="handleCancelGoodsModal">取消</el-button>
<el-button type="primary" @click="handleConfirmGoodsModal">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
<liliDialog
ref="liliDialog"
@selectedGoodsData="selectedGoodsData"
@@ -73,11 +106,15 @@ export default {
},
data() {
return {
currentIndex:0, // 当前商品index
conData:this.data, // 当前数据
selected:{}, // 已选数据
showModal:false, // modal显隐
showContent:true, // 选择后刷新数据用
currentIndex:0,
conData:this.data,
selected:{},
editTarget: null,
showModal:false,
showGoodsModal: false,
showContent:true,
draftNavList: [],
draftGoodsList: [],
}
},
watch:{
@@ -89,41 +126,46 @@ export default {
}
},
methods:{
// tab点击切换
cloneList(list) {
return JSON.parse(JSON.stringify(list || []));
},
cloneItem(item) {
return JSON.parse(JSON.stringify(item || {}));
},
changeCurr(index){
this.currentIndex = index;
},
// 编辑
handleSelectModel (item,type) {
this.selected = item;
this.showModal = true
handleSelectModel () {
this.draftNavList = this.cloneList(this.conData.options.navList);
this.draftGoodsList = this.cloneList(this.conData.options.list);
this.showModal = true;
},
handleSelectGoods(item) { // 调起选择商品弹窗
if(item) this.selected = item;
this.$refs.liliDialog.open('goods', 'single')
handleSelectGoods(item) {
this.editTarget = item;
this.selected = this.cloneItem(item);
this.showGoodsModal = true;
},
handleOpenGoodsPicker() {
this.$refs.liliDialog.open('goods', 'single');
setTimeout(() => {
this.$refs.liliDialog.goodsData = [this.selected]
this.$refs.liliDialog.goodsData = [this.selected];
}, 500);
},
// 选择商品回调
selectedGoodsData(val){
console.log(val)
let goods = val[0]
this.selected.img = goods.thumbnail
this.selected.price = goods.price
this.selected.name = goods.goodsName
this.selected.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`
if (!val?.length) return;
const goods = val[0];
this.selected.img = goods.thumbnail;
this.selected.price = goods.price;
this.selected.name = goods.goodsName;
this.selected.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
},
handleDelNav(index){ // 删除导航
this.conData.options.navList.splice(index,1)
this.conData.options.list.splice(index,1)
handleDelNav(index){
this.draftNavList.splice(index,1);
this.draftGoodsList.splice(index,1);
},
handleAddNav(){ // 添加导航
this.conData.options.navList.push(
{title:'',desc:''}
)
this.conData.options.list.push(
[{ img:'', name:'', price:0, url:'' },
handleAddNav(){
this.draftNavList.push({ title:'', desc:'' });
this.draftGoodsList.push([
{ img:'', name:'', price:0, url:'' },
{ img:'', name:'', price:0, url:'' },
{ img:'', name:'', price:0, url:'' },
@@ -133,27 +175,66 @@ export default {
{ img:'', name:'', price:0, url:'' },
{ img:'', name:'', price:0, url:'' },
{ img:'', name:'', price:0, url:'' },
],
)
this.showContent = false
this.$nextTick(()=>{
{ img:'', name:'', price:0, url:'' },
]);
},
handleCancelModal() {
this.draftNavList = [];
this.draftGoodsList = [];
this.showModal = false;
},
handleConfirmModal() {
this.conData.options.navList = this.cloneList(this.draftNavList);
this.conData.options.list = this.cloneList(this.draftGoodsList);
this.draftNavList = [];
this.draftGoodsList = [];
this.showContent = false;
this.$nextTick(() => {
this.showContent = true;
})
});
this.showModal = false;
},
handleCancelGoodsModal() {
this.editTarget = null;
this.selected = {};
this.showGoodsModal = false;
},
handleConfirmGoodsModal() {
if (this.editTarget) {
Object.assign(this.editTarget, this.cloneItem(this.selected));
}
this.editTarget = null;
this.selected = {};
this.showGoodsModal = false;
},
}
}
</script>
<style lang="scss" scoped>
@import './setup-box.scss';
.not-enough {
width: 100%;
}
.nav-bar{
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 10px;
background-color: rgb(218, 217, 217);
height: 60px;
align-items: center;
position: relative;
}
.nav-setup-box {
left: auto;
right: 0;
width: 100px;
}
.nav-list {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
li{
padding: 0 30px;
text-align: center;
@@ -161,11 +242,15 @@ export default {
font-size: 16px;
border-radius: 50px;
padding: 0 7px;
margin: 0;
line-height: 1.2;
}
p:nth-child(2){
font-size: 14px;
color: #999;
margin: 2px 0 0;
line-height: 1.2;
}
&:hover{
@@ -180,11 +265,9 @@ export default {
li:last-of-type{
border: none;
}
.curr{
p:nth-child(1){
background-color: $theme_color;
color: #fff;
}
p:nth-child(2){
@@ -223,4 +306,4 @@ export default {
}
}
}
</style>
</style>

View File

@@ -8,7 +8,7 @@
<div>
<el-button
size="small"
@click.stop="handleSelectModel(data.options.list)"
@click.stop="handleSelectModel"
>编辑</el-button
>
</div>
@@ -22,7 +22,7 @@
:close-on-click-modal="false"
append-to-body destroy-on-close>
<div class="modal-tab-bar">
<div v-for="(item,index) in data.options.list" :key="index">
<div v-for="(item,index) in draftList" :key="index">
<img :src="item.img" class="three-column-img">
<div>推荐尺寸{{item.size}}</div>
<el-button
@@ -42,6 +42,10 @@
</div>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
<liliDialog
@@ -55,6 +59,7 @@
@callback="callbackSelected"
:is-component="true"
:initialize="picModelFlag"
:max-select="1"
ref="ossManage"
/>
</el-dialog>
@@ -75,37 +80,41 @@ export default {
data () {
return {
showModal:false,
selected: {}, // 已选数据
draftList: [],
picModelFlag:false,
current:0,
};
},
methods: {
cloneList(list) {
return JSON.parse(JSON.stringify(list || []));
},
// 回显图片
callbackSelected(val) {
this.picModelFlag = false;
this.selected[this.current].img = val.url;
console.log(this.selected)
if (!val?.url) return;
this.draftList[this.current].img = val.url;
},
// 编辑模块
handleSelectModel(item) {
this.selected = item;
handleSelectModel() {
this.draftList = this.cloneList(this.data.options.list);
this.showModal = true;
},
// 选择商品回调
selectedGoodsData(val) {
console.log(val);
let goods = val[0];
this.selected.img = goods.thumbnail;
this.selected.price = goods.price;
this.selected.name = goods.goodsName;
this.selected.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
const goods = val[0];
if (!goods) return;
const item = this.draftList[this.current];
item.img = goods.thumbnail;
item.price = goods.price;
item.name = goods.goodsName;
item.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
},
// 选择链接回调
selectedLink(val) {
this.selected.url = this.$filters.formatLinkType(val);
this.selected.type =
const item = this.draftList[this.current];
item.url = this.$filters.formatLinkType(val);
item.type =
val.___type === "other" && val.url === "" ? "link" : "other";
},
handleSelectLink(index) {
@@ -122,7 +131,17 @@ export default {
this.$refs.ossManage.selectImage = true;
}
});
}
},
handleCancelModal() {
this.draftList = [];
this.showModal = false;
},
handleConfirmModal() {
const saved = this.cloneList(this.draftList);
this.data.options.list.splice(0, this.data.options.list.length, ...saved);
this.draftList = [];
this.showModal = false;
},
}
}
</script>

View File

@@ -19,7 +19,7 @@
<div>
<el-button
size="small"
@click.stop="handleSelectModel(data.options.list)"
@click.stop="handleSelectModel"
>编辑</el-button
>
</div>
@@ -34,7 +34,7 @@
:close-on-click-modal="false"
append-to-body destroy-on-close>
<div class="modal-tab-bar">
<div class="modal-tab-bar" v-if="draftList">
<div class="flex flex-a-c">
<el-button class="add-goods" @click="addCurrentGoods">
添加商品
@@ -42,7 +42,7 @@
</div>
<div
v-for="(item, index) in data.options.list"
v-for="(item, index) in draftList"
:key="index"
class="draggable"
>
@@ -65,6 +65,10 @@
</div>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
@@ -88,61 +92,67 @@ export default {
},
data() {
return {
flag:false,
tabIndex:0,
current: 0,
showModal: false,
selected: {}, // 已选数据
picModelFlag: false,
draftList: [],
};
},
mounted(){
},
methods: {
cloneList(list) {
return JSON.parse(JSON.stringify(list || []));
},
// 删除商品
delGoods(val,i) {
this.data.options.list.splice(i, 1);
delGoods(val, i) {
this.draftList.splice(i, 1);
},
// 添加当前选项卡中的商品
addCurrentGoods() {
this.$refs.liliDialog.open("goods");
},
// 编辑模块
handleSelectModel(item) {
console.log(item);
this.selected = item;
handleSelectModel() {
this.draftList = this.cloneList(this.data.options.list);
this.showModal = true;
},
// 选择商品回调
selectedGoodsData(val) {
if (val.length) {
val.forEach((item) => {
const pushGoodsDetail = {
img: item.thumbnail,
price: item.price,
title: item.goodsName,
desc: "",
url: `/goodsDetail?skuId=${item.id}&goodsId=${item.goodsId}`,
};
this.data.options.list.push(pushGoodsDetail);
if (!val?.length || !this.draftList) return;
val.forEach((item) => {
this.draftList.push({
img: item.thumbnail,
price: item.price,
title: item.goodsName,
desc: "",
url: `/goodsDetail?skuId=${item.id}&goodsId=${item.goodsId}`,
});
}
});
},
// 选择链接回调
selectedLink(val) {
this.selected[this.current].url =
this.$filters.formatLinkType(val);
this.selected[this.current].type =
if (!this.draftList?.length) return;
const item = this.draftList[this.current];
item.url = this.$filters.formatLinkType(val);
item.type =
val.___type === "other" && val.url === "" ? "link" : "other";
console.log(this.selected[this.current]);
},
handleSelectLink(index) {
// 调起选择链接弹窗
this.$refs.liliDialog.open("link");
this.current = index;
},
handleCancelModal() {
this.draftList = [];
this.showModal = false;
},
handleConfirmModal() {
const saved = this.cloneList(this.draftList);
this.data.options.list.splice(0, this.data.options.list.length, ...saved);
this.draftList = [];
this.showModal = false;
},
},
};
</script>

View File

@@ -101,13 +101,13 @@
<el-dialog
v-model="showModal"
title="装修"
width="800"
width="800px"
:close-on-click-modal="false"
append-to-body destroy-on-close>
append-to-body
destroy-on-close
>
<div class="modal-top-advert">
<div>
<div class="modal-form-item modal-form-preview">
<img
class="show-image"
width="160"
@@ -133,67 +133,86 @@
alt
/>
</div>
<div><span>图片主标题</span><el-input v-model="selected.name" /></div>
<div><span>图片描述</span><el-input v-model="selected.describe" /></div>
<div class="tips">
建议尺寸<span>{{ selected.size }}</span>
<div class="modal-form-item">
<span class="modal-label">图片主标题</span>
<el-input v-model="selected.name" />
</div>
<div>
图片链接<el-input
class="outsideUrl"
v-model="selected.url"
:disabled="!!selected.type && selected.type !== 'link'"
placeholder="https://"
/>
<el-button
size="small"
class="ml_10"
type="primary"
@click="handleSelectLink"
>选择链接</el-button
>
<div class="modal-form-item">
<span class="modal-label">图片描述</span>
<el-input v-model="selected.describe" />
</div>
<div>
<div class="tips">建议尺寸{{ selected.size }}</div>
<div class="modal-form-item modal-form-link">
<span class="modal-label">图片链接</span>
<div class="modal-link-field">
<el-input
class="outsideUrl"
v-model="selected.url"
:disabled="!!selected.type && selected.type !== 'link'"
placeholder="https://"
/>
<el-button size="small" type="primary" @click="handleSelectLink"
>选择链接</el-button
>
</div>
</div>
<div class="modal-form-item modal-form-actions">
<el-button size="small" type="primary" @click="handleSelectImg"
>选择图片</el-button
>&nbsp;
>
<el-button size="small" type="primary" @click="handleSelectGoods"
>选择商品</el-button
>
</div>
</div>
<template #footer>
<el-button @click="handleCancelModal">取消</el-button>
<el-button type="primary" @click="handleConfirmModal">确定</el-button>
</template>
</el-dialog>
<el-dialog
v-model="showModal1"
title="装修"
width="800"
width="800px"
:close-on-click-modal="false"
append-to-body destroy-on-close>
append-to-body
destroy-on-close
>
<div class="modal-top-advert">
<div><span>主标题</span><el-input v-model="selected.title" /></div>
<div><span>副标题</span><el-input v-model="selected.secondTitle" /></div>
<div>
<span
>副标题链接<el-input
<div class="modal-form-item">
<span class="modal-label">主标题</span>
<el-input v-model="selected.title" />
</div>
<div class="modal-form-item">
<span class="modal-label">副标题</span>
<el-input v-model="selected.secondTitle" />
</div>
<div class="modal-form-item modal-form-link">
<span class="modal-label">副标题链接</span>
<div class="modal-link-field">
<el-input
class="outsideUrl"
v-model="selected.url"
:disabled="!!selected.type && selected.type !== 'link'"
placeholder="https://" /></span
><el-button
size="small"
class="ml_10"
type="primary"
@click="handleSelectLink"
>选择链接</el-button
>
placeholder="https://"
/>
<el-button size="small" type="primary" @click="handleSelectLink"
>选择链接</el-button
>
</div>
</div>
<div>
<span>背景色</span><el-input v-model="selected.bgColor" />
<el-color-picker v-if="selected.bgColor" v-model="selected.bgColor" />
<div class="modal-form-item">
<span class="modal-label">背景色</span>
<div class="modal-color-field">
<el-input v-model="selected.bgColor" />
<el-color-picker v-if="selected.bgColor" v-model="selected.bgColor" />
</div>
</div>
</div>
<template #footer>
<el-button @click="handleCancelModal1">取消</el-button>
<el-button type="primary" @click="handleConfirmModal1">确定</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
<liliDialog
@@ -207,6 +226,7 @@
@callback="callbackSelected"
:is-component="true"
:initialize="picModelFlag"
:max-select="1"
ref="ossManage"
/>
</el-dialog>
@@ -230,14 +250,19 @@ export default {
msgRight: this.data.options.contentRight, // 右侧数据
showModal: false, // modal显隐
showModal1: false, // modal显隐
selected: {}, // 已选数据
selected: {}, // 弹窗编辑草稿
editTarget: null, // 确认后写回的原数据
picModelFlag: false, // 图片选择
};
},
methods: {
cloneItem(item) {
return JSON.parse(JSON.stringify(item || {}));
},
// 编辑
handleSelectModel(item, type) {
this.selected = item;
this.editTarget = item;
this.selected = this.cloneItem(item);
if (type) {
this.showModal1 = true;
} else {
@@ -279,8 +304,35 @@ export default {
// 选择图片回调
callbackSelected(val) {
this.picModelFlag = false;
if (!val?.url) return;
this.selected.img = val.url;
},
handleCancelModal() {
this.editTarget = null;
this.selected = {};
this.showModal = false;
},
handleConfirmModal() {
if (this.editTarget) {
Object.assign(this.editTarget, this.cloneItem(this.selected));
}
this.editTarget = null;
this.selected = {};
this.showModal = false;
},
handleCancelModal1() {
this.editTarget = null;
this.selected = {};
this.showModal1 = false;
},
handleConfirmModal1() {
if (this.editTarget) {
Object.assign(this.editTarget, this.cloneItem(this.selected));
}
this.editTarget = null;
this.selected = {};
this.showModal1 = false;
},
},
};
</script>
@@ -446,7 +498,78 @@ export default {
}
.modal-top-advert {
align-items: start;
padding: 0 30px;
display: flex;
flex-direction: column;
align-items: stretch;
padding: 0 20px;
width: 100%;
box-sizing: border-box;
.modal-form-item {
display: flex;
align-items: center;
width: 100%;
margin-bottom: 16px;
.modal-label {
flex-shrink: 0;
width: 100px;
text-align: right;
padding-right: 12px;
}
.el-input {
flex: 1;
}
}
.modal-form-preview {
justify-content: center;
margin-bottom: 8px;
}
.modal-form-link {
align-items: flex-start;
.modal-label {
padding-top: 8px;
}
.modal-link-field {
flex: 1;
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
.outsideUrl {
flex: 1;
min-width: 0;
}
}
}
.modal-form-actions {
padding-left: 112px;
gap: 10px;
}
.modal-color-field {
flex: 1;
display: flex;
align-items: center;
gap: 10px;
.el-input {
flex: 1;
}
}
.tips {
padding-left: 112px;
margin-bottom: 16px;
color: #999;
font-size: 12px;
}
}
</style>

View File

@@ -203,8 +203,8 @@ export default {
}
.section {
width: 1000px;
// background: #efefef;
flex: 1;
min-width: 0;
overflow-x: auto;
overflow-y: hidden;
.seckill-list {

View File

@@ -203,8 +203,8 @@ export default {
}
.section {
width: 1000px;
// background: #efefef;
flex: 1;
min-width: 0;
overflow-x: auto;
overflow-y: hidden;
.seckill-list {

View File

@@ -16,7 +16,8 @@
z-index: 99;
width: 100%;
height: 100%;
.ivu-btn{
.ivu-btn,
.el-button{
float: right;
margin-right: 5px;
margin-top: 5px;
@@ -46,17 +47,4 @@
tbody>tr:hover{
background-color: aliceblue;
}
}
.modal-top-advert{
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
>*{
margin-bottom: 10px;
}
.ivu-input-wrapper{
width: 200px;
}
}

View File

@@ -173,7 +173,7 @@ export default {
overflow-y: auto;
padding: 10px;
background: #fff;
margin-top: 60px;
margin-top: 80px;
position: fixed;
z-index: 100;
box-shadow: 1px 1px 10px #999;

View File

@@ -165,6 +165,7 @@ export default {
}, // 图片选择器回显
callbackSelected(val) {
this.picModelFlag = false;
if (!val?.url) return;
this.advertising[0].img = val.url;
}
},

View File

@@ -156,6 +156,7 @@ export default {
// 图片选择器回显
callbackSelected(val) {
this.picModelFlag = false;
if (!val?.url) return;
this.advertising[0].img = val.url;
},
// 点击选择图片

View File

@@ -498,11 +498,23 @@
@selectedGoodsData="selectedGoodsData"
></liliDialog>
<el-dialog width="800px" title="选择分类" v-model="enableSelectCategory" append-to-body destroy-on-close>
<el-dialog
width="800px"
title="选择分类"
v-model="enableSelectCategory"
append-to-body
destroy-on-close
:close-on-click-modal="false"
@close="handleCancelCategoryModal"
>
<categoryTemplate
v-if="enableSelectCategory"
@selected="confirmCategory"
@selected="onCategorySelected"
/>
<template #footer>
<el-button @click="handleCancelCategoryModal">取消</el-button>
<el-button type="primary" @click="handleConfirmCategoryModal">确定</el-button>
</template>
</el-dialog>
<hotzone ref="hotzone" @changeZone="changeZone"></hotzone>
@@ -542,6 +554,7 @@ export default {
selectedLinks: "", // 已选链接
modelList: "", // 装修列表
enableSelectCategory: false, //商品是否绑定分类
pendingCategory: null, // 选择分类弹窗草稿
goodsSelectedIndex: 0, //绑定商品分类的索引
};
},
@@ -553,7 +566,21 @@ export default {
},
props: ["res"],
methods: {
// 选择分类
// 选择分类(弹窗内暂存,确定后写回)
onCategorySelected(val) {
this.pendingCategory = val;
},
handleCancelCategoryModal() {
this.pendingCategory = null;
this.enableSelectCategory = false;
},
handleConfirmCategoryModal() {
if (this.pendingCategory?.length) {
this.confirmCategory(this.pendingCategory);
}
this.pendingCategory = null;
this.enableSelectCategory = false;
},
confirmCategory(val) {
let data = {
...this.res.options.list[0].titleWay[this.goodsSelectedIndex],
@@ -687,6 +714,7 @@ export default {
},
// 绑定分类
bindGoodsCategory(index, key) {
this.pendingCategory = null;
this.enableSelectCategory = true;
this.goodsSelectedIndex = index;
},
@@ -771,6 +799,7 @@ export default {
// 图片选择器回显
callbackSelected(val) {
this.picModelFlag = false;
if (!val?.url) return;
this.selectedGoods.img = val.url;
},
// 点击选择图片

View File

@@ -18,22 +18,39 @@
/>
</el-form-item>
<el-form-item label="获取方式" prop="getType">
<el-select v-model="searchForm.getType" placeholder="请选择" clearable style="width: 240px">
<el-option label="免费获取" value="FREE" />
<el-option label="活动获取" value="ACTIVITY" />
<el-select
v-model="searchForm.getType"
placeholder="请选择"
:clearable="!isGetTypeFilterLocked"
:disabled="isGetTypeFilterLocked"
teleported
popper-class="coupon-filter-popper"
style="width: 240px"
>
<el-option
v-for="item in getTypeFilterOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="活动状态" prop="promotionStatus">
<el-select
v-model="searchForm.promotionStatus"
placeholder="请选择"
clearable
:clearable="!isPromotionStatusFilterLocked"
:disabled="isPromotionStatusFilterLocked"
teleported
popper-class="coupon-filter-popper"
style="width: 240px"
>
<el-option label="未开始" value="NEW" />
<el-option label="已开始/上架" value="START" />
<el-option label="已结束/下架" value="END" />
<el-option label="紧急关闭/作废" value="CLOSE" />
<el-option
v-for="item in promotionStatusFilterOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="活动时间">
@@ -41,6 +58,8 @@
v-model="selectDate"
type="daterange"
clearable
teleported
popper-class="coupon-filter-popper"
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width: 240px"
@@ -218,6 +237,8 @@ export default {
sort: "create_time",
order: "desc",
getType: "",
promotionStatus: "",
couponName: "",
},
selectList: [],
selectCount: 0,
@@ -229,6 +250,36 @@ export default {
isSyncingSelection: false,
};
},
computed: {
getTypeFilterOptions() {
const all = [
{ label: "免费获取", value: "FREE" },
{ label: "活动获取", value: "ACTIVITY" },
];
if (this.getType) {
return all.filter((item) => item.value === this.getType);
}
return all;
},
promotionStatusFilterOptions() {
const all = [
{ label: "未开始", value: "NEW" },
{ label: "已开始/上架", value: "START" },
{ label: "已结束/下架", value: "END" },
{ label: "紧急关闭/作废", value: "CLOSE" },
];
if (this.promotionStatus) {
return all.filter((item) => item.value === this.promotionStatus);
}
return all;
},
isGetTypeFilterLocked() {
return !!this.getType;
},
isPromotionStatusFilterLocked() {
return !!this.promotionStatus;
},
},
watch: {
$route(to) {
if (to.fullPath == "/promotions/manager-coupon") {
@@ -342,7 +393,9 @@ export default {
this.selectList = nextList;
this.selectCount = nextList.length;
this.$nextTick(() => {
this.isSyncingSelection = false;
this.$nextTick(() => {
this.isSyncingSelection = false;
});
});
},
check() {
@@ -398,9 +451,9 @@ export default {
}
getPlatformCouponList(this.searchForm)
.then((res) => {
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
if (res && res.success && res.result) {
this.data = res.result.records || [];
this.total = res.result.total || 0;
this.$nextTick(() => {
if (this.selectedList.length) {
this.syncTableSelection(this.selectedList);
@@ -408,6 +461,7 @@ export default {
});
}
})
.catch(() => {})
.finally(() => {
this.loading = false;
});
@@ -523,3 +577,9 @@ export default {
margin-bottom: 10px;
}
</style>
<style lang="scss">
.coupon-filter-popper {
z-index: 4000 !important;
}
</style>

View File

@@ -366,28 +366,31 @@ export default {
padding-top: 1px;
}
.gift-card-validity-group {
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
padding-top: 0;
padding-bottom: 0;
}
.gift-card-validity-group :deep(.el-radio) {
display: inline-flex;
align-items: center;
line-height: 1;
}
.gift-card-validity-group :deep(.el-radio:first-child) {
margin-top: 0;
}
.gift-card-validity-group :deep(.el-radio) {
display: flex;
flex-wrap: wrap;
align-items: center;
margin-right: 0;
margin-bottom: 10px;
line-height: 32px;
min-height: 32px;
height: auto;
}
.gift-card-validity-group :deep(.el-radio:last-child) {
margin-bottom: 0;
}
.gift-card-validity-group :deep(.el-radio__label) {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
line-height: 32px;
}
.gift-card-validity-input-gap {
width: 168px;
margin: 0 10px;

View File

@@ -3,67 +3,82 @@
<el-card>
<el-form :model="liveForm" ref="liveForm" :rules="liveRulesForm" label-width="120px">
<el-form-item label="直播标题" prop="name">
<el-input disabled v-model="liveForm.name" style="width:460px"></el-input>
<div class="tips">直播间名字最短3个汉字最长17个汉字1个汉字相当于2个字符</div>
<div class="form-field-block">
<el-input disabled v-model="liveForm.name" style="width:460px"></el-input>
<div class="form-tip">直播间名字最短3个汉字最长17个汉字1个汉字相当于2个字符</div>
</div>
</el-form-item>
<el-form-item label="主播昵称" prop="anchorName">
<el-input disabled v-model="liveForm.anchorName" style="width:360px"></el-input>
<div class="tips">主播昵称最短2个汉字最长15个汉字1个汉字相当于2个字符</div>
<div class="form-field-block">
<el-input disabled v-model="liveForm.anchorName" style="width:360px"></el-input>
<div class="form-tip">主播昵称最短2个汉字最长15个汉字1个汉字相当于2个字符</div>
</div>
</el-form-item>
<el-form-item label="直播时间" prop="startTime">
<el-date-picker
disabled
format="YYYY-MM-DD HH:mm"
type="datetimerange"
v-model="times"
@change="handleChangeTime"
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="直播计划开始时间-直播计划结束时间"
style="width: 300px"
/>
<div class="tips">直播开播时间需要在当前时间的10分钟后 并且 开始时间不能在 6 个月后</div>
<div class="form-field-block">
<el-date-picker
disabled
format="YYYY-MM-DD HH:mm"
type="datetimerange"
v-model="times"
@change="handleChangeTime"
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="直播计划开始时间-直播计划结束时间"
style="width: 300px"
/>
<div class="form-tip">直播开播时间需要在当前时间的10分钟后 并且 开始时间不能在 6 个月后</div>
</div>
</el-form-item>
<el-form-item label="主播微信号" prop="anchorWechat">
<el-input disabled v-model="liveForm.anchorWechat" style="width:360px" placeholder="主播微信号"></el-input>
<div class="tips">主播微信号如果未实名认证需要先前往小程序直播小程序进行<a target="_black" href="https://res.wx.qq.com/op_res/9rSix1dhHfK4rR049JL0PHJ7TpOvkuZ3mE0z7Ou_Etvjf-w1J_jVX0rZqeStLfwh">实名验证</a></div>
<div class="form-field-block">
<el-input disabled v-model="liveForm.anchorWechat" style="width:360px" placeholder="主播微信号"></el-input>
<div class="form-tip">主播微信号如果未实名认证需要先前往小程序直播小程序进行<a target="_black" href="https://res.wx.qq.com/op_res/9rSix1dhHfK4rR049JL0PHJ7TpOvkuZ3mE0z7Ou_Etvjf-w1J_jVX0rZqeStLfwh">实名验证</a></div>
</div>
</el-form-item>
<el-form-item label="分享卡片封面" prop="feedsImg">
<div class="upload-list" v-if="liveForm.feedsImg">
<img :src="liveForm.feedsImg">
<div class="upload-list-cover" @click="handleView(liveForm.feedsImg)">
<span class="view-icon">查看</span>
<div class="form-field-block">
<div class="upload-list" v-if="liveForm.feedsImg">
<img :src="liveForm.feedsImg">
<div class="upload-list-cover" @click="handleView(liveForm.feedsImg)">
<span class="view-icon">查看</span>
</div>
</div>
<div class="form-tip">
直播间分享图图片规则建议像素800*640大小不超过1M
</div>
</div>
<div class="tips">
直播间分享图图片规则建议像素800*640大小不超过1M
</div>
</el-form-item>
<el-form-item label="直播间背景墙" prop="coverImg">
<div class="upload-list" v-if="liveForm.coverImg">
<img :src="liveForm.coverImg">
<div class="upload-list-cover" @click="handleView(liveForm.coverImg)">
<span class="view-icon">查看</span>
<div class="form-field-block">
<div class="upload-list" v-if="liveForm.coverImg">
<img :src="liveForm.coverImg">
<div class="upload-list-cover" @click="handleView(liveForm.coverImg)">
<span class="view-icon">查看</span>
</div>
</div>
<div class="form-tip"> 直播间背景图图片规则建议像素1080*1920大小不超过1M</div>
</div>
<div class="tips"> 直播间背景图图片规则建议像素1080*1920大小不超过1M</div>
</el-form-item>
<el-form-item label="直播间分享图" prop="shareImg">
<div class="upload-list" v-if="liveForm.shareImg">
<img :src="liveForm.shareImg">
<div class="upload-list-cover" @click="handleView(liveForm.shareImg)">
<span class="view-icon">查看</span>
<div class="form-field-block">
<div class="upload-list" v-if="liveForm.shareImg">
<img :src="liveForm.shareImg">
<div class="upload-list-cover" @click="handleView(liveForm.shareImg)">
<span class="view-icon">查看</span>
</div>
</div>
<div class="form-tip"> 直播间分享图图片规则建议像素800*640大小不超过1M</div>
</div>
<div class="tips"> 直播间分享图图片规则建议像素800*640大小不超过1M</div>
</el-form-item>
<el-form-item label="商品" v-if="$route.query.id">
<el-table class="goods-table" border :data="liveData" style="width: 100%">
<div class="form-field-block">
<el-table class="goods-table" border :data="liveData" style="width: 100%">
<el-table-column label="商品" min-width="200">
<template #default="{ row, $index }">
<div v-if="row" class="flex-goods">
@@ -91,8 +106,9 @@
</template>
</el-table-column>
</el-table>
<div class="tips">
直播间商品中前两个商品将自动被选为封面伴随直播间在直播列表中显示
<div class="form-tip">
直播间商品中前两个商品将自动被选为封面伴随直播间在直播列表中显示
</div>
</div>
</el-form-item>
@@ -198,13 +214,21 @@ export default {
margin-right: 10px;
}
}
.tips {
.form-field-block {
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
}
.form-tip {
color: #999;
font-size: 12px;
line-height: 1.5;
margin-top: 4px;
}
.goods-table {
width: 1000px;
margin: 10px 0;
margin: 10px 0 0;
}
.upload-list {
display: inline-block;

View File

@@ -99,10 +99,10 @@ export default {
content: "更新后店铺以及用户地区绑定数据将全部错乱",
onOk: () => {
this.asyncLoading = true;
let closeCountdown = null;
let messageInstance = null;
const showCountdown = () => {
if (closeCountdown) closeCountdown();
closeCountdown = ElMessage.info({
if (messageInstance) messageInstance.close();
messageInstance = ElMessage.info({
message: `地区数据将在 ${this.num} 秒后更新`,
duration: 0,
showClose: true,
@@ -115,7 +115,7 @@ export default {
}, 1000);
setTimeout(() => {
clearInterval(number);
if (closeCountdown) closeCountdown();
if (messageInstance) messageInstance.close();
ElMessage.closeAll();
asyncRegion().then(() => {
this.asyncLoading = false;

View File

@@ -59,13 +59,12 @@
</el-form-item>
<el-form-item label="店铺logo" class="storeLogo">
<el-avatar
v-if="shopForm.storeLogo"
shape="square"
:size="100"
:src="shopForm.storeLogo"
/>
<div>
<div class="store-logo-block">
<el-avatar
shape="square"
:size="100"
:src="shopForm.storeLogo"
/>
<el-button @click="handleCLickImg('storeLogo')" type="primary"
>选择图片</el-button
>
@@ -187,13 +186,12 @@
</el-form-item>
<el-form-item label="营业执照电子版" prop="licencePhoto">
<el-avatar
v-if="shopForm.licencePhoto"
shape="square"
:size="100"
:src="shopForm.licencePhoto"
/>
<div>
<div class="store-logo-block">
<el-avatar
shape="square"
:size="100"
:src="shopForm.licencePhoto"
/>
<el-button @click="handleCLickImg('licencePhoto')" type="primary"
>选择图片</el-button
>
@@ -208,21 +206,25 @@
<el-input v-model="shopForm.legalId" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="法人身份证照片" prop="legalPhoto">
<el-avatar
class="legal-photo"
shape="square"
:size="100"
@click="handleCLickImg('legalPhoto', 0)"
:src="shopForm.legalPhoto[0]"
/>
<el-avatar
class="ml_10 legal-photo"
shape="square"
:size="100"
@click="handleCLickImg('legalPhoto', 1)"
:src="shopForm.legalPhoto[1]"
/>
<span>点击图片上传身份证正反面要求身份证清晰四角无缺漏</span>
<div class="legal-photo-block">
<div class="legal-photo-list">
<el-avatar
class="legal-photo"
shape="square"
:size="100"
@click="handleCLickImg('legalPhoto', 0)"
:src="shopForm.legalPhoto[0]"
/>
<el-avatar
class="legal-photo"
shape="square"
:size="100"
@click="handleCLickImg('legalPhoto', 1)"
:src="shopForm.legalPhoto[1]"
/>
</div>
<div class="form-tip">点击图片上传身份证正反面要求身份证清晰四角无缺漏</div>
</div>
</el-form-item>
<el-divider content-position="left">结算银行信息</el-divider>
@@ -867,4 +869,26 @@ export default {
height: 100px;
cursor: pointer;
}
.store-logo-block {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.legal-photo-block {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.legal-photo-list {
display: flex;
align-items: center;
gap: 10px;
}
.form-tip {
color: #999;
font-size: 12px;
line-height: 1.5;
}
</style>

View File

@@ -24,12 +24,11 @@
<el-tree
ref="treeRef"
:data="data"
lazy
:load="loadNode"
:props="treeProps"
node-key="id"
show-checkbox
highlight-current
default-expand-all
:check-strictly="!strict"
:current-node-key="currentNodeKey"
@node-click="onNodeClick"
@@ -109,7 +108,6 @@
<script>
import {
initDepartment,
loadDepartment,
addDepartment,
editDepartment,
deleteDepartment,
@@ -176,23 +174,20 @@ export default {
initDepartment().then((res) => {
this.loading = false;
if (res.success) {
this.data = this.bubbleSort(res.result);
const list = Array.isArray(res.result) ? res.result : [];
this.data = this.bubbleSort(this.normalizeDepartmentTree(list));
}
});
},
loadNode(node, resolve) {
const id = node.data?.id;
if (id === undefined || id === null) {
resolve([]);
return;
}
loadDepartment(id).then((res) => {
this.loadingEdit = false;
if (res.success) {
resolve(res.result || []);
normalizeDepartmentTree(list) {
return list.map((item) => {
const node = { ...item };
if (Array.isArray(node.children) && node.children.length > 0) {
node.children = this.normalizeDepartmentTree(node.children);
} else {
resolve([]);
delete node.children;
}
return node;
});
},
onNodeClick(data) {
@@ -235,6 +230,7 @@ export default {
this.selectCount = checkedNodes.length;
},
bubbleSort(array) {
if (!Array.isArray(array)) return [];
const len = array.length;
if (len < 2) return array;
for (let i = 0; i < len; i++) {

View File

@@ -77,7 +77,7 @@ export default {
selectDate: null,
searchForm: {
type: 1,
key: "",
searchKey: "",
operatorName: "",
pageNumber: 1,
pageSize: 20,
@@ -114,15 +114,23 @@ export default {
},
handleSearch() {
this.searchForm.pageNumber = 1;
if (this.selectDate && this.selectDate.length === 2) {
this.searchForm.startDate = this.selectDate[0];
this.searchForm.endDate = this.selectDate[1];
}
this.getLogList();
},
getLogList() {
this.loading = true;
getLogListData(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
if (res && res.success) {
this.data = res.result.records;
this.total = res.result.total;
} else {
this.data = [];
this.total = 0;
this.$Message.error((res && res.message) || "日志查询失败");
}
});
},

View File

@@ -135,8 +135,10 @@
v-model="menuModalVisible"
:title="modalTitle"
width="500px"
align-center
append-to-body
:close-on-click-modal="false"
top="30px"
class="menu-manage-dialog"
>
<el-form ref="formAdd" :model="formAdd" label-width="100px" :rules="formValidate">
<div v-if="showParent">
@@ -557,3 +559,10 @@ export default {
align-items: center;
}
</style>
<style lang="scss">
.menu-manage-dialog .el-dialog__body {
max-height: calc(100vh - 220px);
overflow-y: auto;
}
</style>

View File

@@ -135,8 +135,10 @@
v-model="menuModalVisible"
:title="modalTitle"
width="500px"
align-center
append-to-body
:close-on-click-modal="false"
top="30px"
class="menu-manage-dialog"
>
<el-form ref="formAdd" :model="formAdd" label-width="100px" :rules="formValidate">
<div v-if="showParent">
@@ -557,3 +559,10 @@ export default {
align-items: center;
}
</style>
<style lang="scss">
.menu-manage-dialog .el-dialog__body {
max-height: calc(100vh - 220px);
overflow-y: auto;
}
</style>

View File

@@ -0,0 +1,484 @@
<template>
<div class="search">
<el-card>
<div class="toolbar">
<div class="toolbar-title">消息模板管理</div>
<div>
<el-button size="small" style="margin-right: 8px" @click="reloadCurrent">刷新</el-button>
<el-button size="small" style="margin-right: 8px" :loading="syncOaLoading" @click="syncOaTemplates">
同步服务号模板
</el-button>
<el-button size="small" :loading="syncMpLoading" @click="syncMpTemplates">同步小程序模板</el-button>
</div>
</div>
<el-tabs v-model="activeTab">
<el-tab-pane label="会员消息模板" name="member">
<el-alert
type="info"
show-icon
:closable="false"
style="margin-bottom: 10px"
title="支持维护站内信、微信服务号消息、微信小程序订阅消息开关。未匹配模板的渠道会显示禁用。"
/>
<el-table v-loading="loading" border :data="memberRows" style="width: 100%">
<el-table-column prop="index" label="序号" width="80" align="center" />
<el-table-column prop="noticeNode" label="模板应用场景" min-width="180" show-overflow-tooltip />
<el-table-column label="站内信" width="110" align="center">
<template #default="{ row }">
<el-switch
:model-value="isOpen(row.noticeStatus)"
@change="(v) => toggleNoticeChannel(v, row)"
/>
</template>
</el-table-column>
<el-table-column label="微信服务号消息" width="140" align="center">
<template #default="{ row }">
<el-switch
:model-value="isOpen(row.oaStatus)"
:disabled="!row.oaId"
@change="(v) => toggleOaChannel(v, row)"
/>
</template>
</el-table-column>
<el-table-column label="微信小程序订阅消息" width="160" align="center">
<template #default="{ row }">
<el-switch
:model-value="isOpen(row.mpStatus)"
:disabled="!row.mpId"
@change="(v) => toggleMpChannel(v, row)"
/>
</template>
</el-table-column>
<el-table-column label="邮箱" width="120" align="center">
<template #default="{ row }">
<el-switch
:model-value="isOpen(row.emailStatus)"
:disabled="!row.emailContent"
@change="(v) => toggleEmailChannel(v, row)"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="400" align="center" fixed="right">
<template #default="{ row }">
<div class="ops">
<a class="link-text" @click="openNoticeModal(row)">站内信</a>
<span class="op-split">|</span>
<a
:class="row.oaId ? 'link-text' : 'link-disabled'"
@click="row.oaId && openOaModal(row)"
>
微信服务号消息
</a>
<span class="op-split">|</span>
<a
:class="row.mpId ? 'link-text' : 'link-disabled'"
@click="row.mpId && openMpModal(row)"
>
微信小程序订阅消息
</a>
<span class="op-split">|</span>
<a class="link-text" @click="openEmailModal(row)">邮箱</a>
</div>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:total="total"
:page-sizes="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="loadMemberPage"
@size-change="changePageSize"
/>
</div>
</el-tab-pane>
<el-tab-pane label="商户消息模板" name="store">
<el-alert type="info" :closable="false" title="商户消息模板待扩展,后续可复用会员模板能力。" />
</el-tab-pane>
<el-tab-pane label="平台消息模板" name="platform">
<el-alert type="info" :closable="false" title="平台消息模板待扩展,后续可复用会员模板能力。" />
</el-tab-pane>
<el-tab-pane label="邮箱消息模板" name="email">
<el-alert type="info" :closable="false" title="邮箱消息模板待扩展,后续可按场景映射邮箱模板。" />
</el-tab-pane>
</el-tabs>
</el-card>
<el-dialog v-model="noticeModalVisible" title="站内信模板" width="700px">
<el-form label-width="120px">
<el-form-item label="模板应用场景">
<el-input :model-value="currentRow.noticeNode || '-'" readonly />
</el-form-item>
<el-form-item label="启用">
<el-switch
:model-value="isOpen(currentRow.noticeStatus)"
@change="(v) => toggleNoticeChannel(v, currentRow)"
/>
</el-form-item>
<el-form-item label="模板内容">
<el-input :model-value="noticeDetailText" type="textarea" :autosize="{ minRows: 4, maxRows: 8 }" readonly />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="noticeModalVisible = false">关闭</el-button>
</template>
</el-dialog>
<el-dialog v-model="oaModalVisible" title="微信服务号消息模板" width="700px">
<el-form label-width="140px">
<el-form-item label="模板应用场景">
<el-input :model-value="currentRow.noticeNode || '-'" readonly />
</el-form-item>
<el-form-item label="启用">
<el-switch
:model-value="isOpen(currentRow.oaStatus)"
:disabled="!currentRow.oaId"
@change="(v) => toggleOaChannel(v, currentRow)"
/>
</el-form-item>
<el-form-item label="模板名称">
<el-input :model-value="currentRow.oaName || '-'" readonly />
</el-form-item>
<el-form-item label="微信模板ID">
<el-input :model-value="currentRow.oaCode || '-'" readonly />
</el-form-item>
<el-form-item label="模板内容">
<el-input
:model-value="currentRow.oaContent || '-'"
type="textarea"
:autosize="{ minRows: 4, maxRows: 8 }"
readonly
/>
</el-form-item>
<el-alert v-if="!currentRow.oaId" type="warning" :closable="false" title="当前场景未匹配到微信服务号模板。" />
</el-form>
<template #footer>
<el-button @click="oaModalVisible = false">关闭</el-button>
</template>
</el-dialog>
<el-dialog v-model="emailModalVisible" title="邮箱消息模板" width="700px">
<el-form label-width="120px">
<el-form-item label="模板应用场景">
<el-input :model-value="currentRow.noticeNode || '-'" readonly />
</el-form-item>
<el-form-item label="启用">
<el-switch
:model-value="isOpen(currentRow.emailStatus)"
@change="(v) => toggleEmailChannel(v, currentRow)"
/>
</el-form-item>
<el-form-item label="模板内容">
<el-input :model-value="emailDetailText" type="textarea" :autosize="{ minRows: 4, maxRows: 8 }" readonly />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="emailModalVisible = false">关闭</el-button>
</template>
</el-dialog>
<el-dialog v-model="mpModalVisible" title="微信小程序订阅消息模板" width="700px">
<el-form label-width="150px">
<el-form-item label="模板应用场景">
<el-input :model-value="currentRow.noticeNode || '-'" readonly />
</el-form-item>
<el-form-item label="启用">
<el-switch
:model-value="isOpen(currentRow.mpStatus)"
:disabled="!currentRow.mpId"
@change="(v) => toggleMpChannel(v, currentRow)"
/>
</el-form-item>
<el-form-item label="模板名称">
<el-input :model-value="currentRow.mpName || '-'" readonly />
</el-form-item>
<el-form-item label="公共模板库ID">
<el-input :model-value="currentRow.mpTemplateId || '-'" readonly />
</el-form-item>
<el-form-item label="订阅消息模板ID">
<el-input :model-value="currentRow.mpCode || '-'" readonly />
</el-form-item>
<el-form-item label="模板内容">
<el-input
:model-value="currentRow.mpContent || '-'"
type="textarea"
:autosize="{ minRows: 4, maxRows: 8 }"
readonly
/>
</el-form-item>
<el-alert v-if="!currentRow.mpId" type="warning" :closable="false" title="当前场景未匹配到微信小程序模板。" />
</el-form>
<template #footer>
<el-button @click="mpModalVisible = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import * as API_Setting from "@/api/setting.js";
export default {
name: "messageTemplate",
data() {
return {
loading: false,
activeTab: "member",
syncOaLoading: false,
syncMpLoading: false,
total: 0,
searchForm: {
pageNumber: 1,
pageSize: 20,
},
memberRows: [],
currentRow: {},
noticeDetailText: "",
noticeModalVisible: false,
oaModalVisible: false,
mpModalVisible: false,
emailModalVisible: false,
emailDetailText: "",
};
},
methods: {
isOpen(status) {
return String(status || "").toUpperCase() === "OPEN";
},
wechatEnableToOpenClose(enable) {
if (enable === true || enable === "true" || enable === 1) return "OPEN";
return "CLOSE";
},
toOpenClose(flag) {
return flag ? "OPEN" : "CLOSE";
},
safeText(v) {
return v === undefined || v === null ? "" : String(v);
},
pickByKeys(obj, keys) {
for (let i = 0; i < keys.length; i++) {
const val = obj ? obj[keys[i]] : "";
if (val !== undefined && val !== null && val !== "") return val;
}
return "";
},
formatOaContent(item) {
if (!item) return "";
const first = this.pickByKeys(item, ["first", "title", "name"]);
const remark = this.pickByKeys(item, ["remark", "content"]);
const keywords = Array.isArray(item.keywords)
? item.keywords.map((k) => this.pickByKeys(k, ["name", "value", "label"])).filter(Boolean).join(" / ")
: this.safeText(item.keywordsText || "");
return [first, keywords, remark].filter(Boolean).join("\n");
},
formatMpContent(item) {
if (!item) return "";
const keywordsText = this.safeText(item.keywordsText);
if (keywordsText) return keywordsText;
if (Array.isArray(item.keywords)) {
return item.keywords.map((k) => this.pickByKeys(k, ["name", "value", "label"])).filter(Boolean).join(" / ");
}
return this.safeText(item.content);
},
async loadMemberPage() {
this.loading = true;
try {
const res = await API_Setting.getMessageTemplateAggregatePage(this.searchForm);
if (!(res && res.success && res.result)) {
this.memberRows = [];
this.total = 0;
this.$Message.error((res && res.message) || "加载消息模板失败");
return;
}
const records = res.result.records || [];
this.total = res.result.total || 0;
this.memberRows = records.map((agg, idx) => {
const item = { ...(agg.notice || {}) };
const oa = agg.wechatOa;
const mp = agg.wechatMp;
return {
...item,
emailStatus: item.emailStatus || "CLOSE",
emailContent: item.emailContent || "",
index: (this.searchForm.pageNumber - 1) * this.searchForm.pageSize + idx + 1,
oaId: oa ? oa.id : "",
oaName: oa ? this.pickByKeys(oa, ["name", "templateName", "title"]) : "",
oaCode: oa ? this.pickByKeys(oa, ["code", "templateId"]) : "",
oaStatus: oa ? this.wechatEnableToOpenClose(oa.enable) : "",
oaRaw: oa || null,
oaContent: this.formatOaContent(oa),
mpId: mp ? mp.id : "",
mpName: mp ? this.pickByKeys(mp, ["name", "templateName", "title"]) : "",
mpTemplateId: mp ? this.pickByKeys(mp, ["templateId"]) : "",
mpCode: mp ? this.pickByKeys(mp, ["code"]) : "",
mpStatus: mp ? this.wechatEnableToOpenClose(mp.enable) : "",
mpRaw: mp || null,
mpContent: this.formatMpContent(mp),
};
});
} finally {
this.loading = false;
}
},
reloadCurrent() {
if (this.activeTab === "member") this.loadMemberPage();
},
async syncOaTemplates() {
this.syncOaLoading = true;
try {
const res = await API_Setting.wechatMessageSync();
if (res && res.success) {
await this.loadMemberPage();
this.$Message.success("服务号模板同步成功");
} else {
this.$Message.error((res && res.message) || "服务号模板同步失败");
}
} finally {
this.syncOaLoading = false;
}
},
async syncMpTemplates() {
this.syncMpLoading = true;
try {
const res = await API_Setting.wechatMPMessageSync();
if (res && res.success) {
await this.loadMemberPage();
this.$Message.success("小程序模板同步成功");
} else {
this.$Message.error((res && res.message) || "小程序模板同步失败");
}
} finally {
this.syncMpLoading = false;
}
},
changePageSize() {
this.searchForm.pageNumber = 1;
this.loadMemberPage();
},
async openNoticeModal(row) {
this.currentRow = { ...row };
this.noticeDetailText = row.noticeContent || "";
this.noticeModalVisible = true;
const res = await API_Setting.getNoticeMessageDetail(row.id);
if (res && res.success && res.result) {
const d = res.result;
this.noticeDetailText = [d.noticeTitle, d.noticeContent].filter(Boolean).join("\n");
}
},
openOaModal(row) {
this.currentRow = { ...row };
this.oaModalVisible = true;
},
openMpModal(row) {
this.currentRow = { ...row };
this.mpModalVisible = true;
},
async openEmailModal(row) {
this.currentRow = { ...row };
const fallback = row.emailContent || row.noticeContent || "";
this.emailDetailText = row.noticeTitle ? [row.noticeTitle, fallback].filter(Boolean).join("\n") : fallback;
this.emailModalVisible = true;
const res = await API_Setting.getNoticeMessageDetail(row.id);
if (res && res.success && res.result) {
const d = res.result;
if (d.emailContent) {
this.emailDetailText = d.emailContent;
} else {
this.emailDetailText = [d.noticeTitle, d.noticeContent].filter(Boolean).join("\n");
}
}
},
updateRowStatus(row, key, val) {
const idx = this.memberRows.findIndex((x) => x.id === row.id);
if (idx >= 0) this.memberRows[idx][key] = val;
if (this.currentRow && this.currentRow.id === row.id) this.currentRow[key] = val;
},
async toggleNoticeChannel(v, row) {
const old = row.noticeStatus;
this.updateRowStatus(row, "noticeStatus", this.toOpenClose(v));
const res = await API_Setting.updateMessageStatus(row.id, this.toOpenClose(v));
if (!(res && res.success)) {
this.updateRowStatus(row, "noticeStatus", old);
this.$Message.error((res && res.message) || "站内信开关更新失败");
} else {
this.$Message.success("站内信开关更新成功");
}
},
async toggleOaChannel(v, row) {
if (!row.oaId) return;
const old = row.oaStatus;
this.updateRowStatus(row, "oaStatus", this.toOpenClose(v));
const params = { id: row.oaId, enable: v };
try {
const res = await API_Setting.editWechatMessageTemplate(row.oaId, params);
if (!(res && res.success)) {
this.updateRowStatus(row, "oaStatus", old);
this.$Message.error((res && res.message) || "服务号开关更新失败");
} else {
this.$Message.success("服务号开关更新成功");
}
} catch (e) {
this.updateRowStatus(row, "oaStatus", old);
this.$Message.error((e && e.message) || "服务号开关更新失败");
}
},
async toggleMpChannel(v, row) {
if (!row.mpId) return;
const old = row.mpStatus;
this.updateRowStatus(row, "mpStatus", this.toOpenClose(v));
const params = { id: row.mpId, enable: v };
const res = await API_Setting.editWechatMPMessageTemplate(row.mpId, params);
if (!(res && res.success)) {
this.updateRowStatus(row, "mpStatus", old);
this.$Message.error((res && res.message) || "小程序开关更新失败");
} else {
this.$Message.success("小程序开关更新成功");
}
},
async toggleEmailChannel(v, row) {
const old = row.emailStatus || "CLOSE";
this.updateRowStatus(row, "emailStatus", this.toOpenClose(v));
const res = await API_Setting.updateNoticeMessageEmailStatus(row.id, this.toOpenClose(v));
if (!(res && res.success)) {
this.updateRowStatus(row, "emailStatus", old);
this.$Message.error((res && res.message) || "邮箱开关更新失败");
} else {
this.$Message.success("邮箱开关更新成功");
}
},
},
mounted() {
this.loadMemberPage();
},
};
</script>
<style lang="scss" scoped>
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.toolbar-title {
font-weight: 600;
}
.ops a {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
.link-disabled {
color: #c0c4cc;
cursor: not-allowed;
text-decoration: none;
}
.op-split {
display: inline-block;
margin: 0 8px;
color: #dcdfe6;
}
</style>

View File

@@ -57,7 +57,7 @@
<div v-if="row" class="ops">
<a class="link-text" @click="detail(row)">详细</a>
<span class="op-split">|</span>
<a class="link-text" @click="delete(row.id)">删除</a>
<a class="link-text" @click="removeMessage(row.id)">删除</a>
</div>
</template>
</el-table-column>
@@ -207,16 +207,21 @@
</el-select>
</el-form-item>
<el-form-item v-if="memberShow" label="选择会员" prop="scopeType">
<el-button type="primary" plain @click="addVip">选择会员</el-button>
<div v-if="messageSendForm.messageClient == 'member'" style="margin-top: 24px">
<el-table border :data="selectedMember" style="width: 100%">
<el-table-column prop="nickName" label="用户名称" min-width="120" />
<el-table-column label="手机号" min-width="120">
<div class="member-select-wrap">
<el-button type="primary" plain @click="addVip">选择会员</el-button>
<el-table
v-if="messageSendForm.messageClient == 'member'"
border
:data="selectedMember"
class="member-select-table"
>
<el-table-column prop="nickName" label="用户名称" min-width="140" show-overflow-tooltip />
<el-table-column label="手机号" min-width="140">
<template #default="{ row }">
<span v-if="row">{{ row.mobile || "暂未填写" }}</span>
</template>
</el-table-column>
<el-table-column label="操作" min-width="50" align="center">
<el-table-column label="操作" width="80" align="center">
<template #default="{ row, $index }">
<a v-if="row" class="link-text" @click="delUser($index)">删除</a>
</template>
@@ -235,9 +240,14 @@
<userList
v-if="checkUserList"
ref="memberLayout"
:selectedMember="true"
:selectedList="selectedMember"
@callback="callbackSelectUser"
/>
<template #footer>
<el-button @click="cancelMemberSelect">取消</el-button>
<el-button type="primary" @click="confirmMemberSelect">确定</el-button>
</template>
</el-dialog>
<el-dialog
@@ -359,6 +369,7 @@ export default {
return {
activeTab: "MESSAGE",
checkUserList: false,
memberSelectSnapshot: [],
selectedMember: [],
loading: true,
modalVisible: false,
@@ -455,10 +466,17 @@ export default {
this.loading = false;
},
addVip() {
this.memberSelectSnapshot = JSON.parse(JSON.stringify(this.selectedMember));
this.checkUserList = true;
this.$nextTick(() => {
this.$refs.memberLayout.selectedMember = true;
});
},
cancelMemberSelect() {
this.selectedMember = JSON.parse(JSON.stringify(this.memberSelectSnapshot));
this.reSelectMember();
this.checkUserList = false;
},
confirmMemberSelect() {
this.reSelectMember();
this.checkUserList = false;
},
paneChange(v) {
if (v == "SETTING") {
@@ -526,7 +544,7 @@ export default {
return shop ? shop.storeName : "";
});
},
delete(id) {
removeMessage(id) {
this.$Modal.confirm({
title: "确认删除",
content: "您确认删除此站内信 ?",
@@ -545,6 +563,7 @@ export default {
this.messageModalTitle = "发送站内信";
this.shopShow = false;
this.memberShow = false;
this.selectedMember = [];
this.messageSendForm = {
messageRange: "ALL",
messageClient: "member",
@@ -719,4 +738,11 @@ export default {
margin: 0 8px;
color: #dcdfe6;
}
.member-select-wrap {
width: 100%;
}
.member-select-table {
width: 100%;
margin-top: 12px;
}
</style>

View File

@@ -83,15 +83,27 @@ methodItem h4 {
border-radius: 6px;
}
}
.choose-member-item {
width: 100%;
.el-form-item__content {
margin-left: 0 !important;
width: 100%;
}
}
.choose-member {
display: flex;
width: 100%;
min-width: 580px;
height: 400px;
border: 1px solid #eee;
margin-bottom: 20px;
}
.source-member {
width: 50%;
flex: 3;
min-width: 0;
border-right: 1px solid #eee;
padding: 10px;
}
@@ -116,7 +128,8 @@ methodItem h4 {
white-space: nowrap;
}
.traget-member {
width: 50%;
flex: 2;
min-width: 0;
padding: 10px;
}
.scroll-card {

View File

@@ -184,7 +184,7 @@
<el-dialog
v-model="sendSmsModal"
:title="sendSmsModalTitle"
width="900px"
width="1100px"
:close-on-click-modal="false"
destroy-on-close
>
@@ -234,7 +234,7 @@
<el-radio value="2">自定义选择</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="customSms">
<el-form-item v-if="customSms" label-width="0" class="choose-member-item">
<div class="choose-member">
<div class="source-member">
<el-input

View File

@@ -161,6 +161,9 @@
box-sizing: border-box;
margin: 0 13px 8px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
.card {
width: 120px;
height: 120px;
@@ -172,12 +175,19 @@
img {
width: 100%;
height: 100%;
object-fit: cover;
}
.checkbox {
.card-checkbox {
position: absolute;
top: 10px;
right: 10px;
top: 8px;
right: 8px;
z-index: 1000;
height: auto;
margin: 0;
:deep(.el-checkbox__label) {
display: none;
}
}
.preview {
width: 100%;
@@ -226,35 +236,6 @@
display: flex;
flex-direction: row-reverse;
}
/* Checkbox默认的样式 */
.check-box {
.ivu-checkbox {
position: absolute;
right: 10px;
top: 10px;
z-index: 100;
}
}
/* 覆盖iView默认的Checkbox样式 */
.ivu-checkbox-wrapper {
/*font-size: 16px; !* 修改字体大小 *!*/
/*color: #495060; !* 修改文本颜色 *!*/
/* 添加其他需要的样式 */
}
.ivu-checkbox-inner {
/*width: 20px; !* 修改选框大小 *!*/
/*height: 20px;*/
/*border-color: #dcdee2; !* 修改边框颜色 *!*/
/* 添加其他需要的样式 */
}
/* 当Checkbox被选中时的样式 */
.ivu-checkbox-checked .ivu-checkbox-inner {
/*background-color: #2db7f5; !* 修改选中时的背景颜色 *!*/
}
/* 当Checkbox不可用时的样式 */
.ivu-checkbox-disabled .ivu-checkbox-inner {
/*background-color: #e9e9e9; !* 修改禁用状态下的背景颜色 *!*/
}
.demo-tree-render .ivu-tree-title{
width: 94%;

View File

@@ -35,6 +35,15 @@
}
}
.oss-select-footer {
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid #ededed;
display: flex;
justify-content: flex-end;
gap: 10px;
}
.tab-empty {
padding: 48px 0;
color: #999;
@@ -59,6 +68,10 @@
padding: 0 4px;
}
.oss-manage-date-popper {
z-index: 4000 !important;
}
</style>
<template>
<div class="search">
@@ -86,6 +99,8 @@
<el-date-picker
v-model="selectDate"
clearable
teleported
popper-class="oss-manage-date-popper"
value-format="YYYY-MM-DD"
start-placeholder="开始日期"
end-placeholder="结束日期"
@@ -165,37 +180,39 @@
<el-checkbox-group v-model="selectedOss" @change="selectOssChange">
<div class="img-box">
<div v-for="(item, index) in data" :key="index" class="img-item">
<el-checkbox :value="item.id + ',' + item.url" class="check-box">
<div
class="card"
@mouseenter="onMouseOver(item, index)"
@mouseleave="onMouseOut(item, index)"
>
<img :src="item.url" alt="" />
<div v-if="item.isShowPreview" class="preview">
<div @click.prevent="download(item)">
<el-tooltip content="下载" placement="top">
<el-icon :size="18"><Download /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="remove(item)">
<el-tooltip content="删除" placement="top">
<el-icon :size="18"><Delete /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="showPic(item)">
<el-tooltip content="预览" placement="top">
<el-icon :size="22"><View /></el-icon>
</el-tooltip>
</div>
<div
class="card"
:class="{ 'custom-checkbox-card-checked': selectedOss.includes(item.id + ',' + item.url) }"
@mouseenter="onMouseOver(item, index)"
@mouseleave="onMouseOut(item, index)"
>
<el-checkbox
:value="item.id + ',' + item.url"
class="card-checkbox"
@click.stop
/>
<img :src="item.url" alt="" />
<div v-if="item.isShowPreview" class="preview">
<div @click.prevent="download(item)">
<el-tooltip content="下载" placement="top">
<el-icon :size="18"><Download /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="remove(item)">
<el-tooltip content="删除" placement="top">
<el-icon :size="18"><Delete /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="showPic(item)">
<el-tooltip content="预览" placement="top">
<el-icon :size="22"><View /></el-icon>
</el-tooltip>
</div>
</div>
</el-checkbox>
<div>
<el-tooltip :content="item.name" placement="bottom">
<div class="text">{{ item.name }}</div>
</el-tooltip>
</div>
<el-tooltip :content="item.name" placement="bottom">
<div class="text">{{ item.name }}</div>
</el-tooltip>
</div>
</div>
</el-checkbox-group>
@@ -210,6 +227,10 @@
@current-change="pageChange"
/>
</div>
<div v-if="isComponent && selectImage" class="oss-select-footer">
<el-button @click="handleSelectCancel">取消</el-button>
<el-button type="primary" @click="handleSelectConfirm">确定</el-button>
</div>
</div>
</el-col>
</el-row>
@@ -488,6 +509,10 @@ export default {
type: Boolean,
default: false,
},
maxSelect: {
type: Number,
default: 0,
},
},
data() {
return {
@@ -954,8 +979,8 @@ export default {
if (val) this.selectImage = val
},
selectedOss(val) {
if (val && val.length) {
this.$emit("callback", {url: val[val.length - 1].split(',')[1]});
if (val && val.length && !(this.isComponent && this.selectImage)) {
this.$emit("callback", { url: val[val.length - 1].split(",")[1] });
}
},
// 初始化监听 是否清空所选图片
@@ -1062,6 +1087,11 @@ export default {
},
// 复选框值改变时触发
selectOssChange(e) {
if (this.maxSelect === 1 && e.length > 1) {
const last = e[e.length - 1];
this.selectedOss = [last];
e = [last];
}
if (e) {
this.selectList = e.map(item => {
return {id: item.split(',')[0]}
@@ -1291,6 +1321,18 @@ export default {
selectedParams(val) {
this.$emit("callback", val);
},
handleSelectConfirm() {
if (!this.selectedOss.length) {
this.$Message.warning("请选择图片");
return;
}
const last = this.selectedOss[this.selectedOss.length - 1];
this.$emit("callback", { url: last.split(",")[1] });
},
handleSelectCancel() {
this.selectedOss = [];
this.$emit("callback", { url: "" });
},
// 初始化数据
init() {
this.accessToken = {

View File

@@ -74,9 +74,10 @@
v-model="permModalVisible"
:title="modalTitle"
width="500px"
align-center
append-to-body
:close-on-click-modal="false"
class="permModal"
top="30px"
class="permModal role-perm-dialog"
>
<div v-loading="treeLoading" style="position: relative; max-height: 560px; overflow: auto">
<el-tree
@@ -115,7 +116,14 @@
</template>
</el-dialog>
<el-dialog v-model="selectIsSuperModel" title="选择菜单权限" width="800px">
<el-dialog
v-model="selectIsSuperModel"
title="选择菜单权限"
width="800px"
align-center
append-to-body
class="role-perm-dialog"
>
<div class="btns">
<el-button type="primary" class="btn-item" @click="setRole()">一键选中·数据权限</el-button>
<el-button class="btn-item" @click="setRole('onlyView')">一键选中·查看权限</el-button>
@@ -295,23 +303,30 @@ export default {
addRole() {
this.modalType = 0;
this.modalTitle = "添加角色";
this.$refs.roleForm?.resetFields();
delete this.roleForm.id;
this.roleForm = {
name: "",
description: "",
};
this.roleModalVisible = true;
this.$nextTick(() => {
this.$refs.roleForm?.resetFields();
this.$refs.roleForm?.clearValidate();
});
},
edit(v) {
this.modalType = 1;
this.modalTitle = "编辑角色";
this.$refs.roleForm?.resetFields();
for (let attr in v) {
if (v[attr] == null) {
v[attr] = "";
}
}
let str = JSON.stringify(v);
let roleInfo = JSON.parse(str);
const roleInfo = JSON.parse(JSON.stringify(v));
this.roleForm = roleInfo;
this.roleModalVisible = true;
this.$nextTick(() => {
this.$refs.roleForm?.clearValidate();
});
},
remove(v) {
this.$Modal.confirm({
@@ -553,3 +568,10 @@ export default {
margin-right: 20px;
}
</style>
<style lang="scss">
.role-perm-dialog .el-dialog__body {
max-height: calc(100vh - 220px);
overflow-y: auto;
}
</style>

View File

@@ -7,19 +7,19 @@
<div class="form-list">
<el-form
ref="formValidate"
label-width="120px"
label-width="140px"
label-position="right"
:model="formValidate"
:rules="ruleValidate"
>
<el-form-item label="买家PC端域名" prop="pc">
<el-input v-model="formValidate.pc" class="w200" />
<el-input v-model="formValidate.pc" />
</el-form-item>
<el-form-item label="买家WAP端域名" prop="wap">
<el-input v-model="formValidate.wap" class="w200" />
<el-input v-model="formValidate.wap" />
</el-form-item>
<el-form-item label="登录回调域名" prop="callbackUrl">
<el-input maxlength="300" v-model="formValidate.callbackUrl" class="w200" />
<el-input maxlength="300" v-model="formValidate.callbackUrl" />
</el-form-item>
<el-form-item label="操作">
<el-button @click="setupSetting">保存设置</el-button>
@@ -52,10 +52,11 @@ export default {
},
methods: {
submit(name) {
const that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
setupSetting() {
setSetting(this.type, this.formValidate).then((res) => {
@@ -67,8 +68,8 @@ export default {
});
},
init() {
this.res = JSON.parse(this.res);
this.formValidate = { ...this.res };
const setting = JSON.parse(this.res || "{}");
this.formValidate = { ...setting };
Object.keys(this.formValidate).forEach((item) => {
if (item.indexOf("pId") < 0) {
this.ruleValidate[item] = [
@@ -112,12 +113,21 @@ export default {
justify-content: flex-start;
}
.row {
width: 600px;
width: 100%;
max-width: 720px;
margin-right: 10px;
display: flex;
margin-bottom: 20px;
}
:deep(.el-input) {
width: 480px !important;
}
.el-input {
width: 480px;
}
.label-item {
display: flex;
align-items: center;
@@ -135,4 +145,8 @@ export default {
display: flex;
align-items: center;
}
:deep(.el-form-item__label) {
white-space: nowrap;
}
</style>

View File

@@ -44,6 +44,12 @@
<script>
import { setSetting } from "@/api/index";
import { handleSubmit } from "../setting/validate";
const DEFAULT_QQ_CONNECT_ITEMS = [
{ clientType: "PC", appId: "", appKey: "" },
{ clientType: "H5", appId: "", appKey: "" },
];
export default {
data() {
return {
@@ -63,10 +69,11 @@ export default {
},
methods: {
submit(name) {
const that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
setupSetting() {
setSetting(this.type, {
@@ -81,17 +88,15 @@ export default {
}
});
},
async init() {
this.formValidate = JSON.parse(this.res).qqConnectSettingItemList;
Object.keys(this.formValidate).forEach((item) => {
this.ruleValidate[item] = [
{
required: true,
message: "请填写必填项",
trigger: "blur",
},
];
});
init() {
const setting = JSON.parse(this.res || "{}");
const items = setting.qqConnectSettingItemList;
this.formValidate =
Array.isArray(items) && items.length ? items : DEFAULT_QQ_CONNECT_ITEMS;
this.ruleValidate = {
appId: [{ required: true, message: "请填写必填项", trigger: "blur" }],
appKey: [{ required: true, message: "请填写必填项", trigger: "blur" }],
};
},
},
};

View File

@@ -45,6 +45,13 @@
import { setSetting } from "@/api/index";
import { handleSubmit } from "../setting/validate";
const DEFAULT_WECHAT_CONNECT_ITEMS = [
{ clientType: "PC", appId: "", appSecret: "" },
{ clientType: "H5", appId: "", appSecret: "" },
{ clientType: "APP", appId: "", appSecret: "" },
{ clientType: "WECHAT_MP", appId: "", appSecret: "" },
];
export default {
data() {
return {
@@ -64,10 +71,11 @@ export default {
},
methods: {
submit(name) {
const that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
setupSetting() {
setSetting(this.type, {
@@ -82,17 +90,15 @@ export default {
}
});
},
async init() {
this.formValidate = JSON.parse(this.res).wechatConnectSettingItems;
Object.keys(this.formValidate).forEach((item) => {
this.ruleValidate[item] = [
{
required: true,
message: "请填写必填项",
trigger: "blur",
},
];
});
init() {
const setting = JSON.parse(this.res || "{}");
const items = setting.wechatConnectSettingItems;
this.formValidate =
Array.isArray(items) && items.length ? items : DEFAULT_WECHAT_CONNECT_ITEMS;
this.ruleValidate = {
appId: [{ required: true, message: "请填写必填项", trigger: "blur" }],
appSecret: [{ required: true, message: "请填写必填项", trigger: "blur" }],
};
},
},
};

View File

@@ -49,10 +49,11 @@ export default {
},
methods: {
submit(name) {
let that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
// 保存设置
setupSetting() {
@@ -66,9 +67,8 @@ export default {
},
// 实例化数据
init() {
this.res = JSON.parse(this.res);
this.formValidate = { ...this.res };
const setting = JSON.parse(this.res || "{}");
this.formValidate = { ...setting };
Object.keys(this.formValidate).forEach((item) => {
this.ruleValidate[item] = [
{

View File

@@ -41,6 +41,14 @@
import { setSetting } from "@/api/index";
import { handleSubmit } from "../setting/validate";
import { getPaymentSupportForm } from "@/api/setting";
const DEFAULT_PAYMENT_SUPPORT_ITEMS = [
{ client: "PC", supports: [] },
{ client: "H5", supports: [] },
{ client: "APP", supports: [] },
{ client: "WECHAT_MP", supports: [] },
];
export default {
data() {
return {
@@ -51,7 +59,7 @@ export default {
WECHAT_MP: "小程序端",
PC: "PC端",
},
formValidate: {},
formValidate: [],
payWay: {
ALIPAY: "支付宝支付",
WECHAT: "微信支付",
@@ -67,10 +75,11 @@ export default {
},
methods: {
submit(name) {
const that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
handleChangePayType(val) {
this.$Modal.confirm({
@@ -97,13 +106,17 @@ export default {
}
});
},
async init() {
this.formValidate = JSON.parse(this.res).paymentSupportItems;
init() {
const setting = JSON.parse(this.res || "{}");
const items = setting.paymentSupportItems;
this.formValidate =
Array.isArray(items) && items.length ? items : DEFAULT_PAYMENT_SUPPORT_ITEMS;
this.checkSupport = JSON.parse(JSON.stringify(this.formValidate));
console.log(this.formValidate);
await getPaymentSupportForm().then((res) => {
this.supportForm = res.result;
getPaymentSupportForm().then((res) => {
if (res.success && res.result) {
this.supportForm = res.result;
}
});
},
},

View File

@@ -74,10 +74,11 @@ export default {
},
methods: {
submit(name) {
let that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
// 保存设置
setupSetting() {
@@ -91,9 +92,8 @@ export default {
},
// 实例化数据
init() {
this.res = JSON.parse(this.res);
this.formValidate = { ...this.res };
const setting = JSON.parse(this.res || "{}");
this.formValidate = { ...setting };
Object.keys(this.formValidate).forEach((item) => {
if (item.indexOf("pId") < 0) {
this.ruleValidate[item] = [

View File

@@ -96,7 +96,6 @@ export default {
},
data() {
return {
handleSubmit,
picModelFlag: false,
formValidate: {
buyerSideLogo: "",
@@ -126,10 +125,11 @@ export default {
});
},
submit(name) {
const that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
callbackSelected(val) {
this.picModelFlag = false;

View File

@@ -50,16 +50,18 @@
</el-input>
</el-form-item>
</div>
<div class="label-btns">
<el-button type="primary" @click="submit('formValidate')">保存</el-button>
<div class="es-buttons">
<div class="label-btns goods-setting-btns">
<div class="goods-setting-action-row">
<el-button type="success" @click="createIndex()">重新生成所有商品索引</el-button>
<el-button type="info" @click="deleteDownGoods()">删除ES中下架的商品</el-button>
<el-button type="warning" @click="generateCache()">生成所有商品的缓存</el-button>
<el-button type="danger" @click="deleteNotExistIndex()">删除不存在的索引</el-button>
<div class="progress-item" v-if="showProgress">
<el-progress :percentage="progressVal" />
</div>
</div>
<div class="progress-item" v-if="showProgress">
<el-progress :percentage="progressVal" />
<div class="goods-setting-save-row">
<el-button type="primary" @click="submit('formValidate')">保存</el-button>
</div>
</div>
</el-form>
@@ -207,7 +209,27 @@ export default {
.label-item {
display: flex;
}
.goods-setting-btns {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
margin-left: 150px;
}
.goods-setting-action-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.goods-setting-action-row .progress-item {
flex: 0 0 240px;
min-width: 200px;
}
.goods-setting-action-row .progress-item :deep(.el-progress) {
width: 100%;
}
:deep(.el-input){
width: 100px !important;
width: 180px !important;
}
</style>

View File

@@ -33,10 +33,11 @@ export default {
methods: {
// 保存
submit(name) {
let that = this;
if (handleSubmit(that, name)) {
this.setupSetting()
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
// 保存设置
setupSetting() {

View File

@@ -71,10 +71,11 @@ export default {
},
methods: {
submit(name) {
const that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
setupSetting() {
setSetting(this.type, this.formValidate).then((res) => {

View File

@@ -63,10 +63,11 @@ export default {
},
methods: {
submit(name) {
const that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
setupSetting() {
setSetting(this.type, this.formValidate).then((res) => {
@@ -115,12 +116,12 @@ export default {
}
.el-input {
width: 100px;
width: 180px;
margin-right: 10px;
}
:deep(.el-input){
width: 100px !important;
width: 180px !important;
}
.desc {

View File

@@ -120,10 +120,11 @@ export default {
methods: {
// 保存
submit(name) {
let that = this;
if (handleSubmit(that, name)) {
this.setupSetting()
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
// 保存设置
setupSetting() {

View File

@@ -69,10 +69,11 @@ export default {
},
methods: {
submit(name) {
const that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
setupSetting() {
setSetting(this.type, this.formValidate).then((res) => {
@@ -140,27 +141,27 @@ export default {
display: flex;
> .el-input-number {
width: 100px;
width: 120px;
margin-right: 5px;
}
> .el-input-number:nth-last-of-type(1) {
width: 150px;
width: 180px;
margin-right: 5px;
}
> .el-input {
width: 100px;
width: 180px;
margin: 0 10px;
}
}
:deep(.el-input){
width: 70px !important;
width: 180px !important;
}
.el-input {
width: 70px;
width: 180px;
margin-right: 10px;
}

View File

@@ -115,10 +115,11 @@ export default {
methods: {
// 保存
submit(name) {
let that = this;
if (handleSubmit(that, name)) {
this.setupSetting()
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
// 保存设置
setupSetting() {

View File

@@ -44,10 +44,11 @@ export default {
props: ["res", "type"],
methods: {
submit(name) {
const that = this;
if (handleSubmit(that, name)) {
this.setupSetting();
}
handleSubmit(this, name)
.then(() => {
this.setupSetting();
})
.catch(() => {});
},
setupSetting() {
setSetting(this.type, this.formValidate).then((res) => {

View File

@@ -1,23 +1,28 @@
//表单中必填
export function validateRequired(rule, value, callback) {
if (value != void 0 || value != null) {
if (value !== undefined && value !== null && value !== "") {
callback();
} else {
return callback(new Error("必填项不能为空"));
callback(new Error("必填项不能为空"));
}
}
// 验证必填项
// 验证必填项Element Plus 表单校验为异步,返回 Promise
export function handleSubmit(that, name) {
let flag = false;
that.$refs[name].validate(valid => {
if (valid) {
flag = true;
return flag;
} else {
that.$Message.error("请正确填写内容!");
return flag;
return new Promise((resolve, reject) => {
const form = that.$refs[name];
if (!form) {
that.$Message.error("表单未就绪,请稍后重试");
reject(new Error("form ref missing"));
return;
}
form.validate((valid) => {
if (valid) {
resolve(true);
} else {
that.$Message.error("请正确填写内容!");
reject(new Error("validation failed"));
}
});
});
return flag
}

View File

@@ -9,10 +9,10 @@
:name="tabItem.type"
>
<component
v-if="settingData"
v-if="settingData && selected === tabItem.type"
:is="templateSetting[tabItem.type]"
:res="settingData"
:type="selected"
:type="tabItem.type"
/>
</el-tab-pane>
</el-tabs>
@@ -130,6 +130,8 @@ export default {
getSetting(name).then((res) => {
if (res.result) {
this.settingData = JSON.stringify(res.result);
} else {
this.settingData = "{}";
}
});
},

View File

@@ -94,9 +94,11 @@
v-model="userModalVisible"
:title="modalTitle"
width="500px"
align-center
append-to-body
:close-on-click-modal="false"
top="30px"
destroy-on-close
class="user-manage-dialog"
>
<el-form ref="form" :model="form" label-width="70px" :rules="formValidate">
<el-form-item label="用户名" prop="username">
@@ -106,7 +108,7 @@
<el-input v-model="form.nickName" autocomplete="off" />
</el-form-item>
<el-form-item v-if="modalType == 0" label="密码" prop="password" :error="errorPass">
<el-input v-model="form.password" type="password" show-password autocomplete="off" />
<el-input v-model="form.password" type="password" show-password autocomplete="new-password" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" />
@@ -326,7 +328,6 @@ export default {
add() {
this.modalType = 0;
this.modalTitle = "添加用户";
this.$refs.form?.resetFields();
this.form = {
username: "",
mobile: "",
@@ -336,20 +337,23 @@ export default {
departmentId: 0,
departmentTitle: "",
};
this.$refs.depTree.setData("", "");
this.userModalVisible = true;
this.$nextTick(() => {
this.$refs.form?.resetFields();
this.$refs.depTree?.setData("", "");
});
},
edit(v) {
this.form = JSON.parse(JSON.stringify(v));
this.modalType = 1;
this.modalTitle = "编辑用户";
this.$refs.form?.resetFields();
for (let attr in this.form) {
if (this.form[attr] == null) {
this.form[attr] = "";
}
}
this.$refs.depTree.setData(this.form.departmentId, this.form.departmentTitle);
const departmentId = this.form.departmentId;
const departmentTitle = this.form.departmentTitle;
let selectRolesId = [];
if (this.form.roles) {
this.form.roles.forEach(function (e) {
@@ -358,6 +362,10 @@ export default {
}
this.form.roles = selectRolesId;
this.userModalVisible = true;
this.$nextTick(() => {
this.$refs.form?.resetFields();
this.$refs.depTree?.setData(departmentId, departmentTitle);
});
},
enable(v) {
let params = { status: true };
@@ -462,3 +470,36 @@ export default {
color: #dcdee2;
}
</style>
<style lang="scss">
.user-manage-dialog {
.el-dialog__body {
max-height: calc(100vh - 220px);
overflow-y: auto;
}
.el-input__wrapper {
background-color: #fff !important;
box-shadow: 0 0 0 1px var(--el-border-color) inset !important;
&.is-focus {
box-shadow: 0 0 0 1px var(--el-border-color) inset !important;
}
}
.el-input__inner {
background-color: transparent !important;
&:-webkit-autofill,
&:-webkit-autofill:hover,
&:-webkit-autofill:focus,
&:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 1000px #fff inset !important;
box-shadow: 0 0 0 1000px #fff inset !important;
-webkit-text-fill-color: #606266 !important;
caret-color: #606266;
transition: background-color 99999s ease-out;
}
}
}
</style>