708 lines
19 KiB
Vue
708 lines
19 KiB
Vue
<!-- 编辑弹窗 -->
|
||
<template>
|
||
<ele-modal
|
||
width="65%"
|
||
:visible="visible"
|
||
:maskClosable="false"
|
||
:maxable="maxable"
|
||
:title="isUpdate ? '礼品卡详情' : '礼品卡详情'"
|
||
:body-style="{ paddingBottom: '28px' }"
|
||
@update:visible="updateVisible"
|
||
@ok="save"
|
||
>
|
||
<a-form
|
||
ref="formRef"
|
||
:model="form"
|
||
:rules="rules"
|
||
:label-col="{ span: 6 }"
|
||
:wrapper-col="{ span: 18 }"
|
||
>
|
||
<!-- 基本信息 -->
|
||
<a-divider orientation="left">
|
||
<span style="color: #1890ff; font-weight: 600;">基本信息</span>
|
||
</a-divider>
|
||
|
||
<a-row :gutter="16">
|
||
<a-col :span="12">
|
||
<a-form-item label="礼品卡名称" name="name">
|
||
<a-input
|
||
placeholder="请输入礼品卡名称"
|
||
:disabled="isUpdate"
|
||
v-model:value="form.name"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="12">
|
||
<a-form-item label="礼品卡密钥" name="code">
|
||
<a-input
|
||
placeholder="请输入礼品卡密钥"
|
||
v-model:value="form.code"
|
||
:disabled="isUpdate"
|
||
>
|
||
<template #suffix>
|
||
<a-button v-if="!isUpdate" type="link" size="small" @click="generateCode">
|
||
生成
|
||
</a-button>
|
||
</template>
|
||
</a-input>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<a-row :gutter="16">
|
||
<a-col :span="12">
|
||
<a-form-item label="关联商品" name="goodsId">
|
||
<a-select
|
||
v-model:value="form.goodsId"
|
||
placeholder="请选择关联商品"
|
||
show-search
|
||
:filter-option="false"
|
||
:loading="goodsLoading"
|
||
@search="searchGoods"
|
||
:disabled="isUpdate"
|
||
@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-col>
|
||
<a-col :span="12" v-if="!isUpdate">
|
||
<a-form-item label="生成数量" name="num">
|
||
<a-input-number
|
||
:min="1"
|
||
:max="1000"
|
||
placeholder="请输入生成数量"
|
||
v-model:value="form.num"
|
||
style="width: 100%"
|
||
>
|
||
<template #addonAfter>张</template>
|
||
</a-input-number>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="12">
|
||
<a-form-item label="使用地址" name="useLocation">
|
||
<a-input
|
||
placeholder="请输入使用的门店地址"
|
||
v-model:value="form.useLocation"
|
||
:disabled="isUpdate"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="12">
|
||
<a-form-item label="备注信息" name="comments">
|
||
<a-textarea
|
||
v-model:value="form.comments"
|
||
:disabled="isUpdate"
|
||
placeholder="请输入备注信息"
|
||
:rows="3"
|
||
:maxlength="200"
|
||
show-count
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<!-- 状态设置 -->
|
||
<!-- <a-divider orientation="left">-->
|
||
<!-- <span style="color: #1890ff; font-weight: 600;">状态设置</span>-->
|
||
<!-- </a-divider>-->
|
||
|
||
<!-- <a-row :gutter="16">-->
|
||
<!-- <a-col :span="8">-->
|
||
<!-- <a-form-item label="上架状态" name="status">-->
|
||
<!-- <a-select v-model:value="form.status" placeholder="请选择上架状态">-->
|
||
<!-- <a-select-option :value="0">-->
|
||
<!-- <div class="status-option">-->
|
||
<!-- <a-tag color="success">已上架</a-tag>-->
|
||
<!-- <span>正常销售</span>-->
|
||
<!-- </div>-->
|
||
<!-- </a-select-option>-->
|
||
<!-- <a-select-option :value="1">-->
|
||
<!-- <div class="status-option">-->
|
||
<!-- <a-tag color="warning">待上架</a-tag>-->
|
||
<!-- <span>准备上架</span>-->
|
||
<!-- </div>-->
|
||
<!-- </a-select-option>-->
|
||
<!-- <a-select-option :value="2">-->
|
||
<!-- <div class="status-option">-->
|
||
<!-- <a-tag color="processing">待审核</a-tag>-->
|
||
<!-- <span>等待审核</span>-->
|
||
<!-- </div>-->
|
||
<!-- </a-select-option>-->
|
||
<!-- <a-select-option :value="3">-->
|
||
<!-- <div class="status-option">-->
|
||
<!-- <a-tag color="error">审核不通过</a-tag>-->
|
||
<!-- <span>审核失败</span>-->
|
||
<!-- </div>-->
|
||
<!-- </a-select-option>-->
|
||
<!-- </a-select>-->
|
||
<!-- </a-form-item>-->
|
||
<!-- </a-col>-->
|
||
<!-- <a-col :span="8">-->
|
||
<!-- <a-form-item label="展示状态" name="isShow">-->
|
||
<!-- <a-switch-->
|
||
<!-- v-model:checked="form.isShow"-->
|
||
<!-- checked-children="展示"-->
|
||
<!-- un-checked-children="隐藏"-->
|
||
<!-- />-->
|
||
<!-- </a-form-item>-->
|
||
<!-- </a-col>-->
|
||
<!-- <a-col :span="8">-->
|
||
<!-- <a-form-item label="排序" name="sortNumber">-->
|
||
<!-- <a-input-number-->
|
||
<!-- :min="0"-->
|
||
<!-- placeholder="数字越小越靠前"-->
|
||
<!-- v-model:value="form.sortNumber"-->
|
||
<!-- style="width: 100%"-->
|
||
<!-- />-->
|
||
<!-- </a-form-item>-->
|
||
<!-- </a-col>-->
|
||
<!-- </a-row>-->
|
||
|
||
<!-- 使用信息 -->
|
||
<a-divider orientation="left">
|
||
<span style="color: #1890ff; font-weight: 600;">使用信息</span>
|
||
</a-divider>
|
||
|
||
<a-row :gutter="16">
|
||
<a-col :span="12">
|
||
<a-form-item label="领取时间" name="takeTime">
|
||
<a-date-picker
|
||
v-model:value="form.takeTime"
|
||
placeholder="请选择领取时间"
|
||
:disabled="isUpdate"
|
||
show-time
|
||
style="width: 100%"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="12">
|
||
<a-form-item label="领取用户ID" name="userId">
|
||
<a-input-number
|
||
:min="1"
|
||
placeholder="请输入领取用户ID"
|
||
v-model:value="form.userId"
|
||
:disabled="isUpdate"
|
||
style="width: 100%"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="12">
|
||
<a-form-item label="操作人ID" name="operatorUserId">
|
||
<a-input-number
|
||
:min="1"
|
||
placeholder="请输入操作人用户ID"
|
||
v-model:value="form.operatorUserId"
|
||
:disabled="isUpdate"
|
||
style="width: 300px"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="12">
|
||
<a-form-item label="操作员备注" name="userId">
|
||
<a-textarea
|
||
v-model:value="form.operatorRemarks"
|
||
:disabled="isUpdate"
|
||
placeholder="请输入备注信息"
|
||
:rows="3"
|
||
:maxlength="200"
|
||
show-count
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
|
||
|
||
|
||
<!-- 礼品卡预览 -->
|
||
<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>
|
||
</a-form>
|
||
</ele-modal>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import {ref, reactive, watch} from 'vue';
|
||
import {Form, message} from 'ant-design-vue';
|
||
import {assignObject} from 'ele-admin-pro';
|
||
import {addShopGift, updateShopGift} from '@/api/shop/shopGift';
|
||
import {ShopGift} from '@/api/shop/shopGift/model';
|
||
import {FormInstance} from 'ant-design-vue/es/form';
|
||
import {listShopGoods} from '@/api/shop/shopGoods';
|
||
import {ShopGoods} from '@/api/shop/shopGoods/model';
|
||
import dayjs from 'dayjs';
|
||
|
||
// 是否是修改
|
||
const isUpdate = ref(false);
|
||
const useForm = Form.useForm;
|
||
|
||
const props = defineProps<{
|
||
// 弹窗是否打开
|
||
visible: boolean;
|
||
// 修改回显的数据
|
||
data?: ShopGift | null;
|
||
}>();
|
||
|
||
const emit = defineEmits<{
|
||
(e: 'done'): void;
|
||
(e: 'update:visible', visible: boolean): void;
|
||
}>();
|
||
|
||
// 提交状态
|
||
const loading = ref(false);
|
||
// 是否显示最大化切换按钮
|
||
const maxable = ref(true);
|
||
// 表格选中数据
|
||
const formRef = ref<FormInstance | null>(null);
|
||
|
||
// 表单数据
|
||
const form = reactive<ShopGift>({
|
||
id: undefined,
|
||
name: '',
|
||
code: '',
|
||
goodsId: undefined,
|
||
takeTime: undefined,
|
||
operatorUserId: undefined,
|
||
operatorUserName: undefined,
|
||
operatorRemarks: undefined,
|
||
isShow: true,
|
||
status: 0,
|
||
useLocation: '',
|
||
comments: '',
|
||
sortNumber: 100,
|
||
userId: undefined,
|
||
deleted: 0,
|
||
tenantId: undefined,
|
||
createTime: undefined,
|
||
updateTime: undefined,
|
||
num: 1000
|
||
});
|
||
|
||
// 商品列表
|
||
const goodsList = ref<ShopGoods[]>([]);
|
||
// 商品加载状态
|
||
const goodsLoading = ref(false);
|
||
// 选中的商品
|
||
const selectedGoods = ref<ShopGoods | null>(null);
|
||
|
||
/* 更新visible */
|
||
const updateVisible = (value: boolean) => {
|
||
emit('update:visible', value);
|
||
};
|
||
|
||
// 表单验证规则
|
||
const rules = reactive({
|
||
name: [
|
||
{
|
||
required: true,
|
||
message: '请输入礼品卡名称',
|
||
trigger: 'blur'
|
||
},
|
||
{
|
||
min: 2,
|
||
max: 50,
|
||
message: '礼品卡名称长度应在2-50个字符之间',
|
||
trigger: 'blur'
|
||
}
|
||
],
|
||
code: [
|
||
{
|
||
required: true,
|
||
message: '请输入礼品卡密钥',
|
||
trigger: 'blur'
|
||
},
|
||
{
|
||
min: 6,
|
||
max: 32,
|
||
message: '密钥长度应在6-32个字符之间',
|
||
trigger: 'blur'
|
||
}
|
||
],
|
||
goodsId: [
|
||
{
|
||
required: true,
|
||
message: '请选择关联商品',
|
||
trigger: 'change'
|
||
}
|
||
],
|
||
num: [
|
||
{
|
||
required: true,
|
||
message: '请输入生成数量',
|
||
trigger: 'blur'
|
||
},
|
||
{
|
||
validator: (rule: any, value: any) => {
|
||
if (value && (value < 1 || value > 1000)) {
|
||
return Promise.reject('生成数量必须在1-1000之间');
|
||
}
|
||
return Promise.resolve();
|
||
},
|
||
trigger: 'blur'
|
||
}
|
||
],
|
||
status: [
|
||
{
|
||
required: true,
|
||
message: '请选择上架状态',
|
||
trigger: 'change'
|
||
}
|
||
]
|
||
});
|
||
|
||
/* 生成密钥 */
|
||
const generateCode = () => {
|
||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||
let result = '';
|
||
for (let i = 0; i < 8; i++) {
|
||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||
}
|
||
form.code = result;
|
||
};
|
||
|
||
/* 搜索商品 */
|
||
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 getStatusColor = () => {
|
||
const colorMap = {
|
||
0: 'success',
|
||
1: 'warning',
|
||
2: 'processing',
|
||
3: 'error'
|
||
};
|
||
return colorMap[form.status] || 'default';
|
||
};
|
||
|
||
/* 获取状态文本 */
|
||
const getStatusText = () => {
|
||
const textMap = {
|
||
0: '已上架',
|
||
1: '待上架',
|
||
2: '待审核',
|
||
3: '审核不通过'
|
||
};
|
||
return textMap[form.status] || '未知状态';
|
||
};
|
||
|
||
/* 格式化时间 */
|
||
const formatTime = (time: any) => {
|
||
if (!time) return '';
|
||
return dayjs(time).format('YYYY-MM-DD HH:mm:ss');
|
||
};
|
||
|
||
const {resetFields} = useForm(form, rules);
|
||
|
||
/* 保存编辑 */
|
||
const save = () => {
|
||
if (!formRef.value) {
|
||
return;
|
||
}
|
||
formRef.value
|
||
.validate()
|
||
.then(() => {
|
||
loading.value = true;
|
||
const formData = {
|
||
...form
|
||
};
|
||
|
||
// 处理时间字段转换
|
||
if (formData.takeTime && dayjs.isDayjs(formData.takeTime)) {
|
||
formData.takeTime = formData.takeTime.format('YYYY-MM-DD HH:mm:ss');
|
||
}
|
||
|
||
// 处理数据类型转换
|
||
if (formData.isShow !== undefined) {
|
||
formData.isShow = formData.isShow === '1' || formData.isShow === true;
|
||
}
|
||
|
||
console.log('提交的礼品卡数据:', formData);
|
||
|
||
const saveOrUpdate = isUpdate.value ? updateShopGift : addShopGift;
|
||
saveOrUpdate(formData)
|
||
.then((msg) => {
|
||
loading.value = false;
|
||
message.success(msg);
|
||
updateVisible(false);
|
||
emit('done');
|
||
})
|
||
.catch((e) => {
|
||
loading.value = false;
|
||
message.error(e.message);
|
||
console.error('保存失败:', e);
|
||
});
|
||
})
|
||
.catch((errors) => {
|
||
console.error('表单验证失败:', errors);
|
||
});
|
||
};
|
||
|
||
/* 获取商品列表 */
|
||
const getGoodsList = async () => {
|
||
if (goodsLoading.value) return; // 防止重复加载
|
||
|
||
goodsLoading.value = true;
|
||
try {
|
||
const res = await listShopGoods({pageSize: 50}); // 限制返回数量
|
||
goodsList.value = res || [];
|
||
console.log('获取到的商品列表:', goodsList.value);
|
||
} catch (e) {
|
||
console.error('获取商品列表失败:', e);
|
||
goodsList.value = [];
|
||
} finally {
|
||
goodsLoading.value = false;
|
||
}
|
||
};
|
||
|
||
watch(
|
||
() => props.visible,
|
||
async (visible) => {
|
||
if (visible) {
|
||
await getGoodsList();
|
||
|
||
if (props.data) {
|
||
assignObject(form, props.data);
|
||
|
||
// 处理时间字段转换
|
||
if (props.data.takeTime) {
|
||
form.takeTime = dayjs(props.data.takeTime);
|
||
}
|
||
|
||
// 设置选中的商品
|
||
if (props.data.goodsId) {
|
||
selectedGoods.value = goodsList.value.find(goods => goods.goodsId === props.data.goodsId) || null;
|
||
}
|
||
|
||
isUpdate.value = true;
|
||
} else {
|
||
// 重置为默认值
|
||
Object.assign(form, {
|
||
id: undefined,
|
||
name: '',
|
||
code: '',
|
||
goodsId: undefined,
|
||
takeTime: undefined,
|
||
operatorUserId: undefined,
|
||
isShow: true,
|
||
status: 0,
|
||
comments: '',
|
||
sortNumber: 100,
|
||
userId: undefined,
|
||
deleted: 0,
|
||
tenantId: undefined,
|
||
createTime: undefined,
|
||
updateTime: undefined,
|
||
num: 1000
|
||
});
|
||
|
||
selectedGoods.value = null;
|
||
isUpdate.value = false;
|
||
}
|
||
} else {
|
||
resetFields();
|
||
}
|
||
},
|
||
{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>
|