diff --git a/src/api/point/config/index.ts b/src/api/member/point/config/index.ts similarity index 72% rename from src/api/point/config/index.ts rename to src/api/member/point/config/index.ts index 00adc3f1..9a3c4c48 100644 --- a/src/api/point/config/index.ts +++ b/src/api/member/point/config/index.ts @@ -10,10 +10,10 @@ export interface ConfigVO { // 查询积分设置详情 export const getConfig = async () => { - return await request.get({ url: `/point/config/get` }) + return await request.get({ url: `/member/point/config/get` }) } // 新增修改积分设置 export const saveConfig = async (data: ConfigVO) => { - return await request.put({ url: `/point/config/save`, data }) + return await request.put({ url: `/member/point/config/save`, data }) } diff --git a/src/api/member/point/record/index.ts b/src/api/member/point/record/index.ts new file mode 100644 index 00000000..1c60f754 --- /dev/null +++ b/src/api/member/point/record/index.ts @@ -0,0 +1,21 @@ +import request from '@/config/axios' + +export interface RecordVO { + id: number + bizId: string + bizType: string + title: string + description: string + point: number + totalPoint: number + status: number + userId: number + freezingTime: Date + thawingTime: Date + createDate: Date +} + +// 查询用户积分记录列表 +export const getRecordPage = async (params) => { + return await request.get({ url: `/member/point/record/page`, params }) +} diff --git a/src/api/member/signin/config/index.ts b/src/api/member/signin/config/index.ts new file mode 100644 index 00000000..833c2e16 --- /dev/null +++ b/src/api/member/signin/config/index.ts @@ -0,0 +1,33 @@ +import request from '@/config/axios' + +export interface SignInConfigVO { + id: number + day: number | null + point: number | null + enable: boolean | null +} + +// 查询积分签到规则列表 +export const getSignInConfigList = async () => { + return await request.get({ url: `/member/point/sign-in-config/list` }) +} + +// 查询积分签到规则详情 +export const getSignInConfig = async (id: number) => { + return await request.get({ url: `/member/point/sign-in-config/get?id=` + id }) +} + +// 新增积分签到规则 +export const createSignInConfig = async (data: SignInConfigVO) => { + return await request.post({ url: `/member/point/sign-in-config/create`, data }) +} + +// 修改积分签到规则 +export const updateSignInConfig = async (data: SignInConfigVO) => { + return await request.put({ url: `/member/point/sign-in-config/update`, data }) +} + +// 删除积分签到规则 +export const deleteSignInConfig = async (id: number) => { + return await request.delete({ url: `/member/point/sign-in-config/delete?id=` + id }) +} diff --git a/src/api/member/signin/record/index.ts b/src/api/member/signin/record/index.ts new file mode 100644 index 00000000..94df2029 --- /dev/null +++ b/src/api/member/signin/record/index.ts @@ -0,0 +1,13 @@ +import request from '@/config/axios' + +export interface SignInRecordVO { + id: number + userId: number + day: number + point: number +} + +// 查询用户签到积分列表 +export const getSignInRecordPage = async (params) => { + return await request.get({ url: `/member/point/sign-in-record/page`, params }) +} diff --git a/src/api/member/tag/index.ts b/src/api/member/tag/index.ts new file mode 100644 index 00000000..04d0536c --- /dev/null +++ b/src/api/member/tag/index.ts @@ -0,0 +1,31 @@ +import request from '@/config/axios' + +export interface TagVO { + id: number + name: string +} + +// 查询会员标签列表 +export const getMemberTagPage = async (params) => { + return await request.get({ url: `/member/tag/page`, params }) +} + +// 查询会员标签详情 +export const getMemberTag = async (id: number) => { + return await request.get({ url: `/member/tag/get?id=` + id }) +} + +// 新增会员标签 +export const createMemberTag = async (data: TagVO) => { + return await request.post({ url: `/member/tag/create`, data }) +} + +// 修改会员标签 +export const updateMemberTag = async (data: TagVO) => { + return await request.put({ url: `/member/tag/update`, data }) +} + +// 删除会员标签 +export const deleteMemberTag = async (id: number) => { + return await request.delete({ url: `/member/tag/delete?id=` + id }) +} diff --git a/src/api/point/record/index.ts b/src/api/point/record/index.ts deleted file mode 100644 index 15eaff35..00000000 --- a/src/api/point/record/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import request from '@/config/axios' - -export interface RecordVO { - id: number - bizId: string - bizType: string - type: string - title: string - description: string - point: number - totalPoint: number - status: number - userId: number - freezingTime: Date - thawingTime: Date - createDate: Date -} - -// 查询用户积分记录列表 -export const getRecordPage = async (params) => { - return await request.get({ url: `/point/record/page`, params }) -} - -// 查询用户积分记录详情 -export const getRecord = async (id: number) => { - return await request.get({ url: `/point/record/get?id=` + id }) -} - -// 新增用户积分记录 -export const createRecord = async (data: RecordVO) => { - return await request.post({ url: `/point/record/create`, data }) -} - -// 修改用户积分记录 -export const updateRecord = async (data: RecordVO) => { - return await request.put({ url: `/point/record/update`, data }) -} - -// 删除用户积分记录 -export const deleteRecord = async (id: number) => { - return await request.delete({ url: `/point/record/delete?id=` + id }) -} - -// 导出用户积分记录 Excel -export const exportRecord = async (params) => { - return await request.download({ url: `/point/record/export-excel`, params }) -} diff --git a/src/api/point/signInConfig/index.ts b/src/api/point/signInConfig/index.ts deleted file mode 100644 index 3786c06e..00000000 --- a/src/api/point/signInConfig/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import request from '@/config/axios' - -export interface SignInConfigVO { - id: number - day: number - point: number -} - -// 查询积分签到规则列表 -export const getSignInConfigPage = async (params) => { - return await request.get({ url: `/point/sign-in-config/page`, params }) -} - -// 查询积分签到规则详情 -export const getSignInConfig = async (id: number) => { - return await request.get({ url: `/point/sign-in-config/get?id=` + id }) -} - -// 新增积分签到规则 -export const createSignInConfig = async (data: SignInConfigVO) => { - return await request.post({ url: `/point/sign-in-config/create`, data }) -} - -// 修改积分签到规则 -export const updateSignInConfig = async (data: SignInConfigVO) => { - return await request.put({ url: `/point/sign-in-config/update`, data }) -} - -// 删除积分签到规则 -export const deleteSignInConfig = async (id: number) => { - return await request.delete({ url: `/point/sign-in-config/delete?id=` + id }) -} - -// 导出积分签到规则 Excel -export const exportSignInConfig = async (params) => { - return await request.download({ url: `/point/sign-in-config/export-excel`, params }) -} diff --git a/src/api/point/signInRecord/index.ts b/src/api/point/signInRecord/index.ts deleted file mode 100644 index 0f9b9f64..00000000 --- a/src/api/point/signInRecord/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import request from '@/config/axios' - -export interface SignInRecordVO { - id: number - userId: number - day: number - point: number -} - -// 查询用户签到积分列表 -export const getSignInRecordPage = async (params) => { - return await request.get({ url: `/point/sign-in-record/page`, params }) -} - -// 查询用户签到积分详情 -export const getSignInRecord = async (id: number) => { - return await request.get({ url: `/point/sign-in-record/get?id=` + id }) -} - -// 新增用户签到积分 -export const createSignInRecord = async (data: SignInRecordVO) => { - return await request.post({ url: `/point/sign-in-record/create`, data }) -} - -// 修改用户签到积分 -export const updateSignInRecord = async (data: SignInRecordVO) => { - return await request.put({ url: `/point/sign-in-record/update`, data }) -} - -// 删除用户签到积分 -export const deleteSignInRecord = async (id: number) => { - return await request.delete({ url: `/point/sign-in-record/delete?id=` + id }) -} - -// 导出用户签到积分 Excel -export const exportSignInRecord = async (params) => { - return await request.download({ url: `/point/sign-in-record/export-excel`, params }) -} diff --git a/src/utils/dict.ts b/src/utils/dict.ts index 7a1d53aa..fe9eb9b2 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -142,7 +142,6 @@ export enum DICT_TYPE { // ========== MALL - 会员模块 ========== MEMBER_POINT_BIZ_TYPE = 'member_point_biz_type', // 积分的业务类型 - MEMBER_POINT_STATUS = 'member_point_status', // 积分的状态 // ========== MALL - 商品模块 ========== PRODUCT_UNIT = 'product_unit', // 商品单位 diff --git a/src/views/mall/product/property/index.vue b/src/views/mall/product/property/index.vue index 00597dc3..dc79450b 100644 --- a/src/views/mall/product/property/index.vue +++ b/src/views/mall/product/property/index.vue @@ -73,9 +73,7 @@ > 编辑 </el-button> - <el-button link type="primary"> - <router-link :to="'/property/value/' + scope.row.id">属性值</router-link> - </el-button> + <el-button link type="primary" @click="goValueList(scope.row.id)">属性值</el-button> <el-button v-hasPermi="['product:property:delete']" link @@ -103,6 +101,7 @@ import { dateFormatter } from '@/utils/formatTime' import * as PropertyApi from '@/api/mall/product/property' import PropertyForm from './PropertyForm.vue' +const { push } = useRouter() defineOptions({ name: 'ProductProperty' }) @@ -163,6 +162,11 @@ const handleDelete = async (id: number) => { } catch {} } +/** 跳转商品属性列表 */ +const goValueList = (id: number) => { + push({ path: '/property/value/' + id }) +} + /** 初始化 **/ onMounted(() => { getList() diff --git a/src/views/member/point/config/index.vue b/src/views/member/point/config/index.vue index 0fd1484f..27c52085 100644 --- a/src/views/member/point/config/index.vue +++ b/src/views/member/point/config/index.vue @@ -10,26 +10,36 @@ <el-form-item label="hideId" v-show="false"> <el-input v-model="formData.id" /> </el-form-item> - <!-- TODO @xiaqing:展示给用户的字段名,可以和 crmeb 保持一直,然后每一个表单都有类似 crmeb 的 tip;例如说:积分抵用比例(1积分抵多少金额)单位:元 --> - <el-form-item label="积分抵扣" prop="tradeDeductEnable"> - <el-switch v-model="formData.tradeDeductEnable" /> + <el-form-item label="积分抵扣" prop="tradeDeductEnable" class="item-bottom"> + <el-switch v-model="formData.tradeDeductEnable" style="user-select: none" /> </el-form-item> - <!-- TODO @xiaqing:用户看到的是元,最多 2 位;分是后端的存储哈 --> - <el-form-item label="抵扣单位(分)" prop="tradeDeductUnitPrice"> + <el-form-item> + <el-text class="mx-1" size="small" type="info">下单积分是否抵用订单金额</el-text> + </el-form-item> + <el-form-item label="积分抵扣" prop="tradeDeductUnitPrice" class="item-bottom"> <el-input-number - v-model="formData.tradeDeductUnitPrice" - placeholder="请输入抵扣单位(分)" + v-model="computedTradeDeductUnitPrice" + placeholder="请输入积分抵扣金额" style="width: 300px" + :precision="2" /> </el-form-item> - <el-form-item label="积分抵扣最大值" prop="tradeDeductMaxPrice"> + <el-form-item> + <el-text class="mx-1" size="small" type="info"> + 积分抵用比例(1 积分抵多少金额),单位:元 + </el-text> + </el-form-item> + <el-form-item label="积分抵扣最大值" prop="tradeDeductMaxPrice" class="item-bottom"> <el-input-number v-model="formData.tradeDeductMaxPrice" placeholder="请输入积分抵扣最大值" style="width: 300px" /> </el-form-item> - <el-form-item label="1 元赠送多少分" prop="tradeGivePoint"> + <el-form-item> + <el-text class="mx-1" size="small" type="info">单次下单积分使用上限,0 不限制</el-text> + </el-form-item> + <el-form-item label="1 元赠送多少分" prop="tradeGivePoint" class="item-bottom"> <el-input-number v-model="formData.tradeGivePoint" placeholder="请输入 1 元赠送多少积分" @@ -37,13 +47,20 @@ /> </el-form-item> <el-form-item> - <el-button type="primary" @click="onSubmit">提交</el-button> + <el-text class="mx-1" size="small" type="info"> + 下单支付金额按比例赠送积分(实际支付 1 元赠送多少积分) + </el-text> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="onSubmit">保存</el-button> </el-form-item> </el-form> </ContentWrap> </template> <script lang="ts" setup> -import * as ConfigApi from '@/api/point/config' +import * as ConfigApi from '@/api/member/point/config' + +defineOptions({ name: 'MemberPointConfig' }) const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 @@ -51,12 +68,20 @@ const message = useMessage() // 消息弹窗 const dialogVisible = ref(false) // 弹窗的是否展示 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formData = ref({ - id: undefined, - tradeDeductEnable: undefined, - tradeDeductUnitPrice: undefined, - tradeDeductMaxPrice: undefined, - tradeGivePoint: undefined + tradeDeductEnable: true, + tradeDeductUnitPrice: 0, + tradeDeductMaxPrice: 0, + tradeGivePoint: 0 }) + +// 创建一个计算属性,用于将 tradeDeductUnitPrice 显示为带两位小数的形式 +const computedTradeDeductUnitPrice = computed({ + get: () => (formData.value.tradeDeductUnitPrice / 100).toFixed(2), + set: (newValue) => { + formData.value.tradeDeductUnitPrice = Math.round(newValue * 100) + } +}) + const formRules = reactive({}) const formRef = ref() // 表单 Ref @@ -82,6 +107,9 @@ const onSubmit = async () => { const getConfig = async () => { try { const data = await ConfigApi.getConfig() + if (data === null) { + return + } formData.value = data } finally { } @@ -91,3 +119,9 @@ onMounted(() => { getConfig() }) </script> + +<style scoped> +.item-bottom { + margin-bottom: 0; +} +</style> diff --git a/src/views/member/point/record/RecordForm.vue b/src/views/member/point/record/RecordForm.vue deleted file mode 100644 index a75eb9a5..00000000 --- a/src/views/member/point/record/RecordForm.vue +++ /dev/null @@ -1,180 +0,0 @@ -<template> - <Dialog :title="dialogTitle" v-model="dialogVisible"> - <el-form - ref="formRef" - :model="formData" - :rules="formRules" - label-width="120px" - v-loading="formLoading" - > - <el-form-item label="业务编码" prop="bizId"> - <el-input v-model="formData.bizId" placeholder="请输入业务编码" /> - </el-form-item> - <el-form-item label="业务类型" prop="bizType"> - <el-select v-model="formData.bizType" placeholder="请选择业务类型"> - <el-option - v-for="dict in getStrDictOptions(DICT_TYPE.MEMBER_POINT_BIZ_TYPE)" - :key="dict.value" - :label="dict.label" - :value="dict.value" - /> - </el-select> - </el-form-item> - <el-form-item label="操作类型" prop="type"> - <el-select v-model="formData.type" placeholder="操作类型"> - <el-option label="增加" value="1" /> - <el-option label="扣减" value="0" /> - </el-select> - </el-form-item> - <el-form-item label="积分标题" prop="title"> - <el-input v-model="formData.title" placeholder="请输入积分标题" /> - </el-form-item> - <el-form-item label="积分描述"> - <Editor :model-value="formData.description" height="150px" /> - </el-form-item> - <el-form-item label="积分" prop="point"> - <el-input v-model="formData.point" placeholder="请输入积分" /> - </el-form-item> - <el-form-item label="变动后的积分" prop="totalPoint"> - <el-input v-model="formData.totalPoint" placeholder="请输入变动后的积分" /> - </el-form-item> - <el-form-item label="积分状态" prop="status"> - <el-select v-model="formData.status" placeholder="积分状态"> - <el-option - v-for="dict in getIntDictOptions(DICT_TYPE.MEMBER_POINT_STATUS)" - :key="dict.value" - :label="dict.label" - :value="dict.value" - /> - </el-select> - </el-form-item> - <el-form-item label="用户id" prop="userId"> - <el-input v-model="formData.userId" placeholder="请输入用户id" /> - </el-form-item> - <el-form-item label="冻结时间" prop="freezingTime"> - <el-date-picker - v-model="formData.freezingTime" - type="date" - value-format="x" - placeholder="选择冻结时间" - /> - </el-form-item> - <el-form-item label="解冻时间" prop="thawingTime"> - <el-date-picker - v-model="formData.thawingTime" - type="date" - value-format="x" - placeholder="选择解冻时间" - /> - </el-form-item> - <el-form-item label="发生时间" prop="createDate"> - <el-date-picker - v-model="formData.createDate" - type="date" - value-format="x" - placeholder="选择发生时间" - /> - </el-form-item> - </el-form> - <template #footer> - <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> - <el-button @click="dialogVisible = false">取 消</el-button> - </template> - </Dialog> -</template> -<script lang="ts" setup> -import { DICT_TYPE, getStrDictOptions, getIntDictOptions } from '@/utils/dict' -import * as RecordApi from '@/api/point/record' - -const { t } = useI18n() // 国际化 -const message = useMessage() // 消息弹窗 - -const dialogVisible = ref(false) // 弹窗的是否展示 -const dialogTitle = ref('') // 弹窗的标题 -const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 -const formType = ref('') // 表单的类型:create - 新增;update - 修改 -const formData = ref({ - id: undefined, - bizId: undefined, - bizType: undefined, - type: undefined, - title: undefined, - description: undefined, - point: undefined, - totalPoint: undefined, - status: undefined, - userId: undefined, - freezingTime: undefined, - thawingTime: undefined, - createDate: undefined -}) -const formRules = reactive({ - totalPoint: [{ required: true, message: '变动后的积分不能为空', trigger: 'blur' }] -}) -const formRef = ref() // 表单 Ref - -/** 打开弹窗 */ -const open = async (type: string, id?: number) => { - dialogVisible.value = true - dialogTitle.value = t('action.' + type) - formType.value = type - resetForm() - // 修改时,设置数据 - if (id) { - formLoading.value = true - try { - formData.value = await RecordApi.getRecord(id) - } finally { - formLoading.value = false - } - } -} -defineExpose({ open }) // 提供 open 方法,用于打开弹窗 - -/** 提交表单 */ -const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 -const submitForm = async () => { - // 校验表单 - if (!formRef) return - const valid = await formRef.value.validate() - if (!valid) return - // 提交请求 - formLoading.value = true - try { - const data = formData.value as unknown as RecordApi.RecordVO - if (formType.value === 'create') { - await RecordApi.createRecord(data) - message.success(t('common.createSuccess')) - } else { - await RecordApi.updateRecord(data) - message.success(t('common.updateSuccess')) - } - dialogVisible.value = false - // 发送操作成功的事件 - emit('success') - } finally { - formLoading.value = false - } -} - -// TODO @xiaqing:不需要更新操作哇? -/** 重置表单 */ -const resetForm = () => { - formData.value = { - id: undefined, - bizId: undefined, - bizType: undefined, - type: undefined, - title: undefined, - description: undefined, - point: undefined, - totalPoint: undefined, - status: undefined, - userId: undefined, - freezingTime: undefined, - thawingTime: undefined, - createDate: undefined - } - formRef.value?.resetFields() -} -</script> diff --git a/src/views/member/point/record/index.vue b/src/views/member/point/record/index.vue index 517410f7..a21cd85c 100644 --- a/src/views/member/point/record/index.vue +++ b/src/views/member/point/record/index.vue @@ -8,10 +8,10 @@ :inline="true" label-width="68px" > - <el-form-item label="业务编码" prop="bizId"> + <el-form-item label="用户" prop="nickname"> <el-input - v-model="queryParams.bizId" - placeholder="请输入业务编码" + v-model="queryParams.nickname" + placeholder="请输入用户昵称" clearable @keyup.enter="handleQuery" class="!w-240px" @@ -25,19 +25,13 @@ class="!w-240px" > <el-option - v-for="dict in getStrDictOptions(DICT_TYPE.MEMBER_POINT_BIZ_TYPE)" + v-for="dict in getIntDictOptions(DICT_TYPE.MEMBER_POINT_BIZ_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> - <el-form-item label="操作类型" prop="type"> - <el-select v-model="queryParams.type" placeholder="操作类型" clearable class="!w-240px"> - <el-option label="增加" value="1" /> - <el-option label="扣减" value="0" /> - </el-select> - </el-form-item> <el-form-item label="积分标题" prop="title"> <el-input v-model="queryParams.title" @@ -47,16 +41,6 @@ class="!w-240px" /> </el-form-item> - <el-form-item label="积分状态" prop="status"> - <el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px"> - <el-option - v-for="dict in getIntDictOptions(DICT_TYPE.MEMBER_POINT_STATUS)" - :key="dict.value" - :label="dict.label" - :value="dict.value" - /> - </el-select> - </el-form-item> <el-form-item label="获得时间" prop="createDate"> <el-date-picker v-model="queryParams.createDate" @@ -69,8 +53,14 @@ /> </el-form-item> <el-form-item> - <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> - <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button @click="handleQuery"> + <Icon icon="ep:search" class="mr-5px" /> + 搜索 + </el-button> + <el-button @click="resetQuery"> + <Icon icon="ep:refresh" class="mr-5px" /> + 重置 + </el-button> </el-form-item> </el-form> </ContentWrap> @@ -78,53 +68,32 @@ <!-- 列表 --> <ContentWrap> <el-table v-loading="loading" :data="list"> - <el-table-column label="编号" align="center" prop="id" /> - <!-- TODO @xiaqing:展示用户的昵称哈; --> - <el-table-column label="用户" align="center" prop="userId" /> - <el-table-column label="积分标题" align="center" prop="title" /> - <el-table-column label="积分描述" align="center" prop="description" /> + <el-table-column label="编号" align="center" prop="id" width="180" /> <el-table-column label="获得时间" align="center" - prop="createDate" + prop="createTime" :formatter="dateFormatter" + width="180" /> - <!-- todo @xiaqing:可以参考 crmeb 的展示,把积分和增加减少放一起,用红色和绿色展示 --> - <el-table-column - label="操作类型" - align="center" - prop="type" - :formatter=" - (a, b, c) => { - return c === '1' ? '增加' : '扣减' - } - " - /> - <el-table-column label="积分" align="center" prop="point" /> - <el-table-column label="变动后的积分" align="center" prop="totalPoint" /> + <el-table-column label="用户" align="center" prop="nickname" width="200" /> + <el-table-column label="获得积分" align="center" prop="point" width="100"> + <template #default="scope"> + <el-tag v-if="scope.row.point > 0" class="ml-2" type="success" effect="dark"> + +{{ scope.row.point }} + </el-tag> + <el-tag v-else class="ml-2" type="danger" effect="dark"> {{ scope.row.point }} </el-tag> + </template> + </el-table-column> + <el-table-column label="总积分" align="center" prop="totalPoint" width="100" /> + <el-table-column label="标题" align="center" prop="title" /> + <el-table-column label="描述" align="center" prop="description" /> <el-table-column label="业务编码" align="center" prop="bizId" /> <el-table-column label="业务类型" align="center" prop="bizType"> <template #default="scope"> <dict-tag :type="DICT_TYPE.MEMBER_POINT_BIZ_TYPE" :value="scope.row.bizType" /> </template> </el-table-column> - <el-table-column label="状态" align="center" prop="status"> - <template #default="scope"> - <dict-tag :type="DICT_TYPE.MEMBER_POINT_STATUS" :value="scope.row.status" /> - </template> - </el-table-column> - <el-table-column - label="冻结时间" - align="center" - prop="freezingTime" - :formatter="dateFormatter" - /> - <el-table-column - label="解冻时间" - align="center" - prop="thawingTime" - :formatter="dateFormatter" - /> </el-table> <!-- 分页 --> <Pagination @@ -140,10 +109,9 @@ </template> <script lang="ts" setup> -import { DICT_TYPE, getStrDictOptions, getIntDictOptions } from '@/utils/dict' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { dateFormatter } from '@/utils/formatTime' -import * as RecordApi from '@/api/point/record' -import RecordForm from './RecordForm.vue' +import * as RecordApi from '@/api//member/point/record' defineOptions({ name: 'PointRecord' }) @@ -153,11 +121,9 @@ const list = ref([]) // 列表的数据 const queryParams = reactive({ pageNo: 1, pageSize: 10, - bizId: null, + nickname: null, bizType: null, - type: null, title: null, - status: null, createDate: [] }) const queryFormRef = ref() // 搜索的表单 diff --git a/src/views/member/signin/config/SignInConfigForm.vue b/src/views/member/signin/config/SignInConfigForm.vue index 52263c94..4d5c3d2d 100644 --- a/src/views/member/signin/config/SignInConfigForm.vue +++ b/src/views/member/signin/config/SignInConfigForm.vue @@ -10,12 +10,23 @@ <el-form-item label="签到天数" prop="day"> <el-input-number v-model="formData.day" :min="1" :max="7" :precision="0" /> <el-text class="mx-1" style="margin-left: 10px" type="danger"> - 只允许设置1-7,默认签到7天为一个周期</el-text - > + 只允许设置 1-7,默认签到 7 天为一个周期 + </el-text> </el-form-item> <el-form-item label="签到分数" prop="point"> <el-input-number v-model="formData.point" :precision="0" /> </el-form-item> + <el-form-item label="开启状态" prop="status"> + <el-radio-group v-model="formData.status"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> @@ -24,7 +35,9 @@ </Dialog> </template> <script lang="ts" setup> -import * as SignInConfigApi from '@/api/point/signInConfig' +import * as SignInConfigApi from '@/api/member/signin/config' +import { CommonStatusEnum } from '@/utils/constants' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 @@ -71,6 +84,8 @@ const submitForm = async () => { try { const data = formData.value as unknown as SignInConfigApi.SignInConfigVO if (formType.value === 'create') { + //默认新创建的自动启动 + data.enable = true await SignInConfigApi.createSignInConfig(data) message.success(t('common.createSuccess')) } else { @@ -90,7 +105,8 @@ const resetForm = () => { formData.value = { id: undefined, day: undefined, - point: undefined + point: undefined, + status: CommonStatusEnum.ENABLE } formRef.value?.resetFields() } diff --git a/src/views/member/signin/config/index.vue b/src/views/member/signin/config/index.vue index ca4db158..71dc4c6f 100644 --- a/src/views/member/signin/config/index.vue +++ b/src/views/member/signin/config/index.vue @@ -1,55 +1,31 @@ <template> <ContentWrap> <!-- 搜索工作栏 --> - <el-form - class="-mb-15px" - :model="queryParams" - ref="queryFormRef" - :inline="true" - label-width="68px" + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['point:sign-in-config:create']" > - <!-- TODO @xiaqing:搜索可以去掉,因为一共就没几条配置哈 --> - <el-form-item label="签到天数" prop="day"> - <el-input - v-model="queryParams.day" - placeholder="请输入签到天数" - clearable - @keyup.enter="handleQuery" - class="!w-240px" - /> - </el-form-item> - <el-form-item> - <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> - <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> - <el-button - type="primary" - plain - @click="openForm('create')" - v-hasPermi="['point:sign-in-config:create']" - > - <Icon icon="ep:plus" class="mr-5px" /> 新增 - </el-button> - <el-button - type="success" - plain - @click="handleExport" - :loading="exportLoading" - v-hasPermi="['point:sign-in-config:export']" - > - <!-- TODO @xiaqing:四个功能的导出都可以去掉 --> - <Icon icon="ep:download" class="mr-5px" /> 导出 - </el-button> - </el-form-item> - </el-form> + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> </ContentWrap> <!-- 列表 --> <ContentWrap> <el-table v-loading="loading" :data="list"> - <!-- TODO @xiaqing:展示优化下,改成第 1 天、第 2 天这种 --> - <el-table-column label="签到天数" align="center" prop="day" /> + <el-table-column + label="签到天数" + align="center" + prop="day" + :formatter="(_, __, cellValue) => ['第', cellValue, '天'].join(' ')" + /> <el-table-column label="获得积分" align="center" prop="point" /> - <!-- TODO @xiaqing:展示一个是否开启 --> + <el-table-column label="状态" align="center" prop="status"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> <el-table-column label="操作" align="center"> <template #default="scope"> <el-button @@ -71,23 +47,15 @@ </template> </el-table-column> </el-table> - <!-- 分页 --> - <Pagination - :total="total" - v-model:page="queryParams.pageNo" - v-model:limit="queryParams.pageSize" - @pagination="getList" - /> </ContentWrap> <!-- 表单弹窗:添加/修改 --> <SignInConfigForm ref="formRef" @success="getList" /> </template> - <script lang="ts" setup> -import download from '@/utils/download' -import * as SignInConfigApi from '@/api/point/signInConfig' +import * as SignInConfigApi from '@/api/member/signin/config' import SignInConfigForm from './SignInConfigForm.vue' +import { DICT_TYPE } from '@/utils/dict' defineOptions({ name: 'SignInConfig' }) @@ -95,41 +63,20 @@ const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 const loading = ref(true) // 列表的加载中 -const total = ref(0) // 列表的总页数 const list = ref([]) // 列表的数据 -const queryParams = reactive({ - pageNo: 1, - pageSize: 10, - day: null -}) -const queryFormRef = ref() // 搜索的表单 -const exportLoading = ref(false) // 导出的加载中 -// TODO @xiaqing:可以不分页; /** 查询列表 */ const getList = async () => { loading.value = true try { - const data = await SignInConfigApi.getSignInConfigPage(queryParams) - list.value = data.list - total.value = data.total + const data = await SignInConfigApi.getSignInConfigList() + console.log(data) + list.value = data } finally { loading.value = false } } -/** 搜索按钮操作 */ -const handleQuery = () => { - queryParams.pageNo = 1 - getList() -} - -/** 重置按钮操作 */ -const resetQuery = () => { - queryFormRef.value.resetFields() - handleQuery() -} - /** 添加/修改操作 */ const formRef = ref() const openForm = (type: string, id?: number) => { @@ -149,21 +96,6 @@ const handleDelete = async (id: number) => { } catch {} } -/** 导出按钮操作 */ -const handleExport = async () => { - try { - // 导出的二次确认 - await message.exportConfirm() - // 发起导出 - exportLoading.value = true - const data = await SignInConfigApi.exportSignInConfig(queryParams) - download.excel(data, '积分签到规则.xls') - } catch { - } finally { - exportLoading.value = false - } -} - /** 初始化 **/ onMounted(() => { getList() diff --git a/src/views/member/signin/record/index.vue b/src/views/member/signin/record/index.vue index 0dfd2aa6..754663e5 100644 --- a/src/views/member/signin/record/index.vue +++ b/src/views/member/signin/record/index.vue @@ -8,9 +8,9 @@ :inline="true" label-width="68px" > - <el-form-item label="签到用户" prop="userId"> + <el-form-item label="签到用户" prop="nickname"> <el-input - v-model="queryParams.userId" + v-model="queryParams.nickname" placeholder="请输入签到用户" clearable @keyup.enter="handleQuery" @@ -40,15 +40,6 @@ <el-form-item> <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> - <el-button - type="success" - plain - @click="handleExport" - :loading="exportLoading" - v-hasPermi="['point:sign-in-record:export']" - > - <Icon icon="ep:download" class="mr-5px" /> 导出 - </el-button> </el-form-item> </el-form> </ContentWrap> @@ -57,28 +48,27 @@ <ContentWrap> <el-table v-loading="loading" :data="list"> <el-table-column label="编号" align="center" prop="id" /> - <!-- TODO @xiaqing:展示用户昵称 --> - <el-table-column label="签到用户" align="center" prop="userId" /> - <el-table-column label="签到天数" align="center" prop="day" /> - <el-table-column label="获得积分" align="center" prop="point" /> + <el-table-column label="签到用户" align="center" prop="nickname" /> + <el-table-column + label="签到天数" + align="center" + prop="day" + :formatter="(_, __, cellValue) => ['第', cellValue, '天'].join(' ')" + /> + <el-table-column label="获得积分" align="center" prop="point" width="100"> + <template #default="scope"> + <el-tag v-if="scope.row.point > 0" class="ml-2" type="success" effect="dark"> + +{{ scope.row.point }} + </el-tag> + <el-tag v-else class="ml-2" type="danger" effect="dark"> {{ scope.row.point }} </el-tag> + </template> + </el-table-column> <el-table-column label="签到时间" align="center" prop="createTime" :formatter="dateFormatter" /> - <el-table-column label="操作" align="center"> - <template #default="scope"> - <el-button - link - type="danger" - @click="handleDelete(scope.row.id)" - v-hasPermi="['point:sign-in-record:delete']" - > - 删除 - </el-button> - </template> - </el-table-column> </el-table> <!-- 分页 --> <Pagination @@ -88,21 +78,15 @@ @pagination="getList" /> </ContentWrap> - - <!-- 表单弹窗:添加/修改 --> - <SignInRecordForm ref="formRef" @success="getList" /> </template> <script lang="ts" setup> import { dateFormatter } from '@/utils/formatTime' -import download from '@/utils/download' -import * as SignInRecordApi from '@/api/point/signInRecord' -import SignInRecordForm from './SignInRecordForm.vue' +import * as SignInRecordApi from '@/api/member/signin/record' defineOptions({ name: 'SignInRecord' }) const message = useMessage() // 消息弹窗 -const { t } = useI18n() // 国际化 const loading = ref(true) // 列表的加载中 const total = ref(0) // 列表的总页数 @@ -110,7 +94,7 @@ const list = ref([]) // 列表的数据 const queryParams = reactive({ pageNo: 1, pageSize: 10, - userId: null, + nickname: null, day: null, createTime: [] }) @@ -141,40 +125,6 @@ const resetQuery = () => { handleQuery() } -/** 添加/修改操作 */ -// const formRef = ref() -// const openForm = (type: string, id?: number) => { -// formRef.value.open(type, id) -// } - -/** 删除按钮操作 */ -const handleDelete = async (id: number) => { - try { - // 删除的二次确认 - await message.delConfirm() - // 发起删除 - await SignInRecordApi.deleteSignInRecord(id) - message.success(t('common.delSuccess')) - // 刷新列表 - await getList() - } catch {} -} - -/** 导出按钮操作 */ -const handleExport = async () => { - try { - // 导出的二次确认 - await message.exportConfirm() - // 发起导出 - exportLoading.value = true - const data = await SignInRecordApi.exportSignInRecord(queryParams) - download.excel(data, '用户签到积分.xls') - } catch { - } finally { - exportLoading.value = false - } -} - /** 初始化 **/ onMounted(() => { getList() diff --git a/src/views/member/signin/record/SignInRecordForm.vue b/src/views/member/tag/TagForm.vue similarity index 68% rename from src/views/member/signin/record/SignInRecordForm.vue rename to src/views/member/tag/TagForm.vue index 8534aed0..d45ea589 100644 --- a/src/views/member/signin/record/SignInRecordForm.vue +++ b/src/views/member/tag/TagForm.vue @@ -7,14 +7,8 @@ label-width="100px" v-loading="formLoading" > - <el-form-item label="签到用户" prop="userId"> - <el-input v-model="formData.userId" placeholder="请输入签到用户" /> - </el-form-item> - <el-form-item label="签到天数" prop="day"> - <el-input v-model="formData.day" placeholder="请输入签到天数" /> - </el-form-item> - <el-form-item label="签到的分数" prop="point"> - <el-input v-model="formData.point" placeholder="请输入签到的分数" /> + <el-form-item label="标签名称" prop="name"> + <el-input v-model="formData.name" placeholder="请输入标签名称" /> </el-form-item> </el-form> <template #footer> @@ -23,8 +17,8 @@ </template> </Dialog> </template> -<script lang="ts" setup> -import * as SignInRecordApi from '@/api/point/signInRecord' +<script setup lang="ts"> +import * as TagApi from '@/api/member/tag' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 @@ -35,11 +29,11 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加 const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ id: undefined, - userId: undefined, - day: undefined, - point: undefined + name: undefined +}) +const formRules = reactive({ + name: [{ required: true, message: '标签名称不能为空', trigger: 'blur' }] }) -const formRules = reactive({}) const formRef = ref() // 表单 Ref /** 打开弹窗 */ @@ -52,7 +46,7 @@ const open = async (type: string, id?: number) => { if (id) { formLoading.value = true try { - formData.value = await SignInRecordApi.getSignInRecord(id) + formData.value = await TagApi.getMemberTag(id) } finally { formLoading.value = false } @@ -70,12 +64,12 @@ const submitForm = async () => { // 提交请求 formLoading.value = true try { - const data = formData.value as unknown as SignInRecordApi.SignInRecordVO + const data = formData.value as unknown as TagApi.TagVO if (formType.value === 'create') { - await SignInRecordApi.createSignInRecord(data) + await TagApi.createMemberTag(data) message.success(t('common.createSuccess')) } else { - await SignInRecordApi.updateSignInRecord(data) + await TagApi.updateMemberTag(data) message.success(t('common.updateSuccess')) } dialogVisible.value = false @@ -90,9 +84,7 @@ const submitForm = async () => { const resetForm = () => { formData.value = { id: undefined, - userId: undefined, - day: undefined, - point: undefined + name: undefined } formRef.value?.resetFields() } diff --git a/src/views/member/tag/components/MemberTagSelect.vue b/src/views/member/tag/components/MemberTagSelect.vue new file mode 100644 index 00000000..ec4f4617 --- /dev/null +++ b/src/views/member/tag/components/MemberTagSelect.vue @@ -0,0 +1,69 @@ +<template> + <el-select v-model="tagIds" placeholder="请选择用户标签" clearable multiple class="!w-240px"> + <el-option v-for="tag in tags" :key="tag.id" :label="tag.name" :value="tag.id" /> + </el-select> + <el-button + v-if="showAdd" + type="primary" + class="ml-2" + link + @click="openForm('create')" + v-hasPermi="['member:tag:create']" + > + 新增标签 + </el-button> + + <!-- 表单弹窗:添加 --> + <TagForm ref="formRef" @success="getList" /> +</template> + +<script lang="ts" setup> +import * as TagApi from '@/api/member/tag' +import TagForm from '@/views/member/tag/TagForm.vue' + +defineOptions({ name: 'MemberTagSelect' }) + +const props = defineProps({ + /** 下拉框选中值 **/ + modelValue: { + type: Array, + default: undefined + }, + /** 是否显示“新增标签”按钮 **/ + showAdd: { + type: Boolean, + default: false + } +}) +const emit = defineEmits(['update:modelValue']) +defineExpose({ + showAdd: props.showAdd +}) + +const tagIds = computed({ + get() { + return props.modelValue + }, + set(value: any) { + emit('update:modelValue', value) + } +}) + +const tags = ref<TagApi.TagVO[]>([]) + +const getList = async () => { + const data = await TagApi.getMemberTagPage({}) + tags.value = data.list +} + +/** 添加用户标签表单弹框 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 初始化 */ +onMounted(() => { + getList() +}) +</script> diff --git a/src/views/member/tag/index.vue b/src/views/member/tag/index.vue new file mode 100644 index 00000000..05a886a7 --- /dev/null +++ b/src/views/member/tag/index.vue @@ -0,0 +1,153 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="标签名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入标签名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button type="primary" @click="openForm('create')" v-hasPermi="['member:tag:create']"> + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" width="150px" /> + <el-table-column label="标签名称" align="center" prop="name" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center" width="150px"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['member:tag:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['member:tag:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <TagForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts" name="MemberTag"> +import { dateFormatter } from '@/utils/formatTime' +import download from '@/utils/download' +import * as TagApi from '@/api/member/tag' +import TagForm from './TagForm.vue' +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: null, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await TagApi.getMemberTagPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await TagApi.deleteMemberTag(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> diff --git a/src/views/member/user/UserForm.vue b/src/views/member/user/UserForm.vue index a6d02582..c10d3fea 100644 --- a/src/views/member/user/UserForm.vue +++ b/src/views/member/user/UserForm.vue @@ -57,6 +57,9 @@ :render-after-expand="true" /> </el-form-item> + <el-form-item label="用户标签" prop="tagIds"> + <MemberTagSelect v-model="formData.tagIds" show-add /> + </el-form-item> <el-form-item label="会员备注" prop="mark"> <el-input type="textarea" v-model="formData.mark" placeholder="请输入会员备注" /> </el-form-item> @@ -72,6 +75,7 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import * as UserApi from '@/api/member/user' import * as AreaApi from '@/api/system/area' import { defaultProps } from '@/utils/tree' +import MemberTagSelect from '@/views/member/tag/components/MemberTagSelect.vue' const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 @@ -90,7 +94,8 @@ const formData = ref({ sex: undefined, areaId: undefined, birthday: undefined, - mark: undefined + mark: undefined, + tagIds: [] }) const formRules = reactive({ mobile: [{ required: true, message: '手机号不能为空', trigger: 'blur' }], @@ -163,7 +168,8 @@ const resetForm = () => { areaId: undefined, birthday: undefined, mark: undefined, - createTime: undefined + createTime: undefined, + tagIds: [] } formRef.value?.resetFields() } diff --git a/src/views/member/user/index.vue b/src/views/member/user/index.vue index 925eca04..0389de37 100644 --- a/src/views/member/user/index.vue +++ b/src/views/member/user/index.vue @@ -48,6 +48,9 @@ class="!w-240px" /> </el-form-item> + <el-form-item label="用户标签" prop="tagIds"> + <MemberTagSelect v-model="queryParams.tagIds" /> + </el-form-item> <el-form-item> <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> @@ -69,6 +72,16 @@ <!-- TODO 芋艿:待接入 --> <el-table-column label="等级" align="center" width="100px" /> <el-table-column label="分组" align="center" width="100px" /> + <el-table-column + label="用户标签" + align="center" + prop="tagNames" + :show-overflow-tooltip="false" + > + <template #default="scope"> + <el-tag v-for="(tagName, index) in scope.row.tagNames" :key="index">{{ tagName }}</el-tag> + </template> + </el-table-column> <el-table-column label="积分" align="center" width="100px" /> <el-table-column label="状态" align="center" prop="status" width="100px"> <template #default="scope"> @@ -119,6 +132,7 @@ import { dateFormatter } from '@/utils/formatTime' import * as UserApi from '@/api/member/user' import UserForm from './UserForm.vue' import { DICT_TYPE } from '@/utils/dict' +import MemberTagSelect from '@/views/member/tag/components/MemberTagSelect.vue' defineOptions({ name: 'MemberUser' }) @@ -134,7 +148,8 @@ const queryParams = reactive({ nickname: null, mobile: null, loginDate: [], - createTime: [] + createTime: [], + tagIds: [] }) const queryFormRef = ref() // 搜索的表单 const exportLoading = ref(false) // 导出的加载中