fix: 商品、订单部分相关代码生成,下一步先把商品搞完再搞订单
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch, onMounted, nextTick } from 'vue';
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { Modal, Upload } from 'ant-design-vue';
|
||||
@@ -92,7 +92,7 @@ watch(
|
||||
// console.log('[UploadImage] 父组件传入新的 modelValue:', newVal);
|
||||
syncModelToFileList(newVal);
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
const customRequest = async (e: any) => {
|
||||
@@ -137,7 +137,9 @@ const handlePreview = async (file) => {
|
||||
previewImage.value = file.response?.url || file.url || file.preview;
|
||||
previewVisible.value = true;
|
||||
previewTitle.value =
|
||||
file.name || file.url?.slice(Math.max(0, file.url.lastIndexOf('/') + 1)) || '预览图片';
|
||||
file.name ||
|
||||
file.url?.slice(Math.max(0, file.url.lastIndexOf('/') + 1)) ||
|
||||
'预览图片';
|
||||
};
|
||||
|
||||
// 计算是否还能上传更多图片
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
const list = ref('');
|
||||
const previewTitle = ref('二维码');
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
modalApi.close();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
if (isOpen) {
|
||||
const { values, title } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
list.value = values;
|
||||
previewTitle.value = title;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal title="提示框" class="w-[50%]">
|
||||
您的处方中有以下药品存在{{ previewTitle }}
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -1,169 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Avatar, Button, Image } from 'ant-design-vue';
|
||||
import html2canvas from 'html2canvas';
|
||||
|
||||
import { getDoctorInfoByStoreApi } from '#/views/doctor/doctor/api';
|
||||
|
||||
const qrCodeUrl = ref('');
|
||||
const htmlToImage = ref('');
|
||||
const previewAddress = ref('');
|
||||
const goodAt = ref('');
|
||||
const doctorInfo = ref();
|
||||
const doctorId = ref(0);
|
||||
const storeId = ref(0);
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
footer: false,
|
||||
showCancelButton: true,
|
||||
showConfirmButton: true,
|
||||
confirmText: '保存医生卡片图片',
|
||||
cancelText: '保存医生二维码图片',
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
doctorInfo.value = null;
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
doctorInfo.value = null;
|
||||
modalApi.close();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
modalApi.setState({
|
||||
loading: true,
|
||||
});
|
||||
if (isOpen) {
|
||||
const { values } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
if (values.doctor_id !== undefined) {
|
||||
doctorId.value = values.doctor_id;
|
||||
}
|
||||
if (values.store_id !== undefined) {
|
||||
storeId.value = values.store_id;
|
||||
}
|
||||
getDoctorInfoByStore();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const getDoctorInfoByStore = () => {
|
||||
getDoctorInfoByStoreApi({
|
||||
doctor_id: doctorId.value,
|
||||
store_id: storeId.value,
|
||||
}).then((res) => {
|
||||
doctorInfo.value = res;
|
||||
goodAt.value = res.doctor.good_at;
|
||||
previewAddress.value =
|
||||
res.store.province.name + res.store.city.name + res.store.position;
|
||||
|
||||
modalApi.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 下载二维码
|
||||
* @param name
|
||||
* @param isQr
|
||||
*/
|
||||
const downloadQRCode = async (name: string, isQr = false) => {
|
||||
const element = isQr ? qrCodeUrl.value : htmlToImage.value;
|
||||
html2canvas(element, {
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
logging: false,
|
||||
}).then((canvas) => {
|
||||
// 创建a标签下载
|
||||
const link = document.createElement('a'); // 创建a标签
|
||||
link.href = canvas.toDataURL(); // 是canvas对象的一种方法,用于将canvas对象转换为base64位编码
|
||||
link.setAttribute('download', `${doctorInfo.value.doctor.name}${name}.png`); // 利用了a标签的download 来下载 canvas图片
|
||||
link.style.display = 'none'; // 将图片隐藏起来
|
||||
document.body.append(link); // 插入到其中
|
||||
link.click();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<Modal v-if="doctorInfo" title="医生二维码" class="w-[50%]">
|
||||
<template #footer>
|
||||
<Button type="primary" @click="downloadQRCode('二维码卡片下载')">
|
||||
二维码卡片下载
|
||||
</Button>
|
||||
<Button type="primary" @click="downloadQRCode('二维码下载', true)">
|
||||
二维码下载
|
||||
</Button>
|
||||
</template>
|
||||
<div class="qrBox" style="text-align: center">
|
||||
<div ref="htmlToImage" class="qrModal">
|
||||
<div id="qrcode" ref="qrCodeUrl" class="qrcode">
|
||||
<Avatar :src="doctorInfo.doctor.avatar" />
|
||||
<div class="qrTitle">
|
||||
{{ doctorInfo.doctor.name }} {{ doctorInfo.doctor.title.name }}
|
||||
</div>
|
||||
<div class="qrAddress">
|
||||
<span class="addressTitle">诊所地址:</span>{{ previewAddress }}
|
||||
</div>
|
||||
<Image
|
||||
:preview="false"
|
||||
:src="doctorInfo.qr_code"
|
||||
class="mt-3"
|
||||
height="30"
|
||||
width="30"
|
||||
/>
|
||||
<h3 class="border-l-4 border-blue-500 pl-3 text-lg font-semibold">
|
||||
擅长领域
|
||||
</h3>
|
||||
<div
|
||||
class="prose max-w-none whitespace-pre-wrap"
|
||||
style="text-indent: 2em"
|
||||
>
|
||||
{{ doctorInfo.doctor.good_at }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.qrModal {
|
||||
padding: 20px 30px;
|
||||
// 宽度适应文本长度
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
background: radial-gradient(
|
||||
circle at center,
|
||||
rgb(173 216 230 / 80%),
|
||||
rgb(135 206 235 / 80%),
|
||||
rgb(100 149 237 / 80%)
|
||||
);
|
||||
|
||||
.qrTitle {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.qrAddress {
|
||||
margin-top: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.addressTitle {
|
||||
color: #3a8ee6;
|
||||
}
|
||||
|
||||
.qrcode {
|
||||
width: fit-content;
|
||||
padding: 20px 30px;
|
||||
//width: 50%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
background-color: #fff;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
479
apps/web-antd/src/components/modal/ProductPreview.vue
Normal file
479
apps/web-antd/src/components/modal/ProductPreview.vue
Normal file
@@ -0,0 +1,479 @@
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
import { getProductInfoApi } from '#/views/my-gen/product/api';
|
||||
|
||||
// 定义商品类型
|
||||
interface Product {
|
||||
id?: string;
|
||||
title?: string;
|
||||
introduction?: string;
|
||||
price?: number;
|
||||
originalPrice?: number;
|
||||
discount?: number;
|
||||
cover?: string;
|
||||
images?: string[];
|
||||
isNew?: boolean;
|
||||
isFavorite?: boolean;
|
||||
viewCount?: number;
|
||||
favoriteCount?: number;
|
||||
soldCount?: number;
|
||||
returnCount?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
// 可选的默认商品数据
|
||||
defaultProduct: {
|
||||
type: Object as () => Product,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['addToCart', 'buyNow', 'favoriteChanged']);
|
||||
|
||||
const product = ref<Product>(props.defaultProduct || {});
|
||||
const previewTitle = ref('商品详情');
|
||||
|
||||
// 轮播图状态
|
||||
const currentSlideIndex = ref(0);
|
||||
let autoplayTimer: null | ReturnType<typeof setInterval> = null;
|
||||
|
||||
// 缩略图容器引用
|
||||
const thumbnailContainer = ref<HTMLDivElement | null>(null);
|
||||
|
||||
// 缩略图滚动状态
|
||||
const canScrollLeft = ref(false);
|
||||
const canScrollRight = ref(false);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
modalApi.close();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
if (isOpen) {
|
||||
const { values } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
getProductInfoApi(values).then((res: Product) => {
|
||||
product.value = res;
|
||||
previewTitle.value = `${res.title || '商品'}详情`;
|
||||
currentSlideIndex.value = 0; // 重置轮播图索引
|
||||
startAutoplay(); // 启动自动轮播
|
||||
|
||||
// 等待DOM更新后滚动到第一个缩略图
|
||||
nextTick(() => {
|
||||
updateScrollState();
|
||||
scrollToActiveThumbnail();
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
stopAutoplay(); // 关闭时停止自动轮播
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 监听外部props变化更新商品数据
|
||||
watch(
|
||||
() => props.defaultProduct,
|
||||
(newVal: Product) => {
|
||||
if (newVal && JSON.stringify(newVal) !== JSON.stringify(product.value)) {
|
||||
product.value = { ...newVal };
|
||||
currentSlideIndex.value = 0; // 重置轮播图索引
|
||||
|
||||
// 等待DOM更新后滚动到第一个缩略图
|
||||
nextTick(() => {
|
||||
updateScrollState();
|
||||
scrollToActiveThumbnail();
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
// 轮播图控制方法
|
||||
const nextSlide = () => {
|
||||
const imageCount = product.value.images?.length || 0;
|
||||
if (imageCount > 0) {
|
||||
currentSlideIndex.value = (currentSlideIndex.value + 1) % imageCount;
|
||||
resetAutoplay();
|
||||
}
|
||||
};
|
||||
|
||||
const prevSlide = () => {
|
||||
const imageCount = product.value.images?.length || 0;
|
||||
if (imageCount > 0) {
|
||||
currentSlideIndex.value =
|
||||
(currentSlideIndex.value - 1 + imageCount) % imageCount;
|
||||
resetAutoplay();
|
||||
}
|
||||
};
|
||||
|
||||
// 自动轮播控制
|
||||
const startAutoplay = () => {
|
||||
stopAutoplay();
|
||||
const imageCount = product.value.images?.length || 0;
|
||||
if (imageCount > 1) {
|
||||
autoplayTimer = setInterval(() => {
|
||||
nextSlide();
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
const stopAutoplay = () => {
|
||||
if (autoplayTimer) {
|
||||
clearInterval(autoplayTimer);
|
||||
autoplayTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
const resetAutoplay = () => {
|
||||
stopAutoplay();
|
||||
startAutoplay();
|
||||
};
|
||||
|
||||
// 图片加载错误处理
|
||||
const handleImageError = (e: Event) => {
|
||||
(e.target as HTMLImageElement).src =
|
||||
'https://picsum.photos/800/600?random=error';
|
||||
};
|
||||
|
||||
// 缩略图滚动控制
|
||||
const scrollThumbnails = (direction: number) => {
|
||||
if (!thumbnailContainer.value) return;
|
||||
|
||||
const container = thumbnailContainer.value;
|
||||
const scrollAmount = container.clientWidth * 0.7; // 每次滚动70%的容器宽度
|
||||
|
||||
container.scrollBy({
|
||||
left: direction * scrollAmount,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
|
||||
// 更新滚动状态
|
||||
setTimeout(updateScrollState, 300);
|
||||
};
|
||||
|
||||
// 滚动到当前选中的缩略图
|
||||
const scrollToActiveThumbnail = () => {
|
||||
if (
|
||||
!thumbnailContainer.value ||
|
||||
!product.value.images ||
|
||||
product.value.images.length === 0
|
||||
)
|
||||
return;
|
||||
|
||||
const container = thumbnailContainer.value;
|
||||
const thumbnails = container.querySelectorAll('img');
|
||||
const activeThumbnail = thumbnails[currentSlideIndex.value];
|
||||
|
||||
if (activeThumbnail) {
|
||||
// 计算将当前缩略图居中所需的滚动位置
|
||||
const thumbnailRect = activeThumbnail.getBoundingClientRect();
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const scrollPosition =
|
||||
activeThumbnail.offsetLeft -
|
||||
(container.clientWidth - thumbnailRect.width) / 2;
|
||||
|
||||
// 平滑滚动到计算的位置
|
||||
container.scrollTo({
|
||||
left: scrollPosition,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
|
||||
// 更新滚动状态
|
||||
setTimeout(updateScrollState, 300);
|
||||
}
|
||||
};
|
||||
|
||||
// 更新滚动按钮状态
|
||||
const updateScrollState = () => {
|
||||
if (!thumbnailContainer.value) return;
|
||||
|
||||
const container = thumbnailContainer.value;
|
||||
canScrollLeft.value = container.scrollLeft > 10;
|
||||
canScrollRight.value =
|
||||
container.scrollLeft < container.scrollWidth - container.clientWidth - 10;
|
||||
};
|
||||
|
||||
// 监听当前图片索引变化,滚动到对应缩略图
|
||||
watch(currentSlideIndex, () => {
|
||||
scrollToActiveThumbnail();
|
||||
});
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
startAutoplay();
|
||||
|
||||
// 监听容器滚动事件,更新滚动按钮状态
|
||||
if (thumbnailContainer.value) {
|
||||
thumbnailContainer.value.addEventListener('scroll', updateScrollState);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
stopAutoplay();
|
||||
|
||||
// 移除滚动事件监听器
|
||||
if (thumbnailContainer.value) {
|
||||
thumbnailContainer.value.removeEventListener('scroll', updateScrollState);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="previewTitle" class="w-[50%]">
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 gap-8 lg:grid-cols-5">
|
||||
<!-- 图片轮播区域(内联实现) -->
|
||||
<div class="space-y-4 lg:col-span-2">
|
||||
<div
|
||||
class="carousel-shadow group relative overflow-hidden rounded-xl"
|
||||
>
|
||||
<!-- 主图轮播 -->
|
||||
<div
|
||||
class="flex transition-transform duration-500 ease-in-out"
|
||||
:style="{ transform: `translateX(-${currentSlideIndex * 100}%)` }"
|
||||
>
|
||||
<img
|
||||
v-for="(img, index) in product.images || []"
|
||||
:key="index"
|
||||
:src="img"
|
||||
alt="商品图片"
|
||||
class="h-auto w-full object-cover"
|
||||
height="600"
|
||||
@error="handleImageError"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 轮播控制器 -->
|
||||
<button
|
||||
@click="prevSlide"
|
||||
class="text-primary absolute left-4 top-1/2 z-10 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full bg-white/80 opacity-70 shadow-md transition-all duration-300 hover:bg-white group-hover:opacity-100"
|
||||
>
|
||||
<span class="icon-[icon-park-outline--left]"></span>
|
||||
</button>
|
||||
<button
|
||||
@click="nextSlide"
|
||||
class="text-primary absolute right-4 top-1/2 z-10 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full bg-white/80 opacity-70 shadow-md transition-all duration-300 hover:bg-white group-hover:opacity-100"
|
||||
>
|
||||
<span class="icon-[icon-park-outline--right]"></span>
|
||||
</button>
|
||||
|
||||
<!-- 指示器 -->
|
||||
<div
|
||||
class="absolute bottom-4 left-1/2 flex -translate-x-1/2 space-x-2"
|
||||
>
|
||||
<button
|
||||
v-for="(img, index) in product.images || []"
|
||||
:key="index"
|
||||
@click="currentSlideIndex = index"
|
||||
class="h-3 w-3 rounded-full transition-all duration-300"
|
||||
:class="{
|
||||
'bg-primary': index === currentSlideIndex,
|
||||
'bg-white/50 hover:bg-white/80': index !== currentSlideIndex,
|
||||
}"
|
||||
></button>
|
||||
</div>
|
||||
|
||||
<!-- 标签和计数器 -->
|
||||
<div class="absolute left-4 top-4 z-10" v-if="product.isNew">
|
||||
<span
|
||||
class="bg-secondary rounded-full px-3 py-1 text-sm font-medium text-white shadow-md"
|
||||
>新品</span
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="absolute right-4 top-4 rounded bg-black/50 px-2 py-1 text-xs font-medium text-white shadow-md"
|
||||
>
|
||||
{{ currentSlideIndex + 1 }} /
|
||||
{{ (product.images && product.images.length) || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 缩略图区域 -->
|
||||
<div class="relative">
|
||||
<!-- 左侧滑动按钮 -->
|
||||
<button
|
||||
@click="scrollThumbnails(-1)"
|
||||
:disabled="!canScrollLeft"
|
||||
class="text-primary absolute left-0 top-1/2 z-10 flex h-8 w-8 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/90 opacity-80 shadow-md transition-all duration-300 hover:bg-white"
|
||||
:class="{ 'cursor-not-allowed opacity-40': !canScrollLeft }"
|
||||
>
|
||||
<span class="icon-[icon-park-outline--left]"></span>
|
||||
</button>
|
||||
|
||||
<!-- 缩略图容器 -->
|
||||
<div
|
||||
ref="thumbnailContainer"
|
||||
class="scrollbar-hide flex space-x-3 overflow-x-auto pb-2 pl-10 pr-10"
|
||||
>
|
||||
<img
|
||||
v-for="(img, index) in product.images || []"
|
||||
:key="index"
|
||||
:src="img"
|
||||
alt="商品缩略图"
|
||||
@click="currentSlideIndex = index"
|
||||
class="thumbnail-shadow h-20 w-20 cursor-pointer rounded-lg border-2 object-cover transition-all duration-300 sm:h-24 sm:w-24"
|
||||
:class="{
|
||||
'border-primary ring-primary/30 ring-2':
|
||||
index === currentSlideIndex,
|
||||
'hover:border-primary/50 border-transparent':
|
||||
index !== currentSlideIndex,
|
||||
}"
|
||||
@error="handleImageError"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 右侧滑动按钮 -->
|
||||
<button
|
||||
@click="scrollThumbnails(1)"
|
||||
:disabled="!canScrollRight"
|
||||
class="text-primary absolute right-0 top-1/2 z-10 flex h-8 w-8 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/90 opacity-80 shadow-md transition-all duration-300 hover:bg-white"
|
||||
:class="{ 'cursor-not-allowed opacity-40': !canScrollRight }"
|
||||
>
|
||||
<span class="icon-[icon-park-outline--right]"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 商品信息区域保持不变 -->
|
||||
<div class="lg:col-span-3">
|
||||
<!-- 保持原有内容不变 -->
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h1
|
||||
class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold leading-tight"
|
||||
>
|
||||
{{ product.title }}
|
||||
</h1>
|
||||
<p class="mt-2 text-neutral-500">{{ product.introduction }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 价格区域 -->
|
||||
<div class="rounded-lg p-4">
|
||||
<div class="mb-2 flex items-end">
|
||||
<span class="text-3xl font-bold">¥{{ product.price }}</span>
|
||||
<span
|
||||
v-if="product.originalPrice"
|
||||
class="ml-3 text-lg line-through"
|
||||
>¥{{ product.originalPrice || 0 }}</span>
|
||||
<span
|
||||
v-if="product.discount"
|
||||
class="bg-secondary/10 text-secondary ml-2 rounded px-2 py-1 text-sm"
|
||||
>
|
||||
省¥{{ product.discount }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-x-6 gap-y-2 text-sm">
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-eye mr-1"></i>
|
||||
<span>浏览: {{ product.viewCount || 0 }}</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-heart mr-1"></i>
|
||||
<span>收藏: {{ product.favoriteCount || 0 }}</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-shopping-bag mr-1"></i>
|
||||
<span>已售: {{ product.soldCount || 0 }}</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-exchange mr-1"></i>
|
||||
<span>退货: {{ product.returnCount || 0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 商品特性 -->
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-start">
|
||||
<div class="mt-1 w-16 flex-shrink-0 text-neutral-500">配送</div>
|
||||
<div class="ml-4">
|
||||
<div class="flex items-center">
|
||||
<span class="text-neutral-800">全国大部分地区包邮</span>
|
||||
<span
|
||||
class="text-primary ml-2 cursor-pointer hover:underline"
|
||||
>配送查询</span
|
||||
>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-neutral-500">预计2-5天送达</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start">
|
||||
<div class="mt-1 w-16 flex-shrink-0 text-neutral-500">服务</div>
|
||||
<div class="ml-4 flex flex-wrap gap-4">
|
||||
<div
|
||||
v-for="item in product.labels"
|
||||
:key="item.id"
|
||||
class="flex items-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="28"
|
||||
height="28"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
stroke="#059669"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
color="#059669"
|
||||
>
|
||||
<path
|
||||
d="M18.99 19H19m-.01 0c-.622.617-1.75.464-2.542.464c-.972 0-1.44.19-2.133.883C13.725 20.937 12.934 22 12 22s-1.725-1.063-2.315-1.653c-.694-.693-1.162-.883-2.133-.883c-.791 0-1.92.154-2.543-.464c-.627-.622-.473-1.756-.473-2.552c0-1.007-.22-1.47-.937-2.186C2.533 13.196 2 12.662 2 12s.533-1.196 1.6-2.262c.64-.64.936-1.274.936-2.186c0-.791-.154-1.92.464-2.543c.622-.627 1.756-.473 2.552-.473c.912 0 1.546-.297 2.186-.937C10.804 2.533 11.338 2 12 2s1.196.533 2.262 1.6c.64.64 1.274.936 2.186.936c.791 0 1.92-.154 2.543.464c.627.622.473 1.756.473 2.552c0 1.007.22 1.47.937 2.186C21.467 10.804 22 11.338 22 12s-.533 1.196-1.6 2.262c-.716.717-.936 1.18-.936 2.186c0 .796.154 1.93-.473 2.552"
|
||||
/>
|
||||
<path
|
||||
d="M9 12.893s1.2.652 1.8 1.607c0 0 1.8-3.75 4.2-5"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<Tag color="#7166f0">{{ item.name }}</Tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.carousel-shadow {
|
||||
box-shadow:
|
||||
0 10px 25px -5px rgba(0, 0, 0, 0.1),
|
||||
0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.thumbnail-shadow {
|
||||
box-shadow:
|
||||
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
||||
0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.scrollbar-hide {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.opacity-40 {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.cursor-not-allowed {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
@@ -1,135 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image } from 'ant-design-vue';
|
||||
import html2canvas from 'html2canvas';
|
||||
|
||||
const qrCodeUrl = ref('');
|
||||
const htmlToImage = ref('');
|
||||
const url = ref('');
|
||||
const previewTitle = ref('二维码');
|
||||
const previewAddress = ref('');
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
footer: false,
|
||||
header: false,
|
||||
showCancelButton: true,
|
||||
showConfirmButton: true,
|
||||
confirmText: '保存诊所卡片图片',
|
||||
cancelText: '保存诊所二维码图片',
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
modalApi.close();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
if (isOpen) {
|
||||
const { values, title, address } =
|
||||
modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
url.value = values;
|
||||
qrCodeUrl.value = values;
|
||||
previewTitle.value = title;
|
||||
previewAddress.value = address;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
/**
|
||||
* 下载二维码
|
||||
* @param name
|
||||
* @param isQr
|
||||
*/
|
||||
const downloadQRCode = async (name: string, isQr = false) => {
|
||||
const element = isQr ? qrCodeUrl.value : htmlToImage.value;
|
||||
html2canvas(element, {
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
logging: false,
|
||||
}).then((canvas) => {
|
||||
// 创建a标签下载
|
||||
const link = document.createElement('a'); // 创建a标签
|
||||
link.href = canvas.toDataURL(); // 是canvas对象的一种方法,用于将canvas对象转换为base64位编码
|
||||
link.setAttribute('download', `${previewTitle.value}${name}.png`); // 利用了a标签的download 来下载 canvas图片
|
||||
link.style.display = 'none'; // 将图片隐藏起来
|
||||
document.body.append(link); // 插入到其中
|
||||
link.click();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="`${previewTitle}二维码`" class="w-[50%]">
|
||||
<div class="qrBox" style="text-align: center">
|
||||
<div ref="htmlToImage" class="qrModal">
|
||||
<div id="qrcode" ref="qrCodeUrl" class="qrcode">
|
||||
<div class="qrTitle">{{ previewTitle }}</div>
|
||||
<Image :preview="false" :src="url" height="30" width="30" />
|
||||
<div class="qrAddress">
|
||||
<span class="addressTitle">诊所地址:</span>{{ previewAddress }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
style="margin: 20px auto"
|
||||
type="primary"
|
||||
@click="downloadQRCode('二维码卡片下载')"
|
||||
>
|
||||
二维码卡片下载
|
||||
</Button>
|
||||
<Button
|
||||
style="margin: 20px auto 20px 20px"
|
||||
type="primary"
|
||||
@click="downloadQRCode('二维码下载', true)"
|
||||
>
|
||||
二维码下载
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.qrModal {
|
||||
padding: 20px 30px;
|
||||
// 宽度适应文本长度
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
background: radial-gradient(
|
||||
circle at center,
|
||||
rgb(173 216 230 / 80%),
|
||||
rgb(135 206 235 / 80%),
|
||||
rgb(100 149 237 / 80%)
|
||||
);
|
||||
|
||||
.qrTitle {
|
||||
margin-bottom: 30px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.qrAddress {
|
||||
margin-top: 30px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.addressTitle {
|
||||
color: #3a8ee6;
|
||||
}
|
||||
|
||||
.qrcode {
|
||||
width: fit-content;
|
||||
padding: 20px 30px;
|
||||
//width: 50%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
background-color: #fff;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
import type { JsonViewerAction, JsonViewerValue } from '@vben/common-ui';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { JsonViewer, Page } from '@vben/common-ui';
|
||||
|
||||
import { Button, Input, Select, Switch, Table } from 'ant-design-vue';
|
||||
import { Button, Input, message, Select, Switch, Table } from 'ant-design-vue';
|
||||
|
||||
import filedType from '#/util/json/filedType.json';
|
||||
import formType from '#/util/json/formType.json';
|
||||
@@ -67,11 +69,39 @@ const columns = computed(() => [
|
||||
fixed: 'right',
|
||||
},
|
||||
]);
|
||||
|
||||
function handleKeyClick(key: string) {
|
||||
message.info(`点击了Key ${key}`);
|
||||
}
|
||||
|
||||
function handleValueClick(value: JsonViewerValue) {
|
||||
message.info(`点击了Value ${JSON.stringify(value)}`);
|
||||
}
|
||||
|
||||
function handleCopied(_event: JsonViewerAction) {
|
||||
message.success('已复制JSON');
|
||||
}
|
||||
const jsonValue = ref('');
|
||||
|
||||
const changeJsonValue = () => {
|
||||
tableData.value = JSON.parse(jsonValue.value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
{{ tableData }}
|
||||
<Input v-model:value="jsonValue" />
|
||||
<Button @click="changeJsonValue">写入json</Button>
|
||||
<JsonViewer
|
||||
:value="tableData"
|
||||
:expand-depth="3"
|
||||
copyable
|
||||
:sort="false"
|
||||
@key-click="handleKeyClick"
|
||||
@value-click="handleValueClick"
|
||||
@copied="handleCopied"
|
||||
boxed
|
||||
/>
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data-source="tableData"
|
||||
@@ -122,7 +152,7 @@ const columns = computed(() => [
|
||||
<!-- 基础配置 -->
|
||||
<template v-if="column.dataIndex === 'base'">
|
||||
<!-- 开关控件 -->
|
||||
<div>可以为空: <Switch v-model:checked="record.null" /></div>
|
||||
<div>不能为空: <Switch v-model:checked="record.not_null" /></div>
|
||||
<div class="mt-2">
|
||||
表单显示: <Switch v-model:checked="record.formShow" />
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'store/';
|
||||
const prefix = 'mall/';
|
||||
/**
|
||||
* 分页查询店铺管理列表
|
||||
* @param data
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import { getStoreClassificationOptionApi } from '#/views/my-gen/store-classification/api';
|
||||
import { getStoreClassificationOptionApi } from '#/views/my-gen/mall-classification/api';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
@@ -27,17 +27,29 @@ export const gridOptions: VxeGridProps<RowType> = {
|
||||
// 生成的列表字段
|
||||
{ field: 'name', title: '店铺名称' },
|
||||
{ field: 'level', title: '店铺等级' },
|
||||
{ field: 'avatar', title: '店铺头像' },
|
||||
{ field: 'avatar', title: '店铺头像', slots: { default: 'avatar' } },
|
||||
{ field: 'introduction', title: '店铺简介' },
|
||||
{ field: 'description', title: '详细介绍' },
|
||||
// { field: 'description', title: '详细介绍' },
|
||||
{ field: 'phone', title: '商家电话号码' },
|
||||
{ field: 'person', title: '联系人名称' },
|
||||
{ field: 'id_card', title: '身份证号码' },
|
||||
{ field: 'qr_code', title: '店铺二维码' },
|
||||
{ field: 'is_featured', title: '是否为推荐店铺' },
|
||||
{ field: 'business_license', title: '营业执照' },
|
||||
{ field: 'id_card_portrait_side', title: '身份证人面' },
|
||||
{ field: 'id_card_national_emblem_side', title: '身份证国徽面' },
|
||||
{
|
||||
field: 'business_license',
|
||||
title: '营业执照',
|
||||
slots: { default: 'business_license' },
|
||||
},
|
||||
{
|
||||
field: 'id_card_portrait_side',
|
||||
title: '身份证人面',
|
||||
slots: { default: 'id_card_portrait_side' },
|
||||
},
|
||||
{
|
||||
field: 'id_card_national_emblem_side',
|
||||
title: '身份证国徽面',
|
||||
slots: { default: 'id_card_national_emblem_side' },
|
||||
},
|
||||
{ field: 'chain', title: '是否连锁' },
|
||||
{ field: 'business_suspended', title: '是否暂停营业' },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'store-classification/';
|
||||
const prefix = 'mall-classification/';
|
||||
/**
|
||||
* 分页查询店铺行业列表
|
||||
* @param data
|
||||
@@ -0,0 +1,49 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'mall-membership-level/';
|
||||
/**
|
||||
* 分页查询店铺会员等级列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getStoreMembershipLevelListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取店铺会员等级下拉列表
|
||||
*/
|
||||
export async function getStoreMembershipLevelOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取店铺会员等级详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getStoreMembershipLevelInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增店铺会员等级
|
||||
* @param data
|
||||
*/
|
||||
export async function createStoreMembershipLevelApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑店铺会员等级
|
||||
* @param data
|
||||
*/
|
||||
export async function updateStoreMembershipLevelApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除店铺会员等级
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteStoreMembershipLevelApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import {
|
||||
createStoreMembershipLevelApi,
|
||||
updateStoreMembershipLevelApi,
|
||||
} from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'StoreMembershipLevelDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value
|
||||
? updateStoreMembershipLevelApi
|
||||
: createStoreMembershipLevelApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}店铺会员等级`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '等级名称',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'experience',
|
||||
label: '升级所需经验',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '等级名称',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getStoreMembershipLevelListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
// 生成的列表字段
|
||||
{ field: 'name', title: '等级名称' },
|
||||
{ field: 'experience', title: '升级所需经验' },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getStoreMembershipLevelListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
128
apps/web-antd/src/views/my-gen/mall-membership-level/index.vue
Normal file
128
apps/web-antd/src/views/my-gen/mall-membership-level/index.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteStoreMembershipLevelApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
checkboxAll() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents,
|
||||
});
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
ids.push(row);
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteStoreMembershipLevelApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="店铺会员等级管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增',
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: showModal.bind(null),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
ifShow: hasTopTableDropDownActions,
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, false),
|
||||
},
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px"> 批量操作 </Button>
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #avatar="{ row }">
|
||||
<Image :src="row.avatar" height="30" width="30" />
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
42
apps/web-antd/src/views/my-gen/mall/api/index.ts
Normal file
42
apps/web-antd/src/views/my-gen/mall/api/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'mall/';
|
||||
/**
|
||||
* 分页查询店铺管理列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getStoreListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取店铺管理详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getStoreInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增店铺管理
|
||||
* @param data
|
||||
*/
|
||||
export async function createStoreApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑店铺管理
|
||||
* @param data
|
||||
*/
|
||||
export async function updateStoreApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除店铺管理
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteStoreApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
66
apps/web-antd/src/views/my-gen/mall/components/modal.vue
Normal file
66
apps/web-antd/src/views/my-gen/mall/components/modal.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import { createStoreApi, updateStoreApi } from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'StoreDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value ? updateStoreApi : createStoreApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}店铺管理`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
179
apps/web-antd/src/views/my-gen/mall/config/form.ts
Normal file
179
apps/web-antd/src/views/my-gen/mall/config/form.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import { getStoreClassificationOptionApi } from '#/views/my-gen/mall-classification/api';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '店铺名称',
|
||||
component: 'VbenInput',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'level',
|
||||
label: '店铺等级',
|
||||
component: 'InputNumber',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'introduction',
|
||||
label: '店铺简介',
|
||||
component: 'Textarea',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'description',
|
||||
label: '详细介绍',
|
||||
component: 'Textarea',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'phone',
|
||||
label: '商家电话号码',
|
||||
component: 'VbenInput',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'person',
|
||||
label: '联系人名称',
|
||||
component: 'VbenInput',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'id_card',
|
||||
label: '身份证号码',
|
||||
component: 'VbenInput',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'classification_id',
|
||||
label: '行业',
|
||||
component: 'ApiTreeSelect',
|
||||
componentProps: {
|
||||
childrenField: 'children',
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
api: getStoreClassificationOptionApi,
|
||||
},
|
||||
formItemClass: 'col-span-6',
|
||||
defaultValue: 0,
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'is_featured',
|
||||
label: '推荐店铺',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '是',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '否',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
formItemClass: 'col-span-6',
|
||||
defaultValue: 0,
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'chain',
|
||||
label: '连锁',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '是',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '否',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
formItemClass: 'col-span-6',
|
||||
defaultValue: 0,
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'business_suspended',
|
||||
label: '暂停营业',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '是',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '否',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
formItemClass: 'col-span-6',
|
||||
defaultValue: 0,
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'avatar',
|
||||
label: '店铺头像',
|
||||
component: 'Avatar',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'business_license',
|
||||
label: '营业执照',
|
||||
component: 'Avatar',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'id_card_portrait_side',
|
||||
label: '身份证人面',
|
||||
component: 'Avatar',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'id_card_national_emblem_side',
|
||||
label: '身份证国徽面',
|
||||
component: 'Avatar',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
55
apps/web-antd/src/views/my-gen/mall/config/search.ts
Normal file
55
apps/web-antd/src/views/my-gen/mall/config/search.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '店铺名称',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'level',
|
||||
label: '店铺等级',
|
||||
component: 'InputNumber',
|
||||
},
|
||||
{
|
||||
fieldName: 'phone',
|
||||
label: '商家电话号码',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'person',
|
||||
label: '联系人名称',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'is_featured',
|
||||
label: '是否为推荐店铺',
|
||||
component: 'VbenSelect',
|
||||
},
|
||||
{
|
||||
fieldName: 'chain',
|
||||
label: '是否连锁',
|
||||
component: 'VbenSelect',
|
||||
},
|
||||
{
|
||||
fieldName: 'business_suspended',
|
||||
label: '是否暂停营业',
|
||||
component: 'VbenSelect',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
94
apps/web-antd/src/views/my-gen/mall/config/table.ts
Normal file
94
apps/web-antd/src/views/my-gen/mall/config/table.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getStoreListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
// 生成的列表字段
|
||||
{ field: 'name', title: '店铺名称' },
|
||||
{ field: 'level', title: '店铺等级' },
|
||||
{ field: 'avatar', title: '店铺头像', slots: { default: 'avatar' } },
|
||||
{ field: 'introduction', title: '店铺简介' },
|
||||
// { field: 'description', title: '详细介绍' },
|
||||
{ field: 'phone', title: '商家电话号码' },
|
||||
{ field: 'person', title: '联系人名称' },
|
||||
{ field: 'id_card', title: '身份证号码' },
|
||||
{ field: 'qr_code', title: '店铺二维码' },
|
||||
{ field: 'is_featured', title: '是否为推荐店铺' },
|
||||
{
|
||||
field: 'business_license',
|
||||
title: '营业执照',
|
||||
slots: { default: 'business_license' },
|
||||
},
|
||||
{
|
||||
field: 'id_card_portrait_side',
|
||||
title: '身份证人面',
|
||||
slots: { default: 'id_card_portrait_side' },
|
||||
},
|
||||
{
|
||||
field: 'id_card_national_emblem_side',
|
||||
title: '身份证国徽面',
|
||||
slots: { default: 'id_card_national_emblem_side' },
|
||||
},
|
||||
{ field: 'chain', title: '是否连锁' },
|
||||
{ field: 'business_suspended', title: '是否暂停营业' },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getStoreListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
137
apps/web-antd/src/views/my-gen/mall/index.vue
Normal file
137
apps/web-antd/src/views/my-gen/mall/index.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteStoreApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
checkboxAll() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents,
|
||||
});
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
ids.push(row);
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteStoreApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="店铺管理管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增',
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: showModal.bind(null),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
ifShow: hasTopTableDropDownActions,
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, false),
|
||||
},
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px"> 批量操作 </Button>
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #avatar="{ row }">
|
||||
<Image :src="row.avatar" height="30" width="30" />
|
||||
</template>
|
||||
<template #business_license="{ row }">
|
||||
<Image :src="row.business_license" height="30" width="30" />
|
||||
</template>
|
||||
<template #id_card_portrait_side="{ row }">
|
||||
<Image :src="row.id_card_portrait_side" height="30" width="30" />
|
||||
</template>
|
||||
<template #id_card_national_emblem_side="{ row }">
|
||||
<Image :src="row.id_card_national_emblem_side" height="30" width="30" />
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
49
apps/web-antd/src/views/my-gen/order-detail/api/index.ts
Normal file
49
apps/web-antd/src/views/my-gen/order-detail/api/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'order-detail/';
|
||||
/**
|
||||
* 分页查询订单详情列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getOrderDetailListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单详情下拉列表
|
||||
*/
|
||||
export async function getOrderDetailOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单详情详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getOrderDetailInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增订单详情
|
||||
* @param data
|
||||
*/
|
||||
export async function createOrderDetailApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑订单详情
|
||||
* @param data
|
||||
*/
|
||||
export async function updateOrderDetailApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除订单详情
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteOrderDetailApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import { createOrderDetailApi, updateOrderDetailApi } from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'OrderDetailDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value
|
||||
? updateOrderDetailApi
|
||||
: createOrderDetailApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}订单详情`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
58
apps/web-antd/src/views/my-gen/order-detail/config/form.ts
Normal file
58
apps/web-antd/src/views/my-gen/order-detail/config/form.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'order_id',
|
||||
label: '订单ID',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品ID',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'quantity',
|
||||
label: '商品数量',
|
||||
component: 'VbenInputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'subtotal',
|
||||
label: '小计金额',
|
||||
component: 'VbenInputNumber',
|
||||
rules: '',
|
||||
},
|
||||
{
|
||||
fieldName: 'snapshot',
|
||||
label: '商品快照',
|
||||
component: 'VbenInput',
|
||||
rules: '',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
30
apps/web-antd/src/views/my-gen/order-detail/config/search.ts
Normal file
30
apps/web-antd/src/views/my-gen/order-detail/config/search.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'order_id',
|
||||
label: '订单ID',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品ID',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
73
apps/web-antd/src/views/my-gen/order-detail/config/table.ts
Normal file
73
apps/web-antd/src/views/my-gen/order-detail/config/table.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getOrderDetailListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
// 生成的列表字段
|
||||
{ field: 'order_id', title: '订单ID' },
|
||||
{ field: 'product_id', title: '商品ID' },
|
||||
{ field: 'quantity', title: '商品数量' },
|
||||
{ field: 'price', title: '商品单价' },
|
||||
{ field: 'subtotal', title: '小计金额' },
|
||||
{ field: 'snapshot', title: '商品快照' },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getOrderDetailListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
128
apps/web-antd/src/views/my-gen/order-detail/index.vue
Normal file
128
apps/web-antd/src/views/my-gen/order-detail/index.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteOrderDetailApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
checkboxAll() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents,
|
||||
});
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
ids.push(row);
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteOrderDetailApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="订单详情管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增',
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: showModal.bind(null),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
ifShow: hasTopTableDropDownActions,
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, false),
|
||||
},
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px"> 批量操作 </Button>
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #avatar="{ row }">
|
||||
<Image :src="row.avatar" height="30" width="30" />
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
49
apps/web-antd/src/views/my-gen/order/api/index.ts
Normal file
49
apps/web-antd/src/views/my-gen/order/api/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'order/';
|
||||
/**
|
||||
* 分页查询订单管理列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getOrderListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单管理下拉列表
|
||||
*/
|
||||
export async function getOrderOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单管理详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getOrderInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增订单管理
|
||||
* @param data
|
||||
*/
|
||||
export async function createOrderApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑订单管理
|
||||
* @param data
|
||||
*/
|
||||
export async function updateOrderApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除订单管理
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteOrderApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
66
apps/web-antd/src/views/my-gen/order/components/modal.vue
Normal file
66
apps/web-antd/src/views/my-gen/order/components/modal.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import { createOrderApi, updateOrderApi } from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'OrderDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value ? updateOrderApi : createOrderApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}订单管理`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
52
apps/web-antd/src/views/my-gen/order/config/form.ts
Normal file
52
apps/web-antd/src/views/my-gen/order/config/form.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'user_id',
|
||||
label: '用户ID',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'order_no',
|
||||
label: '订单编号',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'order_status',
|
||||
label: '订单状态',
|
||||
component: 'VbenSelect',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'total_amount',
|
||||
label: '订单总金额',
|
||||
component: 'VbenInputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
35
apps/web-antd/src/views/my-gen/order/config/search.ts
Normal file
35
apps/web-antd/src/views/my-gen/order/config/search.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'user_id',
|
||||
label: '用户ID',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'order_no',
|
||||
label: '订单编号',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'order_status',
|
||||
label: '订单状态',
|
||||
component: 'VbenSelect',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
71
apps/web-antd/src/views/my-gen/order/config/table.ts
Normal file
71
apps/web-antd/src/views/my-gen/order/config/table.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getOrderListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
// 生成的列表字段
|
||||
{ field: 'user_id', title: '用户ID' },
|
||||
{ field: 'order_no', title: '订单编号' },
|
||||
{ field: 'order_status', title: '订单状态' },
|
||||
{ field: 'total_amount', title: '订单总金额' },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getOrderListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
@@ -10,7 +10,7 @@ import { Button, Image, message } from 'ant-design-vue';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteStoreApi } from './api';
|
||||
import { deleteOrderApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
@@ -55,7 +55,7 @@ const hasDelete = (row: any) => {
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteStoreApi({ ids }).then(() => {
|
||||
deleteOrderApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
@@ -63,7 +63,7 @@ const hasDelete = (row: any) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="店铺管理管理">
|
||||
<Page auto-content-height title="订单管理管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
@@ -0,0 +1,55 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'product-label-relations/';
|
||||
/**
|
||||
* 分页查询商品标签关联列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getProduct_label_relationsListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品标签关联下拉列表
|
||||
*/
|
||||
export async function getProduct_label_relationsOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品标签关联详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getProduct_label_relationsInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增商品标签关联
|
||||
* @param data
|
||||
*/
|
||||
export async function createProduct_label_relationsApi(
|
||||
data: Record<string, any>,
|
||||
) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑商品标签关联
|
||||
* @param data
|
||||
*/
|
||||
export async function updateProduct_label_relationsApi(
|
||||
data: Record<string, any>,
|
||||
) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除商品标签关联
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteProduct_label_relationsApi(
|
||||
data: Record<string, any>,
|
||||
) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import {
|
||||
createProduct_label_relationsApi,
|
||||
updateProduct_label_relationsApi,
|
||||
} from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'ProductLabelRelationsDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value
|
||||
? updateProduct_label_relationsApi
|
||||
: createProduct_label_relationsApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}商品标签关联`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'label_id',
|
||||
label: '标签',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'label_id',
|
||||
label: '标签',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getProduct_label_relationsListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
// 生成的列表字段
|
||||
{ field: 'product_id', title: '商品' },
|
||||
{ field: 'label_id', title: '标签' },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getProduct_label_relationsListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
128
apps/web-antd/src/views/my-gen/product-label-relations/index.vue
Normal file
128
apps/web-antd/src/views/my-gen/product-label-relations/index.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteProduct_label_relationsApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
checkboxAll() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents,
|
||||
});
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
ids.push(row);
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteProduct_label_relationsApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="商品标签关联管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增',
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: showModal.bind(null),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
ifShow: hasTopTableDropDownActions,
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, false),
|
||||
},
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px"> 批量操作 </Button>
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #avatar="{ row }">
|
||||
<Image :src="row.avatar" height="30" width="30" />
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -9,6 +9,14 @@ export async function getProductLabelListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品标签详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getProductLabelOptionApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}option`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品标签详情
|
||||
* @param id
|
||||
|
||||
@@ -9,6 +9,13 @@ export async function getProductListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品详情
|
||||
*/
|
||||
export async function getProductOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品详情
|
||||
* @param id
|
||||
|
||||
@@ -13,7 +13,6 @@ import { modalFormProps } from '../config/form';
|
||||
defineOptions({
|
||||
name: 'ProductDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import { getProductClassificationTreeOptionApi } from '#/views/my-gen/product-classification/api';
|
||||
import { getProductLabelOptionApi } from '#/views/my-gen/product-label/api';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
@@ -25,16 +26,16 @@ export const modalFormProps: VbenFormProps = {
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'title',
|
||||
label: '商品名称',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'cover',
|
||||
label: '封面图',
|
||||
component: 'Avatar',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'title',
|
||||
label: '商品名称',
|
||||
component: 'VbenInput',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
},
|
||||
@@ -66,6 +67,28 @@ export const modalFormProps: VbenFormProps = {
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'labels',
|
||||
label: '产品标签',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getProductLabelOptionApi,
|
||||
filterOption: (input: string, option: any) => {
|
||||
// 自定义过滤逻辑,确保可以根据 name 进行搜索
|
||||
return option?.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
},
|
||||
showSearch: true,
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; name: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
mode: 'multiple',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'images',
|
||||
label: '产品图片',
|
||||
|
||||
@@ -23,21 +23,40 @@ export const gridOptions: VxeGridProps<RowType> = {
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 50 },
|
||||
// 生成的列表字段
|
||||
{ field: 'title', title: '商品名称' },
|
||||
{ field: 'mall_id', title: '商家ID' },
|
||||
{ field: 'user_id', title: '上传用户ID' },
|
||||
{ field: 'introduction', title: '简介' },
|
||||
{ field: 'cover', title: '封面图', slots: { default: 'cover' } },
|
||||
{
|
||||
field: 'title',
|
||||
title: '商品名称',
|
||||
slots: { default: 'title' },
|
||||
width: 200,
|
||||
},
|
||||
{ field: 'mall', title: '商家', slots: { default: 'mall' }, width: 250 },
|
||||
{
|
||||
field: 'user',
|
||||
title: '上传用户',
|
||||
slots: { default: 'user' },
|
||||
width: 150,
|
||||
},
|
||||
// { field: 'introduction', title: '简介' },
|
||||
{ field: 'images', title: '商品图册', slots: { default: 'images' } },
|
||||
{ field: 'description', title: '详细说明' },
|
||||
{ field: 'price', title: '价格' },
|
||||
{ field: 'classification_id', title: '产品分类' },
|
||||
// { field: 'description', title: '详细说明' },
|
||||
// { field: 'price', title: '默认价格' },
|
||||
{
|
||||
field: 'classification',
|
||||
title: '产品分类',
|
||||
slots: { default: 'classification' },
|
||||
},
|
||||
{ field: 'label', title: '产品分类', slots: { default: 'label' } },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
{
|
||||
type: 'html',
|
||||
title: '操作',
|
||||
slots: { default: 'action' },
|
||||
width: 150,
|
||||
},
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
|
||||
@@ -5,10 +5,12 @@ import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, ImagePreviewGroup, message } from 'ant-design-vue';
|
||||
import { Button, Image, ImagePreviewGroup, message, Tag } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import ProductDetail from '#/components/modal/ProductPreview.vue';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
import ProductSkuDemo from '#/views/my-gen/sku/components/product-modal.vue';
|
||||
|
||||
import { deleteProductApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
@@ -38,6 +40,14 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const [ProductSkuDemoModal, ProductSkuDemoModalApi] = useVbenModal({
|
||||
connectedComponent: ProductSkuDemo,
|
||||
});
|
||||
|
||||
const [ProductDetailModal, ProductDetailModalApi] = useVbenModal({
|
||||
connectedComponent: ProductDetail,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
@@ -48,6 +58,16 @@ const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const showSkuModal = (data = {}, isUpdate = false) => {
|
||||
ProductSkuDemoModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
ProductSkuDemoModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
@@ -60,11 +80,21 @@ const hasDelete = (row: any) => {
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
|
||||
const showProductDetailModal = (row: any) => {
|
||||
ProductDetailModalApi.setData({
|
||||
// 表单值
|
||||
values: row,
|
||||
});
|
||||
ProductDetailModalApi.open();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="产品管理">
|
||||
<FormModal />
|
||||
<ProductDetailModal />
|
||||
<ProductSkuDemoModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
@@ -93,8 +123,75 @@ const hasDelete = (row: any) => {
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #cover="{ row }">
|
||||
<Image :src="row.cover" height="30" width="30" />
|
||||
<template #title="{ row }">
|
||||
<div
|
||||
class="h-20 w-20 flex-shrink-0 overflow-hidden rounded-full"
|
||||
style="margin: 0 auto"
|
||||
>
|
||||
<Image :src="row.cover" class="h-full w-full object-cover" />
|
||||
</div>
|
||||
<Button type="link" @click="showProductDetailModal(row.id)">
|
||||
{{ row.title }}
|
||||
</Button>
|
||||
<div>
|
||||
价格:<span class="font-bold text-red-500">¥{{ row.price }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #mall="{ row }">
|
||||
<div class="items-center space-x-3">
|
||||
<div
|
||||
class="h-10 w-10 flex-shrink-0 overflow-hidden rounded-full text-left"
|
||||
style="margin: 0 auto"
|
||||
>
|
||||
<Image
|
||||
:src="row.mall.avatar"
|
||||
:alt="row.mall.name"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{{ row.mall.name }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||
ID: {{ row.mall.id }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #user="{ row }">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div
|
||||
class="h-10 w-10 flex-shrink-0 overflow-hidden rounded-full text-left"
|
||||
>
|
||||
<Image
|
||||
:src="row.user.avatar"
|
||||
:alt="row.user.nick_name"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{{ row.user.nick_name }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||
ID: {{ row.user.id }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #classification="{ row }">
|
||||
<Tag color="#7166f0">{{ row.classification.name }}</Tag>
|
||||
</template>
|
||||
<template #label="{ row }">
|
||||
<Tag
|
||||
v-for="(item, index) in row.labels_name"
|
||||
:key="index"
|
||||
class="mt-3"
|
||||
color="#7166f0"
|
||||
>
|
||||
{{ item }}
|
||||
</Tag>
|
||||
</template>
|
||||
<template #images="{ row }">
|
||||
<div class="relative inline-block">
|
||||
@@ -129,6 +226,14 @@ const hasDelete = (row: any) => {
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '新增sku',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showSkuModal.bind(null, row, false),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
|
||||
49
apps/web-antd/src/views/my-gen/sku/api/index.ts
Normal file
49
apps/web-antd/src/views/my-gen/sku/api/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'sku/';
|
||||
/**
|
||||
* 分页查询sku管理列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getSkuListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取sku管理下拉列表
|
||||
*/
|
||||
export async function getSkuOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取sku管理详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getSkuInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增sku管理
|
||||
* @param data
|
||||
*/
|
||||
export async function createSkuApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑sku管理
|
||||
* @param data
|
||||
*/
|
||||
export async function updateSkuApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除sku管理
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteSkuApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
66
apps/web-antd/src/views/my-gen/sku/components/modal.vue
Normal file
66
apps/web-antd/src/views/my-gen/sku/components/modal.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import { createSkuApi, updateSkuApi } from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'SkuDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value ? updateSkuApi : createSkuApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}sku管理`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,68 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import { createSkuApi, updateSkuApi } from '../api';
|
||||
import { productModalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'ProductSkuDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(productModalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value ? updateSkuApi : createSkuApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues({
|
||||
product_id: values.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}sku管理`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
148
apps/web-antd/src/views/my-gen/sku/config/form.ts
Normal file
148
apps/web-antd/src/views/my-gen/sku/config/form.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import { getProductOptionApi } from '#/views/my-gen/product/api';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品ID',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getProductOptionApi,
|
||||
filterOption: (input: string, option: any) => {
|
||||
// 自定义过滤逻辑,确保可以根据 name 进行搜索
|
||||
return option?.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
},
|
||||
showSearch: true,
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; title: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.title,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: 'sku名称',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'cover',
|
||||
label: 'sku图片',
|
||||
component: 'Avatar',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'inventory',
|
||||
label: '商品库存',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'price',
|
||||
label: '商品单价',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
|
||||
export const productModalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品ID',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getProductOptionApi,
|
||||
filterOption: (input: string, option: any) => {
|
||||
// 自定义过滤逻辑,确保可以根据 name 进行搜索
|
||||
return option?.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
},
|
||||
showSearch: true,
|
||||
// 菜单接口转options格式
|
||||
afterFetch: (data: { id: number; title: string }[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.title,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
},
|
||||
disabled: true,
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: 'sku名称',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'cover',
|
||||
label: 'sku图片',
|
||||
component: 'Avatar',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'inventory',
|
||||
label: '商品库存',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'price',
|
||||
label: '商品单价',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
25
apps/web-antd/src/views/my-gen/sku/config/search.ts
Normal file
25
apps/web-antd/src/views/my-gen/sku/config/search.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品ID',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
72
apps/web-antd/src/views/my-gen/sku/config/table.ts
Normal file
72
apps/web-antd/src/views/my-gen/sku/config/table.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getSkuListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
// 生成的列表字段
|
||||
{ field: 'product.title', title: '商品' },
|
||||
{ field: 'name', title: 'sku名称' },
|
||||
{ field: 'cover', title: 'sku图片', slots: { default: 'cover' } },
|
||||
{ field: 'inventory', title: '商品库存' },
|
||||
{ field: 'price', title: '商品单价' },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getSkuListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
128
apps/web-antd/src/views/my-gen/sku/index.vue
Normal file
128
apps/web-antd/src/views/my-gen/sku/index.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteSkuApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
checkboxAll() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents,
|
||||
});
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
ids.push(row);
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteSkuApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="sku管理管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增',
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: showModal.bind(null),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
ifShow: hasTopTableDropDownActions,
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, false),
|
||||
},
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px"> 批量操作 </Button>
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #cover="{ row }">
|
||||
<Image :src="row.cover" height="30" width="30" />
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -0,0 +1,49 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'user-browsing-history/';
|
||||
/**
|
||||
* 分页查询用户浏览记录列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getUserBrowsingHistoryListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户浏览记录下拉列表
|
||||
*/
|
||||
export async function getUserBrowsingHistoryOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户浏览记录详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getUserBrowsingHistoryInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户浏览记录
|
||||
* @param data
|
||||
*/
|
||||
export async function createUserBrowsingHistoryApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑用户浏览记录
|
||||
* @param data
|
||||
*/
|
||||
export async function updateUserBrowsingHistoryApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户浏览记录
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteUserBrowsingHistoryApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import {
|
||||
createUserBrowsingHistoryApi,
|
||||
updateUserBrowsingHistoryApi,
|
||||
} from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'UserBrowsingHistoryDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value
|
||||
? updateUserBrowsingHistoryApi
|
||||
: createUserBrowsingHistoryApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}用户浏览记录`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'user_id',
|
||||
label: '用户',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品id',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'user_id',
|
||||
label: '用户',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品id',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getUserBrowsingHistoryListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
// 生成的列表字段
|
||||
{ field: 'user', title: '用户', slots: { default: 'user' } },
|
||||
{ field: 'product', title: '商品', slots: { default: 'product' } },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getUserBrowsingHistoryListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
168
apps/web-antd/src/views/my-gen/user-browsing-history/index.vue
Normal file
168
apps/web-antd/src/views/my-gen/user-browsing-history/index.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteUserBrowsingHistoryApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
checkboxAll() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents,
|
||||
});
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
ids.push(row);
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteUserBrowsingHistoryApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="用户浏览记录管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增',
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: showModal.bind(null),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
ifShow: hasTopTableDropDownActions,
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, false),
|
||||
},
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px"> 批量操作 </Button>
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #user="{ row }">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div
|
||||
class="h-10 w-10 flex-shrink-0 overflow-hidden rounded-full text-left"
|
||||
>
|
||||
<Image
|
||||
:src="row.user.avatar"
|
||||
:alt="row.user.nick_name"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{{ row.user.nick_name }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||
ID: {{ row.user.id }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #product="{ row }">
|
||||
<div class="items-center space-x-3">
|
||||
<div
|
||||
class="h-20 w-20 flex-shrink-0 overflow-hidden rounded-full"
|
||||
style="margin: 0 auto"
|
||||
>
|
||||
<Image
|
||||
:src="row.product.cover"
|
||||
:alt="row.product.nick_name"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{{ row.product.title }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||
ID: {{ row.product.id }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
49
apps/web-antd/src/views/my-gen/user-collect/api/index.ts
Normal file
49
apps/web-antd/src/views/my-gen/user-collect/api/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'user-collect/';
|
||||
/**
|
||||
* 分页查询用户收藏列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getUserCollectListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收藏下拉列表
|
||||
*/
|
||||
export async function getUserCollectOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收藏详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getUserCollectInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户收藏
|
||||
* @param data
|
||||
*/
|
||||
export async function createUserCollectApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑用户收藏
|
||||
* @param data
|
||||
*/
|
||||
export async function updateUserCollectApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户收藏
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteUserCollectApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import { createUserCollectApi, updateUserCollectApi } from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'UserCollectDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value
|
||||
? updateUserCollectApi
|
||||
: createUserCollectApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}用户收藏`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
40
apps/web-antd/src/views/my-gen/user-collect/config/form.ts
Normal file
40
apps/web-antd/src/views/my-gen/user-collect/config/form.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'user_id',
|
||||
label: '用户',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品id',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
30
apps/web-antd/src/views/my-gen/user-collect/config/search.ts
Normal file
30
apps/web-antd/src/views/my-gen/user-collect/config/search.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'user_id',
|
||||
label: '用户',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'product_id',
|
||||
label: '商品id',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
69
apps/web-antd/src/views/my-gen/user-collect/config/table.ts
Normal file
69
apps/web-antd/src/views/my-gen/user-collect/config/table.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getUserCollectListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
// 生成的列表字段
|
||||
{ field: 'user', title: '用户', slots: { default: 'user' } },
|
||||
{ field: 'product', title: '商品', slots: { default: 'product' } },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getUserCollectListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
168
apps/web-antd/src/views/my-gen/user-collect/index.vue
Normal file
168
apps/web-antd/src/views/my-gen/user-collect/index.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteUserCollectApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
checkboxAll() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents,
|
||||
});
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
ids.push(row);
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteUserCollectApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="用户收藏管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增',
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: showModal.bind(null),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
ifShow: hasTopTableDropDownActions,
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, false),
|
||||
},
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px"> 批量操作 </Button>
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #user="{ row }">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div
|
||||
class="h-10 w-10 flex-shrink-0 overflow-hidden rounded-full text-left"
|
||||
>
|
||||
<Image
|
||||
:src="row.user.avatar"
|
||||
:alt="row.user.nick_name"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{{ row.user.nick_name }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||
ID: {{ row.user.id }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #product="{ row }">
|
||||
<div class="items-center space-x-3">
|
||||
<div
|
||||
class="h-20 w-20 flex-shrink-0 overflow-hidden rounded-full"
|
||||
style="margin: 0 auto"
|
||||
>
|
||||
<Image
|
||||
:src="row.product.cover"
|
||||
:alt="row.product.nick_name"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{{ row.product.title }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||
ID: {{ row.product.id }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
49
apps/web-antd/src/views/my-gen/user-membership/api/index.ts
Normal file
49
apps/web-antd/src/views/my-gen/user-membership/api/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'user-membership/';
|
||||
/**
|
||||
* 分页查询用户会员列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getUserMembershipListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户会员下拉列表
|
||||
*/
|
||||
export async function getUserMembershipOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户会员详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getUserMembershipInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户会员
|
||||
* @param data
|
||||
*/
|
||||
export async function createUserMembershipApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑用户会员
|
||||
* @param data
|
||||
*/
|
||||
export async function updateUserMembershipApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户会员
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteUserMembershipApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import { createUserMembershipApi, updateUserMembershipApi } from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'UserMembershipDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value
|
||||
? updateUserMembershipApi
|
||||
: createUserMembershipApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}用户会员`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '会员等级说明',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'experience',
|
||||
label: '升级所需经验',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '会员等级说明',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getUserMembershipListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
// 生成的列表字段
|
||||
{ field: 'name', title: '会员等级说明' },
|
||||
{ field: 'experience', title: '升级所需经验' },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getUserMembershipListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
128
apps/web-antd/src/views/my-gen/user-membership/index.vue
Normal file
128
apps/web-antd/src/views/my-gen/user-membership/index.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteUserMembershipApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
checkboxAll() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents,
|
||||
});
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
ids.push(row);
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteUserMembershipApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="用户会员管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增',
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: showModal.bind(null),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
ifShow: hasTopTableDropDownActions,
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, false),
|
||||
},
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px"> 批量操作 </Button>
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #avatar="{ row }">
|
||||
<Image :src="row.avatar" height="30" width="30" />
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -0,0 +1,49 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'user-receiving-address/';
|
||||
/**
|
||||
* 分页查询用户收货地址列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getUserReceivingAddressListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收货地址下拉列表
|
||||
*/
|
||||
export async function getUserReceivingAddressOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收货地址详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getUserReceivingAddressInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户收货地址
|
||||
* @param data
|
||||
*/
|
||||
export async function createUserReceivingAddressApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑用户收货地址
|
||||
* @param data
|
||||
*/
|
||||
export async function updateUserReceivingAddressApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户收货地址
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteUserReceivingAddressApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import {
|
||||
createUserReceivingAddressApi,
|
||||
updateUserReceivingAddressApi,
|
||||
} from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'UserReceivingAddressDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value
|
||||
? updateUserReceivingAddressApi
|
||||
: createUserReceivingAddressApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
:title="`${isUpdate === true ? '编辑' : '新增'}用户收货地址`"
|
||||
class="w-[30%]"
|
||||
>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'user_id',
|
||||
label: '用户',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'province',
|
||||
label: '省',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'city',
|
||||
label: '市',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'district',
|
||||
label: '区',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'detailed_address',
|
||||
label: '详细地址',
|
||||
component: 'Textarea',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'complete_address',
|
||||
label: '完整地址',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'user_id',
|
||||
label: '用户',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'province',
|
||||
label: '省',
|
||||
component: 'InputNumber',
|
||||
},
|
||||
{
|
||||
fieldName: 'city',
|
||||
label: '市',
|
||||
component: 'InputNumber',
|
||||
},
|
||||
{
|
||||
fieldName: 'district',
|
||||
label: '区',
|
||||
component: 'InputNumber',
|
||||
},
|
||||
{
|
||||
fieldName: 'detailed_address',
|
||||
label: '详细地址',
|
||||
component: 'Textarea',
|
||||
},
|
||||
{
|
||||
fieldName: 'complete_address',
|
||||
label: '完整地址',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getUserReceivingAddressListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
// 生成的列表字段
|
||||
{ field: 'user_id', title: '用户' },
|
||||
{ field: 'province', title: '省' },
|
||||
{ field: 'city', title: '市' },
|
||||
{ field: 'district', title: '区' },
|
||||
{ field: 'detailed_address', title: '详细地址' },
|
||||
{ field: 'complete_address', title: '完整地址' },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getUserReceivingAddressListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
128
apps/web-antd/src/views/my-gen/user-receiving-address/index.vue
Normal file
128
apps/web-antd/src/views/my-gen/user-receiving-address/index.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteUserReceivingAddressApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
checkboxAll() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents,
|
||||
});
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
ids.push(row);
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteUserReceivingAddressApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="用户收货地址管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增',
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: showModal.bind(null),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
ifShow: hasTopTableDropDownActions,
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, false),
|
||||
},
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px"> 批量操作 </Button>
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #avatar="{ row }">
|
||||
<Image :src="row.avatar" height="30" width="30" />
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
49
apps/web-antd/src/views/my-gen/user/api/index.ts
Normal file
49
apps/web-antd/src/views/my-gen/user/api/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'user/';
|
||||
/**
|
||||
* 分页查询用户列表
|
||||
* @param data
|
||||
*/
|
||||
export async function getUserListApi(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户下拉列表
|
||||
*/
|
||||
export async function getUserOptionApi() {
|
||||
return requestClient.get<any>(`${prefix}option`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户详情
|
||||
* @param id
|
||||
*/
|
||||
export async function getUserInfoApi(id: number) {
|
||||
return requestClient.get<any>(`${prefix}detail`, { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户
|
||||
* @param data
|
||||
*/
|
||||
export async function createUserApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑用户
|
||||
* @param data
|
||||
*/
|
||||
export async function updateUserApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}update`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
* @param data
|
||||
*/
|
||||
export async function deleteUserApi(data: Record<string, any>) {
|
||||
return requestClient.post<any>(`${prefix}delete`, data);
|
||||
}
|
||||
63
apps/web-antd/src/views/my-gen/user/components/modal.vue
Normal file
63
apps/web-antd/src/views/my-gen/user/components/modal.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import { createUserApi, updateUserApi } from '../api';
|
||||
import { modalFormProps } from '../config/form';
|
||||
|
||||
defineOptions({
|
||||
name: 'UserDemo',
|
||||
});
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const gridApi = ref();
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = isUpdate.value ? updateUserApi : createUserApi;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (isOpen) {
|
||||
const { values, update } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
isUpdate.value = update;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="`${isUpdate === true ? '编辑' : '新增'}用户`" class="w-[30%]">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
70
apps/web-antd/src/views/my-gen/user/config/form.ts
Normal file
70
apps/web-antd/src/views/my-gen/user/config/form.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const modalFormProps: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-12', // 24栅格,
|
||||
commonConfig: {
|
||||
formItemClass: 'col-span-12',
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// handleSubmit: onSubmit,
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenInput',
|
||||
fieldName: 'id',
|
||||
label: 'ID',
|
||||
formItemClass: 'col-span-6',
|
||||
dependencies: {
|
||||
show: false,
|
||||
triggerFields: ['id'],
|
||||
},
|
||||
},
|
||||
// 生成的表单字段
|
||||
{
|
||||
fieldName: 'nick_name',
|
||||
label: '用户昵称',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'phone',
|
||||
label: '手机号码',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'email',
|
||||
label: '邮箱',
|
||||
component: 'VbenInput',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'level',
|
||||
label: '用户会员等级',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'balance',
|
||||
label: '用户余额',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'avatar',
|
||||
label: '用户头像',
|
||||
component: 'Avatar',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'integral',
|
||||
label: '积分',
|
||||
component: 'InputNumber',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
};
|
||||
45
apps/web-antd/src/views/my-gen/user/config/search.ts
Normal file
45
apps/web-antd/src/views/my-gen/user/config/search.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
// 生成的搜索字段
|
||||
{
|
||||
fieldName: 'open_id',
|
||||
label: '平台OpenId',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'nick_name',
|
||||
label: '用户昵称',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'phone',
|
||||
label: '手机号码',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'email',
|
||||
label: '邮箱',
|
||||
component: 'VbenInput',
|
||||
},
|
||||
{
|
||||
fieldName: 'level',
|
||||
label: '用户会员等级',
|
||||
component: 'InputNumber',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
80
apps/web-antd/src/views/my-gen/user/config/table.ts
Normal file
80
apps/web-antd/src/views/my-gen/user/config/table.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getUserListApi } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
introduce: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
// 生成的列表字段
|
||||
{ field: 'avatar', title: '用户信息', slots: { default: 'avatar' } },
|
||||
{
|
||||
field: 'open_id',
|
||||
title: '手机号码&邮箱&平台OpenId',
|
||||
slots: { default: 'open_id' },
|
||||
width: 250,
|
||||
},
|
||||
// { field: 'phone', title: '手机号码' },
|
||||
// { field: 'email', title: '邮箱' },
|
||||
{ field: 'level', title: '用户会员等级' },
|
||||
{ field: 'balance', title: '用户余额' },
|
||||
{ field: 'integral', title: '积分' },
|
||||
// { field: 'wx_open_id', title: '微信小程序唯一标识' },
|
||||
// { field: 'zfb_open_id', title: '支付宝小程序唯一标识' },
|
||||
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'updated_at', title: '编辑时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getUserListApi({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
177
apps/web-antd/src/views/my-gen/user/index.vue
Normal file
177
apps/web-antd/src/views/my-gen/user/index.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { Button, Image, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
|
||||
import { deleteUserApi } from './api';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
const gridEvents: VxeGridListeners<any> = {
|
||||
checkboxChange() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
checkboxAll() {
|
||||
const records = gridApi.grid.getCheckboxRecords();
|
||||
hasTopTableDropDownActions.value = records.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents,
|
||||
});
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
});
|
||||
|
||||
const showModal = (data = {}, isUpdate = false) => {
|
||||
formModalApi.setData({
|
||||
// 表单值
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
|
||||
const hasDelete = (row: any) => {
|
||||
let ids = [];
|
||||
if (row) {
|
||||
ids.push(row);
|
||||
} else {
|
||||
ids = gridApi.grid.getCheckboxRecords().map((item) => item.id);
|
||||
}
|
||||
deleteUserApi({ ids }).then(() => {
|
||||
message.success('删除成功!');
|
||||
gridApi.reload();
|
||||
});
|
||||
};
|
||||
const source = ref('Hello');
|
||||
const { copy } = useClipboard({ legacy: true, source });
|
||||
const copyText = (string: string) => {
|
||||
copy(string);
|
||||
message.success('复制成功!');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="用户管理">
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增',
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: showModal.bind(null),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
ifShow: hasTopTableDropDownActions,
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, false),
|
||||
},
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px"> 批量操作 </Button>
|
||||
</template>
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #avatar="{ row }">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div
|
||||
class="h-10 w-10 flex-shrink-0 overflow-hidden rounded-full text-left"
|
||||
>
|
||||
<Image
|
||||
:src="row.avatar"
|
||||
:alt="row.nick_name"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{{ row.nick_name }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||
ID: {{ row.id }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <Image :src="row.avatar" height="30" width="30" />-->
|
||||
</template>
|
||||
<template #open_id="{ row }">
|
||||
<Button
|
||||
type="link"
|
||||
@click="copyText(row.phone)"
|
||||
class="text-sm font-medium"
|
||||
>
|
||||
{{ row.phone }}
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
@click="copyText(row.email)"
|
||||
class="text-sm font-medium"
|
||||
>
|
||||
{{ row.email }}
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
@click="copyText(row.open_id)"
|
||||
class="text-sm font-medium"
|
||||
>
|
||||
{{ row.open_id }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'link',
|
||||
icon: 'uil:edit',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
onClick: showModal.bind(null, row, true),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
icon: 'ant-design:delete-outlined',
|
||||
size: 'small',
|
||||
// auth: ['admin', 'sys:role:detail'],
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: hasDelete.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -5,7 +5,7 @@ export default defineConfig(async () => {
|
||||
application: {},
|
||||
vite: {
|
||||
server: {
|
||||
port: 18866,
|
||||
port: 18_866,
|
||||
proxy: {
|
||||
'/api': {
|
||||
changeOrigin: true,
|
||||
|
||||
Reference in New Issue
Block a user