优化网站导航模块

This commit is contained in:
2024-08-23 22:28:24 +08:00
parent 1d81fa9270
commit 13832d9de0
964 changed files with 90774 additions and 31362 deletions

View File

@@ -0,0 +1,211 @@
<template>
<a-card
:title="`收银员:${loginUser?.realName}`"
:bordered="false"
:body-style="{ minHeight: '60vh', width: '380px', padding: '0' }"
:head-style="{ className: 'bg-gray-500' }"
>
<template #extra>
<a-button danger @click="removeAll" size="small">整单取消</a-button>
</template>
<div class="goods-list overflow-x-hidden px-3 h-[500px]">
<a-empty v-if="cashier?.totalNums === 0" :image="simpleImage" />
<a-spin :spinning="spinning">
<template v-for="(item, index) in cashier?.cashiers" :key="index">
<div class="goods-item py-4 flex">
<div class="goods-image">
<a-avatar :src="item.image" shape="square" :size="42" />
</div>
<div class="goods-info w-full ml-2">
<div class="goods-bar flex justify-between">
<div class="goods-name flex-1">{{ item.goodsName }}</div>
</div>
<div class="spec text-gray-400">{{ item.spec }}</div>
<div class="goods-price flex justify-between items-center">
<div class="flex">
<div class="price text-red-500">{{ item.price }}</div>
<div class="total-num ml-5 text-gray-400"
>x {{ item.cartNum }}</div
>
</div>
<div class="add flex items-center text-gray-500">
<MinusCircleOutlined class="text-xl" @click="subNum(item)" />
<div class="block text-center px-3">{{ item.cartNum }}</div>
<PlusCircleOutlined class="text-xl" @click="addNum(item)" />
</div>
</div>
</div>
<div class="total-nums"> </div>
</div>
<a-divider dashed />
</template>
</a-spin>
</div>
<template #actions>
<div class="flex flex-col">
<div class="text-left pb-4 pl-4 text-gray-500"
>共计 {{ cashier.totalNums }} 已优惠0.00
</div>
<div class="flex justify-between px-2">
<a-button size="large" class="w-full mx-1" @click="getListByGroup"
>取单</a-button
>
<a-button size="large" class="w-full mx-1" @click="onPack"
>挂单</a-button
>
<a-button
type="primary"
class="w-full mx-1"
size="large"
@click="onPay"
>收款{{ formatNumber(cashier.totalPrice) }}
</a-button>
</div>
</div>
</template>
</a-card>
</template>
<script lang="ts" setup>
import {
DeleteOutlined,
PlusCircleOutlined,
MinusCircleOutlined,
ExclamationCircleOutlined
} from '@ant-design/icons-vue';
import { ref, watch, createVNode } from 'vue';
import { Cashier, CashierVo } from '@/api/shop/cashier/model';
import { User } from '@/api/system/user/model';
import { formatNumber } from 'ele-admin-pro/es';
import {
subCartNum,
addCartNum,
removeBatchCashier,
removeCashier,
packCashier
} from '@/api/shop/cashier';
import { Empty, Modal, message } from 'ant-design-vue';
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
const props = withDefaults(
defineProps<{
value?: string;
placeholder?: string;
loginUser?: User;
cashier?: CashierVo;
count?: number;
spinning?: boolean;
}>(),
{
placeholder: undefined
}
);
const emit = defineEmits<{
(e: 'done'): void;
(e: 'reload'): void;
(e: 'group'): void;
(e: 'pay'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 当前编辑数据
const current = ref<Cashier | null>(null);
const cashiers = ref<Cashier[]>([]);
/* 打开编辑弹窗 */
const onPay = () => {
emit('pay');
};
/* 打开编辑弹窗 */
const openEdit = (row?: Cashier) => {
current.value = row ?? null;
};
const subNum = (row: Cashier) => {
if (row.cartNum === 1) {
Modal.confirm({
title: '提示',
content: '确定要删除该商品吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeCashier(row?.id)
.then((msg) => {
hide();
message.success(msg);
emit('reload');
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
return false;
}
subCartNum(row).then(() => {
emit('reload');
});
};
const addNum = (row: Cashier) => {
addCartNum(row).then(() => {
emit('reload');
});
};
const removeAll = () => {
const ids = props.cashier?.cashiers?.map((d) => d.id);
if (ids) {
removeBatchCashier(ids)
.then(() => {})
.finally(() => {
emit('reload');
});
}
};
// 取单
const getListByGroup = () => {
emit('group');
};
// 挂单
const onPack = () => {
cashiers.value =
props.cashier?.cashiers?.map((d) => {
return {
id: d.id,
groupId: Number(d.groupId) + 1
};
}) || [];
if (cashiers.value.length === 0) {
return false;
}
packCashier(cashiers.value).then((res) => {
emit('reload');
});
};
watch(
() => props.count,
(count) => {
console.log(count);
},
{ immediate: true }
);
</script>
<script lang="ts">
import * as MenuIcons from '@/layout/menu-icons';
export default {
name: 'Cashier',
components: MenuIcons
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,168 @@
<template>
<a-card
class="w-full relative"
:bordered="false"
:body-style="{ padding: '16px' }"
>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@batchMove="openMove"
/>
<a-tabs v-model:activeKey="categoryId" type="card" @change="onCategory">
<a-tab-pane :key="0" tab="全部" />
<a-tab-pane
v-for="item in category"
:key="item.categoryId"
:tab="item.title"
/>
</a-tabs>
<div class="goods-list w-full flex gap-5 flex-wrap">
<template v-for="(item, index) in goods" :key="index">
<a-card class="item" :body-style="{ padding: '0' }">
<a-avatar
:src="item.image"
:preview="false"
shape="square"
:size="190"
@click="add(item)"
/>
<div
class="goods-info flex justify-between items-center p-2 text-ellipsis"
>
<div class="goods-name-bar flex flex-col">
<text class="goods-name text-gray-500 w-[174px]">{{
item.goodsName
}}</text>
<div class="goods-price flex justify-between">
<text class="text-red-500">{{ item.price }}</text>
<ShoppingCartOutlined
class="text-xl"
style="color: #808080"
@click="add(item)"
/>
</div>
</div>
</div>
</a-card>
</template>
<div class="h-20 flex items-center m-auto w-full justify-center">
<a-pagination
v-model:current="page"
:total="count"
show-less-items
@change="reload"
/>
</div>
</div>
<!-- 编辑弹窗 -->
<SpecForm
v-model:visible="showSpecForm"
:data="current"
@done="addSpecCashier"
/>
</a-card>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { ShoppingCartOutlined } from '@ant-design/icons-vue';
import Search from './search.vue';
import SpecForm from './specForm.vue';
import { pageGoods } from '@/api/shop/goods';
import type { Goods, GoodsParam } from '@/api/shop/goods/model';
import { useRouter } from 'vue-router';
import { Cashier } from '@/api/shop/cashier/model';
import { User } from '@/api/system/user/model';
import { listGoodsCategory } from '@/api/shop/goodsCategory';
import { GoodsCategory } from '@/api/shop/goodsCategory/model';
const { currentRoute } = useRouter();
withDefaults(
defineProps<{
value?: string;
placeholder?: string;
loginUser?: User;
}>(),
{
placeholder: undefined
}
);
const emit = defineEmits<{
(e: 'done'): void;
(e: 'reload'): void;
}>();
// 表格选中数据
const selection = ref<Goods[]>([]);
// 当前编辑数据
const current = ref<Cashier | null>(null);
const form = ref<Cashier>({});
const category = ref<GoodsCategory[]>([]);
const goods = ref<Goods[] | null>();
const count = ref<number>(0);
const page = ref<number>(1);
// 是否显示批量移动弹窗
const showMove = ref(false);
const specs = ref<string>();
const showSpecForm = ref<boolean>(false);
const categoryId = ref<number>(0);
/* 打开编辑弹窗 */
const openEdit = (row?: Cashier) => {
current.value = row ?? null;
showSpecForm.value = true;
};
/* 打开编辑弹窗 */
const openSpecForm = (row?: Cashier) => {
current.value = row ?? null;
if (current.value) {
current.value.cartNum = 1;
}
showSpecForm.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
const add = (row?: Cashier) => {
if (row) {
form.value = row;
form.value.spec = specs.value;
openSpecForm(row);
}
};
const addSpecCashier = () => {
emit('reload');
};
const onCategory = (e) => {
reload({ categoryId: e.categoryId });
};
const reload = (where: GoodsParam) => {
listGoodsCategory({ parentId: 0, type: 0 }).then((list) => {
category.value = list;
});
if (categoryId.value > 0) {
where.parentId = categoryId.value;
}
pageGoods({ ...where, page: page.value }).then((res) => {
if (res?.list) {
goods.value = res.list;
count.value = res.count;
}
});
};
reload({});
watch(currentRoute, () => {}, { immediate: true });
</script>

View File

@@ -0,0 +1,156 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="1000"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:footer="null"
:title="isUpdate ? '取单' : '取单'"
:body-style="{
paddingTop: '10px',
paddingBottom: '38px',
backgroundColor: '#f3f3f3'
}"
@update:visible="updateVisible"
@ok="save"
>
<a-space direction="vertical" class="w-full">
<div v-for="(item, index) in groups" :key="index">
<a-card
:title="`订单总价: ¥${formatNumber(item.totalPrice)}`"
:body-style="{ padding: '10px' }"
>
<div class="flex flex-wrap">
<div v-for="(v, i) in item.cashiers" :key="i" class="flex m-2">
<div class="item flex flex-col bg-gray-50 p-3 w-[215px]">
<text>{{ v.goodsName }}</text>
<text class="text-gray-400">规格{{ v.spec }}</text>
<text class="text-gray-400">数量{{ v.cartNum }}</text>
<text>小计{{ v.totalPrice }}</text>
</div>
</div>
</div>
<!-- <div class="comments p-2 text-gray-400">-->
<!-- {{ item.comments }}-->
<!-- </div>-->
<template #actions>
<div class="text-left px-3">
<a-space>
<a-button type="primary" @click="getCashier(item.groupId)"
>取单</a-button
>
<a-button danger type="primary" @click="remove(item.groupId)"
>删除</a-button
>
<!-- <a-button>备注</a-button>-->
</a-space>
</div>
</template>
</a-card>
</div>
</a-space>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { message } from 'ant-design-vue';
import { addMpMenu, updateMpMenu } from '@/api/cms/mp-menu';
import { MpMenu } from '@/api/cms/mp-menu/model';
import { FormInstance } from 'ant-design-vue/es/form';
import { getByGroup, listByGroupId, removeByGroup } from '@/api/shop/cashier';
import { formatNumber } from 'ele-admin-pro/es';
import { CashierVo } from '@/api/shop/cashier/model';
// 是否是修改
const isUpdate = ref(false);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 类型 0服务 1订单
type?: number;
// 页面ID
pageId?: number;
// 修改回显的数据
data?: MpMenu | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
const pageId = ref(0);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
//
const groups = ref<CashierVo[]>();
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const getCashier = (groupId: number) => {
getByGroup(groupId).then(() => {
updateVisible(false);
emit('done');
});
};
const remove = (groupId: number) => {
removeByGroup(groupId).then(() => {
reload();
});
};
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
pageId: pageId.value
};
const saveOrUpdate = isUpdate.value ? updateMpMenu : addMpMenu;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
const reload = () => {
listByGroupId({}).then((data) => {
groups.value = data;
});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
reload();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,263 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="1000"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '支付结算' : '支付结算'"
okText="确定收款"
:ok-button-props="{ disabled: !user }"
:body-style="{
padding: '0',
paddingBottom: '0',
backgroundColor: '#f3f3f3'
}"
@update:visible="updateVisible"
@ok="save"
>
<!-- {{ form }}-->
<div class="flex justify-between py-3 px-4 columns-3 gap-3">
<div class="payment flex flex-col bg-white px-4 py-2">
<div class="title text-gray-500 leading-10">请选择支付方式</div>
<template v-for="(item, index) in payments" :key="index">
<div
class="item border-2 border-solid px-3 py-1 flex items-center mb-2 cursor-pointer"
:class="
form.type === item.type
? 'active border-red-300'
: 'border-gray-200'
"
@click="onType(item)"
>
<a-avatar :src="item.image" />
<text class="pl-1">{{ item.name }}</text>
</div>
</template>
</div>
<div class="settlement flex flex-col bg-white px-4 py-2 flex-1">
<div class="title text-gray-500 leading-10">结算信息</div>
<div class="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="备注信息"
v-model:value="form.comments"
/>
</div>
<div class="discount flex mt-3">
<div class="item w-1/2">
折扣()
<div class="input">
<a-input-number
allow-clear
min="0"
max="10"
placeholder="请输入折扣"
style="width: 200px"
v-model:value="form.discount"
@input="onDiscount"
/>
</div>
</div>
<div class="item w-1/2">
立减
<div class="input">
<a-input-number
allow-clear
placeholder="请输入立减金额"
style="width: 200px"
v-model:value="form.reducePrice"
/>
</div>
</div>
</div>
<div class="discount flex mt-8 items-center h-20 leading-10">
<div class="item w-1/2"> 优惠金额0.00</div>
<div class="item w-1/2 text-xl">
实付金额<span class="text-red-500 text-2xl">{{
formatNumber(data.totalPrice)
}}</span>
</div>
</div>
</div>
<div class="user-bar flex flex-col bg-white px-4 py-2">
<div class="title text-gray-500 leading-10">会员信息</div>
<div class="user-info text-center" v-if="!user">
<a-empty :description="`当前为游客`" class="text-gray-300" />
<SelectUserByButton @done="selectUser" />
</div>
<view class="tox-user" v-if="user">
<div class="mb-2 flex justify-between items-center">
<div class="avatar">
<a-avatar :src="user.avatar" :size="40" />
<text class="ml-1 text-gray-500">{{ user.nickname }}</text>
</div>
<CloseOutlined @click="removeUser" />
</div>
<div
class="border-dashed py-1 px-2 border-gray-300 text-gray-500 mt-2"
>名称{{ user.realName }}
</div>
<div
class="border-dashed py-1 px-2 border-gray-300 text-gray-500 mt-2"
>手机号{{ user.mobile || '未绑定' }}
</div>
<div
class="border-dashed py-1 px-2 border-gray-300 text-gray-500 mt-2"
>可用余额{{ user.balance }}
</div>
<div
class="border-dashed py-1 px-2 border-gray-300 text-gray-500 mt-2"
>可用积分{{ user.points }}
</div>
</view>
</div>
</div>
<UserForm v-model:visible="showUserForm" :type="type" @done="reload" />
</ele-modal>
</template>
<script lang="ts" setup>
import { CloseOutlined } from '@ant-design/icons-vue';
import { ref, watch, reactive } from 'vue';
import { message } from 'ant-design-vue';
import { FormInstance } from 'ant-design-vue/es/form';
import { formatNumber } from 'ele-admin-pro/es';
import { listPayment } from '@/api/system/payment';
import { Payment } from '@/api/system/payment/model';
import { User } from '@/api/system/user/model';
import { Cashier } from '@/api/shop/cashier/model';
import { addOrder, updateOrder } from '@/api/shop/order';
import { Order } from '@/api/shop/order/model';
// 是否是修改
const isUpdate = ref(false);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 类型 0服务 1订单
type?: number;
// 修改回显的数据
data?: Cashier | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 用户信息
const form = reactive<Order>({
// ID
// 类型 0商城 1外卖
type: 4,
// 唯一标识
// 商品价格
price: undefined,
// 单商品合计
totalPrice: undefined,
// 商户ID
merchantId: undefined,
// 用户ID
userId: undefined,
// 租户id
tenantId: undefined,
// 创建时间
createTime: undefined,
// 修改时间
updateTime: undefined
});
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const disabled = ref<boolean>(true);
const payments = ref<Payment[]>();
const showUserForm = ref<boolean>(false);
const user = ref<User | null>();
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const selectUser = (item: User) => {
user.value = item;
};
const onType = (item: Payment) => {
form.type = item.type;
};
const removeUser = () => {
user.value = null;
};
/* 保存编辑 */
const save = () => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateOrder : addOrder;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
};
const reload = () => {
listPayment({}).then((data) => {
payments.value = data;
});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
reload();
}
},
{ immediate: true }
);
</script>
<style lang="less" scoped>
.active {
position: relative;
border: 2px solid #007cec;
&:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
border: 11px solid #007cec;
border-top-color: transparent;
border-left-color: transparent;
}
&:after {
content: '';
width: 4px;
height: 8px;
position: absolute;
right: 3px;
bottom: 4px;
border: 1px solid #fff;
border-top-color: transparent;
border-left-color: transparent;
transform: rotate(45deg);
}
}
</style>

View File

@@ -1,19 +1,23 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
<a-space :size="10" class="flex-wrap mb-5">
<a-input-search
allow-clear
size="large"
style="width: 400px"
placeholder="商品名称"
v-model:value="where.goodsName"
@pressEnter="handleSearch"
@search="handleSearch"
/>
<a-button @click="handleSearch" size="large">搜索</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
import useSearch from '@/utils/use-search';
import { GoodsParam } from '@/api/shop/goods/model';
const props = withDefaults(
defineProps<{
@@ -24,15 +28,21 @@
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'search', where?: GoodsParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
// 表单数据
const { where } = useSearch<GoodsParam>({
goodsId: undefined,
goodsName: undefined,
keywords: undefined
});
const handleSearch = () => {
emit('search', where);
};
watch(

View File

@@ -0,0 +1,213 @@
<!-- 编辑弹窗 -->
<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="名称" name="goodsName">
{{ form.goodsName }}
</a-form-item>
<a-form-item label="价格" name="price">
<div class="text-red-500">{{ formatNumber(form.price) }}</div>
</a-form-item>
<a-form-item label="购买数量" name="cartNum">
<a-input-number
:min="1"
:max="9999"
placeholder="请输入排序号"
v-model:value="form.cartNum"
@pressEnter="save"
/>
</a-form-item>
<a-form-item v-if="form.specs === 1" label="规格" name="spec">
<template v-for="(item, index) in form?.goodsSpecValue" :key="index">
<div class="flex flex-col">
<span class="font-bold leading-8 text-gray-400">{{
item.value
}}</span>
<a-radio-group v-model:value="form.goodsSpecValue[index].spec">
<template v-for="(v, i) in item.detail" :key="i">
<a-radio-button :value="v">{{ v }}</a-radio-button>
</template>
</a-radio-group>
</div>
</template>
</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 } from 'ele-admin-pro';
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 { Cashier } from '@/api/shop/cashier/model';
import { formatNumber } from 'ele-admin-pro/es';
import { addCashier, updateCashier } from '@/api/shop/cashier';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 类型 0服务 1订单
type?: number;
// 修改回显的数据
data?: Cashier | 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 spec = ref<string>();
// 用户信息
const form = reactive<Cashier>({
// ID
id: undefined,
// 类型 0商城 1外卖
type: undefined,
// 唯一标识
code: undefined,
// 商品ID
goodsId: undefined,
// 商品名称
goodsName: undefined,
// 商品封面图
image: undefined,
// 商品规格
spec: undefined,
// 商品价格
price: undefined,
// 商品数量
cartNum: undefined,
// 单商品合计
totalPrice: undefined,
// 0 = 未购买 1 = 已购买
isPay: undefined,
// 是否为立即购买
isNew: undefined,
// 是否选中
selected: undefined,
// 商户ID
merchantId: undefined,
// 用户ID
userId: undefined,
// 收银员ID
cashierId: undefined,
// 收银单分组ID
groupId: undefined,
// 租户id
tenantId: undefined,
// 创建时间
createTime: undefined,
// 修改时间
updateTime: undefined,
// 是否多规格
specs: undefined,
// 商品规格数据
goodsSpecValue: []
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({});
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
console.log('>>>>>2');
if (form.specs === 1) {
const text = form.goodsSpecValue.map((d) => d.spec).join(',');
if (text === ',') {
message.error('请选择规格');
return false;
}
spec.value = text;
}
console.log('>>>>>3');
const formData = {
...form,
spec: spec.value
};
const saveOrUpdate = isUpdate.value ? updateCashier : addCashier;
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 = [];
spec.value = '';
isUpdate.value = !!props.data?.id;
if (props.type) {
form.type = props.type;
}
if (props.data) {
assignObject(form, props.data);
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -1,305 +1,75 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cashierId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
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 === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CashierEdit v-model:visible="showEdit" :data="current" @done="reload" />
<a-page-header :title="`收银台`" @back="() => $router.go(-1)">
<div class="flex gap-5">
<!-- 载入购物车 -->
<Cart
:spinning="spinning"
:loginUser="loginUser"
:cashier="cashier"
:count="cashier?.totalNums"
@group="openGroupForm"
@reload="reload"
@pay="onPay"
/>
<!-- 商品搜索 -->
<Goods :loginUser="loginUser" @reload="reload" />
</div>
</div>
</a-page-header>
<!-- 取单 -->
<GroupForm v-model:visible="showCashierForm" @done="reload" />
<!-- 支付结算 -->
<Pay v-model:visible="showPayForm" :data="cashier" @done="reload" />
</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 type { EleProTable } 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 CashierEdit from './components/cashierEdit.vue';
import { pageCashier, removeCashier, removeBatchCashier } from '@/api/shop/cashier';
import type { Cashier, CashierParam } from '@/api/shop/cashier/model';
import { computed, ref, watch } from 'vue';
import GroupForm from './components/group.vue';
import Cart from './components/cashier.vue';
import Goods from './components/goods.vue';
import Pay from './components/pay.vue';
import * as CashierApi from '@/api/shop/cashier';
import { useUserStore } from '@/store/modules/user';
import { CashierVo } from '@/api/shop/cashier/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 当前用户信息
const userStore = useUserStore();
const loginUser = computed(() => userStore.info ?? {});
const cashier = ref<CashierVo>({});
// 表格选中数据
const selection = ref<Cashier[]>([]);
// 当前编辑数据
const current = ref<Cashier | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 加载中状态
const spinning = ref<boolean>(true);
const showCashierForm = ref<boolean>(false);
const showPayForm = ref<boolean>(false);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCashier({
...where,
...orders,
page,
limit
});
const openGroupForm = () => {
showCashierForm.value = true;
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '收银单ID',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '类型 0商城 1外卖',
dataIndex: 'type',
key: 'type',
align: 'center',
},
{
title: '唯一标识',
dataIndex: 'code',
key: 'code',
align: 'center',
},
{
title: '商品ID',
dataIndex: 'goodsId',
key: 'goodsId',
align: 'center',
},
{
title: '商品规格',
dataIndex: 'spec',
key: 'spec',
align: 'center',
},
{
title: '商品价格',
dataIndex: 'price',
key: 'price',
align: 'center',
},
{
title: '商品数量',
dataIndex: 'cartNum',
key: 'cartNum',
align: 'center',
},
{
title: '单商品合计',
dataIndex: 'totalPrice',
key: 'totalPrice',
align: 'center',
},
{
title: '0 = 未购买 1 = 已购买',
dataIndex: 'isPay',
key: 'isPay',
align: 'center',
},
{
title: '是否为立即购买',
dataIndex: 'isNew',
key: 'isNew',
align: 'center',
},
{
title: '是否选中',
dataIndex: 'selected',
key: 'selected',
align: 'center',
},
{
title: '商户ID',
dataIndex: 'merchantId',
key: 'merchantId',
align: 'center',
},
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '收银员ID',
dataIndex: 'cashierId',
key: 'cashierId',
align: 'center',
},
{
title: '收银单分组ID',
dataIndex: 'groupId',
key: 'groupId',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '修改时间',
dataIndex: 'updateTime',
key: 'updateTime',
align: 'center',
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CashierParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
const onPay = () => {
showPayForm.value = true;
};
/* 打开编辑弹窗 */
const openEdit = (row?: Cashier) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: Cashier) => {
const hide = message.loading('请求中..', 0);
removeCashier(row.cashierId)
.then((msg) => {
hide();
message.success(msg);
reload();
/* 加载数据 */
const reload = () => {
spinning.value = true;
CashierApi.listCashier({ groupId: 0 })
.then((data) => {
if (data) {
cashier.value = data;
}
})
.catch((e) => {
hide();
message.error(e.message);
.finally(() => {
console.log('.......');
spinning.value = false;
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCashier(selection.value.map((d) => d.cashierId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: Cashier) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
watch(
loginUser,
({ userId }) => {
console.log(userId);
reload();
},
{ immediate: true }
);
</script>
<script lang="ts">
export default {
name: 'Cashier'
};
</script>
<style lang="less" scoped></style>

View File

@@ -23,6 +23,11 @@
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'goodsName'">
<a @click="openUrl(`${domain}/product/${record.goodsId}`)">{{
record.goodsName
}}</a>
</template>
<template v-if="column.key === 'type'">
<a-tag v-if="record.type === 0">虚拟商品</a-tag>
<a-tag v-if="record.type === 1">实物商品</a-tag>
@@ -100,6 +105,8 @@
import type { Goods, GoodsParam } from '@/api/shop/goods/model';
import { formatNumber } from 'ele-admin-pro/es';
import router from '@/router';
import { openUrl } from '@/utils/common';
import { getSiteDomain } from '@/utils/domain';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -114,6 +121,8 @@
const showMove = ref(false);
// 店铺ID
const merchantId = ref<number>();
// 网站域名
const domain = getSiteDomain();
// 表格数据源
const datasource: DatasourceFunction = ({
@@ -146,6 +155,12 @@
align: 'center',
width: 90
},
{
title: '商品名称',
dataIndex: 'goodsName',
key: 'goodsName',
align: 'center'
},
{
title: '商品图片',
dataIndex: 'image',
@@ -159,12 +174,6 @@
ellipsis: true,
align: 'center'
},
{
title: '商品名称',
dataIndex: 'goodsName',
key: 'goodsName',
align: 'center'
},
{
title: '商品编码',
dataIndex: 'code',

View File

@@ -30,10 +30,9 @@
label="订单状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.orderStatus == 0"></a-tag>
<a-tag v-if="form.orderStatus == 0">未使用</a-tag>
<a-tag v-if="form.orderStatus == 1">已付款</a-tag>
<a-tag v-if="form.orderStatus == 2">已取消</a-tag>
<a-tag v-if="form.orderStatus == 3">取消中</a-tag>
<a-tag v-if="form.orderStatus == 3">已取消</a-tag>
<a-tag v-if="form.orderStatus == 4">退款申请中</a-tag>
<a-tag v-if="form.orderStatus == 5">退款被拒绝</a-tag>
<a-tag v-if="form.orderStatus == 6">退款成功</a-tag>
@@ -80,6 +79,7 @@
:labelStyle="{ width: '90px', color: '#808080' }"
>
<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 == 2">积分</a-tag>
<a-tag v-if="form.payType == 3">支付宝</a-tag>
@@ -150,7 +150,7 @@
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form } from 'ant-design-vue';
import { Form, message } from "ant-design-vue";
import { assignObject } from 'ele-admin-pro';
import { Order } from '@/api/shop/order/model';
import { ColumnItem } from 'ele-admin-pro/es/ele-pro-table/types';
@@ -159,6 +159,7 @@
CloseOutlined,
CoffeeOutlined
} from '@ant-design/icons-vue';
import { pageOrderInfo } from '@/api/shop/orderInfo';
const useForm = Form.useForm;
@@ -271,16 +272,16 @@
const columns = ref<ColumnItem[]>([
{
title: '商品名称',
title: '场馆名称',
dataIndex: 'merchantName',
key: 'merchantName'
},
{
title: '购买数量',
title: '场地',
dataIndex: 'fieldName'
},
{
title: '商品规格',
title: '预定信息',
dataIndex: 'comments',
key: 'comments'
},
@@ -365,8 +366,12 @@
(visible) => {
if (visible) {
if (props.data) {
loading.value = false;
loading.value = true;
assignObject(form, props.data);
pageOrderInfo({ oid: form.orderId }).then((res) => {
form.orderInfoList = res?.list;
loading.value = false;
});
loadSteps(props.data);
}
} else {

View File

@@ -1,43 +1,75 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<SelectMerchantDown
:placeholder="`选择店铺`"
class="input-item"
v-model:value="where.merchantCode"
@change="search"
/>
<a-input-search
allow-clear
v-model:value="where.keywords"
placeholder="请输入关键词"
@search="search"
@pressEnter="search"
/>
<!-- <a-button @click="getCode">生成支付二维码</a-button>-->
<a-button @click="reset">重置</a-button>
</a-space>
<ele-modal
:width="500"
:visible="showQrcode"
:maskClosable="false"
title="使用微信扫一扫完成支付"
:body-style="{ paddingBottom: '28px' }"
@cancel="closeQrcode"
@ok="closeQrcode"
>
<div class="qrcode">
<ele-qr-code-svg v-if="text" :value="text" :size="200" />
<div class="ele-text-secondary">使用微信扫一扫完成支付</div>
</div>
</ele-modal>
<div class="search">
<a-space class="flex items-center" :size="10" style="flex-wrap: wrap">
<span class="text-gray-400">付款状态</span>
<a-radio-group v-model:value="where.payStatus" @change="search">
<a-radio-button :value="0">未付款</a-radio-button>
<a-radio-button :value="1">已付款</a-radio-button>
</a-radio-group>
<span class="text-gray-400">付款方式</span>
<a-radio-group v-model:value="where.payType" @change="search">
<a-radio-button :value="4">现金</a-radio-button>
<a-radio-button :value="0">余额支付</a-radio-button>
<a-radio-button :value="1">微信支付</a-radio-button>
<a-radio-button :value="2">会员卡</a-radio-button>
</a-radio-group>
<span class="text-gray-400">订单状态</span>
<a-radio-group v-model:value="where.orderStatus" @change="search">
<a-radio-button :value="0">未使用</a-radio-button>
<a-radio-button :value="1">已完成</a-radio-button>
<a-radio-button :value="2">已取消</a-radio-button>
<a-radio-button :value="4">退款中</a-radio-button>
<a-radio-button :value="5">退款被拒</a-radio-button>
<a-radio-button :value="6">退款成功</a-radio-button>
</a-radio-group>
<span class="text-gray-400">开票状态</span>
<a-radio-group v-model:value="where.isInvoice" @change="search">
<a-radio-button :value="0">未开票</a-radio-button>
<a-radio-button :value="1">已开票</a-radio-button>
<a-radio-button :value="2">不能开票</a-radio-button>
</a-radio-group>
</a-space>
<a-space :size="10" class="mt-5" style="flex-wrap: wrap">
<SelectMerchant
:placeholder="`选择场馆`"
class="input-item"
v-model:value="where.merchantName"
@done="chooseMerchantId"
/>
<a-range-picker
v-model:value="dateRange"
@change="search"
value-format="YYYY-MM-DD"
class="ele-fluid"
/>
<a-input-search
allow-clear
placeholder="请输入订单号|编号|手机号"
v-model:value="where.keywords"
@pressEnter="search"
@search="search"
style="width: 380px"
/>
<a-button @click="reset">重置</a-button>
<a-button class="ele-btn-icon" :disabled="loading" @click="handleExport">
<template #icon>
<download-outlined />
</template>
<span>导出订单</span>
</a-button>
</a-space>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import useSearch from '@/utils/use-search';
import { OrderParam } from '@/api/shop/order/model';
import { getNativeCode } from '@/api/system/payment';
import { ref, watch } from 'vue';
import { utils, writeFile } from 'xlsx';
import { message } from 'ant-design-vue';
import { Merchant } from '@/api/shop/merchant/model';
import { listOrder } from '@/api/shop/order';
import { Order, OrderParam } from '@/api/shop/order/model';
const props = withDefaults(
defineProps<{
@@ -51,17 +83,40 @@
(e: 'search', where?: OrderParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
(e: 'advanced'): void;
}>();
// 表单数据
const { where, resetFields } = useSearch<OrderParam>({
keywords: ''
type: 1,
createTimeStart: undefined,
createTimeEnd: undefined,
categoryId: undefined,
week: undefined,
keywords: undefined
});
// 请求状态
const loading = ref(false);
// 日期范围选择
const dateRange = ref<[string, string]>(['', '']);
const orders = ref<Order[] | any[]>();
const xlsFileName = ref<string>();
/* 搜索 */
const search = () => {
emit('search', where);
const [d1, d2] = dateRange.value ?? [];
xlsFileName.value = `${d1}${d2}`;
// where.createTimeStart = d1 ? d1 + ' 00:00:00' : undefined;
// where.createTimeEnd = d2 ? d2 + ' 00:00:00' : undefined;
emit('search', {
...where
});
};
const chooseMerchantId = (data: Merchant) => {
where.merchantId = data.merchantId;
where.merchantName = data.merchantName;
search();
};
/* 重置 */
@@ -70,19 +125,69 @@
search();
};
// 二维码内容
const text = ref('');
const showQrcode = ref(false);
const closeQrcode = () => {
showQrcode.value = !showQrcode.value;
};
const getCode = () => {
getNativeCode({}).then((data) => {
text.value = String(data);
showQrcode.value = true;
});
// 导出
const handleExport = async () => {
loading.value = true;
const array: (string | number)[][] = [
[
'订单号',
'订单编号',
'姓名',
'电话号码',
'订单信息',
'消费金额',
'实付金额',
'下单日期'
]
];
await listOrder(where)
.then((list) => {
orders.value = list;
orders.value?.forEach((d: Order) => {
array.push([
`${d.orderId}`,
`${d.orderNo}`,
`${d.realName}`,
`${d.phone}`,
`${d.comments}`,
`${d.totalPrice}`,
`${d.payPrice}`,
`${d.createTime}`
]);
});
const sheetName = `订单数据导出`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{ wch: 10 },
{ wch: 10 },
{ wch: 10 },
{ wch: 10 },
{ wch: 20 },
{ wch: 40 },
{ wch: 10 }
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${
where.createTimeEnd ? xlsFileName.value + '_' : ''
}${sheetName}.xlsx`
);
loading.value = false;
}, 2000);
})
.catch((msg) => {
message.error(msg);
loading.value = false;
})
.finally(() => {});
};
watch(
@@ -90,13 +195,3 @@
() => {}
);
</script>
<style lang="less" scoped>
.qrcode {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
padding: 40px 0;
}
</style>

View File

@@ -27,6 +27,7 @@
</template>
<template v-if="column.key === 'payType'">
<template v-if="record.payStatus == 1">
<a-tag v-if="record.payType == 0">余额支付</a-tag>
<a-tag v-if="record.payType == 1"
><WechatOutlined class="tag-icon" />微信支付</a-tag
>
@@ -144,15 +145,14 @@
{{ record.orderInfoList }}
</template>
<template v-if="column.key === 'orderStatus'">
<span
v-if="record.orderStatus == 0"
class="ele-text-primary"
></span>
<span v-if="record.orderStatus == 0" class="ele-text-primary"
>未使用</span
>
<span v-if="record.orderStatus == 2" class="ele-text-placeholder"
>已取消</span
>
<span v-if="record.orderStatus == 1" class="ele-text-success"
>付款</span
>完成</span
>
<span v-if="record.orderStatus == 3" class="ele-text-placeholder"
>已取消</span
@@ -182,8 +182,6 @@
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">详情</a>
<!-- <a-divider type="vertical" />-->
<!-- <a @click="openEdit(record)">编辑</a>-->
</a-space>
</template>
</template>
@@ -202,10 +200,6 @@
ExclamationCircleOutlined,
CheckOutlined,
CloseOutlined,
ClockCircleOutlined,
IdcardOutlined,
WechatOutlined,
CoffeeOutlined,
AlipayCircleOutlined
} from '@ant-design/icons-vue';
import { EleProTable, toDateString } from 'ele-admin-pro';
@@ -215,14 +209,9 @@
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import OrderInfo from './components/orderInfo.vue';
import {
pageOrder,
removeOrder,
removeBatchOrder
} from '@/api/booking/order';
import type { Order, OrderParam } from '@/api/booking/order/model';
import { pageOrder, removeOrder, removeBatchOrder } from '@/api/shop/order';
import type { Order, OrderParam } from '@/api/shop/order/model';
import { formatNumber } from 'ele-admin-pro/es';
import { getMerchantId } from '@/utils/common';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -249,9 +238,10 @@
if (filters) {
where.status = filters.status;
}
// 商城订单
where.type = 0;
// where.sceneType = 'showOrderGoods';
where.merchantId = getMerchantId();
// where.sceneType = 'showOrderInfo';
// where.merchantId = getMerchantId();
return pageOrder({
...where,
...orders,

View File

@@ -0,0 +1,301 @@
<!-- 编辑弹窗 -->
<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="oid">
<a-input
allow-clear
placeholder="请输入关联订单表id"
v-model:value="form.oid"
/>
</a-form-item>
<a-form-item label="关联场馆id" name="sid">
<a-input
allow-clear
placeholder="请输入关联场馆id"
v-model:value="form.sid"
/>
</a-form-item>
<a-form-item label="关联场地id" name="fid">
<a-input
allow-clear
placeholder="请输入关联场地id"
v-model:value="form.fid"
/>
</a-form-item>
<a-form-item label="场馆" name="siteName">
<a-input
allow-clear
placeholder="请输入场馆"
v-model:value="form.siteName"
/>
</a-form-item>
<a-form-item label="场地" name="fieldName">
<a-input
allow-clear
placeholder="请输入场地"
v-model:value="form.fieldName"
/>
</a-form-item>
<a-form-item label="预约时间段" name="dateTime">
<a-input
allow-clear
placeholder="请输入预约时间段"
v-model:value="form.dateTime"
/>
</a-form-item>
<a-form-item label="单价" name="price">
<a-input
allow-clear
placeholder="请输入单价"
v-model:value="form.price"
/>
</a-form-item>
<a-form-item label="儿童价" name="childrenPrice">
<a-input
allow-clear
placeholder="请输入儿童价"
v-model:value="form.childrenPrice"
/>
</a-form-item>
<a-form-item label="成人人数" name="adultNum">
<a-input
allow-clear
placeholder="请输入成人人数"
v-model:value="form.adultNum"
/>
</a-form-item>
<a-form-item label="儿童人数" name="childrenNum">
<a-input
allow-clear
placeholder="请输入儿童人数"
v-model:value="form.childrenNum"
/>
</a-form-item>
<a-form-item label="1已付款2未付款3无需付款或占用状态" name="payStatus">
<a-input
allow-clear
placeholder="请输入1已付款2未付款3无需付款或占用状态"
v-model:value="form.payStatus"
/>
</a-form-item>
<a-form-item label="是否免费1免费、2收费" name="isFree">
<a-input
allow-clear
placeholder="请输入是否免费1免费、2收费"
v-model:value="form.isFree"
/>
</a-form-item>
<a-form-item label="是否支持儿童票1支持2不支持" name="isChildren">
<a-input
allow-clear
placeholder="请输入是否支持儿童票1支持2不支持"
v-model:value="form.isChildren"
/>
</a-form-item>
<a-form-item label="预订类型1全场2半场" name="type">
<a-input
allow-clear
placeholder="请输入预订类型1全场2半场"
v-model:value="form.type"
/>
</a-form-item>
<a-form-item label="组合数据:日期+时间段+场馆id+场地id" name="mergeData">
<a-input
allow-clear
placeholder="请输入组合数据:日期+时间段+场馆id+场地id"
v-model:value="form.mergeData"
/>
</a-form-item>
<a-form-item label="开场时间" name="startTime">
<a-input
allow-clear
placeholder="请输入开场时间"
v-model:value="form.startTime"
/>
</a-form-item>
<a-form-item label="下单时间" name="orderTime">
<a-input
allow-clear
placeholder="请输入下单时间"
v-model:value="form.orderTime"
/>
</a-form-item>
<a-form-item label="毫秒时间戳" name="timeFlag">
<a-input
allow-clear
placeholder="请输入毫秒时间戳"
v-model:value="form.timeFlag"
/>
</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 { addOrderInfo, updateOrderInfo } from '@/api/shop/orderInfo';
import { OrderInfo } from '@/api/shop/orderInfo/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?: OrderInfo | 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<OrderInfo>({
id: undefined,
oid: undefined,
sid: undefined,
fid: undefined,
siteName: undefined,
fieldName: undefined,
dateTime: undefined,
price: undefined,
childrenPrice: undefined,
adultNum: undefined,
childrenNum: undefined,
payStatus: undefined,
isFree: undefined,
isChildren: undefined,
type: undefined,
mergeData: undefined,
startTime: undefined,
orderTime: undefined,
timeFlag: undefined,
tenantId: undefined,
createTime: undefined
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
orderInfoName: [
{
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 ? updateOrderInfo : addOrderInfo;
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,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -1,106 +0,0 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type { OrderInfo, OrderInfoParam } from './model';
import { MODULES_API_URL } from '@/config/setting';
/**
* 分页查询
*/
export async function pageOrderInfo(params: OrderInfoParam) {
const res = await request.get<ApiResult<PageResult<OrderInfo>>>(
MODULES_API_URL + '/shop/order-info/page',
{
params
}
);
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 查询列表
*/
export async function listOrderInfo(params?: OrderInfoParam) {
const res = await request.get<ApiResult<OrderInfo[]>>(
MODULES_API_URL + '/shop/order-info',
{
params
}
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 添加
*/
export async function addOrderInfo(data: OrderInfo) {
const res = await request.post<ApiResult<unknown>>(
MODULES_API_URL + '/shop/order-info',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 修改
*/
export async function updateOrderInfo(data: OrderInfo) {
const res = await request.put<ApiResult<unknown>>(
MODULES_API_URL + '/shop/order-info',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 删除
*/
export async function removeOrderInfo(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/shop/order-info/' + id
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 批量删除
*/
export async function removeBatchOrderInfo(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/shop/order-info/batch',
{
data
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 根据id查询
*/
export async function getOrderInfo(id: number) {
const res = await request.get<ApiResult<OrderInfo>>(
MODULES_API_URL + '/shop/order-info/' + 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,323 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="orderInfoId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
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 === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<OrderInfoEdit 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 type { EleProTable } 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 OrderInfoEdit from './components/orderInfoEdit.vue';
import { pageOrderInfo, removeOrderInfo, removeBatchOrderInfo } from '@/api/shop/orderInfo';
import type { OrderInfo, OrderInfoParam } from '@/api/shop/orderInfo/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<OrderInfo[]>([]);
// 当前编辑数据
const current = ref<OrderInfo | 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 pageOrderInfo({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '关联订单表id',
dataIndex: 'oid',
key: 'oid',
align: 'center',
},
{
title: '关联场馆id',
dataIndex: 'sid',
key: 'sid',
align: 'center',
},
{
title: '关联场地id',
dataIndex: 'fid',
key: 'fid',
align: 'center',
},
{
title: '场馆',
dataIndex: 'siteName',
key: 'siteName',
align: 'center',
},
{
title: '场地',
dataIndex: 'fieldName',
key: 'fieldName',
align: 'center',
},
{
title: '预约时间段',
dataIndex: 'dateTime',
key: 'dateTime',
align: 'center',
},
{
title: '单价',
dataIndex: 'price',
key: 'price',
align: 'center',
},
{
title: '儿童价',
dataIndex: 'childrenPrice',
key: 'childrenPrice',
align: 'center',
},
{
title: '成人人数',
dataIndex: 'adultNum',
key: 'adultNum',
align: 'center',
},
{
title: '儿童人数',
dataIndex: 'childrenNum',
key: 'childrenNum',
align: 'center',
},
{
title: '1已付款2未付款3无需付款或占用状态',
dataIndex: 'payStatus',
key: 'payStatus',
align: 'center',
},
{
title: '是否免费1免费、2收费',
dataIndex: 'isFree',
key: 'isFree',
align: 'center',
},
{
title: '是否支持儿童票1支持2不支持',
dataIndex: 'isChildren',
key: 'isChildren',
align: 'center',
},
{
title: '预订类型1全场2半场',
dataIndex: 'type',
key: 'type',
align: 'center',
},
{
title: '组合数据:日期+时间段+场馆id+场地id',
dataIndex: 'mergeData',
key: 'mergeData',
align: 'center',
},
{
title: '开场时间',
dataIndex: 'startTime',
key: 'startTime',
align: 'center',
},
{
title: '下单时间',
dataIndex: 'orderTime',
key: 'orderTime',
align: 'center',
},
{
title: '毫秒时间戳',
dataIndex: 'timeFlag',
key: 'timeFlag',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: OrderInfoParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: OrderInfo) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: OrderInfo) => {
const hide = message.loading('请求中..', 0);
removeOrderInfo(row.orderInfoId)
.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);
removeBatchOrderInfo(selection.value.map((d) => d.orderInfoId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: OrderInfo) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'OrderInfo'
};
</script>
<style lang="less" scoped></style>

View File

@@ -1,55 +0,0 @@
import type { PageParam } from '@/api';
/**
*
*/
export interface OrderInfo {
//
id?: number;
// 关联订单表id
oid?: number;
// 关联场馆id
sid?: number;
// 关联场地id
fid?: number;
// 场馆
siteName?: string;
// 场地
fieldName?: string;
// 预约时间段
dateTime?: string;
// 单价
price?: string;
// 儿童价
childrenPrice?: string;
// 成人人数
adultNum?: string;
// 儿童人数
childrenNum?: string;
// 1已付款2未付款3无需付款或占用状态
payStatus?: string;
// 是否免费1免费、2收费
isFree?: string;
// 是否支持儿童票1支持2不支持
isChildren?: string;
// 预订类型1全场2半场
type?: string;
// 组合数据:日期+时间段+场馆id+场地id
mergeData?: string;
// 开场时间
startTime?: number;
// 下单时间
orderTime?: number;
// 毫秒时间戳
timeFlag?: string;
// 租户id
tenantId?: number;
}
/**
* 搜索条件
*/
export interface OrderInfoParam extends PageParam {
id?: number;
keywords?: string;
}

View File

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

View File

@@ -5,7 +5,7 @@
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑收银' : '添加收银'"
:title="isUpdate ? '编辑' : '添加'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
@@ -19,109 +19,102 @@
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="类型 0商城 1外卖" name="type">
<a-form-item label="用户唯一小程序id" name="openId">
<a-input
allow-clear
placeholder="请输入类型 0商城 1外卖"
v-model:value="form.type"
placeholder="请输入用户唯一小程序id"
v-model:value="form.openId"
/>
</a-form-item>
<a-form-item label="唯一标识" name="code">
<a-form-item label="小程序用户秘钥" name="sessionKey">
<a-input
allow-clear
placeholder="请输入唯一标识"
v-model:value="form.code"
placeholder="请输入小程序用户秘钥"
v-model:value="form.sessionKey"
/>
</a-form-item>
<a-form-item label="商品ID" name="goodsId">
<a-form-item label="用户名" name="username">
<a-input
allow-clear
placeholder="请输入商品ID"
v-model:value="form.goodsId"
placeholder="请输入用户名"
v-model:value="form.username"
/>
</a-form-item>
<a-form-item label="商品规格" name="spec">
<a-form-item label="头像地址" name="avatarUrl">
<a-input
allow-clear
placeholder="请输入商品规格"
v-model:value="form.spec"
placeholder="请输入头像地址"
v-model:value="form.avatarUrl"
/>
</a-form-item>
<a-form-item label="商品价格" name="price">
<a-form-item label="1男2女" name="gender">
<a-input
allow-clear
placeholder="请输入商品价格"
v-model:value="form.price"
placeholder="请输入1男2女"
v-model:value="form.gender"
/>
</a-form-item>
<a-form-item label="商品数量" name="cartNum">
<a-form-item label="国家" name="country">
<a-input
allow-clear
placeholder="请输入商品数量"
v-model:value="form.cartNum"
placeholder="请输入国家"
v-model:value="form.country"
/>
</a-form-item>
<a-form-item label="单商品合计" name="totalPrice">
<a-form-item label="省份" name="province">
<a-input
allow-clear
placeholder="请输入单商品合计"
v-model:value="form.totalPrice"
placeholder="请输入省份"
v-model:value="form.province"
/>
</a-form-item>
<a-form-item label="0 = 未购买 1 = 已购买" name="isPay">
<a-form-item label="城市" name="city">
<a-input
allow-clear
placeholder="请输入0 = 未购买 1 = 已购买"
v-model:value="form.isPay"
placeholder="请输入城市"
v-model:value="form.city"
/>
</a-form-item>
<a-form-item label="是否为立即购买" name="isNew">
<a-form-item label="手机号码" name="phone">
<a-input
allow-clear
placeholder="请输入是否为立即购买"
v-model:value="form.isNew"
placeholder="请输入手机号码"
v-model:value="form.phone"
/>
</a-form-item>
<a-form-item label="是否选中" name="selected">
<a-form-item label="积分" name="integral">
<a-input
allow-clear
placeholder="请输入是否选中"
v-model:value="form.selected"
placeholder="请输入积分"
v-model:value="form.integral"
/>
</a-form-item>
<a-form-item label="商户ID" name="merchantId">
<a-form-item label="余额" name="money">
<a-input
allow-clear
placeholder="请输入商户ID"
v-model:value="form.merchantId"
placeholder="请输入余额"
v-model:value="form.money"
/>
</a-form-item>
<a-form-item label="用户ID" name="userId">
<a-form-item label="" name="idcard">
<a-input
allow-clear
placeholder="请输入用户ID"
v-model:value="form.userId"
placeholder="请输入"
v-model:value="form.idcard"
/>
</a-form-item>
<a-form-item label="收银员ID" name="cashierId">
<a-form-item label="" name="truename">
<a-input
allow-clear
placeholder="请输入收银员ID"
v-model:value="form.cashierId"
placeholder="请输入"
v-model:value="form.truename"
/>
</a-form-item>
<a-form-item label="收银单分组ID" name="groupId">
<a-form-item label="是否管理员1是2否" name="isAdmin">
<a-input
allow-clear
placeholder="请输入收银单分组ID"
v-model:value="form.groupId"
/>
</a-form-item>
<a-form-item label="修改时间" name="updateTime">
<a-input
allow-clear
placeholder="请输入修改时间"
v-model:value="form.updateTime"
placeholder="请输入是否管理员1是2否"
v-model:value="form.isAdmin"
/>
</a-form-item>
</a-form>
@@ -132,8 +125,8 @@
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCashier, updateCashier } from '@/api/shop/cashier';
import { Cashier } from '@/api/shop/cashier/model';
import { addUsers, updateUsers } from '@/api/shop/users';
import { Users } from '@/api/shop/users/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
@@ -151,7 +144,7 @@
//
visible: boolean;
//
data?: Cashier | null;
data?: Users | null;
}>();
const emit = defineEmits<{
@@ -168,27 +161,26 @@
const images = ref<ItemType[]>([]);
//
const form = reactive<Cashier>({
const form = reactive<Users>({
id: undefined,
type: undefined,
code: undefined,
goodsId: undefined,
spec: undefined,
price: undefined,
cartNum: undefined,
totalPrice: undefined,
isPay: undefined,
isNew: undefined,
selected: undefined,
merchantId: undefined,
userId: undefined,
cashierId: undefined,
groupId: undefined,
tenantId: undefined,
openId: undefined,
sessionKey: undefined,
username: undefined,
avatarUrl: undefined,
gender: undefined,
country: undefined,
province: undefined,
city: undefined,
phone: undefined,
integral: undefined,
money: undefined,
createTime: undefined,
updateTime: undefined,
cashierId: undefined,
cashierName: '',
idcard: undefined,
truename: undefined,
isAdmin: undefined,
tenantId: undefined,
usersId: undefined,
usersName: '',
status: 0,
comments: '',
sortNumber: 100
@@ -201,11 +193,11 @@
//
const rules = reactive({
cashierName: [
usersName: [
{
required: true,
type: 'string',
message: '请填写收银名称',
message: '请填写名称',
trigger: 'blur'
}
]
@@ -239,7 +231,7 @@
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCashier : addCashier;
const saveOrUpdate = isUpdate.value ? updateUsers : addUsers;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;

View File

@@ -4,7 +4,7 @@
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="userId"
row-key="usersId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
@@ -21,32 +21,23 @@
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'avatarUrl'">
<a-avatar
:size="36"
:src="`${record.avatarUrl}`"
style="margin-right: 4px"
>
<template #icon>
<UserOutlined />
</template>
</a-avatar>
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 1" color="green">启用</a-tag>
<a-tag v-if="record.status === 2" color="red">禁用</a-tag>
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a>积分充值</a>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a>分配特殊卡</a>
<!-- <a-popconfirm-->
<!-- title="确定要删除此记录吗?"-->
<!-- @confirm="remove(record)"-->
<!-- >-->
<!-- <a class="ele-text-danger">删除</a>-->
<!-- </a-popconfirm>-->
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
@@ -54,7 +45,7 @@
</a-card>
<!-- 编辑弹窗 -->
<UserEdit v-model:visible="showEdit" :data="current" @done="reload" />
<UsersEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
@@ -62,10 +53,7 @@
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import {
ExclamationCircleOutlined,
UserOutlined
} from '@ant-design/icons-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
@@ -73,8 +61,8 @@
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import UserEdit from './components/userEdit.vue';
import { pageUsers, removeBatchUsers, removeUsers } from '@/api/shop/users';
import UsersEdit from './components/usersEdit.vue';
import { pageUsers, removeUsers, removeBatchUsers } from '@/api/shop/users';
import type { Users, UsersParam } from '@/api/shop/users/model';
// 表格实例
@@ -113,97 +101,105 @@
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'userId',
key: 'userId',
width: 90
title: '',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '头像',
dataIndex: 'avatarUrl',
key: 'avatarUrl',
align: 'center'
title: '用户唯一小程序id',
dataIndex: 'openId',
key: 'openId',
align: 'center',
},
{
title: '小程序用户秘钥',
dataIndex: 'sessionKey',
key: 'sessionKey',
align: 'center',
},
{
title: '用户名',
dataIndex: 'username',
key: 'username',
align: 'center'
align: 'center',
},
{
title: '手机号码',
dataIndex: 'phone',
key: 'phone',
align: 'center'
title: '头像地址',
dataIndex: 'avatarUrl',
key: 'avatarUrl',
align: 'center',
},
{
title: '性别',
dataIndex: 'sexName',
key: 'sexName',
align: 'center'
title: '1男2女',
dataIndex: 'gender',
key: 'gender',
align: 'center',
},
{
title: '国家',
dataIndex: 'country',
key: 'country',
align: 'center',
hideInTable: true
},
{
title: '省份',
dataIndex: 'province',
key: 'province',
align: 'center'
align: 'center',
},
{
title: '城市',
dataIndex: 'city',
key: 'city',
align: 'center'
align: 'center',
},
{
title: '所在辖区',
dataIndex: 'region',
key: 'region',
title: '手机号码',
dataIndex: 'phone',
key: 'phone',
align: 'center',
hideInTable: true
},
{
title: '邮箱是否验证, 0否, 1是',
dataIndex: 'emailVerified',
key: 'emailVerified',
align: 'center',
hideInTable: true
},
{
title: '积分',
dataIndex: 'points',
key: 'points',
sorter: true,
align: 'center'
dataIndex: 'integral',
key: 'integral',
align: 'center',
},
{
title: '余额',
dataIndex: 'balance',
key: 'balance',
sorter: true,
align: 'center'
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
align: 'center'
dataIndex: 'money',
key: 'money',
align: 'center',
},
{
title: '注册时间',
dataIndex: 'addTime',
key: 'addTime',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '',
dataIndex: 'idcard',
key: 'idcard',
align: 'center',
},
{
title: '',
dataIndex: 'truename',
key: 'truename',
align: 'center',
},
{
title: '是否管理员1是2否',
dataIndex: 'isAdmin',
key: 'isAdmin',
align: 'center',
},
{
title: '操作',
key: 'action',
@@ -234,7 +230,7 @@
/* 删除单个 */
const remove = (row: Users) => {
const hide = message.loading('请求中..', 0);
removeUsers(row.userId)
removeUsers(row.usersId)
.then((msg) => {
hide();
message.success(msg);
@@ -259,7 +255,7 @@
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchUsers(selection.value.map((d) => d.userId))
removeBatchUsers(selection.value.map((d) => d.usersId))
.then((msg) => {
hide();
message.success(msg);