Merge remote-tracking branch 'yudao/dev' into dev

This commit is contained in:
jason
2025-12-01 20:47:51 +08:00
41 changed files with 451 additions and 261 deletions

View File

@@ -21,6 +21,7 @@ export namespace MallCombinationActivityApi {
limitDuration?: number; // 限制时长
combinationPrice?: number; // 拼团价格
products: CombinationProduct[]; // 商品列表
picUrl?: any;
}
/** 拼团活动所需属性 */

View File

@@ -31,6 +31,7 @@ export namespace MallSeckillActivityApi {
totalStock?: number; // 秒杀总库存
seckillPrice?: number; // 秒杀价格
products?: SeckillProduct[]; // 秒杀商品列表
picUrl?: any;
}
}

View File

@@ -45,7 +45,7 @@ const bpmnInstances = () => (window as any)?.bpmnInstances;
const generateStandardId = (type: string): string => {
const prefix = type === 'message' ? 'Message_' : 'Signal_';
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 6).toUpperCase();
const random = Math.random().toString(36).slice(2, 6).toUpperCase();
return `${prefix}${timestamp}_${random}`;
};

View File

@@ -153,14 +153,14 @@ watch(
.header-editor {
.header-list {
max-height: 400px;
overflow-y: auto;
margin-bottom: 16px;
overflow-y: auto;
}
.header-item {
display: flex;
align-items: center;
gap: 8px;
align-items: center;
margin-bottom: 12px;
.header-key {
@@ -168,8 +168,8 @@ watch(
}
.separator {
color: #606266;
font-weight: 500;
color: #606266;
}
.header-value {

View File

@@ -1,3 +1,4 @@
<!-- eslint-disable prettier/prettier -->
<script lang="ts" setup>
import { inject, nextTick, onBeforeUnmount, ref, toRaw, watch } from 'vue';
@@ -203,7 +204,7 @@ const updateHttpExtensions = (force = false) => {
? String(!!rawValue)
: (rawValue === undefined
? ''
: String(rawValue));
: rawValue.toString());
desiredEntries.push([name, persisted]);
});

View File

@@ -13,7 +13,7 @@ import { useGridColumns, useGridFormSchema } from './data';
defineOptions({ name: 'BpmManagerTask' });
/** 查看历史 */
function handleHistory(row: BpmTaskApi.TaskManager) {
function handleHistory(row: BpmTaskApi.Task) {
router.push({
name: 'BpmProcessInstanceDetail',
query: {

View File

@@ -2,8 +2,8 @@ import type { Ref } from 'vue';
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeGridProps, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MallSpuApi } from '#/api/mall/product/spu';
import type { MallCategoryApi } from '#/api/mall/product/category';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { computed } from 'vue';

View File

@@ -133,6 +133,13 @@ export function useFormSchema(): VbenFormSchema[] {
placeholder: '请输入最大砍价金额',
},
},
// TODO @puhui999这里交互不太对可以对比下 element-plus 版本呢
{
fieldName: 'spuId',
label: '砍价商品',
component: 'Input',
rules: 'required',
},
];
}

View File

@@ -14,6 +14,7 @@ import {
updateBargainActivity,
} from '#/api/mall/promotion/bargain/bargainActivity';
import { $t } from '#/locales';
import { SpuShowcase } from '#/views/mall/product/spu/components';
import { useFormSchema } from '../data';
@@ -21,7 +22,7 @@ defineOptions({ name: 'PromotionBargainActivityForm' });
const emit = defineEmits(['success']);
const formData = ref<MallBargainActivityApi.BargainActivity>();
const formData = ref<Partial<MallBargainActivityApi.BargainActivity>>({});
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['砍价活动'])
@@ -49,8 +50,11 @@ const [Modal, modalApi] = useVbenModal({
}
modalApi.lock();
// 提交表单
const data =
(await formApi.getValues()) as MallBargainActivityApi.BargainActivity;
const values = await formApi.getValues();
const data = {
...values,
spuId: formData.value.spuId,
} as MallBargainActivityApi.BargainActivity;
try {
await (formData.value?.id
? updateBargainActivity(data)
@@ -65,7 +69,7 @@ const [Modal, modalApi] = useVbenModal({
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
formData.value = {};
return;
}
// 加载数据
@@ -86,8 +90,12 @@ const [Modal, modalApi] = useVbenModal({
</script>
<template>
<!-- TODO @puhui999这里缺少商品的选择 -->
<Modal class="w-2/5" :title="getTitle">
<Form class="mx-4" />
<Form class="mx-4">
<!-- 自定义插槽商品选择 -->
<template #spuId>
<SpuShowcase v-model="formData.spuId" :limit="1" />
</template>
</Form>
</Modal>
</template>

View File

@@ -105,11 +105,12 @@ export function useFormSchema(): VbenFormSchema[] {
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
},
},
// TODO @puhui999这里交互不太对可以对比下 element-plus 版本呢
{
// TODO <!-- TODO @puhui999这里缺少商品的选择 -->
fieldName: 'spuId',
label: '拼团商品',
component: 'Input',
rules: 'required',
},
];
}

View File

@@ -13,13 +13,16 @@ import {
updateCombinationActivity,
} from '#/api/mall/promotion/combination/combinationActivity';
import { $t } from '#/locales';
import { SpuShowcase } from '#/views/mall/product/spu/components';
import { useFormSchema } from '../data';
defineOptions({ name: 'CombinationActivityForm' });
const emit = defineEmits(['success']);
const formData = ref<MallCombinationActivityApi.CombinationActivity>();
const formData = ref<Partial<MallCombinationActivityApi.CombinationActivity>>(
{},
);
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['拼团活动'])
@@ -47,8 +50,11 @@ const [Modal, modalApi] = useVbenModal({
}
modalApi.lock();
// 提交表单
const data =
(await formApi.getValues()) as MallCombinationActivityApi.CombinationActivity;
const values = await formApi.getValues();
const data = {
...values,
spuId: formData.value.spuId,
} as MallCombinationActivityApi.CombinationActivity;
try {
await (formData.value?.id
? updateCombinationActivity(data)
@@ -63,7 +69,7 @@ const [Modal, modalApi] = useVbenModal({
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
formData.value = {};
return;
}
// 加载数据
@@ -86,6 +92,11 @@ const [Modal, modalApi] = useVbenModal({
<template>
<Modal class="w-3/5" :title="getTitle">
<Form />
<Form>
<!-- 自定义插槽商品选择 -->
<template #spuId>
<SpuShowcase v-model="formData.spuId" :limit="1" />
</template>
</Form>
</Modal>
</template>

View File

@@ -111,7 +111,6 @@ function emitActivityChange() {
>
<Tooltip :title="activity.name">
<div class="relative h-full w-full">
<!-- TODO @芋艿 -->
<Image
:src="activity.picUrl"
class="h-full w-full rounded-lg object-cover"

View File

@@ -133,9 +133,8 @@ function handleSliderChange(prop: string) {
</TabPane>
<!-- 每个组件的通用内容 -->
<!-- TODO @xingyu装修这里的样式貌似没 ele 版本的好看 -->
<TabPane tab="样式" key="style" force-render>
<p class="text-lg font-bold">组件样式</p>
<div class="mb-2 bg-gray-100 p-2 text-sm">组件样式</div>
<div class="flex flex-col gap-2 rounded-md p-4 shadow-lg">
<Form :model="formData">
<FormItem
@@ -181,7 +180,7 @@ function handleSliderChange(prop: string) {
class="mb-0 w-full"
>
<Row>
<Col :span="11">
<Col :span="19">
<Slider
v-model:value="
formData[dataRef.prop as keyof ComponentStyle]
@@ -192,8 +191,9 @@ function handleSliderChange(prop: string) {
class="mr-4"
/>
</Col>
<Col :span="2">
<Col :span="4">
<InputNumber
class="w-[50px]"
:max="100"
:min="0"
v-model:value="

View File

@@ -98,7 +98,7 @@ const handleDeleteComponent = () => {
<component :is="component.id" :property="component.property" />
</div>
<div
class="component-wrap absolute -bottom-1 -left-0.5 -right-0.5 -top-1 block h-full w-full"
class="component-wrap absolute -bottom-1 -left-0.5 -right-0.5 block h-full w-full"
>
<!-- 左侧组件名悬浮的小贴条 -->
<div class="component-name" v-if="component.name">
@@ -109,9 +109,6 @@ const handleDeleteComponent = () => {
class="component-toolbar"
v-if="showToolbar && component.name && active"
>
<!-- TODO @xingyu装修按钮少的时候会存在遮住的情况 -->
<!-- TODO @xingyu装修貌似中间的选中框框没全部框柱上面多了点下面少了点 -->
<!-- TODO @xingyu装修从左侧的组件拖拽到中间时没有这个组件的小卡片预览ele 版本是有的 -->
<VerticalButtonGroup size="small">
<Button
:disabled="!canMoveUp"
@@ -172,7 +169,6 @@ const handleDeleteComponent = () => {
</div>
</div>
</template>
<style scoped lang="scss">
$active-border-width: 2px;
$hover-border-width: 1px;

View File

@@ -100,5 +100,4 @@ function handleCloneComponent(component: DiyComponent<any>) {
</Collapse.Panel>
</Collapse>
</div>
<!-- TODO @xingyu装修ele 里面有一些 style看看是不是都迁移完了特别是 drag-area 是全局样式 -->
</template>

View File

@@ -106,7 +106,6 @@ watch(
</div>
</Carousel>
</template>
<style lang="scss">
// Ant Design Vue Carousel 样式调整
:deep(.ant-carousel .ant-carousel-dots) {

View File

@@ -14,7 +14,7 @@ defineProps<{ property: UserCardProperty }>();
<div class="flex flex-col">
<div class="flex items-center justify-between px-4 py-6">
<div class="flex flex-1 items-center gap-4">
<Avatar :size="60">
<Avatar :size="60" class="flex items-center">
<IconifyIcon icon="ep:avatar" :size="60" />
</Avatar>
<span class="text-[18px] font-bold">芋道源码</span>

View File

@@ -175,8 +175,7 @@ function handleComponentSelected(
index: number = -1,
) {
// 使用深拷贝避免响应式追踪循环警告
// TODO @xingyu【装修】这个是必须的么ele 没有哈。
selectedComponent.value = cloneDeep(component);
selectedComponent.value = component;
selectedComponentIndex.value = index;
}
@@ -344,7 +343,7 @@ onMounted(() => {
<!-- 中心设计区域ComponentContainer -->
<Col :span="12">
<div
class="relative flex max-h-[calc(80vh)] w-full flex-1 flex-col justify-center overflow-y-auto"
class="editor-center relative flex max-h-[calc(80vh)] w-full flex-1 flex-col overflow-y-auto"
@click="handlePageSelected"
>
<!-- 手机顶部 -->
@@ -378,20 +377,20 @@ onMounted(() => {
</div>
<!-- 手机页面编辑区域 -->
<div
class="min-h-full w-full"
class="mx-auto min-h-full w-96 bg-no-repeat"
:style="{
// backgroundColor: pageConfigComponent.property.backgroundColor,
backgroundImage: `url(${pageConfigComponent.property.backgroundImage})`,
}"
>
<div
class="relative mx-auto my-0 min-h-full w-96 items-center justify-center bg-auto bg-no-repeat"
class="relative my-0 min-h-full w-full items-center justify-center bg-auto bg-no-repeat"
>
<draggable
v-model="pageComponents"
:animation="200"
:force-fallback="false"
class="min-h-full w-full"
class="min-h-[70vh] w-full"
filter=".component-toolbar"
ghost-class="draggable-ghost"
group="component"
@@ -508,5 +507,4 @@ onMounted(() => {
</div>
</PreviewModal>
</Page>
<!-- TODO @xingyu装修这里改造完后类似 web-ele/src/views/mall/promotion/components/diy-editor/index.vue 里的全局样式递推到子组件里的就没没了类似 property-group -->
</template>

View File

@@ -1,7 +1,6 @@
<script setup lang="ts">
import { Space } from 'ant-design-vue';
// TODO @xingyu【装修】貌似上下移动的按钮被遮住了
/**
* 垂直按钮组
* Ant Design Vue 的按钮组,通过 Space 实现垂直布局

View File

@@ -60,14 +60,10 @@ export function useFormSchema(): VbenFormSchema[] {
rules: 'required',
defaultValue: PromotionProductScopeEnum.ALL.scope,
},
// TODO @puhui999 商品选择器优化
{
fieldName: 'productSpuIds',
label: '商品',
component: 'Input',
componentProps: {
placeholder: '请选择商品',
},
dependencies: {
triggerFields: ['productScope', 'productScopeValues'],
show: (model) =>
@@ -84,14 +80,10 @@ export function useFormSchema(): VbenFormSchema[] {
},
rules: 'required',
},
// TODO @puhui999 商品分类选择器优化
{
fieldName: 'productCategoryIds',
label: '商品分类',
component: 'Input',
componentProps: {
placeholder: '请选择商品分类',
},
dependencies: {
triggerFields: ['productScope', 'productScopeValues'],
show: (model) =>

View File

@@ -16,11 +16,18 @@ import {
updateCouponTemplate,
} from '#/api/mall/promotion/coupon/couponTemplate';
import { $t } from '#/locales';
import { ProductCategorySelect } from '#/views/mall/product/category/components';
import { SpuShowcase } from '#/views/mall/product/spu/components';
import { useFormSchema } from '../data';
const emit = defineEmits(['success']);
const formData = ref<MallCouponTemplateApi.CouponTemplate>();
const formData = ref<
Partial<MallCouponTemplateApi.CouponTemplate> & {
productCategoryIds?: number | number[];
productSpuIds?: number[];
}
>({});
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['优惠券模板'])
@@ -64,7 +71,7 @@ const [Modal, modalApi] = useVbenModal({
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
formData.value = {};
return;
}
// 加载数据
@@ -75,7 +82,7 @@ const [Modal, modalApi] = useVbenModal({
modalApi.lock();
try {
formData.value = await getCouponTemplate(data.id);
const processedData = await processLoadData(formData.value);
const processedData = await processLoadData(formData.value as any);
// 设置到表单
await formApi.setValues(processedData);
} finally {
@@ -144,6 +151,15 @@ async function processLoadData(
<template>
<Modal :title="getTitle" class="w-2/5">
<Form class="mx-4" />
<Form class="mx-4">
<!-- 自定义插槽商品选择 -->
<template #productSpuIds>
<SpuShowcase v-model="formData.productSpuIds" />
</template>
<!-- 自定义插槽分类选择 -->
<template #productCategoryIds>
<ProductCategorySelect v-model="formData.productCategoryIds" />
</template>
</Form>
</Modal>
</template>

View File

@@ -67,8 +67,15 @@ export function useFormSchema(): VbenFormSchema[] {
placeholder: '请输入备注',
rows: 4,
},
formItemClass: 'col-span-2',
},
{
fieldName: 'spuIds',
label: '活动商品',
component: 'Input',
rules: 'required',
formItemClass: 'col-span-2',
},
// TODO @puhui999少了商品选择~
];
}

View File

@@ -13,13 +13,18 @@ import {
updateDiscountActivity,
} from '#/api/mall/promotion/discount/discountActivity';
import { $t } from '#/locales';
import { SpuShowcase } from '#/views/mall/product/spu/components';
import { useFormSchema } from '../data';
defineOptions({ name: 'DiscountActivityForm' });
const emit = defineEmits(['success']);
const formData = ref<MallDiscountActivityApi.DiscountActivity>();
const formData = ref<
Partial<MallDiscountActivityApi.DiscountActivity> & {
spuIds?: number[];
}
>({});
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['限时折扣活动'])
@@ -69,7 +74,7 @@ const [Modal, modalApi] = useVbenModal({
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
formData.value = {};
return;
}
// 加载数据
@@ -91,6 +96,11 @@ const [Modal, modalApi] = useVbenModal({
<template>
<Modal class="w-3/5" :title="getTitle">
<Form />
<Form>
<!-- 自定义插槽商品选择 -->
<template #spuIds>
<SpuShowcase v-model="formData.spuIds" />
</template>
</Form>
</Modal>
</template>

View File

@@ -20,7 +20,6 @@ import {
} from '#/api/mall/promotion/diy/template';
import { DiyEditor, PAGE_LIBS } from '#/views/mall/promotion/components';
// TODO @xingyu【装修】左上角的“基础设施”、“首页”、“我的”切换时中间的编辑器内容没有正确切换。可对比 ele 版本的效果!
/** 装修模板表单 */
defineOptions({ name: 'DiyTemplateDecorate' });
@@ -28,34 +27,28 @@ const route = useRoute();
const { refreshTab } = useTabs();
const domain = import.meta.env.VITE_MALL_H5_DOMAIN;
// 特殊:存储 reset 重置时,当前 selectedTemplateItem 值,从而进行恢复
const DIY_PAGE_INDEX_KEY = 'diy_page_index';
const DIY_PAGE_INDEX_KEY = 'diy_page_index'; // 特殊:存储 reset 重置时,当前 selectedTemplateItem 值,从而进行恢复
const selectedTemplateItem = ref(0);
// 左上角工具栏操作按钮
const templateItems = ref([
{ key: 0, name: '基础设置', icon: 'lucide:settings' },
{ key: 1, name: '首页', icon: 'lucide:home' },
{ key: 2, name: '我的', icon: 'lucide:user' },
]);
]); // 左上角工具栏操作按钮
const formData = ref<MallDiyTemplateApi.DiyTemplateProperty>();
// 当前编辑的属性
const currentFormData = ref<
MallDiyPageApi.DiyPage | MallDiyTemplateApi.DiyTemplateProperty
>({
property: '',
} as MallDiyPageApi.DiyPage);
// templateItem 对应的缓存
} as MallDiyPageApi.DiyPage); // 当前编辑的属性
const currentFormDataMap = ref<
Map<string, MallDiyPageApi.DiyPage | MallDiyTemplateApi.DiyTemplateProperty>
>(new Map());
// 商城 H5 预览地址
const previewUrl = ref('');
// 模板组件库
const templateLibs = [] as DiyComponentLibrary[];
// 当前组件库
const libs = ref<DiyComponentLibrary[]>(templateLibs);
>(new Map()); // templateItem 对应的缓存
const previewUrl = ref(''); // 商城 H5 预览地址
const templateLibs = [] as DiyComponentLibrary[]; // 模板组件库
const libs = ref<DiyComponentLibrary[]>(templateLibs); // 当前组件库
/** 获取详情 */
async function getPageDetail(id: any) {
@@ -74,23 +67,23 @@ async function getPageDetail(id: any) {
}
/** 模板选项切换 */
function handleTemplateItemChange(val: any) {
const changeValue = val.target.value;
function handleTemplateItemChange(valObj: any) {
const val = valObj.target.value;
// 缓存模版编辑数据
currentFormDataMap.value.set(
templateItems.value[changeValue]!.name,
templateItems.value[selectedTemplateItem.value]?.name || '',
currentFormData.value!,
);
// 切换模版
selectedTemplateItem.value = changeValue;
// 读取模版缓存
const data = currentFormDataMap.value.get(
templateItems.value[changeValue]!.name,
templateItems.value[val]?.name || '',
);
// 切换模版
selectedTemplateItem.value = val;
// 情况一:编辑模板
if (changeValue === 0) {
if (val === 0) {
libs.value = templateLibs;
currentFormData.value = (isEmpty(data) ? formData.value : data) as
| MallDiyPageApi.DiyPage
@@ -104,7 +97,7 @@ function handleTemplateItemChange(val: any) {
isEmpty(data)
? formData.value!.pages.find(
(page: MallDiyPageApi.DiyPage) =>
page.name === templateItems.value[changeValue]!.name,
page.name === templateItems.value[val]?.name,
)
: data
) as MallDiyPageApi.DiyPage | MallDiyTemplateApi.DiyTemplateProperty;

View File

@@ -133,10 +133,6 @@ export function useFormSchema(): VbenFormSchema[] {
component: 'Input',
rules: 'required',
formItemClass: 'col-span-2',
// TODO @puhui999貌似 renderComponentContent 不需要哇?
renderComponentContent: () => ({
default: () => null,
}),
},
];
}

View File

@@ -42,7 +42,6 @@ function handleEdit(row: MallRewardActivityApi.RewardActivity) {
/** 关闭满减送活动 */
async function handleClose(row: MallRewardActivityApi.RewardActivity) {
// TODO @puhui999这个国际化需要加下哈closing、closeSuccess
const hideLoading = message.loading({
content: $t('ui.actionMessage.closing', [row.name]),
duration: 0,

View File

@@ -29,10 +29,10 @@ const emit = defineEmits(['success']);
const formData = ref<Partial<MallRewardActivityApi.RewardActivity>>({
conditionType: PromotionConditionTypeEnum.PRICE.type,
productScope: PromotionProductScopeEnum.ALL.scope,
rules: [],
});
// TODO @puhui999点击“编辑”后会出现 Cannot read properties of null (reading 'type') 报错;
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['满减送'])

View File

@@ -8,12 +8,14 @@ import { useVbenModal } from '@vben/common-ui';
import { Button, message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { getSpu } from '#/api/mall/product/spu';
import {
createSeckillActivity,
getSeckillActivity,
updateSeckillActivity,
} from '#/api/mall/promotion/seckill/seckillActivity';
import { $t } from '#/locales';
import { SpuSkuSelect } from '#/views/mall/product/spu/components';
import { useFormSchema } from '../data';
@@ -31,25 +33,37 @@ const spuId = ref<number>();
const spuName = ref<string>('');
const skuTableData = ref<any[]>([]);
// 选择商品(占位函数,实际需要对接商品选择组件)
const spuSkuSelectRef = ref(); // 商品选择弹窗 Ref
/** 打开商品选择弹窗 */
const handleSelectProduct = () => {
message.info('商品选择功能需要对接商品选择组件');
// TODO: 打开商品选择弹窗
// 实际使用时需要:
// 1. 打开商品选择弹窗
// 2. 选择商品后调用以下逻辑设置数据:
// spuId.value = selectedSpu.id;
// spuName.value = selectedSpu.name;
// skuTableData.value = selectedSkus.map(sku => ({
// skuId: sku.id,
// skuName: sku.name || '',
// picUrl: sku.picUrl || selectedSpu.picUrl || '',
// price: sku.price || 0,
// stock: 0,
// seckillPrice: 0,
// }));
spuSkuSelectRef.value?.open();
};
/** 选择商品后的回调 */
async function handleSpuSelected(selectedSpuId: number, skuIds?: number[]) {
const spu = await getSpu(selectedSpuId);
if (!spu) return;
spuId.value = spu.id;
spuName.value = spu.name || '';
// 筛选指定的 SKU
const selectedSkus = skuIds
? spu.skus?.filter((sku) => skuIds.includes(sku.id!))
: spu.skus;
skuTableData.value =
selectedSkus?.map((sku) => ({
skuId: sku.id!,
skuName: sku.name || '',
picUrl: sku.picUrl || spu.picUrl || '',
price: sku.price || 0,
stock: 0,
seckillPrice: 0,
})) || [];
}
// ================= end =================
const [Form, formApi] = useVbenForm({
@@ -137,10 +151,30 @@ const [Modal, modalApi] = useVbenModal({
await nextTick();
await formApi.setValues(formData.value);
// TODO: 加载商品和 SKU 信息
// 需要调用商品 API 获取 SPU 详情
// spuId.value = formData.value.spuId;
// await loadProductDetails(formData.value.spuId, formData.value.products);
// 加载商品和 SKU 信息
if (formData.value.spuId) {
const spu = await getSpu(formData.value.spuId);
if (spu) {
spuId.value = spu.id;
spuName.value = spu.name || '';
// 回填 SKU 配置
const products = formData.value.products || [];
skuTableData.value =
spu.skus
?.filter((sku) => products.some((p) => p.skuId === sku.id))
.map((sku) => {
const product = products.find((p) => p.skuId === sku.id);
return {
skuId: sku.id!,
skuName: sku.name || '',
picUrl: sku.picUrl || spu.picUrl || '',
price: sku.price || 0,
stock: product?.stock || 0,
seckillPrice: (product?.seckillPrice || 0) / 100, // 分转元
};
}) || [];
}
}
} finally {
modalApi.unlock();
}
@@ -154,7 +188,6 @@ const [Modal, modalApi] = useVbenModal({
<Form />
<!-- 商品选择区域 -->
<!-- TODO @puhui999这里缺少商品的选择 -->
<div class="mt-4">
<div class="mb-2 flex items-center">
<span class="text-sm font-medium">秒杀活动商品:</span>
@@ -218,4 +251,11 @@ const [Modal, modalApi] = useVbenModal({
</div>
</div>
</Modal>
<!-- 商品选择器弹窗 -->
<SpuSkuSelect
ref="spuSkuSelectRef"
:is-select-sku="true"
@select="handleSpuSelected"
/>
</template>

View File

@@ -133,7 +133,6 @@ function emitActivityChange() {
class="flex h-[60px] w-[60px] cursor-pointer items-center justify-center rounded-lg border border-dashed border-gray-300 hover:border-blue-400"
@click="handleOpenActivitySelect"
>
<!-- TODO @芋艿等待和 /Users/yunai/Java/yudao-ui-admin-vben-v5/apps/web-antd/src/views/mall/product/spu/components/spu-showcase.vue 进一步统一 -->
<IconifyIcon icon="lucide:plus" class="text-xl text-gray-400" />
</div>
</Tooltip>

View File

@@ -14,7 +14,7 @@ defineOptions({ name: 'TabNews' });
const props = defineProps<{
modelValue: Reply;
newsType: NewsType;
newsType?: NewsType;
}>();
const emit = defineEmits<{

View File

@@ -4,13 +4,13 @@ import type { MpMaterialApi } from '#/api/mp/material';
import { watch } from 'vue';
import { $t } from '@vben/locales';
import { openWindow } from '@vben/utils';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { WxVideoPlayer } from '#/views/mp/components';
import { useVideoGridColumns } from './data';
import {$t} from '@vben/locales';
const props = defineProps<{
list: MpMaterialApi.Material[];