优化小程序菜单管理功能

This commit is contained in:
gxwebsoft
2024-06-01 02:45:33 +08:00
parent 4bae8599e1
commit e3fb9ba283
36 changed files with 2161 additions and 430 deletions

View File

@@ -10,6 +10,10 @@ export interface Card {
cardName?: string; cardName?: string;
// 会员卡标识 // 会员卡标识
cardCode?: string; cardCode?: string;
// 会员卡类型
type?: string;
// 成人儿童
cardType?: number;
// 会员卡图片 // 会员卡图片
image?: string; image?: string;
// 价格 // 价格
@@ -30,6 +34,8 @@ export interface Card {
merchantName?: string; merchantName?: string;
// 商户类型 // 商户类型
merchantType?: string; merchantType?: string;
// 可使用的场馆ID
merchantIds?: string;
// 备注 // 备注
comments?: string; comments?: string;
// 状态 // 状态

View File

@@ -0,0 +1,106 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type { UserCard, UserCardParam } from './model';
import { MODULES_API_URL } from '@/config/setting';
/**
* 分页查询会员卡
*/
export async function pageUserCard(params: UserCardParam) {
const res = await request.get<ApiResult<PageResult<UserCard>>>(
MODULES_API_URL + '/booking/user-card/page',
{
params
}
);
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 查询会员卡列表
*/
export async function listUserCard(params?: UserCardParam) {
const res = await request.get<ApiResult<UserCard[]>>(
MODULES_API_URL + '/booking/user-card',
{
params
}
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 添加会员卡
*/
export async function addUserCard(data: UserCard) {
const res = await request.post<ApiResult<unknown>>(
MODULES_API_URL + '/booking/user-card',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 修改会员卡
*/
export async function updateUserCard(data: UserCard) {
const res = await request.put<ApiResult<unknown>>(
MODULES_API_URL + '/booking/user-card',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 删除会员卡
*/
export async function removeUserCard(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/booking/user-card/' + id
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 批量删除会员卡
*/
export async function removeBatchUserCard(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/booking/user-card/batch',
{
data
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 根据id查询会员卡
*/
export async function getUserCard(id: number) {
const res = await request.get<ApiResult<UserCard>>(
MODULES_API_URL + '/booking/user-card/' + id
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}

View File

@@ -0,0 +1,109 @@
import type { PageParam } from '@/api';
/**
* 会员卡
*/
export interface UserCard {
//
id?: number;
// 用户id
userId?: number;
// sid场馆id集合适用的场馆
sid?: string;
// 用户id
uid?: number;
// vip卡id
vid?: number;
// 开卡人id
aid?: number;
// 微信订单号
wechatOrder?: string;
// 卡号
code?: string;
// 会员卡名称
name?: string;
// 真实姓名
username?: string;
// 手机号码
phone?: string;
// vip购卡价格
price?: string;
// 会员卡介绍
desc?: string;
// 会员卡说明
info?: string;
// vip卡折扣率
discount?: string;
// 使用次数
count?: number;
// 每使用一次减少的金额
eachMoney?: string;
// 剩余金额
remainingMoney?: string;
// 续费累加次数
number?: number;
// 剩余次数
num?: number;
// 付款状态,1已付款2未付款
status?: number;
// 会员卡年限
term?: number;
// 月限
month?: number;
// IC卡类型1年卡2次卡3月卡4会员IC卡5充值卡
type?: number;
// 卡类型1成人卡2儿童卡
cardType?: number;
// vip卡等级类型1特殊vip卡2普通vip卡
vipType?: number;
// 特殊卡开发凭证图
pic?: string;
// 价格组
prices?: string;
// 1微信支付2支付宝支付3现金4POS机刷卡15平安健康卡
payType?: number;
// 是否赠送积分1赠送2不赠送
isIntegral?: number;
// 是否已开具发票1已开发票2未开发票
isInvoice?: number;
// vip卡到期时间
expireTime?: number;
// 紧急联系人
urgentName?: string;
// 紧急联系人号码
urgentPhone?: string;
// 卡号
cardNum?: string;
// 密码
password?: string;
// 使用时间
useTime?: number;
// 创建时间
createTime?: number;
//
updateTime?: number;
// 身份证号码
idCard?: string;
// 备注
remark?: string;
// 备注
comments?: string;
// 排序号
sortNumber?: number;
// 租户id
tenantId?: number;
}
/**
* 会员卡搜索条件
*/
export interface UserCardParam extends PageParam {
id?: number;
type?: number;
typeName?: string;
userId?: number;
username?: string;
phone?: string;
cardNum?: string;
keywords?: string;
}

View File

@@ -0,0 +1,106 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type { UserCardLog, UserCardLogParam } from './model';
import { MODULES_API_URL } from '@/config/setting';
/**
* 分页查询明细表
*/
export async function pageUserCardLog(params: UserCardLogParam) {
const res = await request.get<ApiResult<PageResult<UserCardLog>>>(
MODULES_API_URL + '/booking/user-card-log/page',
{
params
}
);
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 查询明细表列表
*/
export async function listUserCardLog(params?: UserCardLogParam) {
const res = await request.get<ApiResult<UserCardLog[]>>(
MODULES_API_URL + '/booking/user-card-log',
{
params
}
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 添加明细表
*/
export async function addUserCardLog(data: UserCardLog) {
const res = await request.post<ApiResult<unknown>>(
MODULES_API_URL + '/booking/user-card-log',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 修改明细表
*/
export async function updateUserCardLog(data: UserCardLog) {
const res = await request.put<ApiResult<unknown>>(
MODULES_API_URL + '/booking/user-card-log',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 删除明细表
*/
export async function removeUserCardLog(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/booking/user-card-log/' + id
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 批量删除明细表
*/
export async function removeBatchUserCardLog(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/booking/user-card-log/batch',
{
data
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 根据id查询明细表
*/
export async function getUserCardLog(id: number) {
const res = await request.get<ApiResult<UserCardLog>>(
MODULES_API_URL + '/booking/user-card-log/' + id
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}

View File

@@ -0,0 +1,49 @@
import type { PageParam } from '@/api';
/**
* 明细表
*/
export interface UserCardLog {
// 主键ID
logId?: number;
// 用户ID
userId?: number;
// IC卡类型1年卡2次卡3月卡4会员IC卡5充值卡
type?: number;
// 变动金额
money?: string;
// 变动后余额
balance?: string;
// 管理员备注
remark?: string;
// 订单编号
orderNo?: string;
// 操作人ID
adminId?: number;
// 排序(数字越小越靠前)
sortNumber?: number;
// 备注
comments?: string;
// 状态, 0正常, 1冻结
status?: number;
// 是否删除, 0否, 1是
deleted?: number;
// 商户ID
merchantId?: number;
// 商户编码
merchantCode?: string;
// 租户id
tenantId?: number;
// 注册时间
createTime?: string;
// 修改时间
updateTime?: string;
}
/**
* 明细表搜索条件
*/
export interface UserCardLogParam extends PageParam {
logId?: number;
keywords?: string;
}

View File

@@ -50,6 +50,8 @@ export interface MpMenu {
goodsId?: number; goodsId?: number;
// 用户ID // 用户ID
userId?: number; userId?: number;
// 是否管理人员可见
adminShow?: number;
// 设为首页 // 设为首页
home?: number; home?: number;
// 排序(数字越小越靠前) // 排序(数字越小越靠前)

View File

@@ -17,6 +17,8 @@ export interface Merchant {
realName?: string; realName?: string;
// 店铺类型 // 店铺类型
shopType?: string; shopType?: string;
// 项目类型
itemType?: string;
// 商户分类 // 商户分类
category?: string; category?: string;
// 商户坐标 // 商户坐标
@@ -75,5 +77,7 @@ export interface Merchant {
*/ */
export interface MerchantParam extends PageParam { export interface MerchantParam extends PageParam {
merchantId?: number; merchantId?: number;
merchantIds?: string;
merchantCodes?: string;
keywords?: string; keywords?: string;
} }

View File

@@ -7,6 +7,10 @@ import { OrderInfo } from '@/api/shop/orderInfo/model';
export interface Order { export interface Order {
// 订单号 // 订单号
orderId?: number; orderId?: number;
// 订单类型
type?: number;
// 下单渠道
channel?: number;
// 订单编号 // 订单编号
orderNo?: string; orderNo?: string;
// 微信支付订单号 // 微信支付订单号

View File

@@ -2,6 +2,7 @@ import type { PageParam } from '@/api';
import type { Role } from '../../role/model'; import type { Role } from '../../role/model';
import type { Menu } from '../../menu/model'; import type { Menu } from '../../menu/model';
import { Company } from '@/api/system/company/model'; import { Company } from '@/api/system/company/model';
import { Merchant } from "@/api/shop/merchant/model";
/** /**
* 用户 * 用户
@@ -65,6 +66,8 @@ export interface User {
deliveryTime?: string; deliveryTime?: string;
receiptTime?: string; receiptTime?: string;
merchantId?: number; merchantId?: number;
// 可管理的商户
merchants?: string;
// 创建时间 // 创建时间
createTime?: string; createTime?: string;
// 租户ID // 租户ID

View File

@@ -9,6 +9,7 @@
:placeholder="placeholder" :placeholder="placeholder"
@update:value="updateValue" @update:value="updateValue"
:style="`width: ${width}px`" :style="`width: ${width}px`"
@change="change"
@blur="onBlur" @blur="onBlur"
/> />
</template> </template>
@@ -20,6 +21,7 @@
(e: 'update:value', value: string): void; (e: 'update:value', value: string): void;
(e: 'index', index: number): void; (e: 'index', index: number): void;
(e: 'blur'): void; (e: 'blur'): void;
(e: 'done', item: any): void;
}>(); }>();
const props = withDefaults( const props = withDefaults(
@@ -42,12 +44,16 @@
/* 更新选中数据 */ /* 更新选中数据 */
const updateValue = (value: string) => { const updateValue = (value: string) => {
console.log(value);
emit('update:value', value); emit('update:value', value);
emit('index', Number(props.index)); emit('index', Number(props.index));
}; };
/* 失去焦点 */ /* 失去焦点 */
const onBlur = () => { const onBlur = () => {
emit('blur'); emit('blur');
}; };
const change = (e, item) => {
emit('done', item);
};
</script> </script>

View File

@@ -19,19 +19,16 @@
</a-tree-select> </a-tree-select>
</div> </div>
<!-- 商户名称 --> <!-- 商户名称 -->
<template v-if="getMerchantName()"> <template v-if="merchants.length > 0">
<div class="ele-admin-header-tool-item"> <div class="ele-admin-header-tool-item">
<a-button @click="openUrl(`/booking/school`)">{{ <MerchantSelect
getMerchantName() :merchants="merchants"
}}</a-button> v-model:value="merchantName"
@done="onMerchant"
/>
</div> </div>
</template> </template>
<!-- <div-->
<!-- class="ele-admin-header-tool-item"-->
<!-- @click="openUrl('https://b.gxwebsoft.com')"-->
<!-- >-->
<!-- 旧版-->
<!-- </div>-->
<!-- 消息通知 --> <!-- 消息通知 -->
<div class="ele-admin-header-tool-item" @click="openUrl(`/user/notice`)"> <div class="ele-admin-header-tool-item" @click="openUrl(`/user/notice`)">
<header-notice /> <header-notice />
@@ -163,9 +160,6 @@
import { import {
DownOutlined, DownOutlined,
CopyOutlined, CopyOutlined,
UserOutlined,
MoreOutlined,
LogoutOutlined,
ExclamationCircleOutlined, ExclamationCircleOutlined,
FullscreenOutlined, FullscreenOutlined,
FullscreenExitOutlined, FullscreenExitOutlined,
@@ -183,6 +177,9 @@
import { listMenus } from '@/api/system/menu'; import { listMenus } from '@/api/system/menu';
import { isExternalLink, toTreeData } from 'ele-admin-pro'; import { isExternalLink, toTreeData } from 'ele-admin-pro';
import { getMerchantName } from '@/utils/merchant'; import { getMerchantName } from '@/utils/merchant';
import { Merchant } from '@/api/shop/merchant/model';
import { listMerchant } from '@/api/shop/merchant';
import MerchantSelect from './merchant-select.vue';
// 是否开启响应式布局 // 是否开启响应式布局
const themeStore = useThemeStore(); const themeStore = useThemeStore();
@@ -214,6 +211,8 @@
const menuTree = ref<Menu[]>([]); const menuTree = ref<Menu[]>([]);
const parentId = ref<number>(); const parentId = ref<number>();
const bordered = ref<boolean>(false); const bordered = ref<boolean>(false);
const merchants = ref<Merchant[]>([]);
const merchantName = ref();
/* 用户信息下拉点击 */ /* 用户信息下拉点击 */
const onUserDropClick = ({ key }) => { const onUserDropClick = ({ key }) => {
@@ -261,7 +260,17 @@
if (item?.component) { if (item?.component) {
return push(item.path); return push(item.path);
} }
// bordered.value = true; };
// 选择商户
const onMerchant = (id: number) => {
const item = merchants.value.find((d) => d.merchantId == id);
if (item) {
merchantName.value = item.merchantName;
localStorage.setItem('MerchantName', `${item.merchantName}`);
localStorage.setItem('MerchantId', `${item.merchantId}`);
window.location.reload();
}
}; };
/* 切换全屏 */ /* 切换全屏 */
@@ -274,8 +283,9 @@
settingVisible.value = true; settingVisible.value = true;
}; };
const getMenus = () => { const reload = () => {
if (!getMerchantName()) { if (loginUser.value.username == 'admin') {
// 查询菜单列表
listMenus({ menuType: 0, hide: 0 }).then((data) => { listMenus({ menuType: 0, hide: 0 }).then((data) => {
if (data) { if (data) {
menuList.value = data; menuList.value = data;
@@ -289,9 +299,19 @@
} }
}); });
} }
merchantName.value = getMerchantName();
if (loginUser.value.merchants) {
listMerchant({ merchantIds: loginUser.value.merchants }).then((res) => {
merchants.value = res.map((d) => {
d.label = d.merchantName;
d.value = d.merchantId;
return d;
});
});
}
}; };
getMenus(); reload();
</script> </script>
<script lang="ts"> <script lang="ts">

View File

@@ -0,0 +1,54 @@
<!-- 选择下拉框 -->
<template>
<div>
<a-select
:options="merchants"
v-model:value="value"
:placeholder="placeholder"
@update:value="updateValue"
:style="`width: 200px`"
@blur="onBlur"
@change="onChange"
/>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Merchant } from '@/api/shop/merchant/model';
const emit = defineEmits<{
(e: 'update:value', value: string, item: any): void;
(e: 'done', item: Merchant): void;
(e: 'blur'): void;
}>();
const props = withDefaults(
defineProps<{
value?: any;
merchants?: Merchant[];
placeholder?: string;
}>(),
{
placeholder: '请选择场馆'
}
);
// 字典数据
const options = ref<Merchant[]>([]);
/* 更新选中数据 */
const updateValue = (value: string) => {
const item = options.value?.find((d) => d.merchantName == value);
emit('update:value', value, item);
};
/* 失去焦点 */
const onBlur = () => {
emit('blur');
};
const onChange = (item: any) => {
emit('done', item);
};
</script>

View File

@@ -18,24 +18,6 @@
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' } styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
" "
> >
<a-form-item label="选择场馆" name="merchantId" v-if="!getMerchantId()">
<SelectMerchantMultipleDown
:placeholder="`选择场馆`"
class="input-item"
v-model:value="form.merchantName"
@done="chooseMerchantId"
/>
</a-form-item>
<a-form-item label="选择角色" name="roles">
<role-select v-model:value="form.roles" />
<!-- <SelectRole-->
<!-- :placeholder="`选择角色`"-->
<!-- class="input-item"-->
<!-- :type="`merchant`"-->
<!-- v-model:value="form.roleName"-->
<!-- @done="chooseRoleId"-->
<!-- />-->
</a-form-item>
<a-form-item label="账号" name="username"> <a-form-item label="账号" name="username">
<a-input <a-input
allow-clear allow-clear
@@ -68,6 +50,12 @@
v-model:value="form.realName" v-model:value="form.realName"
/> />
</a-form-item> </a-form-item>
<a-form-item label="选择角色" name="roles">
<role-select v-model:value="form.roles" />
</a-form-item>
<a-form-item label="可管理场馆" name="merchantId">
<MerchantSelect v-model:value="merchants" />
</a-form-item>
</a-form> </a-form>
</ele-modal> </ele-modal>
</template> </template>
@@ -83,9 +71,12 @@
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types'; import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance, RuleObject } from 'ant-design-vue/es/form'; import { FormInstance, RuleObject } from 'ant-design-vue/es/form';
import RoleSelect from './role-select.vue'; import RoleSelect from './role-select.vue';
import MerchantSelect from './merchant-select.vue';
import { getMerchantId } from '@/utils/common'; import { getMerchantId } from '@/utils/common';
import { getMerchantName } from '@/utils/merchant'; import { getMerchantName } from '@/utils/merchant';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { Merchant } from '@/api/shop/merchant/model';
import { listMerchant } from '@/api/shop/merchant';
const userStore = useUserStore(); const userStore = useUserStore();
@@ -118,6 +109,7 @@
const formRef = ref<FormInstance | null>(null); const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]); const images = ref<ItemType[]>([]);
const isSuperAdmin = ref<boolean>(false); const isSuperAdmin = ref<boolean>(false);
const merchants = ref<Merchant[]>([]);
// 用户信息 // 用户信息
const form = reactive<User>({ const form = reactive<User>({
@@ -127,6 +119,7 @@
nickname: '', nickname: '',
realName: '', realName: '',
companyName: '', companyName: '',
merchants: '',
merchantId: getMerchantId(), merchantId: getMerchantId(),
merchantName: getMerchantName(), merchantName: getMerchantName(),
sex: undefined, sex: undefined,
@@ -162,14 +155,14 @@
trigger: 'blur' trigger: 'blur'
} }
], ],
merchantId: [ // merchantId: [
{ // {
required: true, // required: true,
type: 'number', // type: 'string',
message: '请选择场馆', // message: '请选择场馆',
trigger: 'blur' // trigger: 'blur'
} // }
], // ],
roles: [ roles: [
{ {
required: true, required: true,
@@ -236,7 +229,8 @@
.then(() => { .then(() => {
loading.value = true; loading.value = true;
const formData = { const formData = {
...form ...form,
merchants: merchants.value?.map((d) => d.merchantId).join(',')
}; };
if (getMerchantId()) { if (getMerchantId()) {
form.merchantId = getMerchantId(); form.merchantId = getMerchantId();
@@ -263,8 +257,21 @@
(visible) => { (visible) => {
if (visible) { if (visible) {
images.value = []; images.value = [];
merchants.value = [];
if (props.data) { if (props.data) {
assignObject(form, props.data); assignObject(form, props.data);
if (props.data.merchants) {
listMerchant({ merchantIds: props.data.merchants }).then((list) => {
merchants.value = list;
});
}
// if (props.data.comments) {
// listMerchant({ merchantCodes: props.data.comments }).then(
// (list) => {
// merchants.value = list;
// }
// );
// }
isUpdate.value = true; isUpdate.value = true;
} else { } else {
resetFields(); resetFields();

View File

@@ -0,0 +1,73 @@
<!-- 角色选择下拉框 -->
<template>
<a-select
allow-clear
mode="multiple"
:value="merchantIds"
:placeholder="placeholder"
@update:value="updateValue"
@blur="onBlur"
>
<a-select-option
v-for="item in data"
:key="item.merchantId"
:value="item.merchantId"
>
{{ item.merchantName }}
</a-select-option>
</a-select>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import { message } from 'ant-design-vue/es';
import { Merchant } from '@/api/shop/merchant/model';
import { listMerchant } from '@/api/shop/merchant';
const emit = defineEmits<{
(e: 'update:value', value: Merchant[]): void;
(e: 'blur'): void;
}>();
const props = withDefaults(
defineProps<{
// 选中的商户
value?: Merchant[];
//
placeholder?: string;
}>(),
{
placeholder: '请选择场馆'
}
);
// 选中的角色id
const merchantIds = computed(() =>
props.value?.map((d) => d.merchantId as number)
);
// 角色数据
const data = ref<Merchant[]>([]);
/* 更新选中数据 */
const updateValue = (value: number[]) => {
emit(
'update:value',
value.map((v) => ({ merchantId: v }))
);
};
/* 获取角色数据 */
listMerchant({})
.then((list) => {
data.value = list;
})
.catch((e) => {
message.error(e.message);
});
/* 失去焦点 */
const onBlur = () => {
emit('blur');
};
</script>

View File

@@ -7,13 +7,22 @@
</template> </template>
<span>添加</span> <span>添加</span>
</a-button> </a-button>
<a-input-search
allow-clear
placeholder="请输入关键词"
v-model:value="where.keywords"
@pressEnter="search"
@search="search"
/>
<a-button @click="reset">重置</a-button>
</a-space> </a-space>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue'; import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue'; import { watch } from 'vue';
import useSearch from '@/utils/use-search';
import { UserParam } from '@/api/system/user/model';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -24,7 +33,7 @@
); );
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'search', where?: GradeParam): void; (e: 'search', where?: UserParam): void;
(e: 'add'): void; (e: 'add'): void;
(e: 'remove'): void; (e: 'remove'): void;
(e: 'batchMove'): void; (e: 'batchMove'): void;
@@ -35,6 +44,25 @@
emit('add'); emit('add');
}; };
// 表单数据
const { where, resetFields } = useSearch<UserParam>({
userId: undefined,
username: undefined,
phone: undefined,
realName: undefined,
keywords: undefined
});
const search = () => {
emit('search', where);
};
/* 重置 */
const reset = () => {
resetFields();
search();
};
watch( watch(
() => props.selection, () => props.selection,
() => {} () => {}

View File

@@ -38,7 +38,7 @@
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag> <a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template> </template>
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<a-space> <a-space v-if="record.username != 'admin'">
<a @click="openEdit(record)">修改</a> <a @click="openEdit(record)">修改</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popconfirm <a-popconfirm
@@ -72,7 +72,7 @@
import Edit from './components/edit.vue'; import Edit from './components/edit.vue';
import { pageUsers, removeUser, removeUsers } from '@/api/system/user'; import { pageUsers, removeUser, removeUsers } from '@/api/system/user';
import type { User, UserParam } from '@/api/system/user/model'; import type { User, UserParam } from '@/api/system/user/model';
import { getMerchantId } from "@/utils/common"; import { getMerchantId } from '@/utils/common';
// 表格实例 // 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -106,14 +106,6 @@
...orders, ...orders,
page, page,
limit limit
}).then((res) => {
res?.list.map((d) => {
d.roles = d.roles?.filter(
(m) => m.roleCode == 'merchant' || m.roleCode == 'merchantClerk'
);
return d;
});
return res;
}); });
}; };
@@ -126,12 +118,6 @@
align: 'center', align: 'center',
width: 90 width: 90
}, },
{
title: '场馆名称',
dataIndex: 'merchantName',
key: 'merchantName',
align: 'center'
},
{ {
title: '账号', title: '账号',
dataIndex: 'username', dataIndex: 'username',
@@ -156,6 +142,12 @@
key: 'roles', key: 'roles',
align: 'center' align: 'center'
}, },
// {
// title: '可管理场馆',
// dataIndex: 'merchantName',
// key: 'merchantName',
// align: 'center'
// },
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',

View File

@@ -19,13 +19,16 @@
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' } styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
" "
> >
<a-form-item label="选择场馆" name="merchantName"> <!-- <a-form-item label="选择场馆" name="merchantName">-->
<SelectMerchant <!-- <SelectMerchant-->
:placeholder="`选择场馆`" <!-- :placeholder="`选择场馆`"-->
class="input-item" <!-- class="input-item"-->
v-model:value="form.merchantName" <!-- v-model:value="form.merchantName"-->
@done="chooseMerchantId" <!-- @done="chooseMerchantId"-->
/> <!-- />-->
<!-- </a-form-item>-->
<a-form-item label="适用场馆" name="merchants">
<MerchantSelect v-model:value="merchants" />
</a-form-item> </a-form-item>
<a-form-item label="会员卡名称" name="cardName"> <a-form-item label="会员卡名称" name="cardName">
<a-input <a-input
@@ -34,10 +37,23 @@
v-model:value="form.cardName" v-model:value="form.cardName"
/> />
</a-form-item> </a-form-item>
<a-form-item label="会员卡类型" name="cardCode"> <a-form-item label="会员卡类型" name="type">
<a-select
v-model:value="form.type"
placeholder="会员卡类型"
:style="`width: 200px`"
>
<a-select-option :value="1">年卡</a-select-option>
<a-select-option :value="2">次卡</a-select-option>
<a-select-option :value="3">月卡</a-select-option>
<a-select-option :value="4">会员IC卡</a-select-option>
<a-select-option :value="5">充值卡</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="会员卡分组" name="cardCode">
<DictSelect <DictSelect
dict-code="cardPlanId" dict-code="cardPlanId"
:placeholder="`会员卡类型`" :placeholder="`会员卡分组`"
style="width: 200px" style="width: 200px"
v-model:value="form.cardCode" v-model:value="form.cardCode"
/> />
@@ -79,12 +95,32 @@
v-model:value="form.discount" v-model:value="form.discount"
/> />
</a-form-item> </a-form-item>
<a-form-item label="备注" name="comments"> <a-form-item label="成人/儿童" name="cardType">
<a-textarea <a-select
:rows="4" allow-clear
:maxlength="200" v-model:value="form.cardType"
placeholder="请输入描述" placeholder="成人/儿童"
v-model:value="form.comments" :style="`width: 200px`"
>
<a-select-option :value="1">成人</a-select-option>
<a-select-option :value="2">儿童</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="卡片背景" name="image">
<SelectFile
:placeholder="`请选择图片`"
:limit="1"
:data="images"
@done="chooseImage"
@del="onDeleteItem"
/>
</a-form-item>
<a-form-item label="会员卡说明" name="content">
<!-- 编辑器 -->
<tinymce-editor
v-model:value="content"
:init="config"
placeholder="会员卡说明"
/> />
</a-form-item> </a-form-item>
<a-form-item label="状态" name="status"> <a-form-item label="状态" name="status">
@@ -115,9 +151,15 @@
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types'; import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form'; import { FormInstance, RuleObject } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model'; import { FileRecord } from '@/api/system/file/model';
import { Merchant } from '@/api/shop/merchant/model'; import { Merchant } from '@/api/shop/merchant/model';
import gfm from '@bytemd/plugin-gfm';
import zh_HansGfm from '@bytemd/plugin-gfm/locales/zh_Hans.json';
import highlight from '@bytemd/plugin-highlight';
import TinymceEditor from '@/components/TinymceEditor/index.vue';
import MerchantSelect from '@/views/booking/account/components/merchant-select.vue';
import { listMerchant } from '@/api/shop/merchant';
// 是否是修改 // 是否是修改
const isUpdate = ref(false); const isUpdate = ref(false);
@@ -145,12 +187,16 @@
// 表格选中数据 // 表格选中数据
const formRef = ref<FormInstance | null>(null); const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]); const images = ref<ItemType[]>([]);
const content = ref('');
const merchants = ref<Merchant[]>([]);
// 用户信息 // 用户信息
const form = reactive<Card>({ const form = reactive<Card>({
cardId: undefined, cardId: undefined,
cardName: '', cardName: '',
cardCode: undefined, cardCode: undefined,
type: undefined,
cardType: undefined,
image: '', image: '',
price: undefined, price: undefined,
number: 0, number: 0,
@@ -172,6 +218,20 @@
// 表单验证规则 // 表单验证规则
const rules = reactive({ const rules = reactive({
merchants: [
{
required: true,
type: 'string',
message: '请选择可适用的场馆',
trigger: 'blur',
validator: async (_rule: RuleObject) => {
if (merchants.value.length == 0) {
return Promise.reject('请选择可适用的场馆');
}
return Promise.resolve();
}
}
],
merchantName: [ merchantName: [
{ {
required: true, required: true,
@@ -188,11 +248,19 @@
trigger: 'blur' trigger: 'blur'
} }
], ],
type: [
{
required: true,
type: 'number',
message: '请选择会员卡类型',
trigger: 'blur'
}
],
cardCode: [ cardCode: [
{ {
required: true, required: true,
type: 'string', type: 'string',
message: '请选择会员卡类型', message: '请选择会员卡分组',
trigger: 'blur' trigger: 'blur'
} }
], ],
@@ -214,12 +282,17 @@
] ]
}); });
/* 搜索 */ // 插件
const chooseMerchantId = (item: Merchant) => { const plugins = ref([
form.merchantName = item.merchantName; gfm({
form.merchantId = item.merchantId; locale: zh_HansGfm
form.merchantType = item.shopType; }),
}; highlight()
]);
const config = ref({
height: 500
});
const chooseImage = (data: FileRecord) => { const chooseImage = (data: FileRecord) => {
images.value.push({ images.value.push({
@@ -247,7 +320,9 @@
.then(() => { .then(() => {
loading.value = true; loading.value = true;
const formData = { const formData = {
...form ...form,
comments: content.value,
merchantIds: merchants.value?.map((d) => d.merchantId).join(',')
}; };
const saveOrUpdate = isUpdate.value ? updateCard : addCard; const saveOrUpdate = isUpdate.value ? updateCard : addCard;
saveOrUpdate(formData) saveOrUpdate(formData)
@@ -270,6 +345,7 @@
(visible) => { (visible) => {
if (visible) { if (visible) {
images.value = []; images.value = [];
merchants.value = [];
if (props.data) { if (props.data) {
assignObject(form, props.data); assignObject(form, props.data);
if (props.data.image) { if (props.data.image) {
@@ -279,6 +355,16 @@
status: 'done' status: 'done'
}); });
} }
if (props.data.merchantIds) {
listMerchant({ merchantIds: props.data.merchantIds }).then(
(list) => {
merchants.value = list;
}
);
}
if (props.data.comments) {
content.value = props.data.comments;
}
isUpdate.value = true; isUpdate.value = true;
} else { } else {
isUpdate.value = false; isUpdate.value = false;

View File

@@ -7,11 +7,19 @@
</template> </template>
<span>添加</span> <span>添加</span>
</a-button> </a-button>
<a-radio-group v-model:value="where.cardCode" @change="handleSearch"> <DictSelect
<a-radio-button :value="`月/年卡`">/年卡</a-radio-button> dict-code="cardPlanId"
<a-radio-button :value="`次卡`">次卡</a-radio-button> class="form-item"
<a-radio-button :value="`充值卡`">充值卡</a-radio-button> placeholder="按会员卡分组"
</a-radio-group> style="width: 180px"
v-model:value="where.cardCode"
@change="handleSearch"
/>
<!-- <a-radio-group v-model:value="where.cardCode" @change="handleSearch">-->
<!-- <a-radio-button :value="`月/年卡`">/年卡</a-radio-button>-->
<!-- <a-radio-button :value="`次卡`">次卡</a-radio-button>-->
<!-- <a-radio-button :value="`充值卡`">充值卡</a-radio-button>-->
<!-- </a-radio-group>-->
</a-space> </a-space>
</template> </template>
@@ -43,7 +51,7 @@
// 表单数据 // 表单数据
const { where } = useSearch<any>({ const { where } = useSearch<any>({
cardCode: '月/年卡' cardCode: undefined
}); });
const handleSearch = () => { const handleSearch = () => {

View File

@@ -115,9 +115,11 @@
}, },
{ {
title: '会员卡类型', title: '会员卡类型',
dataIndex: 'cardCode', dataIndex: 'type',
key: 'cardCode', key: 'type',
align: 'center' align: 'center',
customRender: ({ text }) =>
['', '年卡', '次卡', '月卡', '会员IC卡', '充值卡'][text]
}, },
{ {
title: '会员卡名称', title: '会员卡名称',

View File

@@ -214,6 +214,7 @@
orders, orders,
filters filters
}) => { }) => {
console.log(props);
if (props.categoryId) { if (props.categoryId) {
where.merchantId = props.categoryId; where.merchantId = props.categoryId;
} }

View File

@@ -1,19 +1,21 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<template> <template>
<a-space :size="10" style="flex-wrap: wrap"> <a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add"> <a-input-search
<template #icon> allow-clear
<PlusOutlined /> placeholder="请输入关键词"
</template> v-model:value="where.keywords"
<span>添加</span> @pressEnter="search"
</a-button> @search="search"
/>
<a-button @click="reset">重置</a-button>
</a-space> </a-space>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue'; import { watch } from 'vue';
import useSearch from '@/utils/use-search';
import { UserParam } from '@/api/system/user/model';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -24,7 +26,7 @@
); );
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'search', where?: GradeParam): void; (e: 'search', where?: UserParam): void;
(e: 'add'): void; (e: 'add'): void;
(e: 'remove'): void; (e: 'remove'): void;
(e: 'batchMove'): void; (e: 'batchMove'): void;
@@ -35,6 +37,25 @@
emit('add'); emit('add');
}; };
// 表单数据
const { where, resetFields } = useSearch<UserParam>({
userId: undefined,
username: undefined,
phone: undefined,
realName: undefined,
keywords: undefined
});
const search = () => {
emit('search', where);
};
/* 重置 */
const reset = () => {
resetFields();
search();
};
watch( watch(
() => props.selection, () => props.selection,
() => {} () => {}

View File

@@ -7,7 +7,6 @@
row-key="integralId" row-key="integralId"
:columns="columns" :columns="columns"
:datasource="datasource" :datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form" tool-class="ele-toolbar-form"
class="sys-org-table" class="sys-org-table"
> >
@@ -101,47 +100,40 @@
// 表格列配置 // 表格列配置
const columns = ref<ColumnItem[]>([ const columns = ref<ColumnItem[]>([
{ {
title: '', title: '用户ID',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '用户id',
dataIndex: 'userId', dataIndex: 'userId',
key: 'userId', key: 'userId',
align: 'center', width: 90
}, },
{ {
title: '微信昵称', title: '微信昵称',
dataIndex: 'username', dataIndex: 'username',
key: 'username', key: 'username',
align: 'center', align: 'center'
}, },
{ {
title: '手机号码', title: '手机号码',
dataIndex: 'phone', dataIndex: 'mobile',
key: 'phone', key: 'mobile',
align: 'center', align: 'center'
}, },
{ {
title: '获得积分', title: '获得积分',
dataIndex: 'integral', dataIndex: 'integral',
key: 'integral', key: 'integral',
align: 'center', align: 'center'
}, },
{ {
title: '日期', title: '日期',
dataIndex: 'dateTime', dataIndex: 'dateTime',
key: 'dateTime', key: 'dateTime',
align: 'center', align: 'center'
}, },
{ {
title: '天', title: '天',
dataIndex: 'day', dataIndex: 'day',
key: 'day', key: 'day',
align: 'center', align: 'center'
}, },
{ {
title: '签到时间', title: '签到时间',
@@ -151,15 +143,15 @@
sorter: true, sorter: true,
ellipsis: true, ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd') customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
} }
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]); ]);
/* 搜索 */ /* 搜索 */

View File

@@ -79,6 +79,7 @@
:labelStyle="{ width: '90px', color: '#808080' }" :labelStyle="{ width: '90px', color: '#808080' }"
> >
<template v-if="form.payStatus == 1"> <template v-if="form.payStatus == 1">
<a-tag v-if="form.payType == 0">余额支付</a-tag>
<a-tag v-if="form.payType == 1">微信支付</a-tag> <a-tag v-if="form.payType == 1">微信支付</a-tag>
<a-tag v-if="form.payType == 2">积分</a-tag> <a-tag v-if="form.payType == 2">积分</a-tag>
<a-tag v-if="form.payType == 3">支付宝</a-tag> <a-tag v-if="form.payType == 3">支付宝</a-tag>

View File

@@ -1,12 +1,12 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<template> <template>
<a-space :size="10" style="flex-wrap: wrap"> <a-space :size="10" style="flex-wrap: wrap">
<SelectMerchantDown <!-- <SelectMerchantDown-->
:placeholder="`选择场馆`" <!-- :placeholder="`选择场馆`"-->
class="input-item" <!-- class="input-item"-->
v-model:value="where.merchantCode" <!-- v-model:value="where.merchantCode"-->
@change="search" <!-- @change="search"-->
/> <!-- />-->
<a-input-search <a-input-search
allow-clear allow-clear
v-model:value="where.keywords" v-model:value="where.keywords"
@@ -14,7 +14,7 @@
@search="search" @search="search"
@pressEnter="search" @pressEnter="search"
/> />
<!-- <a-button @click="getCode">生成支付二维码</a-button>--> <!-- <a-button @click="getCode">生成支付二维码</a-button>-->
<a-button @click="reset">重置</a-button> <a-button @click="reset">重置</a-button>
</a-space> </a-space>
<ele-modal <ele-modal

View File

@@ -27,6 +27,7 @@
</template> </template>
<template v-if="column.key === 'payType'"> <template v-if="column.key === 'payType'">
<template v-if="record.payStatus == 1"> <template v-if="record.payStatus == 1">
<a-tag v-if="record.payType == 0">余额支付</a-tag>
<a-tag v-if="record.payType == 1" <a-tag v-if="record.payType == 1"
><WechatOutlined class="tag-icon" />微信支付</a-tag ><WechatOutlined class="tag-icon" />微信支付</a-tag
> >

View File

@@ -59,6 +59,14 @@
@done="chooseShopType" @done="chooseShopType"
/> />
</a-form-item> </a-form-item>
<a-form-item label="项目类型" name="itemType">
<DictSelect
dict-code="ItemType"
:placeholder="`请选择项目类型`"
style="width: 120px"
v-model:value="form.itemType"
/>
</a-form-item>
<a-form-item label="营业时间" name="businessTime"> <a-form-item label="营业时间" name="businessTime">
<!-- <a-time-picker--> <!-- <a-time-picker-->
<!-- v-model:value="form.timePeriod1"--> <!-- v-model:value="form.timePeriod1"-->
@@ -123,17 +131,17 @@
v-model:value="form.price" v-model:value="form.price"
/> />
</a-form-item> </a-form-item>
<!-- <a-form-item label="手续费(%)" name="commission">--> <!-- <a-form-item label="手续费(%)" name="commission">-->
<!-- <a-input-number--> <!-- <a-input-number-->
<!-- :step="1"--> <!-- :step="1"-->
<!-- :max="100"--> <!-- :max="100"-->
<!-- :min="0.0"--> <!-- :min="0.0"-->
<!-- :precision="2"--> <!-- :precision="2"-->
<!-- class="ele-fluid"--> <!-- class="ele-fluid"-->
<!-- placeholder="请输入手续费"--> <!-- placeholder="请输入手续费"-->
<!-- v-model:value="form.commission"--> <!-- v-model:value="form.commission"-->
<!-- />--> <!-- />-->
<!-- </a-form-item>--> <!-- </a-form-item>-->
<a-form-item label="关键字" name="keywords"> <a-form-item label="关键字" name="keywords">
<a-select <a-select
v-model:value="form.keywords" v-model:value="form.keywords"
@@ -280,6 +288,7 @@
phone: undefined, phone: undefined,
realName: undefined, realName: undefined,
shopType: undefined, shopType: undefined,
itemType: undefined,
category: undefined, category: undefined,
province: '', province: '',
city: '', city: '',

View File

@@ -140,6 +140,12 @@
key: 'phone', key: 'phone',
align: 'center' align: 'center'
}, },
{
title: '项目类型',
dataIndex: 'itemType',
key: 'itemType',
align: 'center'
},
{ {
title: '类型', title: '类型',
dataIndex: 'shopType', dataIndex: 'shopType',

View File

@@ -0,0 +1,104 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<DictSelect
dict-code="ICType"
:placeholder="`IC卡类型`"
style="width: 120px"
v-model:value="where.typeName"
@done="onType"
/>
<a-input-search
allow-clear
placeholder="请输入关键词"
v-model:value="where.keywords"
@pressEnter="search"
@search="search"
>
<template #addonBefore>
<a-select v-model:value="type" style="width: 100px; margin: -5px -12px">
<a-select-option value="userId">用户ID</a-select-option>
<a-select-option value="username">真实姓名</a-select-option>
<a-select-option value="phone">手机号</a-select-option>
<a-select-option value="cardNum">IC卡数字</a-select-option>
</a-select>
</template>
</a-input-search>
<a-button @click="reset">重置</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import useSearch from '@/utils/use-search';
import { UserCardParam } from '@/api/booking/userCard/model';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: UserCardParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
const type = ref('phone');
// 新增
const add = () => {
emit('add');
};
// 表单数据
const { where, resetFields } = useSearch<UserCardParam>({
type: undefined,
userId: undefined,
phone: undefined,
keywords: undefined
});
const search = () => {
if (type.value == 'userId') {
where.userId = Number(where.keywords);
where.keywords = undefined;
emit('search', where);
}
if (type.value == 'username') {
where.username = where.keywords;
where.keywords = undefined;
emit('search', where);
}
if (type.value == 'phone') {
where.phone = where.keywords;
where.keywords = undefined;
emit('search', where);
}
if (type.value == 'cardNum') {
where.cardNum = where.keywords;
where.keywords = undefined;
emit('search', where);
}
// emit('search', where);
};
/* 重置 */
const reset = () => {
resetFields();
search();
};
const onType = (item: any) => {
where.type = item.key;
emit('search', where);
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,492 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑会员卡' : '添加会员卡'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="用户id" name="userId">
<a-input
allow-clear
placeholder="请输入用户id"
v-model:value="form.userId"
/>
</a-form-item>
<a-form-item label="sid场馆id集合适用的场馆" name="sid">
<a-input
allow-clear
placeholder="请输入sid场馆id集合适用的场馆"
v-model:value="form.sid"
/>
</a-form-item>
<a-form-item label="用户id" name="uid">
<a-input
allow-clear
placeholder="请输入用户id"
v-model:value="form.uid"
/>
</a-form-item>
<a-form-item label="vip卡id" name="vid">
<a-input
allow-clear
placeholder="请输入vip卡id"
v-model:value="form.vid"
/>
</a-form-item>
<a-form-item label="开卡人id" name="aid">
<a-input
allow-clear
placeholder="请输入开卡人id"
v-model:value="form.aid"
/>
</a-form-item>
<a-form-item label="微信订单号" name="wechatOrder">
<a-input
allow-clear
placeholder="请输入微信订单号"
v-model:value="form.wechatOrder"
/>
</a-form-item>
<a-form-item label="卡号" name="code">
<a-input
allow-clear
placeholder="请输入卡号"
v-model:value="form.code"
/>
</a-form-item>
<a-form-item label="会员卡名称" name="name">
<a-input
allow-clear
placeholder="请输入会员卡名称"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="真实姓名" name="username">
<a-input
allow-clear
placeholder="请输入真实姓名"
v-model:value="form.username"
/>
</a-form-item>
<a-form-item label="手机号码" name="phone">
<a-input
allow-clear
placeholder="请输入手机号码"
v-model:value="form.phone"
/>
</a-form-item>
<a-form-item label="vip购卡价格" name="price">
<a-input
allow-clear
placeholder="请输入vip购卡价格"
v-model:value="form.price"
/>
</a-form-item>
<a-form-item label="会员卡介绍" name="desc">
<a-input
allow-clear
placeholder="请输入会员卡介绍"
v-model:value="form.desc"
/>
</a-form-item>
<a-form-item label="会员卡说明" name="info">
<a-input
allow-clear
placeholder="请输入会员卡说明"
v-model:value="form.info"
/>
</a-form-item>
<a-form-item label="vip卡折扣率" name="discount">
<a-input
allow-clear
placeholder="请输入vip卡折扣率"
v-model:value="form.discount"
/>
</a-form-item>
<a-form-item label="使用次数" name="count">
<a-input
allow-clear
placeholder="请输入使用次数"
v-model:value="form.count"
/>
</a-form-item>
<a-form-item label="每使用一次减少的金额" name="eachMoney">
<a-input
allow-clear
placeholder="请输入每使用一次减少的金额"
v-model:value="form.eachMoney"
/>
</a-form-item>
<a-form-item label="剩余金额" name="remainingMoney">
<a-input
allow-clear
placeholder="请输入剩余金额"
v-model:value="form.remainingMoney"
/>
</a-form-item>
<a-form-item label="续费累加次数" name="number">
<a-input
allow-clear
placeholder="请输入续费累加次数"
v-model:value="form.number"
/>
</a-form-item>
<a-form-item label="剩余次数" name="num">
<a-input
allow-clear
placeholder="请输入剩余次数"
v-model:value="form.num"
/>
</a-form-item>
<a-form-item label="付款状态,1已付款2未付款" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="会员卡年限" name="term">
<a-input
allow-clear
placeholder="请输入会员卡年限"
v-model:value="form.term"
/>
</a-form-item>
<a-form-item label="月限" name="month">
<a-input
allow-clear
placeholder="请输入月限"
v-model:value="form.month"
/>
</a-form-item>
<a-form-item label="IC卡类型1年卡2次卡3月卡4会员IC卡5充值卡 " name="type">
<a-input
allow-clear
placeholder="请输入IC卡类型1年卡2次卡3月卡4会员IC卡5充值卡 "
v-model:value="form.type"
/>
</a-form-item>
<a-form-item label="卡类型1成人卡2儿童卡" name="cardType">
<a-input
allow-clear
placeholder="请输入卡类型1成人卡2儿童卡"
v-model:value="form.cardType"
/>
</a-form-item>
<a-form-item label="vip卡等级类型1特殊vip卡2普通vip卡" name="vipType">
<a-input
allow-clear
placeholder="请输入vip卡等级类型1特殊vip卡2普通vip卡"
v-model:value="form.vipType"
/>
</a-form-item>
<a-form-item label="特殊卡开发凭证图" name="pic">
<a-input
allow-clear
placeholder="请输入特殊卡开发凭证图"
v-model:value="form.pic"
/>
</a-form-item>
<a-form-item label="价格组" name="prices">
<a-input
allow-clear
placeholder="请输入价格组"
v-model:value="form.prices"
/>
</a-form-item>
<a-form-item label="1微信支付2支付宝支付3现金4POS机刷卡15平安健康卡" name="payType">
<a-input
allow-clear
placeholder="请输入1微信支付2支付宝支付3现金4POS机刷卡15平安健康卡"
v-model:value="form.payType"
/>
</a-form-item>
<a-form-item label="是否赠送积分1赠送2不赠送" name="isIntegral">
<a-input
allow-clear
placeholder="请输入是否赠送积分1赠送2不赠送"
v-model:value="form.isIntegral"
/>
</a-form-item>
<a-form-item label="是否已开具发票1已开发票2未开发票" name="isInvoice">
<a-input
allow-clear
placeholder="请输入是否已开具发票1已开发票2未开发票"
v-model:value="form.isInvoice"
/>
</a-form-item>
<a-form-item label="vip卡到期时间" name="expireTime">
<a-input
allow-clear
placeholder="请输入vip卡到期时间"
v-model:value="form.expireTime"
/>
</a-form-item>
<a-form-item label="紧急联系人" name="urgentName">
<a-input
allow-clear
placeholder="请输入紧急联系人"
v-model:value="form.urgentName"
/>
</a-form-item>
<a-form-item label="紧急联系人号码" name="urgentPhone">
<a-input
allow-clear
placeholder="请输入紧急联系人号码"
v-model:value="form.urgentPhone"
/>
</a-form-item>
<a-form-item label="卡号" name="cardNum">
<a-input
allow-clear
placeholder="请输入卡号"
v-model:value="form.cardNum"
/>
</a-form-item>
<a-form-item label="密码" name="password">
<a-input
allow-clear
placeholder="请输入密码"
v-model:value="form.password"
/>
</a-form-item>
<a-form-item label="使用时间" name="useTime">
<a-input
allow-clear
placeholder="请输入使用时间"
v-model:value="form.useTime"
/>
</a-form-item>
<a-form-item label="" name="updateTime">
<a-input
allow-clear
placeholder="请输入"
v-model:value="form.updateTime"
/>
</a-form-item>
<a-form-item label="身份证号码" name="idCard">
<a-input
allow-clear
placeholder="请输入身份证号码"
v-model:value="form.idCard"
/>
</a-form-item>
<a-form-item label="备注" name="remark">
<a-input
allow-clear
placeholder="请输入备注"
v-model:value="form.remark"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="排序号" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addUserCard, updateUserCard } from '@/api/booking/userCard';
import { UserCard } from '@/api/booking/userCard/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: UserCard | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<UserCard>({
id: undefined,
userId: undefined,
sid: undefined,
uid: undefined,
vid: undefined,
aid: undefined,
wechatOrder: undefined,
code: undefined,
name: undefined,
username: undefined,
phone: undefined,
price: undefined,
desc: undefined,
info: undefined,
discount: undefined,
count: undefined,
eachMoney: undefined,
remainingMoney: undefined,
number: undefined,
num: undefined,
status: undefined,
term: undefined,
month: undefined,
type: undefined,
cardType: undefined,
vipType: undefined,
pic: undefined,
prices: undefined,
payType: undefined,
isIntegral: undefined,
isInvoice: undefined,
expireTime: undefined,
urgentName: undefined,
urgentPhone: undefined,
cardNum: undefined,
password: undefined,
useTime: undefined,
createTime: undefined,
updateTime: undefined,
idCard: undefined,
remark: undefined,
comments: undefined,
sortNumber: undefined,
tenantId: undefined,
userCardId: undefined,
userCardName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
userCardName: [
{
required: true,
type: 'string',
message: '请填写会员卡名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateUserCard : addUserCard;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,505 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="userCardId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
:scroll="{ x: 800 }"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'action'">
<a-space>
<a class="ele-text-success">解除绑定</a>
<a-divider type="vertical" />
<a class="ele-text-warning">变更绑定</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">注销</a>
</a-popconfirm>
<!-- <a-divider type="vertical" />-->
<!-- <a @click="openEdit(record)">修改</a>-->
<!-- <a-divider type="vertical" />-->
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<UserCardEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { EleProTable, formatNumber } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import UserCardEdit from './components/userCardEdit.vue';
import {
pageUserCard,
removeUserCard,
removeBatchUserCard
} from '@/api/booking/userCard';
import type { UserCard, UserCardParam } from '@/api/booking/userCard/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<UserCard[]>([]);
// 当前编辑数据
const current = ref<UserCard | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageUserCard({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '姓名',
dataIndex: 'username',
key: 'username',
align: 'center',
width: 120,
fixed: 'left'
},
{
title: 'IC卡号',
dataIndex: 'code',
key: 'code',
width: 120,
fixed: 'left',
align: 'center'
},
{
title: '手机号码',
dataIndex: 'phone',
key: 'phone',
align: 'center',
width: 120,
fixed: 'left'
},
{
title: '会员卡名称',
dataIndex: 'name',
key: 'name',
width: 180,
align: 'center'
},
{
title: '卡号',
dataIndex: 'cardNum',
key: 'cardNum',
width: 120,
align: 'center'
},
{
title: '可用场馆',
dataIndex: 'sid',
key: 'sid',
width: 120,
align: 'center'
},
{
title: '购卡价格',
dataIndex: 'price',
key: 'price',
align: 'center',
width: 120,
customRender: ({ text }) => `${formatNumber(text)}`
},
{
title: '紧急联系人',
dataIndex: 'urgentName',
key: 'urgentName',
width: 120,
align: 'center'
},
{
title: '紧急联系人号码',
dataIndex: 'urgentPhone',
key: 'urgentPhone',
align: 'center',
width: 120
},
{
title: 'vip卡折扣率',
dataIndex: 'discount',
key: 'discount',
width: 120,
align: 'center'
},
{
title: '使用次数',
dataIndex: 'count',
key: 'count',
width: 120,
align: 'center'
},
{
title: '剩余次数',
dataIndex: 'num',
key: 'num',
width: 120,
align: 'center'
},
{
title: '每使用一次减少的金额',
dataIndex: 'eachMoney',
key: 'eachMoney',
width: 120,
align: 'center',
customRender: ({ text }) => `${formatNumber(text)}`
},
{
title: '剩余金额',
dataIndex: 'remainingMoney',
key: 'remainingMoney',
width: 120,
align: 'center',
customRender: ({ text }) => `${formatNumber(text)}`
},
{
title: '月限',
dataIndex: 'month',
key: 'month',
width: 120,
align: 'center'
},
{
title: '会员卡年限',
dataIndex: 'term',
key: 'term',
width: 120,
align: 'center'
},
{
title: 'IC卡类型',
dataIndex: 'type',
key: 'type',
width: 120,
align: 'center',
customRender: ({ text }) =>
['-', '年卡', '次卡', '月卡', '会员IC卡', '充值卡'][text]
},
{
title: '是否赠送积分',
dataIndex: 'isIntegral',
key: 'isIntegral',
width: 120,
align: 'center',
customRender: ({ text }) => ['', '赠送', '不赠送'][text]
},
{
title: '支付状态',
dataIndex: 'status',
key: 'status',
width: 120,
align: 'center',
customRender: ({ text }) => ['未支付', '已支付'][text]
},
{
title: '支付方式',
dataIndex: 'payType',
key: 'payType',
width: 120,
align: 'center',
customRender: ({ text }) =>
[
'余额支付',
'微信支付',
'支付宝支付',
'现金',
'POS机刷卡',
'平安健康卡'
][text]
},
{
title: '是否已开具发票',
dataIndex: 'isInvoice',
key: 'isInvoice',
width: 120,
align: 'center',
customRender: ({ text }) => ['未开票', '已开票', '未开票'][text]
},
{
title: '到期时间',
dataIndex: 'expireTime',
key: 'expireTime',
align: 'center',
width: 180,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd HH:mm')
},
{
title: '购卡时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
width: 180,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
// {
// title: '用户id',
// dataIndex: 'uid',
// key: 'uid',
// width: 190,
// align: 'center',
// },
// {
// title: 'vip卡id',
// dataIndex: 'vid',
// key: 'vid',
// width: 190,
// align: 'center',
// },
// {
// title: '开卡人id',
// dataIndex: 'aid',
// key: 'aid',
// align: 'center',
// },
// {
// title: '微信订单号',
// dataIndex: 'wechatOrder',
// key: 'wechatOrder',
// width: 190,
// align: 'center',
// },
// {
// title: '会员卡介绍',
// dataIndex: 'desc',
// key: 'desc',
// align: 'center',
// },
// {
// title: '会员卡说明',
// dataIndex: 'info',
// key: 'info',
// width: 490,
// align: 'center',
// },
// {
// title: '续费累加次数',
// dataIndex: 'number',
// key: 'number',
// align: 'center',
// },
// {
// title: '卡类型1成人卡2儿童卡',
// dataIndex: 'cardType',
// key: 'cardType',
// align: 'center',
// },
// {
// title: 'vip卡等级类型1特殊vip卡2普通vip卡',
// dataIndex: 'vipType',
// key: 'vipType',
// align: 'center',
// },
// {
// title: '特殊卡开发凭证图',
// dataIndex: 'pic',
// key: 'pic',
// align: 'center',
// },
// {
// title: '价格组',
// dataIndex: 'prices',
// key: 'prices',
// align: 'center',
// },
// {
// title: '密码',
// dataIndex: 'password',
// key: 'password',
// align: 'center',
// },
// {
// title: '使用时间',
// dataIndex: 'useTime',
// key: 'useTime',
// align: 'center',
// },
// {
// title: '',
// dataIndex: 'updateTime',
// key: 'updateTime',
// align: 'center',
// },
// {
// title: '身份证号码',
// dataIndex: 'idCard',
// key: 'idCard',
// align: 'center',
// },
// {
// title: '备注',
// dataIndex: 'remark',
// key: 'remark',
// align: 'center',
// },
// {
// title: '备注',
// dataIndex: 'comments',
// key: 'comments',
// align: 'center',
// },
// {
// title: '排序号',
// dataIndex: 'sortNumber',
// key: 'sortNumber',
// align: 'center'
// },
{
title: '操作',
key: 'action',
width: 240,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: UserCardParam) => {
console.log(where);
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: UserCard) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: UserCard) => {
const hide = message.loading('请求中..', 0);
removeUserCard(row.userCardId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchUserCard(selection.value.map((d) => d.userCardId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: UserCard) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'UserCard'
};
</script>
<style lang="less" scoped></style>

View File

@@ -1,20 +1,19 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<template> <template>
<a-space :size="10" style="flex-wrap: wrap"> <a-space :size="10" style="flex-wrap: wrap">
<a-input-search <a-button type="primary" class="ele-btn-icon" @click="add">
allow-clear <template #icon>
v-model:value="where.keywords" <PlusOutlined />
placeholder="请输入关键词" </template>
@search="search" <span>添加</span>
@pressEnter="search" </a-button>
/>
</a-space> </a-space>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue'; import { watch } from 'vue';
import useSearch from '@/utils/use-search';
import { UsersParam } from '@/api/booking/users/model';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -25,22 +24,12 @@
); );
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'search', where?: UsersParam): void; (e: 'search', where?: GradeParam): void;
(e: 'add'): void; (e: 'add'): void;
(e: 'remove'): void; (e: 'remove'): void;
(e: 'batchMove'): void; (e: 'batchMove'): void;
}>(); }>();
//
const { where } = useSearch<UsersParam>({
keywords: ''
});
/* 搜索 */
const search = () => {
emit('search', where);
};
// //
const add = () => { const add = () => {
emit('add'); emit('add');

View File

@@ -5,7 +5,7 @@
:visible="visible" :visible="visible"
:maskClosable="false" :maskClosable="false"
:maxable="maxable" :maxable="maxable"
:title="isUpdate ? '编辑用户' : '添加用户'" :title="isUpdate ? '编辑明细表' : '添加明细表'"
:body-style="{ paddingBottom: '28px' }" :body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible" @update:visible="updateVisible"
@ok="save" @ok="save"
@@ -19,143 +19,56 @@
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' } styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
" "
> >
<a-form-item label="用户唯一小程序id" name="openid"> <a-form-item label="用户ID" name="userId">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入用户唯一小程序id" placeholder="请输入用户ID"
v-model:value="form.openid" v-model:value="form.userId"
/> />
</a-form-item> </a-form-item>
<a-form-item label="小程序用户秘钥" name="sessionKey"> <a-form-item label="IC卡类型1年卡2次卡3月卡4会员IC卡5充值卡 " name="type">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入小程序用户秘钥" placeholder="请输入IC卡类型1年卡2次卡3月卡4会员IC卡5充值卡 "
v-model:value="form.sessionKey" v-model:value="form.type"
/> />
</a-form-item> </a-form-item>
<a-form-item label="用户名" name="username"> <a-form-item label="变动金额" name="money">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入用户名" placeholder="请输入变动金额"
v-model:value="form.username" v-model:value="form.money"
/> />
</a-form-item> </a-form-item>
<a-form-item label="头像地址" name="avatarUrl"> <a-form-item label="变动后余额" name="balance">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入头像地址" placeholder="请输入变动后余额"
v-model:value="form.avatarUrl"
/>
</a-form-item>
<a-form-item label="1男2女" name="gender">
<a-input
allow-clear
placeholder="请输入1男2女"
v-model:value="form.gender"
/>
</a-form-item>
<a-form-item label="国家" name="country">
<a-input
allow-clear
placeholder="请输入国家"
v-model:value="form.country"
/>
</a-form-item>
<a-form-item label="省份" name="province">
<a-input
allow-clear
placeholder="请输入省份"
v-model:value="form.province"
/>
</a-form-item>
<a-form-item label="城市" name="city">
<a-input
allow-clear
placeholder="请输入城市"
v-model:value="form.city"
/>
</a-form-item>
<a-form-item label="所在辖区" name="region">
<a-input
allow-clear
placeholder="请输入所在辖区"
v-model:value="form.region"
/>
</a-form-item>
<a-form-item label="手机号码" name="phone">
<a-input
allow-clear
placeholder="请输入手机号码"
v-model:value="form.phone"
/>
</a-form-item>
<a-form-item label="邮箱" name="email">
<a-input
allow-clear
placeholder="请输入邮箱"
v-model:value="form.email"
/>
</a-form-item>
<a-form-item label="邮箱是否验证, 0否, 1是" name="emailVerified">
<a-input
allow-clear
placeholder="请输入邮箱是否验证, 0否, 1是"
v-model:value="form.emailVerified"
/>
</a-form-item>
<a-form-item label="积分" name="points">
<a-input
allow-clear
placeholder="请输入积分"
v-model:value="form.points"
/>
</a-form-item>
<a-form-item label="余额" name="balance">
<a-input
allow-clear
placeholder="请输入余额"
v-model:value="form.balance" v-model:value="form.balance"
/> />
</a-form-item> </a-form-item>
<a-form-item label="注册时间" name="addTime"> <a-form-item label="管理员备注" name="remark">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入注册时间" placeholder="请输入管理员备注"
v-model:value="form.addTime" v-model:value="form.remark"
/> />
</a-form-item> </a-form-item>
<a-form-item label="" name="idcard"> <a-form-item label="订单编号" name="orderNo">
<a-input allow-clear placeholder="请输入" v-model:value="form.idcard" />
</a-form-item>
<a-form-item label="" name="truename">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入" placeholder="请输入订单编号"
v-model:value="form.truename" v-model:value="form.orderNo"
/> />
</a-form-item> </a-form-item>
<a-form-item label="是否管理员1是2否" name="isAdmin"> <a-form-item label="操作人ID" name="adminId">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入是否管理员1是2否" placeholder="请输入操作人ID"
v-model:value="form.isAdmin" v-model:value="form.adminId"
/> />
</a-form-item> </a-form-item>
<a-form-item label="客户端ID" name="clientId"> <a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input
allow-clear
placeholder="请输入客户端ID"
v-model:value="form.clientId"
/>
</a-form-item>
<a-form-item label="注册来源客户端 (APP、H5、小程序等)" name="platform">
<a-input
allow-clear
placeholder="请输入注册来源客户端 (APP、H5、小程序等)"
v-model:value="form.platform"
/>
</a-form-item>
<a-form-item label="排序" name="sortNumber">
<a-input-number <a-input-number
:min="0" :min="0"
:max="9999" :max="9999"
@@ -172,7 +85,7 @@
v-model:value="form.comments" v-model:value="form.comments"
/> />
</a-form-item> </a-form-item>
<a-form-item label="状态" name="status"> <a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status"> <a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio> <a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio> <a-radio :value="1">隐藏</a-radio>
@@ -185,6 +98,27 @@
v-model:value="form.deleted" v-model:value="form.deleted"
/> />
</a-form-item> </a-form-item>
<a-form-item label="商户ID" name="merchantId">
<a-input
allow-clear
placeholder="请输入商户ID"
v-model:value="form.merchantId"
/>
</a-form-item>
<a-form-item label="商户编码" name="merchantCode">
<a-input
allow-clear
placeholder="请输入商户编码"
v-model:value="form.merchantCode"
/>
</a-form-item>
<a-form-item label="修改时间" name="updateTime">
<a-input
allow-clear
placeholder="请输入修改时间"
v-model:value="form.updateTime"
/>
</a-form-item>
</a-form> </a-form>
</ele-modal> </ele-modal>
</template> </template>
@@ -193,8 +127,8 @@
import { ref, reactive, watch } from 'vue'; import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue'; import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro'; import { assignObject, uuid } from 'ele-admin-pro';
import { addUsers, updateUsers } from '@/api/booking/users'; import { addUserCardLog, updateUserCardLog } from '@/api/booking/userCardLog';
import { Users } from '@/api/booking/users/model'; import { UserCardLog } from '@/api/booking/userCardLog/model';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types'; import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
@@ -212,7 +146,7 @@
// //
visible: boolean; visible: boolean;
// //
data?: Users | null; data?: UserCardLog | null;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@@ -229,34 +163,29 @@
const images = ref<ItemType[]>([]); const images = ref<ItemType[]>([]);
// //
const form = reactive<Users>({ const form = reactive<UserCardLog>({
uid: undefined, logId: undefined,
openid: undefined, userId: undefined,
sessionKey: undefined, type: undefined,
username: undefined, money: undefined,
avatarUrl: undefined,
sex: undefined,
country: undefined,
province: undefined,
city: undefined,
region: undefined,
phone: undefined,
email: undefined,
emailVerified: undefined,
points: undefined,
balance: undefined, balance: undefined,
addTime: undefined, remark: undefined,
idCard: undefined, orderNo: undefined,
realName: undefined, adminId: undefined,
isAdmin: undefined,
clientId: undefined,
platform: undefined,
sortNumber: undefined, sortNumber: undefined,
comments: undefined, comments: undefined,
status: undefined, status: undefined,
deleted: undefined, deleted: undefined,
merchantId: undefined,
merchantCode: undefined,
tenantId: undefined, tenantId: undefined,
createTime: undefined createTime: undefined,
updateTime: undefined,
userCardLogId: undefined,
userCardLogName: '',
status: 0,
comments: '',
sortNumber: 100
}); });
/* 更新visible */ /* 更新visible */
@@ -266,11 +195,11 @@
// //
const rules = reactive({ const rules = reactive({
userName: [ userCardLogName: [
{ {
required: true, required: true,
type: 'string', type: 'string',
message: '请填写用户名称', message: '请填写明细表名称',
trigger: 'blur' trigger: 'blur'
} }
] ]
@@ -282,12 +211,12 @@
url: data.path, url: data.path,
status: 'done' status: 'done'
}); });
form.avatarUrl = data.path; form.image = data.path;
}; };
const onDeleteItem = (index: number) => { const onDeleteItem = (index: number) => {
images.value.splice(index, 1); images.value.splice(index, 1);
form.avatarUrl = ''; form.image = '';
}; };
const { resetFields } = useForm(form, rules); const { resetFields } = useForm(form, rules);
@@ -304,7 +233,7 @@
const formData = { const formData = {
...form ...form
}; };
const saveOrUpdate = isUpdate.value ? updateUsers : addUsers; const saveOrUpdate = isUpdate.value ? updateUserCardLog : addUserCardLog;
saveOrUpdate(formData) saveOrUpdate(formData)
.then((msg) => { .then((msg) => {
loading.value = false; loading.value = false;
@@ -327,12 +256,12 @@
images.value = []; images.value = [];
if (props.data) { if (props.data) {
assignObject(form, props.data); assignObject(form, props.data);
if (props.data.avatarUrl) { if(props.data.image){
images.value.push({ images.value.push({
uid: uuid(), uid: uuid(),
url: props.data.avatarUrl, url: props.data.image,
status: 'done' status: 'done'
}); })
} }
isUpdate.value = true; isUpdate.value = true;
} else { } else {

View File

@@ -4,7 +4,7 @@
<a-card :bordered="false" :body-style="{ padding: '16px' }"> <a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table <ele-pro-table
ref="tableRef" ref="tableRef"
row-key="uid" row-key="userCardLogId"
:columns="columns" :columns="columns"
:datasource="datasource" :datasource="datasource"
:customRow="customRow" :customRow="customRow"
@@ -21,36 +21,23 @@
/> />
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'avatarUrl'"> <template v-if="column.key === 'image'">
<a-avatar <a-image :src="record.image" :width="50" />
:size="36"
:src="`${record.avatarUrl}`"
style="margin-right: 4px"
>
<template #icon>
<UserOutlined />
</template>
</a-avatar>
</template>
<template v-if="column.key === 'uid'">
{{ record }}
</template> </template>
<template v-if="column.key === 'status'"> <template v-if="column.key === 'status'">
{{ record }} <a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="green">启用</a-tag> <a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
<a-tag v-if="record.status === 2" color="red">禁用</a-tag>
</template> </template>
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<a-space> <a-space>
<a>积分充值</a> <a @click="openEdit(record)">修改</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a>分配特殊卡</a> <a-popconfirm
<!-- <a-popconfirm--> title="确定要删除此记录吗?"
<!-- title="确定要删除此记录吗?"--> @confirm="remove(record)"
<!-- @confirm="remove(record)"--> >
<!-- >--> <a class="ele-text-danger">删除</a>
<!-- <a class="ele-text-danger">删除</a>--> </a-popconfirm>
<!-- </a-popconfirm>-->
</a-space> </a-space>
</template> </template>
</template> </template>
@@ -58,7 +45,7 @@
</a-card> </a-card>
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<UserEdit v-model:visible="showEdit" :data="current" @done="reload" /> <UserCardLogEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div> </div>
</div> </div>
</template> </template>
@@ -66,10 +53,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { createVNode, ref } from 'vue'; import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue'; import { message, Modal } from 'ant-design-vue';
import { import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
ExclamationCircleOutlined,
UserOutlined
} from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro'; import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro'; import { toDateString } from 'ele-admin-pro';
import type { import type {
@@ -77,21 +61,17 @@
ColumnItem ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types'; } from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue'; import Search from './components/search.vue';
import UserEdit from './components/userEdit.vue'; import UserCardLogEdit from './components/userCardLogEdit.vue';
import { import { pageUserCardLog, removeUserCardLog, removeBatchUserCardLog } from '@/api/booking/userCardLog';
pageUsers, import type { UserCardLog, UserCardLogParam } from '@/api/booking/userCardLog/model';
removeBatchUsers,
removeUsers
} from '@/api/booking/users';
import type { Users, UsersParam } from '@/api/booking/users/model';
// //
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// //
const selection = ref<Users[]>([]); const selection = ref<UserCardLog[]>([]);
// //
const current = ref<Users | null>(null); const current = ref<UserCardLog | null>(null);
// //
const showEdit = ref(false); const showEdit = ref(false);
// //
@@ -110,7 +90,7 @@
if (filters) { if (filters) {
where.status = filters.status; where.status = filters.status;
} }
return pageUsers({ return pageUserCardLog({
...where, ...where,
...orders, ...orders,
page, page,
@@ -121,95 +101,105 @@
// //
const columns = ref<ColumnItem[]>([ const columns = ref<ColumnItem[]>([
{ {
title: 'ID', title: '主键ID',
dataIndex: 'uid', dataIndex: 'logId',
key: 'uid', key: 'logId',
width: 90
},
{
title: '头像',
dataIndex: 'avatarUrl',
key: 'avatarUrl',
align: 'center'
},
{
title: '用户名',
dataIndex: 'username',
key: 'username',
align: 'center'
},
{
title: '手机号码',
dataIndex: 'phone',
key: 'phone',
align: 'center'
},
{
title: '性别',
dataIndex: 'sexName',
key: 'sexName',
align: 'center'
},
{
title: '国家',
dataIndex: 'country',
key: 'country',
align: 'center', align: 'center',
hideInTable: true width: 90,
}, },
{ {
title: '省份', title: '用户ID',
dataIndex: 'province', dataIndex: 'userId',
key: 'province', key: 'userId',
align: 'center'
},
{
title: '城市',
dataIndex: 'city',
key: 'city',
align: 'center'
},
{
title: '所在辖区',
dataIndex: 'region',
key: 'region',
align: 'center', align: 'center',
hideInTable: true
}, },
{ {
title: '邮箱是否验证, 0否, 1是', title: 'IC卡类型1年卡2次卡3月卡4会员IC卡5充值卡 ',
dataIndex: 'emailVerified', dataIndex: 'type',
key: 'emailVerified', key: 'type',
align: 'center', align: 'center',
hideInTable: true
}, },
{ {
title: '积分', title: '变动金额',
dataIndex: 'points', dataIndex: 'money',
key: 'points', key: 'money',
align: 'center' align: 'center',
}, },
{ {
title: '余额', title: '变动后余额',
dataIndex: 'balance', dataIndex: 'balance',
key: 'balance', key: 'balance',
align: 'center' align: 'center',
}, },
{ {
title: '状态', title: '管理员备注',
dataIndex: 'remark',
key: 'remark',
align: 'center',
},
{
title: '订单编号',
dataIndex: 'orderNo',
key: 'orderNo',
align: 'center',
},
{
title: '操作人ID',
dataIndex: 'adminId',
key: 'adminId',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '状态, 0正常, 1冻结',
dataIndex: 'status', dataIndex: 'status',
key: 'status', key: 'status',
align: 'center' align: 'center',
},
{
title: '是否删除, 0否, 1是',
dataIndex: 'deleted',
key: 'deleted',
align: 'center',
},
{
title: '商户ID',
dataIndex: 'merchantId',
key: 'merchantId',
align: 'center',
},
{
title: '商户编码',
dataIndex: 'merchantCode',
key: 'merchantCode',
align: 'center',
}, },
{ {
title: '注册时间', title: '注册时间',
dataIndex: 'addTime', dataIndex: 'createTime',
key: 'addTime', key: 'createTime',
align: 'center', align: 'center',
sorter: true, sorter: true,
ellipsis: true, ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd') customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
}, },
{
title: '修改时间',
dataIndex: 'updateTime',
key: 'updateTime',
align: 'center',
},
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
@@ -221,13 +211,13 @@
]); ]);
/* 搜索 */ /* 搜索 */
const reload = (where?: UsersParam) => { const reload = (where?: UserCardLogParam) => {
selection.value = []; selection.value = [];
tableRef?.value?.reload({ where: where }); tableRef?.value?.reload({ where: where });
}; };
/* 打开编辑弹窗 */ /* 打开编辑弹窗 */
const openEdit = (row?: Users) => { const openEdit = (row?: UserCardLog) => {
current.value = row ?? null; current.value = row ?? null;
showEdit.value = true; showEdit.value = true;
}; };
@@ -238,9 +228,9 @@
}; };
/* 删除单个 */ /* 删除单个 */
const remove = (row: Users) => { const remove = (row: UserCardLog) => {
const hide = message.loading('请求中..', 0); const hide = message.loading('请求中..', 0);
removeUsers(row.uid) removeUserCardLog(row.userCardLogId)
.then((msg) => { .then((msg) => {
hide(); hide();
message.success(msg); message.success(msg);
@@ -265,7 +255,7 @@
maskClosable: true, maskClosable: true,
onOk: () => { onOk: () => {
const hide = message.loading('请求中..', 0); const hide = message.loading('请求中..', 0);
removeBatchUsers(selection.value.map((d) => d.uid)) removeBatchUserCardLog(selection.value.map((d) => d.userCardLogId))
.then((msg) => { .then((msg) => {
hide(); hide();
message.success(msg); message.success(msg);
@@ -285,7 +275,7 @@
}; };
/* 自定义行属性 */ /* 自定义行属性 */
const customRow = (record: Users) => { const customRow = (record: UserCardLog) => {
return { return {
// //
onClick: () => { onClick: () => {
@@ -302,7 +292,7 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'Users' name: 'UserCardLog'
}; };
</script> </script>

View File

@@ -57,6 +57,27 @@
v-model:value="form.target" v-model:value="form.target"
/> />
</a-form-item> </a-form-item>
<a-form-item label="菜单类型" name="type">
<a-select
v-model:value="form.type"
placeholder="菜单类型"
:style="`width: 200px`"
>
<a-select-option :value="0">会员功能</a-select-option>
<a-select-option :value="1">订单管理</a-select-option>
<a-select-option :value="2">首页导航</a-select-option>
<a-select-option :value="3">商城导航</a-select-option>
<a-select-option :value="4">管理中心</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="管理人员可见" name="adminShow">
<a-switch
checked-children=""
un-checked-children=""
:checked="form.adminShow === 1"
@update:checked="updateAdminShow"
/>
</a-form-item>
<a-form-item label="排序" name="sortNumber"> <a-form-item label="排序" name="sortNumber">
<a-input-number <a-input-number
:min="0" :min="0"
@@ -127,6 +148,7 @@
position: undefined, position: undefined,
active: undefined, active: undefined,
userId: 0, userId: 0,
adminShow: undefined,
home: undefined, home: undefined,
sortNumber: 100, sortNumber: 100,
comments: '', comments: '',
@@ -172,6 +194,10 @@
form.icon = ''; form.icon = '';
}; };
const updateAdminShow = (value: boolean) => {
form.adminShow = value ? 1 : 0;
};
// 预设颜色 // 预设颜色
const predefineColors = ref([ const predefineColors = ref([
'#40a9ff', '#40a9ff',

View File

@@ -12,7 +12,8 @@
>菜单类型</span >菜单类型</span
> >
<a-radio-group v-model:value="where.type" @change="handleSearch"> <a-radio-group v-model:value="where.type" @change="handleSearch">
<a-radio-button :value="0">功能图标</a-radio-button> <a-radio-button :value="0">会员功能</a-radio-button>
<a-radio-button :value="4">管理中心</a-radio-button>
<a-radio-button :value="1">订单图标</a-radio-button> <a-radio-button :value="1">订单图标</a-radio-button>
</a-radio-group> </a-radio-group>
</a-space> </a-space>

View File

@@ -79,7 +79,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { createVNode, ref, unref, watch } from 'vue'; import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue'; import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro'; import type { EleProTable } from 'ele-admin-pro';
@@ -108,8 +108,6 @@
const showEdit = ref(false); const showEdit = ref(false);
// 是否显示批量移动弹窗 // 是否显示批量移动弹窗
const showMove = ref(false); const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 页面标题 // 页面标题
const title = getPageTitle(); const title = getPageTitle();
const type = ref<number>(2); const type = ref<number>(2);
@@ -121,8 +119,6 @@
showOrderCard: true, showOrderCard: true,
showToolsCard: true showToolsCard: true
}); });
// 菜单列表
const list = ref<any[]>();
// 表格数据源 // 表格数据源
const datasource: DatasourceFunction = ({ const datasource: DatasourceFunction = ({
@@ -135,7 +131,6 @@
if (filters) { if (filters) {
where.status = filters.status; where.status = filters.status;
} }
where.type = type.value;
return pageMpMenu({ return pageMpMenu({
...where, ...where,
...orders, ...orders,
@@ -195,7 +190,11 @@
/* 搜索 */ /* 搜索 */
const reload = (where?: MpMenuParam) => { const reload = (where?: MpMenuParam) => {
type.value = Number(where?.type) || 0; if (where?.type) {
type.value = Number(where?.type);
} else {
type.value = 0;
}
refresh.value = !refresh.value; refresh.value = !refresh.value;
selection.value = []; selection.value = [];
tableRef?.value?.reload({ where: where }); tableRef?.value?.reload({ where: where });