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