From 3a68c4380090ce1ded8941ef30d22a8a576ca6a3 Mon Sep 17 00:00:00 2001
From: mayf <m13160102112@163.com>
Date: 星期二, 03 九月 2024 17:34:07 +0800
Subject: [PATCH] 优惠券,会员等级,积分商城dev
---
components/coupon/member-rule.vue | 125 +++
package-lock.json | 14
pages/marketing/coupon/member/index.vue | 80 ++
components/base-sidebar.vue | 29
pages/marketing/coupon/activity/index.vue | 259 ++++++
pages/marketing/member-level.vue | 146 +++
pages/marketing/point-mall/point-distribution.vue | 224 +++++
config/default-dev.json5 | 2
utils/coupon-form.js | 173 ++++
pages/marketing/point-mall/goods.vue | 242 ++++++
pages/marketing/coupon/activity/_id.vue | 72 +
assets/coupon/detail.scss | 11
components/coupon/select-shop-user.vue | 187 ++++
pages/marketing/coupon/member/_id.vue | 66 +
pages/marketing/point-mall/coupon/index.vue | 238 ++++++
pages/marketing/coupon/user/_id.vue | 65 +
package.json | 4
pages/marketing/coupon/user/index.vue | 138 +++
pages/marketing/point-mall/coupon/_id.vue | 64 +
components/input-select.vue | 129 +++
plugins/mixins/coupon-detail.vue | 27
21 files changed, 2,267 insertions(+), 28 deletions(-)
diff --git a/assets/coupon/detail.scss b/assets/coupon/detail.scss
new file mode 100644
index 0000000..6d60d03
--- /dev/null
+++ b/assets/coupon/detail.scss
@@ -0,0 +1,11 @@
+.coupon-detail {
+ border-radius: 0;
+ .el-bus-title {
+ margin-bottom: 10px;
+ }
+ ::v-deep {
+ .el-form-item {
+ margin-bottom: 0;
+ }
+ }
+}
diff --git a/components/base-sidebar.vue b/components/base-sidebar.vue
index f528164..fd736c8 100644
--- a/components/base-sidebar.vue
+++ b/components/base-sidebar.vue
@@ -162,26 +162,19 @@
::v-deep .el-submenu__icon-arrow {
color: #fff;
}
- .el-submenu {
- @for $i from 1 through 4 {
- .el-menu-item {
- padding-left: 64px !important;
+ .el-menu-item {
+ @for $i from 2 through 4 {
+ &.el-bus-level-#{$i} {
+ padding-left: 20px * $i + 24px !important;
}
- .el-submenu.level-#{$i} {
- ::v-deep {
- .el-submenu__title {
- @if $i==0 {
- padding-left: 20px !important;
- } @else {
- padding-left: 32px * ($i + 1) !important;
- }
- }
- }
+ }
+ }
+ .el-menu {
+ @for $i from 2 through 4 {
+ &:has(.el-submenu.el-bus-level-#{$i}) {
.el-menu-item {
- @if $i==0 {
- padding-left: 64px !important;
- } @else {
- padding-left: 32px * ($i + 1) + 10px !important;
+ &.el-bus-level-#{$i} {
+ padding-left: 20px * $i !important;
}
}
}
diff --git a/components/coupon/member-rule.vue b/components/coupon/member-rule.vue
new file mode 100644
index 0000000..948e2d6
--- /dev/null
+++ b/components/coupon/member-rule.vue
@@ -0,0 +1,125 @@
+<template>
+ <div class="member-rule">
+ <div class="mr-8">消费</div>
+ <el-input-number
+ v-model="amountValue"
+ v-bind="inputAttrs"
+ @input="onAmountChange"
+ ></el-input-number>
+ <div class="mx-8">元等于</div>
+ <el-input-number
+ v-model="growthValue"
+ v-bind="inputAttrs"
+ @input="onGrowthChange"
+ ></el-input-number>
+ <div class="ml-8">成长值</div>
+ </div>
+</template>
+
+<script>
+import { t } from 'el-business'
+import utils from 'el-business-utils'
+
+export default {
+ props: {
+ value: {
+ type: Array,
+ default: () => [],
+ },
+ },
+ rules(item) {
+ const errorMsg = `${t('elBus.common.pleaseEnter')}${
+ item.label ? item.label.replace(/:/g, '').replace(/:/g, '') : ''
+ }`
+ return [
+ {
+ required: true,
+ message: errorMsg,
+ },
+ {
+ validator: (rule, value, callback) => {
+ if (
+ Array.isArray(value) &&
+ value.filter((i) => !utils.isTrueEmpty(i)).length === 2
+ ) {
+ callback()
+ } else {
+ callback(new Error(errorMsg))
+ }
+ },
+ },
+ ]
+ },
+ inputFormat(row, item) {
+ if (
+ Array.isArray(item.commonFormatProps) &&
+ item.commonFormatProps.length === 2
+ ) {
+ const amount = item.commonFormatProps[0]
+ const growth = item.commonFormatProps[1]
+ if (amount in row || growth in row) {
+ return [row[amount], row[growth]]
+ }
+ } else {
+ console.warn('please set commonFormatProps')
+ }
+ },
+ outputFormat(val, item) {
+ if (
+ Array.isArray(item.commonFormatProps) &&
+ item.commonFormatProps.length === 2
+ ) {
+ const amount = item.commonFormatProps[0]
+ const growth = item.commonFormatProps[1]
+ return {
+ [amount]: !utils.isTrueEmpty(val?.[0]) ? val[0] : null,
+ [growth]: !utils.isTrueEmpty(val?.[1]) ? val[1] : null,
+ }
+ } else {
+ console.warn('please set commonFormatProps')
+ }
+ },
+ data() {
+ return {
+ amountValue: null,
+ growthValue: null,
+ inputAttrs: {
+ precision: 0,
+ min: 0,
+ controls: false,
+ },
+ }
+ },
+ watch: {
+ value: {
+ immediate: true,
+ handler(value) {
+ value = value || []
+ this.amountValue = value?.[0] || undefined
+ this.growthValue = value?.[1] || undefined
+ },
+ },
+ },
+ methods: {
+ onAmountChange(e) {
+ const value = [e, this.growthValue]
+ this.emitValue(value)
+ },
+ onGrowthChange(e) {
+ const value = [this.amountValue, e]
+ this.emitValue(value)
+ },
+ emitValue(value) {
+ this.$emit('input', value)
+ this.$emit('change', value)
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.member-rule {
+ display: flex;
+ align-items: center;
+}
+</style>
diff --git a/components/coupon/select-shop-user.vue b/components/coupon/select-shop-user.vue
new file mode 100644
index 0000000..c3da491
--- /dev/null
+++ b/components/coupon/select-shop-user.vue
@@ -0,0 +1,187 @@
+<template>
+ <div class="select-shop-user">
+ <el-button v-if="!disabled" type="primary" @click="chooseUser"
+ >指定用户</el-button
+ >
+ <el-table v-if="value && value.length > 0" :data="value">
+ <el-table-column label="id" prop="id"></el-table-column>
+ <el-table-column label="店铺名称" prop="name"></el-table-column>
+ <el-table-column label="联系方式" prop="tel"></el-table-column>
+ <el-table-column v-if="!disabled" label="操作">
+ <template #default="{ $index }">
+ <text-button type="danger" @click="deleteUser($index)"
+ >删除</text-button
+ >
+ </template>
+ </el-table-column>
+ </el-table>
+ <el-dialog
+ :visible.sync="dialogVisible"
+ title="指定用户"
+ append-to-body
+ :close-on-click-modal="false"
+ custom-class="shop-user-dialog"
+ width="80%"
+ >
+ <div class="dialog-container">
+ <div class="dialog-container__list">
+ <el-bus-crud v-bind="tableConfig" />
+ </div>
+ <div class="dialog-container__selected">
+ <el-bus-title title="已添加用户" size="mini" />
+ <el-tag
+ v-for="(tag, i) in currentValue"
+ :key="tag.id"
+ closable
+ @close="deleteCurrentUser(i)"
+ >{{ tag.name }}</el-tag
+ >
+ </div>
+ </div>
+ <div slot="footer">
+ <el-button @click="cancel">取消</el-button>
+ <el-button type="primary" @click="confirm">确定</el-button>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+import cloneDeep from 'lodash.clonedeep'
+export default {
+ props: {
+ value: {
+ type: Array,
+ default: () => [],
+ },
+ disabled: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ dialogVisible: false,
+ currentValue: [],
+ tableConfig: {
+ url: 'flower/api/customer/page',
+ saveQuery: false,
+ hasEdit: false,
+ hasDelete: false,
+ hasView: false,
+ columns: [
+ { label: 'id', prop: 'id' },
+ { label: '店铺名称', prop: 'name' },
+ { label: '联系方式', prop: 'tel' },
+ ],
+ extraButtons: [
+ {
+ text: '选择',
+ show: (row) =>
+ !this.currentValue.find((item) => item.id === row.id),
+ atClick: (row) => {
+ this.currentValue.push({
+ id: row.id,
+ name: row.name,
+ tel: row.tel,
+ })
+ return false
+ },
+ },
+ {
+ text: '取消选择',
+ show: (row) => this.currentValue.find((item) => item.id === row.id),
+ atClick: (row) => {
+ const index = this.currentValue.findIndex(
+ (item) => item.id === row.id
+ )
+ this.currentValue.splice(index, 1)
+ return false
+ },
+ },
+ ],
+ searchFormAttrs: {
+ labelWidth: 'auto',
+ },
+ searchForm: [
+ {
+ type: 'row',
+ span: 12,
+ items: [
+ { label: 'id:', id: 'id', type: 'input' },
+ { label: '店铺名称:', id: 'name', type: 'input' },
+ { label: '联系方式:', id: 'tel', type: 'input' },
+ ],
+ },
+ ],
+ },
+ }
+ },
+ watch: {
+ value: {
+ immediate: true,
+ handler(value) {
+ this.currentValue = cloneDeep(value || [])
+ },
+ },
+ },
+ methods: {
+ chooseUser() {
+ this.currentValue = cloneDeep(this.value || [])
+ this.dialogVisible = true
+ },
+ deleteCurrentUser(i) {
+ this.currentValue.splice(i, 1)
+ },
+ deleteUser(i) {
+ this.$elBusUtil
+ .confirm('确定要删除吗?')
+ .then(() => {
+ const userList = cloneDeep(this.value || [])
+ userList.splice(i, 1)
+ this.$emit('input', userList)
+ this.$emit('change', userList)
+ })
+ .catch(() => {})
+ },
+ confirm() {
+ this.$emit('input', this.currentValue)
+ this.$emit('change', this.currentValue)
+ this.dialogVisible = false
+ },
+ cancel() {
+ this.dialogVisible = false
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.select-shop-user {
+}
+</style>
+<style lang="scss">
+.shop-user-dialog {
+ .dialog-container {
+ display: flex;
+ align-items: flex-start;
+ &__list {
+ flex: 1;
+ border-right: 1px solid #eee;
+ height: 100%;
+ }
+ &__selected {
+ width: 40%;
+ height: 100%;
+ padding: 24px;
+ .el-bus-title {
+ margin-bottom: 15px;
+ }
+ .el-tag {
+ margin-right: 6px;
+ margin-bottom: 6px;
+ }
+ }
+ }
+}
+</style>
diff --git a/components/input-select.vue b/components/input-select.vue
new file mode 100644
index 0000000..c9aff80
--- /dev/null
+++ b/components/input-select.vue
@@ -0,0 +1,129 @@
+<template>
+ <div class="input-select">
+ <el-input-number
+ v-model="inputValue"
+ v-bind="inputAttrs"
+ class="input-select__input"
+ @input="onInputChange"
+ />
+ <el-bus-select-dict
+ v-model="selectValue"
+ v-bind="selectAttrs"
+ class="input-select__select"
+ @change="onSelectChange"
+ />
+ </div>
+</template>
+
+<script>
+import { t } from 'el-business'
+import utils from 'el-business-utils'
+export default {
+ props: {
+ value: {
+ type: Array,
+ default: () => [],
+ },
+ inputAttrs: {
+ type: Object,
+ default: () => ({}),
+ },
+ selectAttrs: {
+ type: Object,
+ default: () => ({}),
+ },
+ },
+ rules(item) {
+ const errorMsg = `${t('elBus.common.pleaseEnter')}${
+ item.label ? item.label.replace(/:/g, '').replace(/:/g, '') : ''
+ }`
+ return [
+ {
+ required: true,
+ message: errorMsg,
+ },
+ {
+ validator: (rule, value, callback) => {
+ if (
+ Array.isArray(value) &&
+ value.filter((i) => !utils.isTrueEmpty(i)).length === 2
+ ) {
+ callback()
+ } else {
+ callback(new Error(errorMsg))
+ }
+ },
+ },
+ ]
+ },
+ inputFormat(row, item) {
+ if (
+ Array.isArray(item.commonFormatProps) &&
+ item.commonFormatProps.length === 2
+ ) {
+ const input = item.commonFormatProps[0]
+ const select = item.commonFormatProps[1]
+ if (select in row || input in row) {
+ return [row[input], row[select]]
+ }
+ } else {
+ console.warn('please set commonFormatProps')
+ }
+ },
+ outputFormat(val, item) {
+ if (
+ Array.isArray(item.commonFormatProps) &&
+ item.commonFormatProps.length === 2
+ ) {
+ const input = item.commonFormatProps[0]
+ const select = item.commonFormatProps[1]
+ return {
+ [input]: !utils.isTrueEmpty(val?.[0]) ? val[0] : null,
+ [select]: !utils.isTrueEmpty(val?.[1]) ? val[1] : null,
+ }
+ } else {
+ console.warn('please set commonFormatProps')
+ }
+ },
+ data() {
+ return {
+ inputValue: null,
+ selectValue: null,
+ }
+ },
+ watch: {
+ value: {
+ immediate: true,
+ handler(value) {
+ value = value || []
+ this.inputValue = value?.[0] || undefined
+ this.selectValue = value?.[1] || null
+ },
+ },
+ },
+ methods: {
+ onInputChange(e) {
+ const value = [e, this.selectValue]
+ this.emitValue(value)
+ },
+ onSelectChange(e) {
+ const value = [this.inputValue, e]
+ this.emitValue(value)
+ },
+ emitValue(value) {
+ this.$emit('input', value)
+ this.$emit('change', value)
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.input-select {
+ display: flex;
+ align-items: center;
+ &__input {
+ margin-right: 8px;
+ }
+}
+</style>
diff --git a/config/default-dev.json5 b/config/default-dev.json5
index 6dfcbf5..36520fe 100644
--- a/config/default-dev.json5
+++ b/config/default-dev.json5
@@ -1,3 +1,3 @@
{
- httpBaseUri: 'http://106.14.123.210:8080',
+ httpBaseUri: 'http://localhost:8080',
}
diff --git a/package-lock.json b/package-lock.json
index c46d66c..8e00304 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,7 +16,7 @@
"css-vars-ponyfill": "^2.4.8",
"dayjs": "^1.11.10",
"echarts": "^5.5.0",
- "el-business": "^1.1.21",
+ "el-business": "^1.1.22",
"el-business-cache-utils": "^1.0.0",
"el-business-cookie": "^1.0.0",
"el-business-http": "^1.1.4",
@@ -9371,9 +9371,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/el-business": {
- "version": "1.1.21",
- "resolved": "https://registry.npmjs.org/el-business/-/el-business-1.1.21.tgz",
- "integrity": "sha512-rtMLZ1o0M/hMPYi1/mNPgb5LI0DYOSY7jhJwIueocuHw3+yt3G8gPmZ8eP383AsRbvouOMBwq7EwoO9u4b9nFA==",
+ "version": "1.1.22",
+ "resolved": "https://registry.npmjs.org/el-business/-/el-business-1.1.22.tgz",
+ "integrity": "sha512-l5IUsX6HKgAZCusej+pybry+XQCqWwGYhtKGzU8D+hjBp3/46rti+ajeO2ffDyTMydl1w9FqE3tgPHuNRHeLXA==",
"dependencies": {
"axios": "^0.19.2",
"cookie-universal": "^2.1.4",
@@ -30591,9 +30591,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"el-business": {
- "version": "1.1.21",
- "resolved": "https://registry.npmjs.org/el-business/-/el-business-1.1.21.tgz",
- "integrity": "sha512-rtMLZ1o0M/hMPYi1/mNPgb5LI0DYOSY7jhJwIueocuHw3+yt3G8gPmZ8eP383AsRbvouOMBwq7EwoO9u4b9nFA==",
+ "version": "1.1.22",
+ "resolved": "https://registry.npmjs.org/el-business/-/el-business-1.1.22.tgz",
+ "integrity": "sha512-l5IUsX6HKgAZCusej+pybry+XQCqWwGYhtKGzU8D+hjBp3/46rti+ajeO2ffDyTMydl1w9FqE3tgPHuNRHeLXA==",
"requires": {
"axios": "^0.19.2",
"cookie-universal": "^2.1.4",
diff --git a/package.json b/package.json
index 1468c89..d0d17c3 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"version": "1.0.0",
"private": true,
"scripts": {
- "start": "cross-env NODE_APP_INSTANCE=test NODE_ENV=development nuxt",
+ "start": "cross-env NODE_APP_INSTANCE=dev NODE_ENV=development nuxt",
"build:dev": "cross-env NODE_APP_INSTANCE=dev NODE_ENV=production nuxt build",
"build:prod": "cross-env NODE_APP_INSTANCE=prod NODE_ENV=production nuxt build",
"launch": "nuxt start",
@@ -25,7 +25,7 @@
"css-vars-ponyfill": "^2.4.8",
"dayjs": "^1.11.10",
"echarts": "^5.5.0",
- "el-business": "^1.1.21",
+ "el-business": "^1.1.22",
"el-business-cache-utils": "^1.0.0",
"el-business-cookie": "^1.0.0",
"el-business-http": "^1.1.4",
diff --git a/pages/marketing/coupon/activity/_id.vue b/pages/marketing/coupon/activity/_id.vue
new file mode 100644
index 0000000..864d8d9
--- /dev/null
+++ b/pages/marketing/coupon/activity/_id.vue
@@ -0,0 +1,72 @@
+<template>
+ <div class="base-page-wrapper coupon-detail">
+ <el-bus-title title="优惠券信息" size="small" />
+ <el-bus-form
+ ref="form"
+ label-width="auto"
+ :content="formContent"
+ readonly
+ />
+ <div class="base-page-wrapper__line"></div>
+ <el-bus-title title="领取记录" size="small" />
+ <el-bus-crud v-bind="recordTableConfig" />
+ <div class="text-center mt-20">
+ <el-button class="min-w-100" @click="goBack">返回</el-button>
+ </div>
+ </div>
+</template>
+
+<script>
+import {
+ couponForm,
+ couponRecordColumn,
+ recordTableConfig,
+ getActivityReceiveTime,
+ getActivityEffectiveTime,
+} from '@/utils/coupon-form'
+import CouponDetail from '@/plugins/mixins/coupon-detail.vue'
+export default {
+ mixins: [CouponDetail],
+ data() {
+ return {
+ detailUrl: `flower/api/v2/coupon/avtivy`,
+ formContent: [
+ {
+ type: 'row',
+ items: [
+ ...couponForm(),
+ { label: '总数:', id: 'couponAmount', type: 'input' },
+ { label: '已领取:', id: 'getNum', type: 'input' },
+ { label: '剩余:', id: 'unGetNum', type: 'input' },
+ {
+ label: '领取时间:',
+ id: 'getStartDate',
+ inputFormat: (row) => getActivityReceiveTime(row),
+ span: 24,
+ },
+ {
+ label: '有效期:',
+ id: 'usageStartDate',
+ inputFormat: (row) => getActivityEffectiveTime(row),
+ span: 24,
+ },
+ ],
+ },
+ ],
+ recordTableConfig: {
+ ...recordTableConfig(this.$route.params.id),
+ columns: [...couponRecordColumn()],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '活动优惠券详情',
+ }
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/coupon/detail.scss';
+</style>
diff --git a/pages/marketing/coupon/activity/index.vue b/pages/marketing/coupon/activity/index.vue
new file mode 100644
index 0000000..1dd3278
--- /dev/null
+++ b/pages/marketing/coupon/activity/index.vue
@@ -0,0 +1,259 @@
+<template>
+ <el-bus-crud v-bind="tableConfig" />
+</template>
+
+<script>
+import InputSelect from '@/components/input-select'
+import {
+ couponForm,
+ couponSearchForm,
+ getActivityEffectiveTime,
+ getActivityReceiveTime,
+ couponColumn,
+} from '@/utils/coupon-form'
+export default {
+ data() {
+ return {
+ tableConfig: {
+ url: 'flower/api/v2/coupon/avtivy/page',
+ newUrl: 'flower/api/v2/coupon/avtivy',
+ viewUrl: 'flower/api/v2/coupon/avtivy',
+ viewOnPath: true,
+ editUrl: 'flower/api/v2/coupon/avtivy',
+ editMethodType: 'put',
+ editOnPath: true,
+ deleteUrl: 'flower/api/v2/coupon/avtivy',
+ deleteMethodType: 'delete',
+ deleteOnPath: true,
+ canEdit: (row) => row.status === 'inactive' || row.status === 'expired',
+ canDelete: (row) =>
+ row.status === 'inactive' || row.status === 'expired',
+ operationAttrs: {
+ width: 180,
+ fixed: 'right',
+ },
+ onResetView: (row) => {
+ this.$router.push(`${this.$route.path}/${row.id}`)
+ },
+ beforeOpen: (row, isNew) => {
+ if (!isNew) {
+ row.usageTypeStr = getActivityEffectiveTime(row)
+ row.getStartDateStr = getActivityReceiveTime(row)
+ }
+ },
+ columns: [
+ ...couponColumn(),
+ {
+ label: '领取时间',
+ formatter: getActivityReceiveTime,
+ minWidth: 320,
+ },
+ {
+ label: '有效期',
+ formatter: getActivityEffectiveTime,
+ minWidth: 320,
+ },
+ { label: '已领取总数', prop: 'getNum', minWidth: 150 },
+ { label: '剩余未领取总数', prop: 'unGetNum', minWidth: 150 },
+ { label: '状态', prop: 'statusName', minWidth: 120 },
+ { label: '操作人', prop: 'createByName', minWidth: 120 },
+ ],
+ // 页面上要到分,后端要传到秒
+ beforeConfirm: (data) => {
+ if (data.getStartDate) {
+ data.getStartDate = this.$elBusUtil.formatDate(
+ data.getStartDate,
+ 'YYYY-MM-DD HH:mm:ss'
+ )
+ }
+ if (data.getEndDate) {
+ data.getEndDate = this.$elBusUtil.formatDate(
+ data.getEndDate,
+ 'YYYY-MM-DD HH:mm:ss'
+ )
+ }
+ if (data.usageStartDate) {
+ data.usageStartDate = this.$elBusUtil.formatDate(
+ data.usageStartDate,
+ 'YYYY-MM-DD HH:mm:ss'
+ )
+ }
+ if (data.usageEndDate) {
+ data.usageEndDate = this.$elBusUtil.formatDate(
+ data.usageEndDate,
+ 'YYYY-MM-DD HH:mm:ss'
+ )
+ }
+ },
+ searchForm: [
+ {
+ type: 'row',
+ items: [...couponSearchForm()],
+ },
+ ],
+ form: [
+ ...couponForm(),
+ {
+ label: '领取渠道:',
+ id: 'getType',
+ type: 'bus-select-dict',
+ el: {
+ code: 'COUPON_GET_TYPE',
+ style: 'width:100%',
+ },
+ str: true,
+ strKey: 'getTypeName',
+ rules: { required: true, message: '请选择领取渠道' },
+ },
+ {
+ label: '领取时间:',
+ id: 'getStartDate',
+ component: 'el-bus-date-range',
+ el: {
+ type: 'datetime',
+ format: 'yyyy-MM-dd HH:mm',
+ valueFormat: 'yyyy-MM-dd HH:mm',
+ },
+ str: true,
+ commonRules: true,
+ commonFormat: true,
+ commonFormatProps: ['getStartDate', 'getEndDate'],
+ },
+ {
+ label: '使用时间:',
+ id: 'usageType',
+ type: 'bus-select-dict',
+ el: {
+ code: 'COUPON_USAGE_TYPE',
+ style: 'width:100%',
+ },
+ str: true,
+ rules: { required: true, message: '请选择使用时间' },
+ on: {
+ change: (e, updateForm) => {
+ updateForm({
+ usageStartDate: null,
+ usageEndDate: null,
+ usageTimeNum: undefined,
+ usageTimeType: null,
+ })
+ },
+ },
+ },
+ {
+ label: '使用固定时间:',
+ id: 'usageStartDate',
+ component: 'el-bus-date-range',
+ el: {
+ type: 'datetime',
+ format: 'yyyy-MM-dd HH:mm',
+ valueFormat: 'yyyy-MM-dd HH:mm',
+ },
+ commonRules: true,
+ commonFormat: true,
+ commonFormatProps: ['usageStartDate', 'usageEndDate'],
+ hidden: (row, item, mode) =>
+ row.usageType !== 'fixed' || mode === 'view',
+ },
+ {
+ label: '领取后有效时间:',
+ id: 'usageTimeNum',
+ component: InputSelect,
+ commonRules: true,
+ commonFormat: true,
+ commonFormatProps: ['usageTimeNum', 'usageTimeType'],
+ el: {
+ inputAttrs: {
+ min: 1,
+ precision: 0,
+ controls: false,
+ },
+ selectAttrs: {
+ code: 'COUPON_USAGE_TIME_TYPE',
+ },
+ },
+ hidden: (row, item, mode) =>
+ row.usageType !== 'get_after_time' || mode === 'view',
+ },
+ {
+ label: '发放数量:',
+ id: 'couponAmount',
+ type: 'input-number',
+ el: {
+ precision: 0,
+ min: 1,
+ controls: false,
+ },
+ unit: '张',
+ rules: {
+ required: true,
+ message: '请输入发放数量',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '每人限领:',
+ id: 'getLimit',
+ type: 'input-number',
+ el: {
+ precision: 0,
+ min: 1,
+ controls: false,
+ },
+ unit: '张',
+ rules: {
+ required: true,
+ message: '请输入每人限领',
+ trigger: 'blur',
+ },
+ },
+ ],
+ extraButtons: [
+ {
+ text: '发布',
+ show: (row) =>
+ row.status === 'inactive' || row.status === 'expired',
+ atClick: async (row) => {
+ try {
+ await this.$elBusUtil.confirm('确定要发布吗?')
+ const { code } = await this.$elBusHttp.request(
+ `flower/api/v2/coupon/avtivy/active/${row.id}`,
+ { method: 'put' }
+ )
+ if (code === 0) {
+ this.$message.success('发布成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ {
+ text: '下架',
+ show: (row) => row.status === 'active',
+ atClick: async (row) => {
+ try {
+ await this.$elBusUtil.confirm('确定要下架吗?')
+ const { code } = await this.$elBusHttp.request(
+ `flower/api/v2/coupon/avtivy/expire/${row.id}`,
+ { method: 'put' }
+ )
+ if (code === 0) {
+ this.$message.success('下架成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '活动优惠券',
+ }
+ },
+}
+</script>
diff --git a/pages/marketing/coupon/member/_id.vue b/pages/marketing/coupon/member/_id.vue
new file mode 100644
index 0000000..4084dd5
--- /dev/null
+++ b/pages/marketing/coupon/member/_id.vue
@@ -0,0 +1,66 @@
+<template>
+ <div class="base-page-wrapper coupon-detail">
+ <el-bus-title title="优惠券信息" size="small" />
+ <el-bus-form
+ ref="form"
+ label-width="auto"
+ :content="formContent"
+ readonly
+ />
+ <div class="base-page-wrapper__line"></div>
+ <el-bus-title title="发放记录" size="small" />
+ <el-bus-crud v-bind="recordTableConfig" />
+ <div class="text-center mt-20">
+ <el-button class="min-w-100" @click="goBack">返回</el-button>
+ </div>
+ </div>
+</template>
+
+<script>
+import {
+ couponForm,
+ couponRecordColumn,
+ recordTableConfig,
+} from '@/utils/coupon-form'
+import CouponDetail from '@/plugins/mixins/coupon-detail.vue'
+export default {
+ mixins: [CouponDetail],
+ data() {
+ return {
+ detailUrl: `flower/api/v2/coupon/vip`,
+ formContent: [
+ {
+ type: 'row',
+ items: [
+ ...couponForm(),
+ { label: '已发放总数:', id: 'todo' },
+ {
+ label: '有效期:',
+ id: 'usageStartDate',
+ inputFormat: (row) => {
+ return row.usageStartDate
+ ? `${row.usageStartDate} ~ ${row.usageEndDate || ''}`
+ : ''
+ },
+ span: 24,
+ },
+ ],
+ },
+ ],
+ recordTableConfig: {
+ ...recordTableConfig(this.$route.params.id),
+ columns: [...couponRecordColumn()],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '会员优惠券详情',
+ }
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/coupon/detail.scss';
+</style>
diff --git a/pages/marketing/coupon/member/index.vue b/pages/marketing/coupon/member/index.vue
new file mode 100644
index 0000000..f11da18
--- /dev/null
+++ b/pages/marketing/coupon/member/index.vue
@@ -0,0 +1,80 @@
+<template>
+ <el-bus-crud v-bind="tableConfig" />
+</template>
+
+<script>
+import { couponForm, couponSearchForm, couponColumn } from '@/utils/coupon-form'
+export default {
+ data() {
+ return {
+ tableConfig: {
+ url: 'flower/api/v2/coupon/vip/page',
+ newUrl: 'flower/api/v2/coupon/vip',
+ viewUrl: 'flower/api/v2/coupon/vip',
+ viewOnPath: true,
+ editUrl: 'flower/api/v2/coupon/vip',
+ editMethodType: 'put',
+ editOnPath: true,
+ deleteUrl: 'flower/api/v2/coupon/vip',
+ deleteMethodType: 'delete',
+ deleteOnPath: true,
+ onResetView: (row) => {
+ this.$router.push(`${this.$route.path}/${row.id}`)
+ },
+ operationAttrs: {
+ width: 140,
+ fixed: 'right',
+ },
+ columns: [
+ ...couponColumn(),
+ { label: '会员等级', prop: 'memberName' },
+ {
+ label: '有效期',
+ formatter: (row) =>
+ row.usageStartDate
+ ? `${row.usageStartDate || ''} ~ ${row.usageEndDate || ''}`
+ : '',
+ minWidth: 320,
+ },
+ { label: '已发放数量', prop: 'getNum', minWidth: 120 },
+ { label: '操作人', prop: 'createByName', minWidth: 120 },
+ ],
+ searchForm: [
+ {
+ type: 'row',
+ items: [...couponSearchForm()],
+ },
+ ],
+ form: [
+ {
+ label: '会员等级:',
+ id: 'memberId',
+ type: 'bus-select',
+ el: {
+ interfaceUri: 'flower/api/member/list',
+ props: {
+ label: 'name',
+ value: 'id',
+ dataPath: 'records',
+ },
+ extraQuery: {
+ current: 1,
+ size: 2000,
+ },
+ filterable: true,
+ style: 'width:100%',
+ },
+ rules: { required: true, message: '请选择会员等级' },
+ },
+ ...couponForm(),
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '会员优惠券',
+ }
+ },
+}
+</script>
diff --git a/pages/marketing/coupon/user/_id.vue b/pages/marketing/coupon/user/_id.vue
new file mode 100644
index 0000000..a660b02
--- /dev/null
+++ b/pages/marketing/coupon/user/_id.vue
@@ -0,0 +1,65 @@
+<template>
+ <div class="base-page-wrapper coupon-detail">
+ <el-bus-title title="优惠券信息" size="small" />
+ <el-bus-form
+ ref="form"
+ label-width="auto"
+ :content="formContent"
+ readonly
+ />
+ <div class="base-page-wrapper__line"></div>
+ <el-bus-title title="发放记录" size="small" />
+ <el-bus-crud v-bind="recordTableConfig" />
+ <div class="text-center mt-20">
+ <el-button class="min-w-100" @click="goBack">返回</el-button>
+ </div>
+ </div>
+</template>
+
+<script>
+import {
+ couponForm,
+ couponRecordColumn,
+ recordTableConfig,
+} from '@/utils/coupon-form'
+import CouponDetail from '@/plugins/mixins/coupon-detail.vue'
+export default {
+ mixins: [CouponDetail],
+ data() {
+ return {
+ detailUrl: `flower/api/v2/coupon/user`,
+ formContent: [
+ {
+ type: 'row',
+ items: [
+ ...couponForm(),
+ {
+ label: '有效期:',
+ id: 'usageStartDate',
+ inputFormat: (row) => {
+ return row.usageStartDate
+ ? `${row.usageStartDate} ~ ${row.usageEndDate || ''}`
+ : ''
+ },
+ span: 24,
+ },
+ ],
+ },
+ ],
+ recordTableConfig: {
+ ...recordTableConfig(this.$route.params.id),
+ columns: [...couponRecordColumn()],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '用户优惠券详情',
+ }
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/coupon/detail.scss';
+</style>
diff --git a/pages/marketing/coupon/user/index.vue b/pages/marketing/coupon/user/index.vue
new file mode 100644
index 0000000..ce68f1e
--- /dev/null
+++ b/pages/marketing/coupon/user/index.vue
@@ -0,0 +1,138 @@
+<template>
+ <el-bus-crud v-bind="tableConfig" />
+</template>
+
+<script>
+import InputSelect from '@/components/input-select'
+import SelectShopUser from '@/components/coupon/select-shop-user'
+import { couponForm, couponSearchForm, couponColumn } from '@/utils/coupon-form'
+export default {
+ data() {
+ return {
+ tableConfig: {
+ url: 'flower/api/v2/coupon/user/page',
+ newUrl: 'flower/api/v2/coupon/user',
+ viewUrl: 'flower/api/v2/coupon/user',
+ viewOnPath: true,
+ editUrl: 'flower/api/v2/coupon/user',
+ editMethodType: 'put',
+ editOnPath: true,
+ deleteUrl: 'flower/api/v2/coupon/user',
+ deleteMethodType: 'delete',
+ deleteOnPath: true,
+ dialogNeedRequest: true,
+ canEdit: (row) => row.status === 'inactive' || row.status === 'expired',
+ canDelete: (row) =>
+ row.status === 'inactive' || row.status === 'expired',
+ onResetView: (row) => {
+ this.$router.push(`${this.$route.path}/${row.id}`)
+ },
+ operationAttrs: {
+ width: 200,
+ fixed: 'right',
+ },
+ columns: [
+ ...couponColumn(),
+ { label: '发放时间', prop: 'usageStartDate', minWidth: 180 },
+ {
+ label: '有效期',
+ formatter: (row) =>
+ row.usageStartDate
+ ? `${row.usageStartDate} ~ ${row.usageEndDate || ''}`
+ : '',
+ minWidth: 400,
+ },
+ { label: '状态', prop: 'statusName', minWidth: 120 },
+ { label: '操作人', prop: 'createByName', minWidth: 120 },
+ ],
+ searchForm: [
+ {
+ type: 'row',
+ items: [...couponSearchForm()],
+ },
+ ],
+ form: [
+ ...couponForm(),
+ {
+ label: '发放后有效期:',
+ id: 'usageTimeNum',
+ component: InputSelect,
+ commonRules: true,
+ commonFormat: true,
+ commonFormatProps: ['usageTimeNum', 'usageTimeType'],
+ el: {
+ inputAttrs: {
+ min: 1,
+ precision: 0,
+ controls: false,
+ },
+ selectAttrs: {
+ code: 'COUPON_USAGE_TIME_TYPE',
+ },
+ },
+ },
+ {
+ label: '领取用户:',
+ id: 'pointCostomIdList',
+ component: SelectShopUser,
+ rules: { required: true, message: '请选择领取用户' },
+ inputFormat: (row) => {
+ if ('customerList' in row) {
+ return row.customerList
+ }
+ },
+ outputFormat: (val) => {
+ return val?.length ? val.map((i) => i.id) : []
+ },
+ forceDisabled: true,
+ },
+ ],
+ extraButtons: [
+ {
+ text: '发布',
+ show: (row) =>
+ row.status === 'inactive' || row.status === 'expired',
+ atClick: async (row) => {
+ try {
+ await this.$elBusUtil.confirm('确定要发布吗?')
+ const { code } = await this.$elBusHttp.request(
+ `flower/api/v2/coupon/user/active/${row.id}`,
+ { method: 'put' }
+ )
+ if (code === 0) {
+ this.$message.success('发布成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ {
+ text: '下架',
+ show: (row) => row.status === 'active',
+ atClick: async (row) => {
+ try {
+ await this.$elBusUtil.confirm('确定要下架吗?')
+ const { code } = await this.$elBusHttp.request(
+ `flower/api/v2/coupon/user/expire/${row.id}`,
+ { method: 'put' }
+ )
+ if (code === 0) {
+ this.$message.success('下架成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '用户优惠券',
+ }
+ },
+}
+</script>
diff --git a/pages/marketing/member-level.vue b/pages/marketing/member-level.vue
new file mode 100644
index 0000000..8502649
--- /dev/null
+++ b/pages/marketing/member-level.vue
@@ -0,0 +1,146 @@
+<template>
+ <el-bus-crud v-bind="tableConfig" />
+</template>
+
+<script>
+import MemberRule from '@/components/coupon/member-rule.vue'
+export default {
+ data() {
+ return {
+ tableConfig: {
+ url: 'flower/api/member/list',
+ newUrl: 'flower/api/member/new',
+ editUrl: 'flower/api/member/edit',
+ deleteUrl: 'flower/api/member/delete',
+ columns: [
+ { label: '序号', type: 'index' },
+ { label: '等级名称', prop: 'name' },
+ { label: '成长值', prop: 'startPoint' },
+ { label: '等级折扣', prop: 'discountTypeStr' },
+ { label: '操作人', prop: 'createName' },
+ ],
+ beforeOpen: (row, isNew) => {
+ if (!isNew) {
+ row.consumptionAmountStr = `消费${row.consumptionAmount}元等于${row.growthValue}成长值`
+ row.startPointStr = `${
+ this.$elBusUtil.isTrueEmpty(row.startPoint) ? '' : row.startPoint
+ } ~ ${
+ this.$elBusUtil.isTrueEmpty(row.endPoint) ? '' : row.endPoint
+ }`
+ }
+ },
+ searchForm: [
+ {
+ type: 'row',
+ items: [{ label: '等级名称:', id: 'name', type: 'input' }],
+ },
+ ],
+ form: [
+ {
+ label: '会员等级名称:',
+ id: 'name',
+ type: 'input',
+ rules: { required: true, message: '请输入会员等级名称' },
+ },
+ {
+ label: '成长值范围:',
+ id: 'startPoint',
+ component: 'el-bus-number-range',
+ el: {
+ unit: '',
+ separator: '<= 成长值范围 <',
+ inputAttrs: {
+ controls: false,
+ },
+ },
+ commonFormat: true,
+ commonFormatProps: ['startPoint', 'endPoint'],
+ commonRules: true,
+ commonRulesLevel: 1,
+ str: true,
+ },
+ {
+ label: '折扣类型:',
+ id: 'discountType',
+ type: 'bus-select-dict',
+ el: {
+ code: 'DISCOUNT_TYPE',
+ style: 'width:100%',
+ },
+ str: true,
+ rules: { required: true, message: '请选择折扣类型' },
+ on: {
+ change: (e, updateForm) => {
+ updateForm({
+ discountRatio: undefined,
+ discountAmount: undefined,
+ })
+ },
+ },
+ },
+ {
+ label: '会员折扣:',
+ id: 'discountRatio',
+ type: 'input-number',
+ el: {
+ precision: 0,
+ min: 0,
+ max: 100,
+ controls: false,
+ },
+ unit: '%',
+ rules: {
+ required: true,
+ message: '请输入会员折扣',
+ trigger: 'blur',
+ },
+ hidden: (row) => row.discountType !== 'ratio',
+ },
+ {
+ label: '会员优惠:',
+ id: 'discountAmount',
+ type: 'input-number',
+ el: {
+ precision: 2,
+ min: 0,
+ controls: false,
+ },
+ unit: '元',
+ rules: {
+ required: true,
+ message: '请输入会员优惠',
+ trigger: 'blur',
+ },
+ hidden: (row) => row.discountType !== 'amount',
+ },
+ {
+ label: '会员规则:',
+ id: 'consumptionAmount',
+ component: MemberRule,
+ commonRules: true,
+ commonFormat: true,
+ commonFormatProps: ['consumptionAmount', 'growthValue'],
+ str: true,
+ },
+ {
+ label: '降级规则:',
+ id: 'downgradeValue',
+ type: 'input-number',
+ el: {
+ precision: 0,
+ min: 0,
+ controls: false,
+ },
+ unit: '成长值',
+ rules: {
+ required: true,
+ message: '请输入降级规则',
+ trigger: 'blur',
+ },
+ },
+ ],
+ },
+ }
+ },
+}
+</script>
diff --git a/pages/marketing/point-mall/coupon/_id.vue b/pages/marketing/point-mall/coupon/_id.vue
new file mode 100644
index 0000000..3fe87a6
--- /dev/null
+++ b/pages/marketing/point-mall/coupon/_id.vue
@@ -0,0 +1,64 @@
+<template>
+ <div class="base-page-wrapper coupon-detail">
+ <el-bus-title title="优惠券信息" size="small" />
+ <el-bus-form
+ ref="form"
+ label-width="auto"
+ :content="formContent"
+ readonly
+ />
+ <div class="base-page-wrapper__line"></div>
+ <el-bus-title title="兑换记录" size="small" />
+ <el-bus-crud v-bind="recordTableConfig" />
+ <div class="text-center mt-20">
+ <el-button class="min-w-100" @click="goBack">返回</el-button>
+ </div>
+ </div>
+</template>
+
+<script>
+import {
+ couponForm,
+ couponRecordColumn,
+ recordTableConfig,
+} from '@/utils/coupon-form'
+import CouponDetail from '@/plugins/mixins/coupon-detail.vue'
+export default {
+ mixins: [CouponDetail],
+ data() {
+ return {
+ detailUrl: `flower/api/v2/coupon/point`,
+ formContent: [
+ {
+ type: 'row',
+ items: [
+ ...couponForm(),
+ {
+ label: '领取后有效时间:',
+ id: 'usageTimeNum',
+ inputFormat: (row) => {
+ return `${row.usageTimeNum}${row.usageTimeTypeName}`
+ },
+ },
+ { label: '库存:', id: 'couponAmount' },
+ { label: '积分数量:', id: 'point' },
+ ],
+ },
+ ],
+ recordTableConfig: {
+ ...recordTableConfig(this.$route.params.id),
+ columns: [...couponRecordColumn()],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '会员优惠券详情',
+ }
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/coupon/detail.scss';
+</style>
diff --git a/pages/marketing/point-mall/coupon/index.vue b/pages/marketing/point-mall/coupon/index.vue
new file mode 100644
index 0000000..d445c10
--- /dev/null
+++ b/pages/marketing/point-mall/coupon/index.vue
@@ -0,0 +1,238 @@
+<template>
+ <el-bus-crud v-bind="tableConfig" />
+</template>
+
+<script>
+import { couponForm, couponSearchForm, couponColumn } from '@/utils/coupon-form'
+import InputSelect from '@/components/input-select'
+export default {
+ data() {
+ return {
+ tableConfig: {
+ url: 'flower/api/v2/coupon/point/page',
+ newUrl: 'flower/api/v2/coupon/point',
+ viewUrl: 'flower/api/v2/coupon/point',
+ viewOnPath: true,
+ editUrl: 'flower/api/v2/coupon/point',
+ editMethodType: 'put',
+ editOnPath: true,
+ deleteUrl: 'flower/api/v2/coupon/point',
+ deleteMethodType: 'delete',
+ deleteOnPath: true,
+ canEdit: (row) => row.status === 'inactive' || row.status === 'expired',
+ canDelete: (row) =>
+ row.status === 'inactive' || row.status === 'expired',
+ onResetView: (row) => {
+ this.$router.push(`${this.$route.path}/${row.id}`)
+ },
+ persistSelection: true,
+ operationAttrs: {
+ width: 160,
+ fixed: 'right',
+ },
+ columns: [
+ {
+ label: '',
+ type: 'selection',
+ minWidth: 60,
+ },
+ ...couponColumn(),
+ { label: '状态', prop: 'statusName', minWidth: 120 },
+ { label: '库存', prop: 'couponAmount', minWidth: 120 },
+ { label: '所需积分', prop: 'point', minWidth: 120 },
+ ],
+ searchForm: [
+ {
+ type: 'row',
+ items: [...couponSearchForm()],
+ },
+ ],
+ form: [
+ ...couponForm(),
+ {
+ label: '领取后有效时间:',
+ id: 'usageTimeNum',
+ component: InputSelect,
+ commonRules: true,
+ commonFormat: true,
+ commonFormatProps: ['usageTimeNum', 'usageTimeType'],
+ el: {
+ inputAttrs: {
+ min: 1,
+ precision: 0,
+ controls: false,
+ },
+ selectAttrs: {
+ code: 'COUPON_USAGE_TIME_TYPE',
+ },
+ },
+ },
+ {
+ label: '库存:',
+ id: 'couponAmount',
+ type: 'input-number',
+ el: {
+ precision: 0,
+ min: 0,
+ controls: false,
+ },
+ rules: { required: true, message: '请输入库存', trigger: 'blur' },
+ },
+ {
+ label: '积分数量:',
+ id: 'point',
+ type: 'input-number',
+ el: {
+ precision: 0,
+ min: 1,
+ controls: false,
+ },
+ rules: {
+ required: true,
+ message: '请输入积分数量',
+ trigger: 'blur',
+ },
+ },
+ ],
+ extraButtons: [
+ {
+ text: '上架',
+ show: (row) =>
+ row.status === 'inactive' || row.status === 'expired',
+ atClick: async (row) => {
+ try {
+ await this.$elBusUtil.confirm('确定要上架吗?')
+ const { code } = await this.$elBusHttp.request(
+ `flower/api/v2/coupon/point/active/${row.id}`,
+ { method: 'put' }
+ )
+ if (code === 0) {
+ this.$message.success('上架成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ {
+ text: '下架',
+ show: (row) => row.status === 'active',
+ atClick: async (row) => {
+ try {
+ await this.$elBusUtil.confirm('确定要下架吗?')
+ const { code } = await this.$elBusHttp.request(
+ `flower/api/v2/coupon/point/expire/${row.id}`,
+ { method: 'put' }
+ )
+ if (code === 0) {
+ this.$message.success('下架成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ ],
+ headerButtons: [
+ {
+ text: '批量上架',
+ type: 'primary',
+ disabled: (selected) =>
+ selected.filter(
+ (i) => i.status === 'inactive' || i.status === 'expired'
+ ).length === 0,
+ atClick: async (selected) => {
+ try {
+ const items = selected.filter(
+ (i) => i.status === 'inactive' || i.status === 'expired'
+ )
+ await this.$elBusUtil.confirm(
+ `确定要上架这${items.length}个商品吗?`
+ )
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/v2/coupon/point/batch/active',
+ {
+ data: { idList: items.map((i) => i.id) },
+ method: 'post',
+ }
+ )
+ if (code === 0) {
+ this.$message.success('上架成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ {
+ text: '批量下架',
+ type: 'primary',
+ disabled: (selected) =>
+ selected.filter((i) => i.status === 'active').length === 0,
+ atClick: async (selected) => {
+ try {
+ const items = selected.filter((i) => i.status === 'active')
+ await this.$elBusUtil.confirm(
+ `确定要下架这${items.length}个商品吗?`
+ )
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/v2/coupon/point/batch/expire',
+ {
+ data: { idList: items.map((i) => i.id) },
+ method: 'post',
+ }
+ )
+ if (code === 0) {
+ this.$message.success('下架成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ {
+ text: '批量删除',
+ type: 'danger',
+ disabled: (selected) =>
+ selected.filter(
+ (i) => i.status === 'inactive' || i.status === 'expired'
+ ).length === 0,
+ atClick: async (selected) => {
+ try {
+ const items = selected.filter(
+ (i) => i.status === 'inactive' || i.status === 'expired'
+ )
+ await this.$elBusUtil.confirm(
+ `确定要删除这${items.length}个商品吗?`
+ )
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/v2/coupon/point/batch/del',
+ {
+ data: { idList: items.map((i) => i.id) },
+ method: 'post',
+ }
+ )
+ if (code === 0) {
+ this.$message.success('删除成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '积分优惠券',
+ }
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.index {
+}
+</style>
diff --git a/pages/marketing/point-mall/goods.vue b/pages/marketing/point-mall/goods.vue
new file mode 100644
index 0000000..0821523
--- /dev/null
+++ b/pages/marketing/point-mall/goods.vue
@@ -0,0 +1,242 @@
+<template>
+ <el-bus-crud ref="crud" v-bind="tableConfig" />
+</template>
+
+<script>
+export default {
+ data() {
+ return {
+ tableConfig: {
+ url: 'flower/api/point/goods/list',
+ canEdit: (row) => row.status === 'I',
+ canDelete: (row) => row.status === 'I',
+ columns: [
+ { label: '', type: 'selection' },
+ { label: '序号', type: 'index' },
+ {
+ label: '商品图片',
+ formatter: (row) =>
+ row.cover ? (
+ <el-bus-image
+ src={row.cover}
+ lazy={true}
+ style="width:50px;height:50px"
+ ></el-bus-image>
+ ) : null,
+ },
+ { label: '商品名称', prop: 'name' },
+ { label: '状态', prop: 'statusStr' },
+ { label: '库存', prop: 'stock' },
+ { label: '所需积分', prop: 'point' },
+ ],
+ searchForm: [
+ {
+ type: 'row',
+ items: [
+ {
+ label: '商品名称',
+ id: 'name',
+ type: 'input',
+ },
+ {
+ label: '状态',
+ id: 'status',
+ type: 'bus-select-dict',
+ el: {
+ code: 'POINT_GOODS_STATUS',
+ style: 'width:100%',
+ clearable: true,
+ },
+ },
+ ],
+ },
+ ],
+ form: [
+ {
+ label: '商品名称:',
+ id: 'name',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入商品名称',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '商品规格信息:',
+ id: 'description',
+ type: 'input',
+ el: {
+ type: 'textarea',
+ rows: 4,
+ },
+ rules: {
+ required: true,
+ message: '请输入商品规格信息',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '库存:',
+ id: 'stock',
+ type: 'input-number',
+ el: {
+ precision: 0,
+ min: 0,
+ controls: false,
+ },
+ rules: { required: true, message: '请输入库存', trigger: 'blur' },
+ },
+ {
+ label: '商品图片:',
+ id: 'cover',
+ type: 'bus-upload',
+ el: {
+ listType: 'picture-card',
+ limit: 1,
+ limitSize: 2,
+ tipText: '大小不超过2M',
+ valueType: 'string',
+ },
+ forceDisabled: true,
+ rules: {
+ required: true,
+ message: '请上传商品图片',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '积分数量:',
+ id: 'point',
+ type: 'input-number',
+ el: {
+ precision: 0,
+ min: 1,
+ controls: false,
+ },
+ rules: {
+ required: true,
+ message: '请输入积分数量',
+ trigger: 'blur',
+ },
+ },
+ ],
+ extraButtons: [
+ {
+ text: '上架',
+ show: (row) => row.status === 'I',
+ atClick: async (row) => {
+ try {
+ await this.$elBusUtil.confirm('确定要上架吗?')
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/point/goods/list/on',
+ { params: { id: row.id } }
+ )
+ if (code === 0) {
+ this.$message.success('上架成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ {
+ text: '下架',
+ show: (row) => row.status === 'A',
+ atClick: async (row) => {
+ try {
+ await this.$elBusUtil.confirm('确定要下架吗?')
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/point/goods/list/off',
+ { params: { id: row.id } }
+ )
+ if (code === 0) {
+ this.$message.success('下架成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ ],
+ headerButtons: [
+ {
+ text: '批量上架',
+ type: 'primary',
+ disabled: (selected) =>
+ selected.filter((i) => i.status === 'I').length === 0,
+ atClick: async (selected) => {
+ try {
+ const items = selected.filter((i) => i.status === 'I')
+ await this.$elBusUtil.confirm(
+ `确定要上架这${items.length}个商品吗?`
+ )
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/point/goods/list/on',
+ { params: { id: items.map((i) => i.id).join(',') } }
+ )
+ if (code === 0) {
+ this.$message.success('上架成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ {
+ text: '批量下架',
+ type: 'primary',
+ disabled: (selected) =>
+ selected.filter((i) => i.status === 'A').length === 0,
+ atClick: async (selected) => {
+ try {
+ const items = selected.filter((i) => i.status === 'A')
+ await this.$elBusUtil.confirm(
+ `确定要下架这${items.length}个商品吗?`
+ )
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/point/goods/list/off',
+ { params: { id: items.map((i) => i.id).join(',') } }
+ )
+ if (code === 0) {
+ this.$message.success('下架成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ {
+ text: '批量删除',
+ type: 'danger',
+ disabled: (selected) =>
+ selected.filter((i) => i.status === 'I').length === 0,
+ atClick: async (selected) => {
+ try {
+ const items = selected.filter((i) => i.status === 'I')
+ await this.$elBusUtil.confirm(
+ `确定要删除这${items.length}个商品吗?`
+ )
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/point/goods/list/delete',
+ { params: { id: items.map((i) => i.id).join(',') } }
+ )
+ if (code === 0) {
+ this.$message.success('删除成功')
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '积分商品',
+ }
+ },
+}
+</script>
diff --git a/pages/marketing/point-mall/point-distribution.vue b/pages/marketing/point-mall/point-distribution.vue
new file mode 100644
index 0000000..640f3d9
--- /dev/null
+++ b/pages/marketing/point-mall/point-distribution.vue
@@ -0,0 +1,224 @@
+<template>
+ <div>
+ <el-bus-crud ref="crud" v-bind="tableConfig" />
+ <el-dialog title="积分变动记录" :visible.sync="dialogVisible" width="80%">
+ <el-bus-crud
+ v-if="customerId"
+ :key="customerId"
+ :extra-query="{ customerId }"
+ v-bind="recordTableConfig"
+ />
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+export default {
+ data() {
+ return {
+ dialogVisible: false,
+ customerId: null,
+ tableConfig: {
+ url: 'flower/api/customer/point/page',
+ hasNew: false,
+ hasEdit: false,
+ hasDelete: false,
+ hasView: false,
+ columns: [
+ { label: '序号', type: 'index' },
+ { label: '用户信息', prop: 'customerName' },
+ { label: '总积分', prop: 'totalPoint' },
+ { label: '已使用积分', prop: 'usedPoint' },
+ {
+ label: '剩余积分',
+ formatter: (row) =>
+ parseInt(row.totalPoint) -
+ parseInt(row.usedPoint) -
+ parseInt(row.expiredPoint),
+ },
+ ],
+ searchForm: [
+ {
+ type: 'row',
+ items: [{ label: '用户名称', id: 'customerName', type: 'input' }],
+ },
+ ],
+ extraDialogs: [
+ {
+ title: '积分赠送',
+ form: [
+ {
+ id: 'customerId',
+ type: 'input',
+ hidden: () => true,
+ },
+ {
+ label: '积分赠送:',
+ id: 'point',
+ type: 'input-number',
+ el: {
+ precision: 0,
+ min: 1,
+ controls: false,
+ },
+ rules: {
+ required: true,
+ message: '请输入积分赠送',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '原因:',
+ id: 'remarks',
+ type: 'input',
+ el: {
+ type: 'textarea',
+ rows: 6,
+ },
+ rules: {
+ required: true,
+ message: '请输入原因',
+ trigger: 'blur',
+ },
+ },
+ ],
+ atConfirm: async (val) => {
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/customer/point/giveaway',
+ {
+ method: 'post',
+ data: val,
+ }
+ )
+ if (code === 0) {
+ this.$message.success('赠送成功')
+ }
+ },
+ },
+ {
+ title: '积分扣减',
+ form: [
+ {
+ id: 'customerId',
+ type: 'input',
+ hidden: () => true,
+ },
+ {
+ label: '积分扣减:',
+ id: 'point',
+ type: 'input-number',
+ el: {
+ precision: 0,
+ min: 1,
+ controls: false,
+ },
+ rules: {
+ required: true,
+ message: '请输入积分扣减',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '原因:',
+ id: 'remarks',
+ type: 'input',
+ el: {
+ type: 'textarea',
+ rows: 6,
+ },
+ rules: {
+ required: true,
+ message: '请输入原因',
+ trigger: 'blur',
+ },
+ },
+ ],
+ atConfirm: async (val) => {
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/customer/point/deduction',
+ {
+ method: 'post',
+ data: val,
+ }
+ )
+ if (code === 0) {
+ this.$message.success('扣减成功')
+ }
+ },
+ },
+ ],
+ extraButtons: [
+ {
+ text: '积分变动记录',
+ atClick: (row) => {
+ this.customerId = row.customerId
+ this.dialogVisible = true
+ },
+ },
+ {
+ text: '积分赠送',
+ atClick: (row) => {
+ this.$refs.crud.$refs.extraDialog[0].show(row)
+ return false
+ },
+ },
+ {
+ text: '积分扣减',
+ atClick: (row) => {
+ this.$refs.crud.$refs.extraDialog[1].show(row)
+ return false
+ },
+ },
+ ],
+ },
+ recordTableConfig: {
+ url: 'flower/api/customer/point/page/list',
+ saveQuery: false,
+ hasNew: false,
+ hasOperation: false,
+ columns: [
+ { label: '序号', type: 'index' },
+ { label: '变动类型', prop: 'changeTypeStr' },
+ { label: '变动积分', prop: 'point' },
+ { label: '变动原因', prop: 'typeStr' },
+ { label: '变动时间', prop: 'recordDate' },
+ { label: '备注', prop: 'remarks' },
+ ],
+ searchFormAttrs: {
+ labelWidth: 'auto',
+ },
+ searchForm: [
+ {
+ type: 'row',
+ items: [
+ {
+ label: '变动原因:',
+ id: 'type',
+ type: 'bus-radio',
+ el: {
+ code: 'point_type',
+ childType: 'el-radio-button',
+ hasAll: true,
+ },
+ default: '',
+ searchImmediately: true,
+ span: 24,
+ },
+ {
+ label: '备注:',
+ id: 'remarks',
+ type: 'input',
+ },
+ ],
+ },
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '积分发放',
+ }
+ },
+}
+</script>
diff --git a/plugins/mixins/coupon-detail.vue b/plugins/mixins/coupon-detail.vue
new file mode 100644
index 0000000..89194b7
--- /dev/null
+++ b/plugins/mixins/coupon-detail.vue
@@ -0,0 +1,27 @@
+<script>
+export default {
+ data() {
+ return {
+ detailUrl: '',
+ }
+ },
+ mounted() {
+ this.getCouponDetail()
+ },
+ methods: {
+ async getCouponDetail() {
+ const { code, data } = await this.$elBusHttp.request(
+ `${this.detailUrl}/${this.$route.params.id}`
+ )
+ if (code === 0) {
+ if (this.$refs.form) {
+ this.$refs.form.updateForm(data)
+ }
+ }
+ },
+ goBack() {
+ this.$router.back()
+ },
+ },
+}
+</script>
diff --git a/utils/coupon-form.js b/utils/coupon-form.js
new file mode 100644
index 0000000..cf3e885
--- /dev/null
+++ b/utils/coupon-form.js
@@ -0,0 +1,173 @@
+import utils from 'el-business-utils'
+
+// 优惠券表单公共字段
+export const couponForm = () => {
+ return [
+ {
+ label: '优惠券名称:',
+ id: 'couponName',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入优惠券名称',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '优惠券类型:',
+ id: 'couponDiscountType',
+ type: 'bus-select-dict',
+ el: {
+ code: 'COUPON_TYPE',
+ style: 'width:100%',
+ },
+ on: {
+ change: (e, updateForm, obj) => {
+ if (e[0] === 'zero') {
+ updateForm({ minOrderAmount: 0 })
+ obj.elBusForm
+ .getComponentById('minOrderAmount')
+ .$parent.clearValidate()
+ }
+ },
+ },
+ rules: { required: true, message: '请选择优惠券类型' },
+ str: true,
+ strKey: 'couponDiscountTypeName',
+ },
+ {
+ label: '使用规则:',
+ id: 'couponDescription',
+ type: 'input',
+ el: {
+ type: 'textarea',
+ rows: 6,
+ },
+ rules: {
+ required: true,
+ message: '请输入使用规则',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '优惠券使用条件:',
+ id: 'minOrderAmount',
+ type: 'input-number',
+ el: {
+ min: 0,
+ precision: 2,
+ controls: false,
+ },
+ prefix: '满',
+ unit: '元',
+ rules: {
+ required: true,
+ message: '请输入优惠券使用条件',
+ trigger: 'blur',
+ },
+ disabled: (row) => row.couponDiscountType === 'zero',
+ },
+ {
+ label: '优惠券面值:',
+ id: 'couponDiscountValue',
+ type: 'input-number',
+ el: {
+ min: 0.01,
+ precision: 2,
+ controls: false,
+ },
+ unit: '元',
+ rules: {
+ required: true,
+ message: '请输入优惠券面值',
+ trigger: 'blur',
+ },
+ },
+ ]
+}
+
+export const couponSearchForm = () => {
+ return [
+ {
+ label: '优惠券类型',
+ id: 'couponDiscountType',
+ type: 'bus-select-dict',
+ el: {
+ code: 'COUPON_TYPE',
+ style: 'width:100%',
+ clearable: true,
+ },
+ },
+ {
+ label: '优惠券名称',
+ id: 'name',
+ type: 'input',
+ },
+ ]
+}
+
+export const getActivityEffectiveTime = (row) => {
+ if (row.usageType === 'get') {
+ return `${utils.formatDate(row.getStartDate, 'YYYY-MM-DD HH:mm') || ''} ~ ${
+ utils.formatDate(row.getEndDate, 'YYYY-MM-DD HH:mm') || ''
+ }`
+ } else if (row.usageType === 'fixed') {
+ return `${
+ utils.formatDate(row.usageStartDate, 'YYYY-MM-DD HH:mm') || ''
+ } ~ ${utils.formatDate(row.usageEndDate, 'YYYY-MM-DD HH:mm') || ''}`
+ } else if (row.usageType === 'get_after_time') {
+ return `领取后${row.usageTimeNum}${row.usageTimeTypeName || ''}`
+ }
+ return ''
+}
+
+export const getActivityReceiveTime = (row) => {
+ return `${utils.formatDate(
+ row.getStartDate,
+ 'YYYY-MM-DD HH:mm'
+ )} ~ ${utils.formatDate(row.getEndDate, 'YYYY-MM-DD HH:mm')}`
+}
+
+// 优惠券列表公共字段
+export const couponColumn = () => {
+ return [
+ { label: '序号', type: 'index', minWidth: 60, fixed: 'left' },
+ { label: '优惠券名称', prop: 'couponName', minWidth: 150, fixed: 'left' },
+ { label: '优惠券类型', prop: 'couponDiscountTypeName', minWidth: 120 },
+ {
+ label: '使用条件',
+ formatter: (row) => `满${row.minOrderAmount}`,
+ minWidth: 120,
+ },
+ { label: '优惠券面值', prop: 'couponDiscountValue', minWidth: 120 },
+ ]
+}
+
+// 优惠券领取/发放记录
+export const couponRecordColumn = () => {
+ return [
+ { label: '序号', type: 'index' },
+ { label: '店铺名称', prop: 'customerName' },
+ { label: '优惠券类型', prop: 'couponDiscountTypeName' },
+ {
+ label: '使用条件',
+ formatter: (row) => `满${row.minOrderAmount}`,
+ },
+ { label: '优惠券面值', prop: 'couponDiscountValue' },
+ { label: '状态', prop: 'statusName' },
+ { label: '使用时间', prop: 'index' },
+ { label: '订单号', prop: 'orderNo' },
+ ]
+}
+
+export const recordTableConfig = (couponId) => {
+ return {
+ url: 'flower/v2/coupon-record/page',
+ hasNew: false,
+ hasOperation: false,
+ saveQuery: false,
+ extraQuery: {
+ couponId,
+ },
+ }
+}
--
Gitblit v1.9.3