!193 修复批量删除后 checkedIds 未重置的问题

Merge pull request !193 from puhui999/dev
pull/201/head
芋道源码 2025-08-09 07:15:42 +00:00 committed by Gitee
commit 9cac5a2937
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
74 changed files with 917 additions and 505 deletions

View File

@ -91,6 +91,7 @@ async function handleBatchDelete() {
});
try {
await deletePurchaseOrderList(checkedIds.value);
checkedIds.value = [];
message.success({
content: $t('ui.actionMessage.deleteSuccess'),
key: 'action_process_msg',

View File

@ -102,6 +102,7 @@ async function handleDeleteBatch() {
});
try {
await deleteCodegenTableList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -84,6 +84,7 @@ async function handleDeleteBatch() {
});
try {
await deleteConfigList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -66,6 +66,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo01ContactList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -76,6 +76,7 @@ async function onDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -75,6 +75,7 @@ async function onDeleteBatch() {
});
try {
await deleteDemo03CourseList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -75,6 +75,7 @@ async function onDeleteBatch() {
});
try {
await deleteDemo03GradeList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -75,6 +75,7 @@ async function onDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -78,6 +78,7 @@ async function onDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -123,6 +123,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo01ContactList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -134,6 +134,7 @@ async function onDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -81,6 +81,7 @@ async function onDeleteBatch() {
});
try {
await deleteDemo03CourseList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -81,6 +81,7 @@ async function onDeleteBatch() {
});
try {
await deleteDemo03GradeList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -130,6 +130,7 @@ async function onDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -124,6 +124,7 @@ async function onDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -84,6 +84,7 @@ async function handleDeleteBatch() {
});
try {
await deleteFileList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -118,6 +118,7 @@ async function handleDeleteBatch() {
});
try {
await deleteFileConfigList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -130,6 +130,7 @@ async function handleDeleteBatch() {
});
try {
await deleteJobList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -93,6 +93,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDeptList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -90,6 +90,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDictDataList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -88,6 +88,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDictTypeList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -76,6 +76,7 @@ async function handleDeleteBatch() {
});
try {
await deleteMailAccountList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -88,6 +88,7 @@ async function handleDeleteBatch() {
});
try {
await deleteMailTemplateList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -77,6 +77,7 @@ async function handleDeleteBatch() {
});
try {
await deleteNoticeList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -94,6 +94,7 @@ async function handleDeleteBatch() {
});
try {
await deleteNotifyTemplateList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -83,6 +83,7 @@ async function handleDeleteBatch() {
});
try {
await deletePostList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -96,6 +96,7 @@ async function handleDeleteBatch() {
});
try {
await deleteRoleList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -87,6 +87,7 @@ async function handleDeleteBatch() {
});
try {
await deleteSmsChannelList(checkedIds.value);
checkedIds.value = [];
message.success({
content: $t('ui.actionMessage.deleteSuccess', ['短信渠道']),
key: 'action_key_msg',

View File

@ -94,6 +94,7 @@ async function handleDeleteBatch() {
});
try {
await deleteSmsTemplateList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -95,6 +95,7 @@ async function handleDeleteBatch() {
});
try {
await deleteTenantList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -76,6 +76,7 @@ async function handleDeleteBatch() {
});
try {
await deleteTenantPackageList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -118,6 +118,7 @@ async function handleDeleteBatch() {
});
try {
await deleteUserList(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -90,6 +90,7 @@ async function onDelete(row: InfraCodegenApi.CodegenTable) {
async function onDeleteBatch() {
await confirm('确定要批量删除该代码生成配置吗?');
await deleteCodegenTableList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -69,6 +69,7 @@ async function onDelete(row: InfraConfigApi.Config) {
async function onDeleteBatch() {
await confirm('确定要批量删除该参数吗?');
await deleteConfigList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -57,6 +57,7 @@ async function onDelete(row: InfraDataSourceConfigApi.DataSourceConfig) {
async function onDeleteBatch() {
await confirm('确定要批量删除该数据源吗?');
await deleteDataSourceConfigList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -64,6 +64,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo01ContactList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -70,6 +70,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -69,6 +69,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo03CourseList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -69,6 +69,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo03GradeList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -69,6 +69,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -64,6 +64,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
} finally {

View File

@ -121,6 +121,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo01ContactList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -133,6 +133,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -77,6 +77,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo03CourseList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -77,6 +77,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo03GradeList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -129,6 +129,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -122,6 +122,7 @@ async function handleDeleteBatch() {
});
try {
await deleteDemo03StudentList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {

View File

@ -79,6 +79,7 @@ async function onDelete(row: InfraFileApi.File) {
async function onDeleteBatch() {
await confirm('确定要批量删除该文件吗?');
await deleteFileList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -103,6 +103,7 @@ async function onDelete(row: InfraFileConfigApi.FileConfig) {
async function onDeleteBatch() {
await confirm('确定要批量删除该文件配置吗?');
await deleteFileConfigList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -122,6 +122,7 @@ async function onDelete(row: InfraJobApi.Job) {
async function onDeleteBatch() {
await confirm('确定要批量删除该任务吗?');
await deleteJobList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -1,6 +1,7 @@
import type { MallSpuApi } from '#/api/mall/product/spu';
import type { PropertyAndValues } from './model';
import type { MallSpuApi } from '#/api/mall/product/spu';
/**
* -
*

View File

@ -1,14 +1,16 @@
<script lang="ts" setup>
import { useVbenForm } from '#/adapter/form';
import * as ExpressTemplateApi from '#/api/mall/trade/delivery/expressTemplate';
import { watch } from 'vue';
import { ElMessage } from 'element-plus';
import { DICT_TYPE, getIntDictOptions, DeliveryTypeEnum } from '#/utils';
import { useVbenForm } from '#/adapter/form';
const props = defineProps<{
propFormData: Object;
}>();
const emit = defineEmits(['update:activeName']);
/** 将传进来的值赋值给 formData */
watch(
() => props.propFormData,
@ -20,7 +22,6 @@ watch(
},
);
const emit = defineEmits(['update:activeName']);
const validate = async () => {
const { valid } = await formApi.validate();
if (!valid) {
@ -29,10 +30,10 @@ const validate = async () => {
try {
//
Object.assign(props.propFormData, formApi.getValues());
} catch (e) {
} catch (error) {
ElMessage.error('【商品详情】不完善,请填写相关信息');
emit('update:activeName', 'description');
throw e; //
throw error; //
}
};
defineExpose({ validate });

View File

@ -1,5 +1,3 @@
import SkuList from './SkuList.vue';
interface PropertyAndValues {
id: number;
name: string;
@ -22,4 +20,6 @@ interface RuleConfig {
message: string;
}
export { SkuList, PropertyAndValues, RuleConfig, getPropertyList };
export { getPropertyList, PropertyAndValues, RuleConfig };
export { default as SkuList } from './SkuList.vue';

View File

@ -1,14 +1,16 @@
<script lang="ts" setup>
import { useVbenForm } from '#/adapter/form';
import * as ExpressTemplateApi from '#/api/mall/trade/delivery/expressTemplate';
import { watch } from 'vue';
import { ElMessage } from 'element-plus';
import { DICT_TYPE, getIntDictOptions, DeliveryTypeEnum } from '#/utils';
import { useVbenForm } from '#/adapter/form';
const props = defineProps<{
propFormData: Object;
}>();
const emit = defineEmits(['update:activeName']);
/** 将传进来的值赋值给 formData */
watch(
() => props.propFormData,
@ -20,7 +22,6 @@ watch(
},
);
const emit = defineEmits(['update:activeName']);
const validate = async () => {
const { valid } = await formApi.validate();
if (!valid) {
@ -29,10 +30,10 @@ const validate = async () => {
try {
//
Object.assign(props.propFormData, formApi.getValues());
} catch (e) {
} catch (error) {
ElMessage.error('【其它设置】不完善,请填写相关信息');
emit('update:activeName', 'other');
throw e; //
throw error; //
}
};
defineExpose({ validate });

View File

@ -1,4 +1,149 @@
<!-- 商品发布 - 库存价格 - 属性列表 -->
<script lang="ts" setup>
import type { PropType } from 'vue';
import type { PropertyAndValues } from './model';
import type { MallPropertyApi } from '#/api/mall/product/property';
import { computed, ref, watch } from 'vue';
import {
ElButton,
ElCol,
ElDivider,
ElMessage,
ElSpace,
ElTag,
ElText,
} from 'element-plus';
import * as PropertyApi from '#/api/mall/product/property';
import { $t } from '#/locales';
defineOptions({ name: 'ProductAttributes' });
//
const props = defineProps({
propertyList: {
type: Array as PropType<PropertyAndValues[]>,
default: () => [],
},
}); /** 输入框失去焦点或点击回车时触发 */
const emit = defineEmits(['success']);
const inputValue = ref(''); //
const attributeIndex = ref<null | number>(null); // index
//
const inputVisible = computed(() => (index: number) => {
if (attributeIndex.value === null) return false;
if (attributeIndex.value === index) return true;
});
const inputRef = ref<any[]>([]); // Ref
/** 解决 ref 在 v-for 中的获取问题*/
const setInputRef = (el: any) => {
if (el === null || el === undefined) return;
// id
if (
!inputRef.value.some(
(item) => item.inputRef?.attributes.id === el.inputRef?.attributes.id,
)
) {
inputRef.value.push(el);
}
};
const attributeList = ref<PropertyAndValues[]>([]); //
const attributeOptions = ref([] as MallPropertyApi.PropertyValue[]);
watch(
() => props.propertyList,
(data) => {
if (!data) return;
attributeList.value = data as any;
},
{
deep: true,
immediate: true,
},
);
/** 删除属性值*/
const handleCloseValue = (index: number, valueIndex: number) => {
if (index < attributeList.value.length) {
const values = attributeList.value[index]!.values as any[];
values.splice(valueIndex, 1);
}
};
/** 删除属性*/
const handleCloseProperty = (index: number) => {
if (index < attributeList.value.length) {
attributeList.value.splice(index, 1);
emit('success', attributeList.value);
}
};
/** 显示输入框并获取焦点 */
const showInput = async (index: number) => {
if (index < attributeList.value.length) {
attributeIndex.value = index;
inputRef.value[index].focus();
//
await getAttributeOptions(attributeList.value[index]!.id);
}
};
// success
const handleInputConfirm = async (index: number, propertyId: number) => {
if (inputValue.value && index < attributeList.value.length) {
// 1.
const values = attributeList.value[index]!.values as any[];
if (values.find((item) => item.name === inputValue.value)) {
ElMessage.warning('已存在相同属性值,请重试');
attributeIndex.value = null;
inputValue.value = '';
return;
}
// 2.1 使
const existValue = attributeOptions.value.find(
(item) => item.name === inputValue.value,
);
if (existValue) {
attributeIndex.value = null;
inputValue.value = '';
const values = attributeList.value[index]!.values as any[];
values.push({
id: existValue.id!,
name: existValue.name,
});
emit('success', attributeList.value);
return;
}
// 2.2
try {
const id = await PropertyApi.createPropertyValue({
propertyId,
name: inputValue.value,
});
const values = attributeList.value[index]!.values as any[];
values.push({ id, name: inputValue.value });
ElMessage.success($t('common.createSuccess'));
emit('success', attributeList.value);
} catch {
ElMessage.error('添加失败,请重试');
}
}
attributeIndex.value = null;
inputValue.value = '';
};
/** 获取商品属性下拉选项 */
const getAttributeOptions = async (propertyId: number) => {
attributeOptions.value =
await PropertyApi.getPropertyValueSimpleList(propertyId);
};
</script>
<template>
<ElCol v-for="(item, index) in attributeList" :key="index">
<div>
@ -53,144 +198,3 @@
<ElDivider class="my-10px" />
</ElCol>
</template>
<script lang="ts" setup>
import { ref, watch, computed } from 'vue';
import type { PropType } from 'vue';
import * as PropertyApi from '#/api/mall/product/property';
import type { MallPropertyApi } from '#/api/mall/product/property';
import {
ElMessage,
ElCol,
ElTag,
ElText,
ElButton,
ElDivider,
ElSpace,
} from 'element-plus';
import { $t } from '#/locales';
import type { PropertyAndValues } from './model';
defineOptions({ name: 'ProductAttributes' });
const inputValue = ref(''); //
const attributeIndex = ref<number | null>(null); // index
//
const inputVisible = computed(() => (index: number) => {
if (attributeIndex.value === null) return false;
if (attributeIndex.value === index) return true;
});
const inputRef = ref<any[]>([]); //Ref
/** 解决 ref 在 v-for 中的获取问题*/
const setInputRef = (el: any) => {
if (el === null || typeof el === 'undefined') return;
// id
if (
!inputRef.value.some(
(item) => item.inputRef?.attributes.id === el.inputRef?.attributes.id,
)
) {
inputRef.value.push(el);
}
};
const attributeList = ref<PropertyAndValues[]>([]); //
const attributeOptions = ref([] as MallPropertyApi.PropertyValue[]); //
const props = defineProps({
propertyList: {
type: Array as PropType<PropertyAndValues[]>,
default: () => [],
},
});
watch(
() => props.propertyList,
(data) => {
if (!data) return;
attributeList.value = data as any;
},
{
deep: true,
immediate: true,
},
);
/** 删除属性值*/
const handleCloseValue = (index: number, valueIndex: number) => {
if (index < attributeList.value.length) {
const values = attributeList.value[index]!.values as any[];
values.splice(valueIndex, 1);
}
};
/** 删除属性*/
const handleCloseProperty = (index: number) => {
if (index < attributeList.value.length) {
attributeList.value.splice(index, 1);
emit('success', attributeList.value);
}
};
/** 显示输入框并获取焦点 */
const showInput = async (index: number) => {
if (index < attributeList.value.length) {
attributeIndex.value = index;
inputRef.value[index].focus();
//
await getAttributeOptions(attributeList.value[index]!.id);
}
};
/** 输入框失去焦点或点击回车时触发 */
const emit = defineEmits(['success']); // success
const handleInputConfirm = async (index: number, propertyId: number) => {
if (inputValue.value && index < attributeList.value.length) {
// 1.
const values = attributeList.value[index]!.values as any[];
if (values.find((item) => item.name === inputValue.value)) {
ElMessage.warning('已存在相同属性值,请重试');
attributeIndex.value = null;
inputValue.value = '';
return;
}
// 2.1 使
const existValue = attributeOptions.value.find(
(item) => item.name === inputValue.value,
);
if (existValue) {
attributeIndex.value = null;
inputValue.value = '';
const values = attributeList.value[index]!.values as any[];
values.push({
id: existValue.id!,
name: existValue.name,
});
emit('success', attributeList.value);
return;
}
// 2.2
try {
const id = await PropertyApi.createPropertyValue({
propertyId,
name: inputValue.value,
});
const values = attributeList.value[index]!.values as any[];
values.push({ id, name: inputValue.value });
ElMessage.success($t('common.createSuccess'));
emit('success', attributeList.value);
} catch {
ElMessage.error('添加失败,请重试');
}
}
attributeIndex.value = null;
inputValue.value = '';
};
/** 获取商品属性下拉选项 */
const getAttributeOptions = async (propertyId: number) => {
attributeOptions.value =
await PropertyApi.getPropertyValueSimpleList(propertyId);
};
</script>

View File

@ -1,26 +1,25 @@
<script lang="ts" setup>
import { ref, watch } from 'vue';
import type { PropType } from 'vue';
import type { MallPropertyApi } from '#/api/mall/product/property';
import { ref, watch } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { ElMessage } from 'element-plus';
import { useVbenForm } from '#/adapter/form';
import { $t } from '#/locales';
import { getPropertySimpleList } from '#/api/mall/product/property';
import * as PropertyApi from '#/api/mall/product/property';
import type { MallPropertyApi } from '#/api/mall/product/property';
import { $t } from '#/locales';
// Propertyvalues
interface ExtendedProperty extends MallPropertyApi.Property {
values?: any[];
}
const emit = defineEmits(['success']);
const attributeList = ref<ExtendedProperty[]>([]); //
const attributeOptions = ref([] as MallPropertyApi.Property[]); //
//
const props = defineProps({
propertyList: {
@ -29,6 +28,10 @@ const props = defineProps({
},
});
const emit = defineEmits(['success']);
const attributeList = ref<ExtendedProperty[]>([]); //
const attributeOptions = ref([] as MallPropertyApi.Property[]);
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {

View File

@ -1,17 +1,23 @@
<script lang="ts" setup>
import { useVbenForm } from '#/adapter/form';
import { watch, ref } from 'vue';
import { ElMessage, ElSpace } from 'element-plus';
import SkuList from './sku-list.vue';
import { ref, watch } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import ProductPropertyAddForm from './product-property-add-form.vue';
import { ElMessage, ElSpace } from 'element-plus';
import { useVbenForm } from '#/adapter/form';
import { getPropertyList } from './data';
import ProductAttributes from './product-attributes.vue';
import ProductPropertyAddForm from './product-property-add-form.vue';
import SkuList from './sku-list.vue';
const props = defineProps<{
propFormData: Object;
}>();
const emit = defineEmits(['update:activeName']);
interface PropertyAndValues {
id: number;
name: string;
@ -71,7 +77,6 @@ watch(
},
);
const emit = defineEmits(['update:activeName']);
const validate = async () => {
const { valid } = await formApi.validate();
if (!valid) {
@ -80,10 +85,10 @@ const validate = async () => {
try {
//
Object.assign(props.propFormData, formApi.getValues());
} catch (e) {
} catch (error) {
ElMessage.error('【库存价格】不完善,请填写相关信息');
emit('update:activeName', 'sku');
throw e; //
throw error; //
}
};
defineExpose({ validate });
@ -214,9 +219,9 @@ watch(
</template>
<template #specTypeItem>
<ElSpace direction="vertical" alignment="flex-start">
<ElButton type="primary" @click="productPropertyAddFormApi.open()"
>添加属性</ElButton
>
<ElButton type="primary" @click="productPropertyAddFormApi.open()">
添加属性
</ElButton>
<ProductAttributes
:property-list="propertyList"
@success="generateSkus"
@ -238,6 +243,6 @@ watch(
/>
</template>
</Form>
<ProductPropertyAddFormModal :propertyList="propertyList" />
<ProductPropertyAddFormModal :property-list="propertyList" />
</Page>
</template>

View File

@ -1,6 +1,636 @@
<script lang="ts" setup>
import type { PropType } from 'vue';
import type { PropertyAndValues, RuleConfig } from './model';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { ref, watch } from 'vue';
import { formatToFraction, isEmpty } from '@vben/utils';
import { ElInput, ElMessage, ElTable } from 'element-plus';
import UploadImg from '#/components/upload/image-upload.vue';
import { copyValueToTarget } from '#/utils';
defineOptions({ name: 'SkuList' });
const props = defineProps({
propFormData: {
type: Object as PropType<MallSpuApi.Spu>,
default: () => ({}),
},
propertyList: {
type: Array as PropType<PropertyAndValues[]>,
default: () => [],
},
ruleConfig: {
type: Array as PropType<RuleConfig[]>,
default: () => [],
},
isBatch: {
type: Boolean,
default: false,
}, //
isComponent: {
type: Boolean,
default: false,
}, //
isActivityComponent: {
type: Boolean,
default: false,
}, //
});
const emit = defineEmits<{
(e: 'selectionChange', value: MallSpuApi.Sku[]): void;
}>(); const formData = ref<MallSpuApi.Spu>(); //
const skuList = ref<MallSpuApi.Sku[]>([
{
price: 0, //
marketPrice: 0, //
costPrice: 0, //
barCode: '', //
picUrl: '', //
stock: 0, //
weight: 0, //
volume: 0, //
firstBrokeragePrice: 0, //
secondBrokeragePrice: 0, //
},
]); //
/** 批量添加 */
const batchAdd = () => {
validateProperty();
formData.value!.skus!.forEach((item: MallSpuApi.Sku) => {
copyValueToTarget(item, skuList.value[0]);
});
};
/** 校验商品属性属性值 */
const validateProperty = () => {
//
const warningInfo = '存在属性属性值为空,请先检查完善属性值后重试!!!';
for (const item of props.propertyList) {
if (!item.values || isEmpty(item.values)) {
ElMessage.warning(warningInfo);
throw new Error(warningInfo);
}
}
};
/** 删除 sku */
const deleteSku = (row: MallSpuApi.Sku) => {
const index = formData.value!.skus!.findIndex(
//
(sku: MallSpuApi.Sku) =>
JSON.stringify(sku.properties) === JSON.stringify(row.properties),
);
formData.value!.skus!.splice(index, 1);
};
const tableHeaders = ref<{ label: string; prop: string; }[]>([]); //
/**
* 保存时每个商品规格的表单要校验下例如说销售金额最低是 0.01 这种
*/
const validateSku = () => {
validateProperty();
let warningInfo = '请检查商品各行相关属性配置,';
let validate = true; //
for (const sku of formData.value!.skus!) {
//
for (const rule of props?.ruleConfig) {
const arg = getValue(sku, rule.name);
if (!rule.rule(arg)) {
validate = false; //
warningInfo += rule.message;
break;
}
}
//
if (!validate) {
ElMessage.warning(warningInfo);
throw new Error(warningInfo);
}
}
};
const getValue = (obj: any, arg: string) => {
const keys = arg.split('.');
let value = obj;
for (const key of keys) {
if (value && typeof value === 'object' && key in value) {
value = value[key];
} else {
value = undefined;
break;
}
}
return value;
};
/**
* 选择时触发
* @param Sku 传递过来的选中的 sku 是一个数组
*/
const handleSelectionChange = (val: MallSpuApi.Sku[]) => {
emit('selectionChange', val);
};
/**
* 将传进来的值赋值给 skuList
*/
watch(
() => props.propFormData,
(data) => {
if (!data) return;
formData.value = data;
},
{
deep: true,
immediate: true,
},
);
/** 生成表数据 */
const generateTableData = (propertyList: any[]) => {
//
const propertyValues = propertyList.map((item) =>
item.values.map((v: any) => ({
propertyId: item.id,
propertyName: item.name,
valueId: v.id,
valueName: v.name,
})),
);
const buildSkuList = build(propertyValues);
// sku skus
if (!validateData(propertyList)) {
// sku
formData.value!.skus = [];
}
if (buildSkuList && buildSkuList.length > 0) {
for (const item of buildSkuList) {
const row = {
properties: Array.isArray(item) ? item : [item], // property
price: 0,
marketPrice: 0,
costPrice: 0,
barCode: '',
picUrl: '',
stock: 0,
weight: 0,
volume: 0,
firstBrokeragePrice: 0,
secondBrokeragePrice: 0,
};
// sku
const index = formData.value!.skus!.findIndex(
(sku: MallSpuApi.Sku) =>
JSON.stringify(sku.properties) === JSON.stringify(row.properties),
);
if (index !== -1) {
continue;
}
formData.value!.skus!.push(row);
}
}
};
/**
* 生成 skus 前置校验
*/
const validateData = (propertyList: any[]) => {
const skuPropertyIds: number[] = [];
formData.value!.skus!.forEach((sku: MallSpuApi.Sku) =>
sku.properties
?.map((property: any) => property.propertyId)
?.forEach((propertyId: number) => {
if (!skuPropertyIds.indexOf(propertyId!) === -1) {
skuPropertyIds.push(propertyId!);
}
}),
);
const propertyIds = propertyList.map((item) => item.id);
return skuPropertyIds.length === propertyIds.length;
};
/** 构建所有排列组合 */
const build = (
propertyValuesList: MallSpuApi.Property[][],
): MallSpuApi.Property[] | MallSpuApi.Property[][] => {
if (!propertyValuesList || propertyValuesList.length === 0) {
return [];
} else if (propertyValuesList.length === 1) {
return propertyValuesList[0] || [];
} else {
const result: MallSpuApi.Property[][] = [];
const rest = build(propertyValuesList.slice(1));
if (propertyValuesList[0] && Array.isArray(rest)) {
for (let i = 0; i < propertyValuesList[0].length; i++) {
for (const restItem of rest) {
const currentItem = propertyValuesList[0][i];
//
if (Array.isArray(restItem)) {
result.push([currentItem!, ...restItem]);
} else if (restItem) {
// restItemundefined
result.push([currentItem!, restItem as MallSpuApi.Property]);
}
}
}
}
return result;
}
};
/** 监听属性列表,生成相关参数和表头 */
watch(
() => props.propertyList,
(propertyList: PropertyAndValues[]) => {
//
if (!formData.value!.specType) {
return;
}
// 使
if (props.isBatch) {
skuList.value = [
{
price: 0,
marketPrice: 0,
costPrice: 0,
barCode: '',
picUrl: '',
stock: 0,
weight: 0,
volume: 0,
firstBrokeragePrice: 0,
secondBrokeragePrice: 0,
},
];
}
//
if (JSON.stringify(propertyList) === '[]') {
return;
}
//
tableHeaders.value = [];
//
propertyList.forEach((item, index) => {
// nameindex
tableHeaders.value.push({ prop: `name${index}`, label: item.name });
});
// sku
if (validateData(propertyList)) {
return;
}
//
if (propertyList.some((item) => !item.values || isEmpty(item.values))) {
return;
}
// table sku
generateTableData(propertyList);
},
{
deep: true,
immediate: true,
},
);
const activitySkuListRef = ref<InstanceType<typeof ElTable>>();
const getSkuTableRef = () => {
return activitySkuListRef.value;
};
// sku
defineExpose({ generateTableData, validateSku, getSkuTableRef });
</script>
<template>
<!-- 情况一添加/修改 -->
<el-table
<ElTable
v-if="!isActivityComponent"
:data="isBatch ? skuList : formData!.skus!"
border
class="tabNumWidth"
max-height="500"
size="small"
>
<el-table-column align="center" label="图片" min-width="120">
<template #default="{ row }">
<UploadImg
v-model="row.picUrl"
height="50px"
width="50px"
:show-description="false"
/>
</template>
</el-table-column>
<template v-if="formData!.specType && !isBatch">
<!-- 根据商品属性动态添加 -->
<el-table-column
v-for="(item, index) in tableHeaders"
:key="index"
:label="item.label"
align="center"
min-width="120"
>
<template #default="{ row }">
<span style="font-weight: bold; color: #40aaff">
{{ row.properties?.[index]?.valueName }}
</span>
</template>
</el-table-column>
</template>
<el-table-column align="center" label="商品条码" min-width="168">
<template #default="{ row }">
<ElInput v-model="row.barCode" class="w-100%" />
</template>
</el-table-column>
<el-table-column align="center" label="销售价" min-width="168">
<template #default="{ row }">
<el-input-number
v-model="row.price"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
controls-position="right"
/>
</template>
</el-table-column>
<el-table-column align="center" label="市场价" min-width="168">
<template #default="{ row }">
<el-input-number
v-model="row.marketPrice"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
controls-position="right"
/>
</template>
</el-table-column>
<el-table-column align="center" label="成本价" min-width="168">
<template #default="{ row }">
<el-input-number
v-model="row.costPrice"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
controls-position="right"
/>
</template>
</el-table-column>
<el-table-column align="center" label="库存" min-width="168">
<template #default="{ row }">
<el-input-number
v-model="row.stock"
:min="0"
class="w-100%"
controls-position="right"
/>
</template>
</el-table-column>
<el-table-column align="center" label="重量(kg)" min-width="168">
<template #default="{ row }">
<el-input-number
v-model="row.weight"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
controls-position="right"
/>
</template>
</el-table-column>
<el-table-column align="center" label="体积(m^3)" min-width="168">
<template #default="{ row }">
<el-input-number
v-model="row.volume"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
controls-position="right"
/>
</template>
</el-table-column>
<template v-if="formData!.subCommissionType">
<el-table-column align="center" label="一级返佣(元)" min-width="168">
<template #default="{ row }">
<el-input-number
v-model="row.firstBrokeragePrice"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
controls-position="right"
/>
</template>
</el-table-column>
<el-table-column align="center" label="二级返佣(元)" min-width="168">
<template #default="{ row }">
<el-input-number
v-model="row.secondBrokeragePrice"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
controls-position="right"
/>
</template>
</el-table-column>
</template>
<el-table-column
v-if="formData?.specType"
align="center"
fixed="right"
label="操作"
width="80"
>
<template #default="{ row }">
<el-button
v-if="isBatch"
link
size="small"
type="primary"
@click="batchAdd"
>
批量添加
</el-button>
<el-button
v-else
link
size="small"
type="primary"
@click="deleteSku(row)"
>
删除
</el-button>
</template>
</el-table-column>
</ElTable>
<!-- 情况二作为活动组件 -->
<ElTable
v-if="isActivityComponent"
:data="formData!.skus!"
border
max-height="500"
size="small"
style="width: 99%"
>
<el-table-column v-if="isComponent" type="selection" width="45" />
<el-table-column align="center" label="图片" min-width="80">
<template #default="{ row }">
<el-image :src="row.picUrl" class="h-60px w-60px" />
</template>
</el-table-column>
<template v-if="formData!.specType">
<!-- 根据商品属性动态添加 -->
<el-table-column
v-for="(item, index) in tableHeaders"
:key="index"
:label="item.label"
align="center"
min-width="80"
>
<template #default="{ row }">
<span style="font-weight: bold; color: #40aaff">
{{ row.properties?.[index]?.valueName }}
</span>
</template>
</el-table-column>
</template>
<el-table-column align="center" label="商品条码" min-width="100">
<template #default="{ row }">
{{ row.barCode }}
</template>
</el-table-column>
<el-table-column align="center" label="销售价(元)" min-width="80">
<template #default="{ row }">
{{ formatToFraction(row.price) }}
</template>
</el-table-column>
<el-table-column align="center" label="市场价(元)" min-width="80">
<template #default="{ row }">
{{ formatToFraction(row.marketPrice) }}
</template>
</el-table-column>
<el-table-column align="center" label="成本价(元)" min-width="80">
<template #default="{ row }">
{{ formatToFraction(row.costPrice) }}
</template>
</el-table-column>
<el-table-column align="center" label="库存" min-width="80">
<template #default="{ row }">
{{ row.stock }}
</template>
</el-table-column>
<!-- 方便扩展每个活动配置的属性不一样 -->
<slot name="extension"></slot>
</ElTable>
</template>.includes(propertyId!)) {
skuPropertyIds.push(propertyId!);
}
}),
);
const propertyIds = propertyList.map((item) => item.id);
return skuPropertyIds.length === propertyIds.length;
};
/** 构建所有排列组合 */
const build = (
propertyValuesList: MallSpuApi.Property[][],
): MallSpuApi.Property[] | MallSpuApi.Property[][] => {
if (!propertyValuesList || propertyValuesList.length === 0) {
return [];
} else if (propertyValuesList.length === 1) {
return propertyValuesList[0] || [];
} else {
const result: MallSpuApi.Property[][] = [];
const rest = build(propertyValuesList.slice(1));
if (propertyValuesList[0] && Array.isArray(rest)) {
for (let i = 0; i < propertyValuesList[0].length; i++) {
for (const restItem of rest) {
const currentItem = propertyValuesList[0][i];
//
if (Array.isArray(restItem)) {
result.push([currentItem!, ...restItem]);
} else if (restItem) {
// restItemundefined
result.push([currentItem!, restItem as MallSpuApi.Property]);
}
}
}
}
return result;
}
};
/** 监听属性列表,生成相关参数和表头 */
watch(
() => props.propertyList,
(propertyList: PropertyAndValues[]) => {
//
if (!formData.value!.specType) {
return;
}
// 使
if (props.isBatch) {
skuList.value = [
{
price: 0,
marketPrice: 0,
costPrice: 0,
barCode: '',
picUrl: '',
stock: 0,
weight: 0,
volume: 0,
firstBrokeragePrice: 0,
secondBrokeragePrice: 0,
},
];
}
//
if (JSON.stringify(propertyList) === '[]') {
return;
}
//
tableHeaders.value = [];
//
propertyList.forEach((item, index) => {
// nameindex
tableHeaders.value.push({ prop: `name${index}`, label: item.name });
});
// sku
if (validateData(propertyList)) {
return;
}
//
if (propertyList.some((item) => !item.values || isEmpty(item.values))) {
return;
}
// table sku
generateTableData(propertyList);
},
{
deep: true,
immediate: true,
},
);
const activitySkuListRef = ref<InstanceType<typeof ElTable>>();
const getSkuTableRef = () => {
return activitySkuListRef.value;
};
// sku
defineExpose({ generateTableData, validateSku, getSkuTableRef });
</script>
<template>
<!-- 情况一添加/修改 -->
<ElTable
v-if="!isActivityComponent"
:data="isBatch ? skuList : formData!.skus!"
border
@ -162,10 +792,10 @@
>
</template>
</el-table-column>
</el-table>
</ElTable>
<!-- 情况二作为活动组件 -->
<el-table
<ElTable
v-if="isActivityComponent"
:data="formData!.skus!"
border
@ -222,306 +852,5 @@
</el-table-column>
<!-- 方便扩展每个活动配置的属性不一样 -->
<slot name="extension"></slot>
</el-table>
</ElTable>
</template>
<script lang="ts" setup>
import { copyValueToTarget } from '#/utils';
import { formatToFraction, isEmpty } from '@vben/utils';
import type { PropertyAndValues, RuleConfig } from './model';
import UploadImg from '#/components/upload/image-upload.vue';
import { ElTable, ElInput, ElMessage } from 'element-plus';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { ref, watch } from 'vue';
import type { PropType } from 'vue';
defineOptions({ name: 'SkuList' });
const props = defineProps({
propFormData: {
type: Object as PropType<MallSpuApi.Spu>,
default: () => ({}),
},
propertyList: {
type: Array as PropType<PropertyAndValues[]>,
default: () => [],
},
ruleConfig: {
type: Array as PropType<RuleConfig[]>,
default: () => [],
},
isBatch: {
type: Boolean,
default: false,
}, //
isComponent: {
type: Boolean,
default: false,
}, //
isActivityComponent: {
type: Boolean,
default: false,
}, //
});
const formData = ref<MallSpuApi.Spu>(); //
const skuList = ref<MallSpuApi.Sku[]>([
{
price: 0, //
marketPrice: 0, //
costPrice: 0, //
barCode: '', //
picUrl: '', //
stock: 0, //
weight: 0, //
volume: 0, //
firstBrokeragePrice: 0, //
secondBrokeragePrice: 0, //
},
]); //
/** 批量添加 */
const batchAdd = () => {
validateProperty();
formData.value!.skus!.forEach((item: MallSpuApi.Sku) => {
copyValueToTarget(item, skuList.value[0]);
});
};
/** 校验商品属性属性值 */
const validateProperty = () => {
//
const warningInfo = '存在属性属性值为空,请先检查完善属性值后重试!!!';
for (const item of props.propertyList) {
if (!item.values || isEmpty(item.values)) {
ElMessage.warning(warningInfo);
throw new Error(warningInfo);
}
}
};
/** 删除 sku */
const deleteSku = (row: MallSpuApi.Sku) => {
const index = formData.value!.skus!.findIndex(
//
(sku: MallSpuApi.Sku) =>
JSON.stringify(sku.properties) === JSON.stringify(row.properties),
);
formData.value!.skus!.splice(index, 1);
};
const tableHeaders = ref<{ prop: string; label: string }[]>([]); //
/**
* 保存时每个商品规格的表单要校验下例如说销售金额最低是 0.01 这种
*/
const validateSku = () => {
validateProperty();
let warningInfo = '请检查商品各行相关属性配置,';
let validate = true; //
for (const sku of formData.value!.skus!) {
//
for (const rule of props?.ruleConfig) {
const arg = getValue(sku, rule.name);
if (!rule.rule(arg)) {
validate = false; //
warningInfo += rule.message;
break;
}
}
//
if (!validate) {
ElMessage.warning(warningInfo);
throw new Error(warningInfo);
}
}
};
const getValue = (obj: any, arg: string) => {
const keys = arg.split('.');
let value = obj;
for (const key of keys) {
if (value && typeof value === 'object' && key in value) {
value = value[key];
} else {
value = undefined;
break;
}
}
return value;
};
const emit = defineEmits<{
(e: 'selectionChange', value: MallSpuApi.Sku[]): void;
}>();
/**
* 选择时触发
* @param Sku 传递过来的选中的 sku 是一个数组
*/
const handleSelectionChange = (val: MallSpuApi.Sku[]) => {
emit('selectionChange', val);
};
/**
* 将传进来的值赋值给 skuList
*/
watch(
() => props.propFormData,
(data) => {
if (!data) return;
formData.value = data;
},
{
deep: true,
immediate: true,
},
);
/** 生成表数据 */
const generateTableData = (propertyList: any[]) => {
//
const propertyValues = propertyList.map((item) =>
item.values.map((v: any) => ({
propertyId: item.id,
propertyName: item.name,
valueId: v.id,
valueName: v.name,
})),
);
const buildSkuList = build(propertyValues);
// sku skus
if (!validateData(propertyList)) {
// sku
formData.value!.skus = [];
}
if (buildSkuList && buildSkuList.length > 0) {
for (const item of buildSkuList) {
const row = {
properties: Array.isArray(item) ? item : [item], // property
price: 0,
marketPrice: 0,
costPrice: 0,
barCode: '',
picUrl: '',
stock: 0,
weight: 0,
volume: 0,
firstBrokeragePrice: 0,
secondBrokeragePrice: 0,
};
// sku
const index = formData.value!.skus!.findIndex(
(sku: MallSpuApi.Sku) =>
JSON.stringify(sku.properties) === JSON.stringify(row.properties),
);
if (index !== -1) {
continue;
}
formData.value!.skus!.push(row);
}
}
};
/**
* 生成 skus 前置校验
*/
const validateData = (propertyList: any[]) => {
const skuPropertyIds: number[] = [];
formData.value!.skus!.forEach((sku: MallSpuApi.Sku) =>
sku.properties
?.map((property: any) => property.propertyId)
?.forEach((propertyId: number) => {
if (skuPropertyIds.indexOf(propertyId!) === -1) {
skuPropertyIds.push(propertyId!);
}
}),
);
const propertyIds = propertyList.map((item) => item.id);
return skuPropertyIds.length === propertyIds.length;
};
/** 构建所有排列组合 */
const build = (
propertyValuesList: MallSpuApi.Property[][],
): MallSpuApi.Property[] | MallSpuApi.Property[][] => {
if (!propertyValuesList || propertyValuesList.length === 0) {
return [];
} else if (propertyValuesList.length === 1) {
return propertyValuesList[0] || [];
} else {
const result: MallSpuApi.Property[][] = [];
const rest = build(propertyValuesList.slice(1));
if (propertyValuesList[0] && Array.isArray(rest)) {
for (let i = 0; i < propertyValuesList[0].length; i++) {
for (let j = 0; j < rest.length; j++) {
const currentItem = propertyValuesList[0][i];
const restItem = rest[j];
//
if (Array.isArray(restItem)) {
result.push([currentItem!, ...restItem]);
} else if (restItem) {
// restItemundefined
result.push([currentItem!, restItem as MallSpuApi.Property]);
}
}
}
}
return result;
}
};
/** 监听属性列表,生成相关参数和表头 */
watch(
() => props.propertyList,
(propertyList: PropertyAndValues[]) => {
//
if (!formData.value!.specType) {
return;
}
// 使
if (props.isBatch) {
skuList.value = [
{
price: 0,
marketPrice: 0,
costPrice: 0,
barCode: '',
picUrl: '',
stock: 0,
weight: 0,
volume: 0,
firstBrokeragePrice: 0,
secondBrokeragePrice: 0,
},
];
}
//
if (JSON.stringify(propertyList) === '[]') {
return;
}
//
tableHeaders.value = [];
//
propertyList.forEach((item, index) => {
// nameindex
tableHeaders.value.push({ prop: `name${index}`, label: item.name });
});
// sku
if (validateData(propertyList)) {
return;
}
//
if (propertyList.some((item) => !item.values || isEmpty(item.values))) {
return;
}
// table sku
generateTableData(propertyList);
},
{
deep: true,
immediate: true,
},
);
const activitySkuListRef = ref<InstanceType<typeof ElTable>>();
const getSkuTableRef = () => {
return activitySkuListRef.value;
};
// sku
defineExpose({ generateTableData, validateSku, getSkuTableRef });
</script>

View File

@ -1,16 +1,19 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { onMounted, ref, unref } from 'vue';
import { cloneDeep } from '@vben/utils';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { useRouter, useRoute } from 'vue-router';
import { formatToFraction, convertToInteger } from '@vben/utils';
import * as ProductSpuApi from '#/api/mall/product/spu';
import { onMounted, ref, unref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Page } from '@vben/common-ui';
import { cloneDeep, convertToInteger, formatToFraction } from '@vben/utils';
import { ElMessage } from 'element-plus';
import InfoForm from '../components/info-form.vue';
import * as ProductSpuApi from '#/api/mall/product/spu';
import DeliveryForm from '../components/delivery-form.vue';
import DescriptionForm from '../components/description-form.vue';
import InfoForm from '../components/info-form.vue';
import OtherForm from '../components/other-form.vue';
import SkuForm from '../components/sku-form.vue';
@ -115,12 +118,12 @@ const submitForm = async () => {
//
const data = deepCopyFormData as MallSpuApi.Spu;
const id = params.id as unknown as number;
if (!id) {
await ProductSpuApi.createSpu(data);
ElMessage.success('创建成功');
} else {
if (id) {
await ProductSpuApi.updateSpu(data);
ElMessage.success('更新成功');
} else {
await ProductSpuApi.createSpu(data);
ElMessage.success('创建成功');
}
close();
} finally {
@ -144,43 +147,43 @@ onMounted(async () => {
<ElTabs v-model="activeTab">
<ElTabPane label="基础设置" name="info">
<InfoForm
:propFormData="formData"
v-model:activeName="activeName"
:prop-form-data="formData"
v-model:active-name="activeName"
ref="infoRef"
/>
</ElTabPane>
<ElTabPane label="价格库存" name="sku">
<SkuForm
:propFormData="formData"
v-model:activeName="activeName"
:prop-form-data="formData"
v-model:active-name="activeName"
ref="skuRef"
/>
</ElTabPane>
<ElTabPane label="物流设置" name="delivery">
<DeliveryForm
:propFormData="formData"
v-model:activeName="activeName"
:prop-form-data="formData"
v-model:active-name="activeName"
ref="deliveryRef"
/>
</ElTabPane>
<ElTabPane label="商品详情" name="description">
<DescriptionForm
:propFormData="formData"
v-model:activeName="activeName"
:prop-form-data="formData"
v-model:active-name="activeName"
ref="descriptionRef"
/>
</ElTabPane>
<ElTabPane label="其它设置" name="other">
<OtherForm
:propFormData="formData"
v-model:activeName="activeName"
:prop-form-data="formData"
v-model:active-name="activeName"
ref="otherRef"
/>
</ElTabPane>
</ElTabs>
<ElButton type="primary" :loading="formLoading" @click="submitForm"
>保存</ElButton
>
<ElButton type="primary" :loading="formLoading" @click="submitForm">
保存
</ElButton>
<ElButton @click="close"></ElButton>
</Page>
</template>

View File

@ -76,6 +76,7 @@ async function onDelete(row: any) {
async function onDeleteBatch() {
await confirm('确定要批量删除该字典数据吗?');
await deleteDictDataList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -71,6 +71,7 @@ async function onDelete(row: SystemDictTypeApi.DictType) {
async function onDeleteBatch() {
await confirm('确定要批量删除该字典类型吗?');
await deleteDictTypeList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -62,6 +62,7 @@ async function onDelete(row: SystemMailAccountApi.MailAccount) {
async function onDeleteBatch() {
await confirm('确定要批量删除该邮箱账号吗?');
await deleteMailAccountList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -82,6 +82,7 @@ async function onDelete(row: SystemMailTemplateApi.MailTemplate) {
async function onDeleteBatch() {
await confirm('确定要批量删除该邮件模板吗?');
await deleteMailTemplateList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -63,6 +63,7 @@ async function onDelete(row: SystemNoticeApi.Notice) {
async function onDeleteBatch() {
await confirm('确定要批量删除该公告吗?');
await deleteNoticeList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -87,6 +87,7 @@ async function onDeleteBatch() {
});
try {
await deleteNotifyTemplateList(checkedIds.value);
checkedIds.value = [];
loadingInstance.close();
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();

View File

@ -62,6 +62,7 @@ async function onDelete(row: SystemOAuth2ClientApi.OAuth2Client) {
async function onDeleteBatch() {
await confirm('确定要批量删除该 OAuth2 客户端吗?');
await deleteOAuth2ClientList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -69,6 +69,7 @@ async function onDelete(row: SystemPostApi.Post) {
async function onDeleteBatch() {
await confirm('确定要批量删除该岗位吗?');
await deletePostList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -83,6 +83,7 @@ async function onDelete(row: SystemRoleApi.Role) {
async function onDeleteBatch() {
await confirm('确定要批量删除该角色吗?');
await deleteRoleList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -69,6 +69,7 @@ async function onDelete(row: SystemSmsChannelApi.SmsChannel) {
async function onDeleteBatch() {
await confirm('确定要批量删除该短信渠道吗?');
await deleteSmsChannelList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -80,6 +80,7 @@ async function onDelete(row: SystemSmsTemplateApi.SmsTemplate) {
async function onDeleteBatch() {
await confirm('确定要批量删除该短信模板吗?');
await deleteSmsTemplateList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -62,6 +62,7 @@ async function onDelete(row: SystemSocialClientApi.SocialClient) {
async function onDeleteBatch() {
await confirm('确定要批量删除该社交客户端吗?');
await deleteSocialClientList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -81,6 +81,7 @@ async function onDelete(row: SystemTenantApi.Tenant) {
async function onDeleteBatch() {
await confirm('确定要批量删除该租户吗?');
await deleteTenantList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -62,6 +62,7 @@ async function onDelete(row: SystemTenantPackageApi.TenantPackage) {
async function onDeleteBatch() {
await confirm('确定要批量删除该租户套餐吗?');
await deleteTenantPackageList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}

View File

@ -100,6 +100,7 @@ async function onDelete(row: SystemUserApi.User) {
async function onDeleteBatch() {
await confirm('确定要批量删除该用户吗?');
await deleteUserList(checkedIds.value);
checkedIds.value = [];
ElMessage.success($t('ui.actionMessage.deleteSuccess'));
onRefresh();
}