Files
guilixu-admin/src/views/shop/shopGift/components/makeCard.vue
2026-05-31 12:09:14 +08:00

389 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
title="生成礼品卡"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="礼品劵" name="name">
<a-input
allow-clear
placeholder="请输入礼品卡名称"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="关联商品" name="goodsId">
<a-select
v-model:value="form.goodsId"
placeholder="请选择关联商品"
show-search
:filter-option="false"
:loading="goodsLoading"
@search="searchGoods"
@change="onGoodsChange"
@dropdown-visible-change="onDropdownVisibleChange"
>
<a-select-option
v-for="goods in goodsList"
:key="goods.goodsId"
:value="goods.goodsId"
>
<div class="goods-option">
<span>{{ goods.name }}</span>
<a-tag color="blue" style="margin-left: 8px"
>¥{{ goods.price || 0 }}</a-tag
>
</div>
</a-select-option>
<a-select-option v-if="goodsList.length === 0" disabled>
<div style="text-align: center; color: #999">
{{ goodsLoading ? '加载中...' : '暂无商品数据' }}
</div>
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="生成数量" name="num">
<a-input-number v-model:value="form.num" :min="0" />
</a-form-item>
<a-form-item label="使用地址" name="useLocation">
<a-input
placeholder="请输入使用的门店地址"
v-model:value="form.useLocation"
/>
</a-form-item>
<a-form-item label="备注信息" name="comments">
<a-textarea
v-model:value="form.comments"
placeholder="请输入备注信息"
:rows="3"
:maxlength="200"
show-count
/>
</a-form-item>
</a-form>
<!-- 礼品卡预览 -->
<div class="gift-card-preview" v-if="form.name">
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600">礼品卡预览</span>
</a-divider>
<div class="gift-card">
<div class="gift-card-header">
<div class="gift-card-title">{{ form.name }}</div>
<div class="gift-card-status">
<a-tag>
<span v-if="form.takeTime"
>领取时间{{ formatTime(form.takeTime) }}</span
>
<span v-else>未领取</span>
</a-tag>
</div>
</div>
<div class="gift-card-body">
<div class="gift-card-code">
<span class="code-label text-gray-50">卡密</span>
<span class="code-value">{{ form.code || '自动生成' }}</span>
</div>
<div class="gift-card-goods" v-if="selectedGoods">
<span class="goods-label text-gray-50">关联商品</span>
<span class="goods-name">{{ selectedGoods.name }}</span>
<a-tag color="blue" style="margin-left: 8px"
>¥{{ selectedGoods.price }}</a-tag
>
</div>
<div class="gift-card-goods py-2" v-if="selectedGoods">
<span class="goods-label">使用地址</span>
<span class="goods-name">{{ form.useLocation }}</span>
</div>
</div>
<div class="gift-card-footer">
<div class="gift-card-info text-gray-50">
备注: {{ form.comments }}
</div>
</div>
</div>
</div>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { makeShopGift } from '@/api/shop/shopGift';
import { ShopGift } from '@/api/shop/shopGift/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import { listShopGoods } from '@/api/shop/shopGoods';
import { ShopGoods } from '@/api/shop/shopGoods/model';
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
// 商品列表
const goodsList = ref<ShopGoods[]>([]);
// 商品加载状态
const goodsLoading = ref(false);
// 选中的商品
const selectedGoods = ref<ShopGoods | null>(null);
const rules = reactive({
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
goodsId: [{ required: true, message: '请选择商品', trigger: 'change' }],
num: [{ required: true, message: '请输入数量', trigger: 'blur' }]
});
// 用户信息
const form = reactive<ShopGift>({
id: undefined,
name: undefined,
code: undefined,
goodsId: undefined,
takeTime: undefined,
operatorUserId: undefined,
isShow: undefined,
status: undefined,
useLocation: undefined,
comments: undefined,
sortNumber: undefined,
userId: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
updateTime: undefined,
num: 1000
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const getGoodsList = async () => {
goodsList.value = await listShopGoods();
};
getGoodsList();
/* 搜索商品 */
const searchGoods = async (value: string) => {
if (value && value.trim()) {
goodsLoading.value = true;
try {
const res = await listShopGoods({ keywords: value.trim() });
goodsList.value = res || [];
console.log('搜索到的商品:', goodsList.value);
} catch (e) {
console.error('搜索商品失败:', e);
goodsList.value = [];
} finally {
goodsLoading.value = false;
}
}
};
/* 下拉框显示状态改变 */
const onDropdownVisibleChange = (open: boolean) => {
if (open && goodsList.value.length === 0) {
// 当下拉框打开且没有数据时,加载默认商品列表
getGoodsList();
}
};
/* 商品选择改变 */
const onGoodsChange = (goodsId: number) => {
selectedGoods.value =
goodsList.value.find((goods) => goods.goodsId === goodsId) || null;
console.log('选中的商品:', selectedGoods.value);
};
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
makeShopGift(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(() => props.visible, { immediate: true });
</script>
<style lang="less" scoped>
.goods-option,
.status-option {
display: flex;
align-items: center;
justify-content: space-between;
.ant-tag {
margin-left: 8px;
}
span {
color: #666;
font-size: 12px;
}
}
.gift-card-preview {
margin-top: 24px;
.gift-card {
background: linear-gradient(
135deg,
#667eea 0%,
#764ba2 50%,
#f093fb 100%
);
border-radius: 12px;
padding: 20px;
color: #333;
position: relative;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
&::before {
content: '';
position: absolute;
top: -50px;
right: -50px;
width: 100px;
height: 100px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
}
.gift-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.gift-card-title {
font-size: 20px;
font-weight: bold;
color: #f3f3f3;
}
}
.gift-card-body {
margin-bottom: 16px;
.gift-card-code {
margin-bottom: 12px;
.code-label {
font-weight: 600;
color: #f3f3f3;
}
.code-value {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
background: rgba(255, 255, 255, 0.8);
padding: 4px 8px;
border-radius: 4px;
margin-left: 8px;
}
}
.gift-card-goods {
.goods-label {
font-weight: 600;
color: #f3f3f3;
}
.goods-name {
margin-left: 8px;
color: #f3f3f3;
}
}
}
.gift-card-footer {
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.3);
.gift-card-info {
font-size: 12px;
color: #f3f3f3;
}
}
}
}
:deep(.ant-divider-horizontal.ant-divider-with-text-left) {
margin: 24px 0 16px 0;
.ant-divider-inner-text {
padding: 0 16px 0 0;
}
}
:deep(.ant-form-item) {
margin-bottom: 16px;
}
:deep(.ant-select-selection-item) {
display: flex;
align-items: center;
}
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-alert) {
.ant-alert-message {
font-weight: 600;
}
}
</style>