浏览代码

新增会员管理/会员配置/权益配置菜单功能

licc 1 月之前
父节点
当前提交
493d8ed343

+ 62 - 0
src/hooks/useElabPaging.ts

@@ -0,0 +1,62 @@
+import { reactive, toRaw } from 'vue'
+
+// 分页钩子函数
+interface Options {
+    page?: number
+    size?: number
+    fetchFun: (_arg: any) => Promise<any>
+    params?: Record<any, any>
+    firstLoading?: boolean
+}
+
+export function useElabPaging(options: Options) {
+    const { page = 1, size = 15, fetchFun, params = {}, firstLoading = false } = options
+    // 记录分页初始参数
+    const paramsInit: Record<any, any> = Object.assign({}, toRaw(params))
+    // 分页数据
+    const pager = reactive({
+        page,
+        size,
+        loading: firstLoading,
+        count: 0,
+        lists: [] as any[]
+    })
+    // 请求分页接口
+    const getLists = () => {
+        pager.loading = true
+        return fetchFun({
+            pageNo: pager.page,
+            pageSize: pager.size,
+            ...params
+        })
+            .then((res: any) => {
+                pager.count = res?.pageModel.rowTotal
+                pager.lists = res?.pageModel.resultSet
+                return Promise.resolve(res)
+            })
+            .catch((err: any) => {
+                return Promise.reject(err)
+            })
+            .finally(() => {
+                pager.loading = false
+            })
+    }
+    // 重置为第一页
+    const resetPage = () => {
+        pager.page = 1
+        getLists()
+    }
+    // 重置参数
+    const resetParams = () => {
+        Object.keys(paramsInit).forEach((item) => {
+            params[item] = paramsInit[item]
+        })
+        getLists()
+    }
+    return {
+        pager,
+        getLists,
+        resetParams,
+        resetPage
+    }
+}

+ 138 - 0
src/views/membership/benefits/benefits.vue

@@ -0,0 +1,138 @@
+<template>
+  <div>
+    <el-card class="!border-none" shadow="never">
+      <el-form ref="formRef" class="mb-[-16px]" :model="queryParams" :inline="true">
+        <el-form-item label="权益名称">
+          <el-input
+              class="w-[280px]"
+              v-model="queryParams.name"
+              placeholder="精准匹配"
+              clearable
+              @keyup.enter="resetPage"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="resetPage">查询</el-button>
+          <el-button @click="resetParams">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <el-card class="!border-none mt-4" shadow="never" v-loading="pager.loading">
+      <div>
+        <el-button v-perms="['article:cate:add']" type="primary" @click="handleAdd()">
+          <template #icon>
+            <icon name="el-icon-Plus"/>
+          </template>
+          新增权益配置
+        </el-button>
+      </div>
+      <el-table class="mt-4" size="large" :data="pager.lists">
+        <el-table-column type="index" width="55"/>
+        <el-table-column label="权益图标" min-width="100">
+          <template #default="{ row }">
+            <image-contain
+                v-if="row.icon"
+                :src="row.icon"
+                :width="60"
+                :height="45"
+                fit="contain"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column label="权益名称" prop="name" min-width="120" show-tooltip-when-overflow/>
+        <el-table-column label="权益价值(元)" prop="value" min-width="80"/>
+        <el-table-column label="使用方式" prop="useMethod" min-width="100" :formatter="useMethodFormatter"/>
+        <el-table-column label="备注" prop="remark" min-width="120"/>
+        <el-table-column label="操作" min-width="180" fixed="right">
+          <template #default="{ row }">
+            <el-button
+                v-perms="['article:cate:edit']"
+                type="primary"
+                link
+                @click="handleEdit(row)"
+            >
+              编辑
+            </el-button>
+            <el-button
+                v-perms="['article:cate:del']"
+                type="danger"
+                link
+                @click="handleDelete(row.id)"
+            >
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="flex justify-end mt-4">
+        <pagination v-model="pager" @change="getLists"/>
+      </div>
+    </el-card>
+    <edit-popup v-if="showEdit" ref="editRef" @success="getLists" @close="showEdit = false"/>
+  </div>
+</template>
+<script lang="ts" setup>
+import { useElabPaging } from '@/hooks/useElabPaging'
+import feedback from '@/utils/feedback'
+import EditPopup from './edit.vue'
+import {queryBenefits} from "@/api/membership";
+
+const queryParams = reactive({
+  name: ''
+})
+const { pager, getLists, resetPage, resetParams } = useElabPaging({
+  fetchFun: queryBenefits,
+  params: queryParams
+})
+const editRef = shallowRef<InstanceType<typeof EditPopup>>()
+const showEdit = ref(false)
+
+const handleAdd = async () => {
+  showEdit.value = true
+  await nextTick()
+  editRef.value?.open('add')
+}
+
+const handleEdit = async (data: any) => {
+  showEdit.value = true
+  await nextTick()
+  editRef.value?.open('edit')
+  editRef.value?.getDetail(data)
+}
+
+const useMethodFormatter = (row: any) => {
+  return row.useMethod == 0 ? '仅展示': ''
+}
+const volumeFormatter = (row: any) => {
+  if (row.volume == 0) {
+    return '大'
+  } else if (row.volume == 1) {
+    return '中'
+  } else if (row.volume == 2) {
+    return '小'
+  } else if (row.volume == 3) {
+    return '关闭'
+  } else {
+    return '大'
+  }
+}
+
+const handleDelete = async (id: number) => {
+  await feedback.confirm('确定要删除?')
+  // await articleCateDelete({id})
+  await delPrinter({id})
+  feedback.msgSuccess('删除成功')
+  getLists()
+}
+
+const changeStatus = async (id: number, status: any) => {
+  try {
+    await editPrinter({id, status})
+    feedback.msgSuccess('修改成功')
+    getLists()
+  } catch (error) {
+    getLists()
+  }
+}
+getLists()
+</script>

+ 118 - 0
src/views/membership/benefits/edit.vue

@@ -0,0 +1,118 @@
+<template>
+  <div class="edit-popup">
+    <popup
+        ref="popupRef"
+        :title="popupTitle"
+        :async="true"
+        width="550px"
+        @confirm="handleSubmit"
+        @close="handleClose"
+    >
+      <el-form ref="formRef" :model="formData" label-width="120px" :rules="formRules">
+        <el-form-item label="权益名称" prop="name">
+          <el-input v-model="formData.name" placeholder="请输入权益名称" :maxlength="10" clearable/>
+        </el-form-item>
+        <el-form-item label="权益图标" prop="icon">
+          <material-picker v-model="formData.icon" :limit="1" />
+        </el-form-item>
+        <el-form-item label="权益价值" prop="value">
+            <el-input v-model="formData.value" type="number" placeholder="请输入权益价值" clearable />
+        </el-form-item>
+        <el-form-item label="使用方式" prop="useMethod">
+          <el-select v-model="formData.useMethod" placeholder="请选择权益使用方式">
+            <el-option label="仅展示" :value="0"/>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="formData.remark" type="textarea" clearable />
+        </el-form-item>
+      </el-form>
+    </popup>
+  </div>
+</template>
+<script lang="ts" setup>
+import type {FormInstance} from 'element-plus'
+import Popup from '@/components/popup/index.vue'
+import feedback from '@/utils/feedback'
+import {addPrinter, editPrinter, getPrinterDetail} from '@/api/shop'
+import {saveBenefits} from "@/api/membership";
+
+const emit = defineEmits(['success', 'close'])
+const formRef = shallowRef<FormInstance>()
+const popupRef = shallowRef<InstanceType<typeof Popup>>()
+const mode = ref('add')
+const popupTitle = computed(() => {
+  return mode.value == 'edit' ? '编辑' : '新增'
+})
+const formData = reactive({
+  id: '',
+  name: '',
+  icon: '',
+  value: '',
+  useMethod: 0,
+  remark: ''
+})
+
+const formRules = {
+  name: [
+    {
+      required: true,
+      message: '请输入名称',
+      trigger: ['blur']
+    }
+  ],
+  icon: [
+    {
+      required: true,
+      message: '请上传图标',
+      trigger: ['blur']
+    }
+  ],
+  useMethod: [
+    {
+      required: true,
+      message: '请选择使用方式',
+      trigger: ['blur']
+    }
+  ]
+}
+
+const handleSubmit = async () => {
+  await formRef.value?.validate()
+  await saveBenefits(formData)
+  feedback.msgSuccess('操作成功')
+  popupRef.value?.close()
+  emit('success')
+}
+
+const open = (type = 'add') => {
+  mode.value = type
+  popupRef.value?.open()
+}
+
+const setFormData = (data: Record<any, any>) => {
+  for (const key in formData) {
+    if (data[key] != null && data[key] != undefined) {
+      //@ts-ignore
+      formData[key] = data[key]
+    }
+  }
+}
+
+const getDetail = async (row: Record<string, any>) => {
+  // const data = await getPrinterDetail({
+  //   id: row.id
+  // })
+  setFormData(row)
+}
+
+const handleClose = () => {
+  emit('close')
+}
+
+defineExpose({
+  open,
+  setFormData,
+  getDetail
+})
+</script>

+ 120 - 0
src/views/membership/member/edit.vue

@@ -0,0 +1,120 @@
+<template>
+  <div class="edit-popup">
+    <popup
+        ref="popupRef"
+        :title="popupTitle"
+        :async="true"
+        width="550px"
+        @confirm="handleSubmit"
+        @close="handleClose"
+    >
+      <el-form ref="formRef" :model="formData" label-width="120px" :rules="formRules">
+        <el-form-item label="会员名称" prop="name">
+          <el-input v-model="formData.name" placeholder="请输入会员名称" :maxlength="10" clearable/>
+        </el-form-item>
+        <el-form-item label="会员等级" prop="level">
+          <el-input v-model="formData.level" type="number" placeholder="请输入会员等级" clearable/>
+        </el-form-item>
+        <el-form-item label="首充金额" prop="chargeAmount">
+            <el-input v-model="formData.chargeAmount" type="number" placeholder="请输入首充金额" clearable />
+        </el-form-item>
+        <el-form-item label="赠送金额" prop="giftAmount">
+            <el-input v-model="formData.giftAmount" type="number" placeholder="请输入赠送金额" clearable />
+        </el-form-item>
+        <el-form-item label="入园次数" prop="enterCount">
+            <el-input v-model="formData.enterCount" type="number" placeholder="请输入入园次数" clearable />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="formData.remark" type="textarea" clearable />
+        </el-form-item>
+      </el-form>
+    </popup>
+  </div>
+</template>
+<script lang="ts" setup>
+import type {FormInstance} from 'element-plus'
+// import {articleCateEdit} from '@/api/article'
+import Popup from '@/components/popup/index.vue'
+import feedback from '@/utils/feedback'
+import { saveMember} from '@/api/membership'
+
+const emit = defineEmits(['success', 'close'])
+const formRef = shallowRef<FormInstance>()
+const popupRef = shallowRef<InstanceType<typeof Popup>>()
+const mode = ref('add')
+const popupTitle = computed(() => {
+  return mode.value == 'edit' ? '编辑' : '新增'
+})
+const formData = reactive({
+  id: '',
+  name: '',
+  level: 1,
+  chargeAmount: '',
+  giftAmount: '',
+  enterCount: '',
+  remark: ''
+})
+
+const formRules = {
+  name: [
+    {
+      required: true,
+      message: '请输入名称',
+      trigger: ['blur']
+    }
+  ],
+  level: [
+    {
+      required: true,
+      message: '请输入会员等级',
+      trigger: ['blur']
+    }
+  ],
+  chargeAmount: [
+    {
+      required: true,
+      message: '请输入首充金额',
+      trigger: ['blur']
+    }
+  ]
+}
+
+const handleSubmit = async () => {
+  await formRef.value?.validate()
+  await saveMember(formData)
+  feedback.msgSuccess('操作成功')
+  popupRef.value?.close()
+  emit('success')
+}
+
+const open = (type = 'add') => {
+  mode.value = type
+  popupRef.value?.open()
+}
+
+const setFormData = (data: Record<any, any>) => {
+  for (const key in formData) {
+    if (data[key] != null && data[key] != undefined) {
+      //@ts-ignore
+      formData[key] = data[key]
+    }
+  }
+}
+
+const getDetail = async (row: Record<string, any>) => {
+  // const data = await getPrinterDetail({
+  //   id: row.id
+  // })
+  setFormData(row)
+}
+
+const handleClose = () => {
+  emit('close')
+}
+
+defineExpose({
+  open,
+  setFormData,
+  getDetail
+})
+</script>

+ 118 - 0
src/views/membership/member/member.vue

@@ -0,0 +1,118 @@
+<template>
+  <div>
+    <el-card class="!border-none" shadow="never">
+      <el-form ref="formRef" class="mb-[-16px]" :model="queryParams" :inline="true">
+        <el-form-item label="会员名称">
+          <el-input
+              class="w-[280px]"
+              v-model="queryParams.name"
+              placeholder="精准匹配"
+              clearable
+              @keyup.enter="resetPage"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="resetPage">查询</el-button>
+          <el-button @click="resetParams">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <el-card class="!border-none mt-4" shadow="never" v-loading="pager.loading">
+      <div>
+        <el-button v-perms="['article:cate:add']" type="primary" @click="handleAdd()">
+          <template #icon>
+            <icon name="el-icon-Plus"/>
+          </template>
+          新增会员配置
+        </el-button>
+      </div>
+      <el-table class="mt-4" size="large" :data="pager.lists">
+        <el-table-column type="index" width="55"/>
+        <el-table-column label="会员名称" prop="name" min-width="120" show-tooltip-when-overflow/>
+        <el-table-column label="会员等级" prop="level" min-width="80"/>
+        <el-table-column label="首充金额(元)" prop="chargeAmount" min-width="100" :formatter="chargeAmountFormatter"/>
+        <el-table-column label="赠送金额(元)" prop="giftAmount" min-width="100" :formatter="giftAmountFormatter"/>
+        <el-table-column label="入园次数" prop="enterCount" min-width="80"/>
+        <el-table-column label="操作" min-width="180" fixed="right">
+          <template #default="{ row }">
+            <el-button
+                v-perms="['article:cate:edit']"
+                type="primary"
+                link
+                @click="handleEdit(row)"
+            >
+              编辑
+            </el-button>
+            <el-button
+                v-perms="['article:cate:del']"
+                type="danger"
+                link
+                @click="handleDelete(row.id)"
+            >
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="flex justify-end mt-4">
+        <pagination v-model="pager" @change="getLists"/>
+      </div>
+    </el-card>
+    <edit-popup v-if="showEdit" ref="editRef" @success="getLists" @close="showEdit = false"/>
+  </div>
+</template>
+<script lang="ts" setup>
+import { useElabPaging } from '@/hooks/useElabPaging'
+import feedback from '@/utils/feedback'
+import EditPopup from './edit.vue'
+import {queryMember} from "@/api/membership";
+
+const queryParams = reactive({
+  name: ''
+})
+const { pager, getLists, resetPage, resetParams } = useElabPaging({
+  fetchFun: queryMember,
+  params: queryParams
+})
+const editRef = shallowRef<InstanceType<typeof EditPopup>>()
+const showEdit = ref(false)
+
+const handleAdd = async () => {
+  showEdit.value = true
+  await nextTick()
+  editRef.value?.open('add')
+}
+
+const handleEdit = async (data: any) => {
+  showEdit.value = true
+  await nextTick()
+  editRef.value?.open('edit')
+  editRef.value?.getDetail(data)
+}
+
+const chargeAmountFormatter = (row: any) => {
+  return row.chargeAmount / 100
+}
+const giftAmountFormatter = (row: any) => {
+  return row.giftAmount / 100
+}
+
+const handleDelete = async (id: number) => {
+  await feedback.confirm('确定要删除?')
+  // await articleCateDelete({id})
+  await delPrinter({id})
+  feedback.msgSuccess('删除成功')
+  getLists()
+}
+
+const changeStatus = async (id: number, status: any) => {
+  try {
+    await editPrinter({id, status})
+    feedback.msgSuccess('修改成功')
+    getLists()
+  } catch (error) {
+    getLists()
+  }
+}
+getLists()
+</script>

+ 103 - 0
src/views/membership/user/user.vue

@@ -0,0 +1,103 @@
+<template>
+  <div>
+    <el-card class="!border-none" shadow="never">
+      <el-form ref="formRef" class="mb-[-16px]" :model="queryParams" :inline="true">
+        <el-form-item label="手机号">
+          <el-input
+              class="w-[280px]"
+              v-model="queryParams.mobile"
+              placeholder="精准匹配"
+              clearable
+              @keyup.enter="resetPage"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="resetPage">查询</el-button>
+          <el-button @click="resetParams">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <el-card class="!border-none mt-4" shadow="never" v-loading="pager.loading">
+      <el-table class="mt-4" size="large" :data="pager.lists">
+        <el-table-column type="index" width="55"/>
+        <el-table-column label="用户id" prop="userId" min-width="120" show-tooltip-when-overflow/>
+        <el-table-column label="手机号" prop="userMobile" min-width="120" show-tooltip-when-overflow/>
+        <el-table-column label="会员名称" prop="membershipName" min-width="80"/>
+        <el-table-column label="会员编号" prop="membershipNo" min-width="100"/>
+        <el-table-column label="入会时间" prop="time" min-width="120"/>
+<!--        <el-table-column label="操作" min-width="180" fixed="right">-->
+<!--          <template #default="{ row }">-->
+<!--            -->
+<!--          </template>-->
+<!--        </el-table-column>-->
+      </el-table>
+      <div class="flex justify-end mt-4">
+        <pagination v-model="pager" @change="getLists"/>
+      </div>
+    </el-card>
+  </div>
+</template>
+<script lang="ts" setup>
+import { useElabPaging } from '@/hooks/useElabPaging'
+import feedback from '@/utils/feedback'
+import {queryMemberUser} from "@/api/membership";
+
+const queryParams = reactive({
+  mobile: ''
+})
+const { pager, getLists, resetPage, resetParams } = useElabPaging({
+  fetchFun: queryMemberUser,
+  params: queryParams
+})
+const editRef = shallowRef<InstanceType<typeof EditPopup>>()
+const showEdit = ref(false)
+
+const handleAdd = async () => {
+  showEdit.value = true
+  await nextTick()
+  editRef.value?.open('add')
+}
+
+const handleEdit = async (data: any) => {
+  showEdit.value = true
+  await nextTick()
+  editRef.value?.open('edit')
+  editRef.value?.getDetail(data)
+}
+
+const useMethodFormatter = (row: any) => {
+  return row.useMethod == 0 ? '仅展示': ''
+}
+const volumeFormatter = (row: any) => {
+  if (row.volume == 0) {
+    return '大'
+  } else if (row.volume == 1) {
+    return '中'
+  } else if (row.volume == 2) {
+    return '小'
+  } else if (row.volume == 3) {
+    return '关闭'
+  } else {
+    return '大'
+  }
+}
+
+const handleDelete = async (id: number) => {
+  await feedback.confirm('确定要删除?')
+  // await articleCateDelete({id})
+  await delPrinter({id})
+  feedback.msgSuccess('删除成功')
+  getLists()
+}
+
+const changeStatus = async (id: number, status: any) => {
+  try {
+    await editPrinter({id, status})
+    feedback.msgSuccess('修改成功')
+    getLists()
+  } catch (error) {
+    getLists()
+  }
+}
+getLists()
+</script>