1051 lines
31 KiB
Plaintext
1051 lines
31 KiB
Plaintext
<template>
|
|
<a-page-header
|
|
:title="isUpdate ? '编辑商品' : '新增商品'"
|
|
@back="push('/goods/index')"
|
|
>
|
|
<div class="ele-cell ele-cell-align-top">
|
|
<div class="ele-cell-content">
|
|
<a-form
|
|
ref="formRef"
|
|
:model="form"
|
|
:rules="rules"
|
|
:label-col="{ md: { span: 3 }, sm: { span: 4 }, xs: { span: 24 } }"
|
|
:wrapper-col="{
|
|
md: { span: 14 },
|
|
sm: { span: 24 },
|
|
xs: { span: 24 }
|
|
}"
|
|
>
|
|
<a-card title="基础信息" :bordered="false" class="goods-form">
|
|
<a-row :gutter="16">
|
|
<a-col
|
|
v-bind="
|
|
styleResponsive ? { md: 24, sm: 24, xs: 24 } : { span: 8 }
|
|
"
|
|
>
|
|
<a-form-item label="选择店铺" name="merchantId" v-if="!form.merchantId">
|
|
<SelectMerchant
|
|
:placeholder="`选择商户`"
|
|
class="input-item"
|
|
v-model:value="form.merchantName"
|
|
@done="chooseMerchantId"
|
|
/>
|
|
</a-form-item>
|
|
<a-form-item
|
|
label="商品类型"
|
|
name="type"
|
|
>
|
|
<a-space>
|
|
<a-button
|
|
:type="form.type == 1 ? 'primary' : ''"
|
|
@click="onType(1)"
|
|
:ghost="form.type == 1"
|
|
>实物商品</a-button
|
|
>
|
|
<a-button
|
|
:type="form.type == 2 ? 'primary' : ''"
|
|
@click="onType(2)"
|
|
:ghost="form.type == 2"
|
|
>虚拟商品</a-button
|
|
>
|
|
</a-space>
|
|
<div class="ele-text-placeholder">
|
|
{{ form.type == 1
|
|
? `支持快递邮寄、同城配送或到店自提方式发货`
|
|
: '电子券码等,线下到店核销,无需备货' }}
|
|
</div>
|
|
</a-form-item>
|
|
<a-form-item label="商品分类" name="categoryId">
|
|
<SelectGoodsCategory
|
|
:data="data"
|
|
placeholder="请选择商品分类"
|
|
v-model:value="category"
|
|
@done="chooseGoodsCategory"
|
|
/>
|
|
<a-space style="margin-top: 10px">
|
|
<a @click="openUrl('/goods/category')">去新增</a>
|
|
</a-space>
|
|
</a-form-item>
|
|
<a-form-item
|
|
label="商品图片和视频"
|
|
name="images">
|
|
<SelectFile
|
|
:placeholder="`请选择视频文件`"
|
|
:limit="1"
|
|
:data="images"
|
|
@done="chooseImage"
|
|
@del="onDeleteItem"
|
|
/>
|
|
<div class="ele-text-placeholder">上传视频(mp4格式),视频时长不超过60秒,视频大小不超过200M。</div>
|
|
</a-form-item>
|
|
<a-form-item label="商品名称" name="goodsName">
|
|
<a-space style="margin-bottom: 20px">
|
|
<a-input
|
|
allow-clear
|
|
show-count
|
|
:maxlength="60"
|
|
style="width: 558px"
|
|
placeholder="请输入商品名称"
|
|
v-model:value="form.goodsName"
|
|
/>
|
|
</a-space>
|
|
</a-form-item>
|
|
<a-form-item label="商品卖点" name="comments">
|
|
<a-input
|
|
allow-clear
|
|
:maxlength="60"
|
|
style="width: 558px"
|
|
placeholder="此款商品美观大方 性价比较高 不容错过"
|
|
v-model:value="form.comments"
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
</a-row>
|
|
</a-card>
|
|
<a-card title="规格售价与库存" :bordered="false" class="goods-form">
|
|
<a-form-item label="规格类型" name="specs">
|
|
<a-radio-group v-model:value="form.specs">
|
|
<a-radio :value="0">单规格</a-radio>
|
|
<a-radio :value="1">多规格</a-radio>
|
|
</a-radio-group>
|
|
</a-form-item>
|
|
<a-form-item label="商品价格" name="price" v-if="form.specs == 0">
|
|
<a-input-number :placeholder="`商品价格`" style="width: 180px" v-model:value="form.price" />
|
|
<div class="ele-text-placeholder">商品的实际购买金额,最低0.01</div>
|
|
</a-form-item>
|
|
<a-form-item label="划线价格" name="salePrice" v-if="form.specs == 0">
|
|
<a-input-number :placeholder="`划线价格`" style="width: 180px" v-model:value="form.salePrice" />
|
|
<div class="ele-text-placeholder">划线价仅用于商品页展示</div>
|
|
</a-form-item>
|
|
<a-form-item label="当前库存" name="stock" v-if="form.specs == 0">
|
|
<a-input-number :placeholder="`划线价格`" style="width: 180px" v-model:value="form.stock" />
|
|
<div class="ele-text-placeholder">商品的实际库存数量,为0时用户无法下单</div>
|
|
</a-form-item>
|
|
<!-- 商品多规格 -->
|
|
<a-form-item label="商品规格" name="skuList" v-if="form.specs == 1">
|
|
<template v-for="(item, index) in specList" :key="index">
|
|
<a-card class="spec-card">
|
|
<template #title>
|
|
<span style="margin-right: 10px">{{
|
|
`属性${index + 1}`
|
|
}}</span>
|
|
<SelectSpec
|
|
placeholder="请选择属性"
|
|
:width="130"
|
|
:specDict="specDict"
|
|
:index="index"
|
|
v-model:value="specList[index].specName"
|
|
@done="changeAttr"
|
|
/>
|
|
</template>
|
|
<template #extra>
|
|
<a @click="delAttribute(index)">删除属性</a>
|
|
</template>
|
|
<!--属性列表-->
|
|
<div class="spec-list">
|
|
<template v-for="(val,valIndex) in item.valueList" :key="valIndex">
|
|
<a-tag color="#108ee9" closable @close="onClose(index,valIndex)">{{ val.specValue }}</a-tag>
|
|
</template>
|
|
<SelectSpecValue
|
|
placeholder="请选择规格值"
|
|
:width="130"
|
|
:specId="item.specId"
|
|
:index="index"
|
|
@done="changeAttrValue"
|
|
/>
|
|
</div>
|
|
</a-card>
|
|
</template>
|
|
<a-card :bordered="false" v-if="specList.length < 3">
|
|
<a @click="addAttribute"
|
|
><PlusCircleOutlined style="margin-right: 4px" />创建新属性</a
|
|
>
|
|
</a-card>
|
|
skuList: {{ skuList }}
|
|
<!-- <a-divider />-->
|
|
<!-- {{ form.goodsSkus }}-->
|
|
<a-divider />
|
|
<!-- {{ skuColumns }}-->
|
|
<!-- 生生的sku数据 -->
|
|
<div class="sku-table">
|
|
<a-table
|
|
:pagination="false"
|
|
:dataSource="skuList"
|
|
:columns="skuColumns"
|
|
:scroll="{ y: 500 }"
|
|
>
|
|
<template #bodyCell="{ record, column, index }">
|
|
<template v-if="column.key === 'line'">
|
|
{{ index + 1 }}
|
|
</template>
|
|
<template v-if="column.key === 'image'">
|
|
<SelectFile
|
|
:placeholder="`请选择商品图片`"
|
|
:limit="1"
|
|
:data="record.images || []"
|
|
:index="index"
|
|
@done="chooseImageItem"
|
|
@del="onDeleteItem"
|
|
/>
|
|
</template>
|
|
<template v-if="column.key === 'price'">
|
|
<a-input :placeholder="`商品价格`" v-model:value="skuList[index].price" />
|
|
</template>
|
|
<template v-if="column.key === 'salePrice'">
|
|
<a-input :placeholder="`市场价`" v-model:value="record.salePrice" />
|
|
</template>
|
|
<template v-if="column.key === 'stock'">
|
|
<a-input :placeholder="`库存`" v-model:value="record.stock" />
|
|
</template>
|
|
<template v-if="column.key === 'skuNo'">
|
|
<a-input :placeholder="`编码`" v-model:value="record.skuNo" />
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
<!-- {{ skuColumns }}-->
|
|
</div>
|
|
<div class="bath-set">
|
|
<!-- <h5 class="title">批量设置</h5>-->
|
|
<a-space class="list">
|
|
<a-input
|
|
:placeholder="`售卖价`"
|
|
v-model:value="bathSet.price"
|
|
/>
|
|
<a-input
|
|
:placeholder="`市场价`"
|
|
v-model:value="bathSet.salePrice"
|
|
/>
|
|
<a-input :placeholder="`库存`" v-model:value="bathSet.stock" />
|
|
<a-input
|
|
:placeholder="`规格编码`"
|
|
v-model:value="bathSet.skuNo"
|
|
/>
|
|
<a-button type="primary" @click="onBathSet">批量设置</a-button>
|
|
</a-space>
|
|
</div>
|
|
</a-form-item>
|
|
<a-form-item label="库存计算方式" name="deductStockType">
|
|
<a-radio-group v-model:value="form.deductStockType">
|
|
<a-radio :value="10">下单减库存</a-radio>
|
|
<a-radio :value="20">付款减库存</a-radio>
|
|
</a-radio-group>
|
|
</a-form-item>
|
|
</a-card>
|
|
|
|
<a-card title="商品详情" :bordered="false" class="goods-form">
|
|
<!-- 编辑器 -->
|
|
<tinymce-editor
|
|
ref="editorRef"
|
|
class="content"
|
|
v-model:value="content"
|
|
:disabled="disabled"
|
|
:init="config"
|
|
placeholder="图片直接粘贴自动上传"
|
|
@paste="onPaste"
|
|
/>
|
|
<div style="margin-top: 20px">
|
|
<SelectFile
|
|
:placeholder="`请选择视频文件`"
|
|
:limit="9"
|
|
:data="files"
|
|
@done="chooseFile"
|
|
@del="onDeleteFile"
|
|
/>
|
|
</div>
|
|
<div class="ele-text-placeholder"
|
|
>最多上传20个素材,拖拽可进行排序,单张图片需限制在10M以内</div
|
|
>
|
|
</a-card>
|
|
|
|
<a-card title="商品参数" :bordered="false" class="goods-form" />
|
|
|
|
<a-card title="更多设置" :bordered="false" class="goods-form" />
|
|
<a-card :bordered="false" class="goods-form">
|
|
<a-button type="primary" block size="large" @click="save">保存</a-button>
|
|
</a-card>
|
|
|
|
|
|
<div class="body-bottom"></div>
|
|
|
|
|
|
<a-card
|
|
:bordered="false"
|
|
class="goods-form"
|
|
style="position: fixed; bottom: 0; opacity: 0.9"
|
|
>
|
|
<!-- <div style="width: 100px">-->
|
|
<!-- <a-button block danger size="large" @click="save"-->
|
|
<!-- >删除</a-button-->
|
|
<!-- >-->
|
|
<!-- </div>-->
|
|
<div style="width: 100px">
|
|
<a-button type="primary" block size="large" @click="save">保存</a-button>
|
|
</div>
|
|
<!-- <div style="width: 100px">-->
|
|
<!-- <a-button type="primary" block size="large" @click="save"-->
|
|
<!-- >上架</a-button-->
|
|
<!-- >-->
|
|
<!-- </div>-->
|
|
</a-card>
|
|
</a-form>
|
|
</div>
|
|
<!-- 载入模拟器 -->
|
|
<Simulator :form="form" />
|
|
</div>
|
|
</a-page-header>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
// 表单数据
|
|
import useFormData from '@/utils/use-form-data';
|
|
import { reactive, ref, unref, watch } from 'vue';
|
|
import { FormInstance, RuleObject } from 'ant-design-vue/es/form';
|
|
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
|
import { Form, message } from 'ant-design-vue';
|
|
import { getDictionaryOptions, getMerchantId, openUrl } from "@/utils/common";
|
|
import { addGoods, updateGoods } from '@/api/shop/goods';
|
|
import { useRouter } from 'vue-router';
|
|
import { useThemeStore } from '@/store/modules/theme';
|
|
import { storeToRefs } from 'pinia';
|
|
import { BathSet, Goods } from '@/api/shop/goods/model';
|
|
import {PlusCircleOutlined} from '@ant-design/icons-vue';
|
|
import { getGoods } from '@/api/shop/goods';
|
|
import { listGoodsCategory } from '@/api/shop/goodsCategory';
|
|
import { FileRecord } from '@/api/system/file/model';
|
|
import {toTreeData, uuid} from 'ele-admin-pro';
|
|
import { ArticleCategory } from '@/api/cms/category/model';
|
|
import TinymceEditor from "@/components/TinymceEditor/index.vue";
|
|
import {uploadFile, uploadOss} from "@/api/system/file";
|
|
import {ColumnItem} from "ele-admin-pro/es/ele-pro-table/types";
|
|
import {listSpec} from '@/api/shop/spec';
|
|
import {Spec} from "@/api/shop/spec/model";
|
|
import { getMerchantName } from "@/utils/merchant";
|
|
import router from "@/router";
|
|
import { Merchant } from "@/api/shop/merchant/model";
|
|
|
|
const { currentRoute } = useRouter();
|
|
// 是否开启响应式布局
|
|
const themeStore = useThemeStore();
|
|
const { styleResponsive } = storeToRefs(themeStore);
|
|
const { push } = useRouter();
|
|
const useForm = Form.useForm;
|
|
const fileList = ref<any[]>([]);
|
|
const files = ref<ItemType[]>([]);
|
|
const imgList = ref<any[]>([]);
|
|
const specList = ref<any[]>([]);
|
|
const skuList = ref<any[]>([]);
|
|
const specDict = ref<any[]>([]);
|
|
const disabled = ref(false);
|
|
const addColumns = ref<ColumnItem[]>([]);
|
|
// 是否是修改
|
|
const isUpdate = ref(false);
|
|
const content = ref('');
|
|
const loading = ref(false);
|
|
const formRef = ref<FormInstance | null>(null);
|
|
const images = ref<ItemType[]>([]);
|
|
const category = ref<string[]>([]);
|
|
// 树形数据
|
|
const data = ref<ArticleCategory[]>([]);
|
|
// 树展开的key
|
|
const expandedRowKeys = ref<number[]>([]);
|
|
// 树选中的key
|
|
const selectedRowKeys = ref<number[]>([]);
|
|
// 选中数据
|
|
const current = ref<ArticleCategory | any>(null);
|
|
// 字典数据
|
|
const goodsAttr = getDictionaryOptions('goodsAttr');
|
|
const index = ref(0);
|
|
|
|
const { form, assignFields } = useFormData<Goods>({
|
|
goodsId: undefined,
|
|
type: 1,
|
|
goodsName: '',
|
|
image: '',
|
|
content: '',
|
|
code: '',
|
|
categoryId: undefined,
|
|
categoryName: '',
|
|
categoryParent: '',
|
|
categoryChildren: '',
|
|
specs: 0,
|
|
goodsSkus: [],
|
|
goodsSpecs: [],
|
|
position: undefined,
|
|
price: undefined,
|
|
salePrice: undefined,
|
|
sales: undefined,
|
|
stock: 1000,
|
|
deductStockType: 20,
|
|
files: '',
|
|
comments: '',
|
|
recommend: 0,
|
|
sortNumber: undefined,
|
|
status: undefined,
|
|
merchantName: '',
|
|
merchantId: getMerchantId()
|
|
});
|
|
const skuColumns = ref<ColumnItem[]>([
|
|
{
|
|
dataIndex: 'line',
|
|
key: 'line'
|
|
}
|
|
]);
|
|
const defaultColumns = ref<ColumnItem[]>([
|
|
{
|
|
title: '预览图',
|
|
dataIndex: 'image',
|
|
width: 120,
|
|
align: 'center',
|
|
key: 'image'
|
|
},
|
|
{
|
|
title: '商品价格',
|
|
dataIndex: 'price',
|
|
width: 120,
|
|
align: 'center',
|
|
key: 'price'
|
|
},
|
|
{
|
|
title: '划线价格',
|
|
dataIndex: 'salePrice',
|
|
width: 120,
|
|
align: 'center',
|
|
key: 'salePrice'
|
|
},
|
|
{
|
|
title: '库存数量',
|
|
dataIndex: 'stock',
|
|
width: 120,
|
|
align: 'center',
|
|
key: 'stock'
|
|
},
|
|
{
|
|
title: '规格编码',
|
|
dataIndex: 'skuNo',
|
|
width: 120,
|
|
align: 'center',
|
|
key: 'skuNo'
|
|
}
|
|
]);
|
|
|
|
const bathSet = ref<BathSet>({});
|
|
|
|
// 表单验证规则
|
|
const rules = reactive({
|
|
type: [
|
|
{
|
|
required: true,
|
|
message: '请选择商品类型',
|
|
type: 'number',
|
|
trigger: 'blur'
|
|
}
|
|
],
|
|
specs: [
|
|
{
|
|
required: true,
|
|
message: '请选择规格类型',
|
|
type: 'number',
|
|
trigger: 'blur'
|
|
}
|
|
],
|
|
price: [
|
|
{
|
|
required: true,
|
|
message: '请填写商品价格',
|
|
type: 'number',
|
|
trigger: 'blur'
|
|
}
|
|
],
|
|
stock: [
|
|
{
|
|
required: true,
|
|
message: '请填写商品库存',
|
|
type: 'number',
|
|
trigger: 'blur'
|
|
}
|
|
],
|
|
merchantId: [
|
|
{
|
|
required: true,
|
|
message: '请选择店铺',
|
|
type: 'number',
|
|
trigger: 'blur'
|
|
}
|
|
],
|
|
categoryId: [
|
|
{
|
|
required: true,
|
|
type: 'string',
|
|
message: '选择商品分类',
|
|
trigger: 'blur',
|
|
validator: async (_rule: RuleObject, value: string) => {
|
|
if (!form.categoryId) {
|
|
return Promise.reject('选择商品分类');
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
}
|
|
],
|
|
goodsName: [
|
|
{
|
|
required: true,
|
|
message: '请选择商品名称',
|
|
type: 'string',
|
|
trigger: 'blur'
|
|
}
|
|
],
|
|
sortNumber: [
|
|
{
|
|
required: true,
|
|
message: '请输入排序号',
|
|
type: 'number',
|
|
trigger: 'blur'
|
|
}
|
|
]
|
|
});
|
|
|
|
const onType = (index: number) => {
|
|
form.type = index;
|
|
};
|
|
|
|
const addAttribute = () => {
|
|
specList.value.push({
|
|
specId: 0,
|
|
specName: undefined,
|
|
valueList: []
|
|
})
|
|
};
|
|
|
|
const delAttribute = (index) => {
|
|
specList.value.splice(index,1);
|
|
skuColumns.value.splice(index,1);
|
|
};
|
|
|
|
const changeAttr = (item: Spec,index: number) => {
|
|
// 排重(未做)
|
|
specList.value[index].specId = item.specId;
|
|
specList.value[index].specName = item.value;
|
|
specList.value[index].valueList = [];
|
|
};
|
|
|
|
const changeAttrValue = (item: any, index: number) => {
|
|
specList.value[index].specId = item.specId;
|
|
// 添加属性
|
|
const length = specList.value[index].valueList.length;
|
|
specList.value[index].valueList.push({
|
|
specValueId: item.specValueId,
|
|
specValue: item.value,
|
|
specId: item.specId
|
|
})
|
|
// 组装表头
|
|
const arr = ref<any>([]);
|
|
specList.value.map((d) => {
|
|
arr.value.push({
|
|
title: d.specName,
|
|
dataIndex: `specValue${length}`,
|
|
key: `specValue${length}`,
|
|
width: 70,
|
|
align: 'center',
|
|
})
|
|
})
|
|
// 组装skuList
|
|
setTimeout(() => {
|
|
generateSku();
|
|
},200);
|
|
}
|
|
|
|
// 生成sku
|
|
const generateSku = () => {
|
|
skuColumns.value = [
|
|
{
|
|
dataIndex: 'line',
|
|
width: 50,
|
|
key: 'line'
|
|
}
|
|
];
|
|
// 根据选中的sku生成表格列
|
|
specList.value.forEach((item,index) => {
|
|
skuColumns.value.push({
|
|
title: item.specName,
|
|
dataIndex: `specValue${index}`,
|
|
key: `specValue${index}`,
|
|
align: 'center',
|
|
});
|
|
});
|
|
|
|
// 增加额外的列
|
|
const dataList = [
|
|
{
|
|
title: '商品图片',
|
|
dataIndex: 'image',
|
|
width: 120,
|
|
align: 'center',
|
|
key: 'image',
|
|
},
|
|
{
|
|
title: '价格',
|
|
dataIndex: 'price',
|
|
key: 'price',
|
|
width: 120,
|
|
align: 'center',
|
|
},
|
|
{
|
|
title: '市场价',
|
|
dataIndex: 'salePrice',
|
|
width: 120,
|
|
align: 'center',
|
|
key: 'salePrice'
|
|
},
|
|
{
|
|
title: '库存',
|
|
dataIndex: 'stock',
|
|
width: 120,
|
|
align: 'center',
|
|
key: 'stock'
|
|
},
|
|
{
|
|
title: 'SKU编码',
|
|
dataIndex: 'skuNo',
|
|
width: 120,
|
|
align: 'center',
|
|
key: 'skuNo'
|
|
}
|
|
];
|
|
|
|
dataList.forEach((item) => {
|
|
skuColumns.value.push({
|
|
title: item.title,
|
|
dataIndex: item.key,
|
|
key: item.key,
|
|
align: 'center',
|
|
});
|
|
});
|
|
|
|
// 组合规格值
|
|
const attrList = [];
|
|
specList.value.forEach((item, index) => {
|
|
attrList.push([]);
|
|
const propType = `specValue${index}`;
|
|
item.valueList.forEach((subItem) => {
|
|
attrList[index].push({
|
|
propValue: subItem.specValue,
|
|
propCode: null,
|
|
propType: propType
|
|
});
|
|
});
|
|
});
|
|
const attrs = differentCombinations(attrList);
|
|
|
|
// 将规格值加入到表格列中
|
|
attrs.forEach((itemList, index) => {
|
|
// skuList.value[index] = [];
|
|
itemList.forEach((subItem) => {
|
|
console.log(index)
|
|
console.log(subItem);
|
|
const key = subItem.propType;
|
|
const value = subItem.propValue;
|
|
skuList.value[index][key] = value;
|
|
skuList.value[index]['pirce'] = 0.1
|
|
});
|
|
});
|
|
};
|
|
|
|
// 生成sku函数
|
|
const differentCombinations = function (param) {
|
|
return param.reduce(
|
|
(a, b) => {
|
|
const ret = [];
|
|
a.forEach((i) => b.forEach((j) => ret.push(i.concat([j]))));
|
|
return ret;
|
|
},
|
|
[[]]
|
|
);
|
|
};
|
|
|
|
const onClose = (index: number, valIndex: number) => {
|
|
specList.value[index].valueList.splice(valIndex,1);
|
|
}
|
|
|
|
|
|
const chooseImage = (data: FileRecord) => {
|
|
images.value.push({
|
|
uid: data.id,
|
|
url: data.path,
|
|
status: 'done'
|
|
});
|
|
form.image = data.url;
|
|
};
|
|
|
|
const chooseImageItem = (data: FileRecord) => {
|
|
const index = data?.index;
|
|
skuList.value[index].images = []
|
|
skuList.value[index].images.push({
|
|
uid: data.id,
|
|
url: data.path,
|
|
status: 'done'
|
|
});
|
|
skuList.value[index].image = data.path;
|
|
};
|
|
|
|
const onDeleteItem = (index: number) => {
|
|
images.value.splice(index, 1);
|
|
};
|
|
|
|
const chooseFile = (data: FileRecord) => {
|
|
files.value.push({
|
|
uid: data.id,
|
|
url: data.path,
|
|
status: 'done'
|
|
});
|
|
};
|
|
|
|
const onDeleteFile = (index: number) => {
|
|
files.value.splice(index, 1);
|
|
};
|
|
|
|
const chooseGoodsCategory = (item,value) => {
|
|
console.log(item);
|
|
console.log(value);
|
|
form.categoryId = value[1].value;
|
|
form.categoryParent = value[0].label;
|
|
form.categoryChildren = value[1].label;
|
|
}
|
|
|
|
/* 搜索 */
|
|
const chooseMerchantId = (item: Merchant) => {
|
|
form.merchantName = item.merchantName;
|
|
form.merchantId = item.merchantId;
|
|
};
|
|
|
|
const onBathSet = () => {
|
|
skuList.value.map(d => {
|
|
console.log(d);
|
|
d.price = bathSet.value.price;
|
|
d.salePrice = bathSet.value.salePrice;
|
|
d.stock = bathSet.value.stock;
|
|
d.skuNo = bathSet.value.skuNo;
|
|
})
|
|
}
|
|
|
|
|
|
const editorRef = ref<InstanceType<typeof TinymceEditor> | null>(null);
|
|
const config = ref({
|
|
height: 240,
|
|
plugins: 'code preview fullscreen searchreplace save autosave link autolink image media table codesample lists advlist charmap emoticons anchor directionality pagebreak quickbars nonbreaking visualblocks visualchars wordcount',
|
|
toolbar: false,
|
|
images_upload_handler: (blobInfo, success, error) => {
|
|
const file = blobInfo.blob();
|
|
const formData = new FormData();
|
|
formData.append('file', file, file.name);
|
|
uploadOss(file).then(res => {
|
|
success(res.path)
|
|
}).catch((msg) => {
|
|
error(msg);
|
|
})
|
|
},
|
|
});
|
|
|
|
/* 粘贴图片上传服务器并插入编辑器 */
|
|
const onPaste = (e) => {
|
|
const items = (e.clipboardData || e.originalEvent.clipboardData).items;
|
|
let hasFile = false;
|
|
for (let i = 0; i < items.length; i++) {
|
|
if (items[i].type.indexOf('image') !== -1) {
|
|
let file = items[i].getAsFile();
|
|
const item: ItemType = {
|
|
file,
|
|
uid: (file as any).lastModified,
|
|
name: file.name
|
|
};
|
|
uploadFile(<File>item.file)
|
|
.then((result) => {
|
|
const addPath = `<p><img class="content-img" src="${result.url}"></p>`;
|
|
content.value = content.value + addPath
|
|
})
|
|
.catch((e) => {
|
|
message.error(e.message);
|
|
});
|
|
hasFile = true;
|
|
}
|
|
}
|
|
if (hasFile) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
|
|
const reload = () => {
|
|
// 读取商品分类
|
|
listGoodsCategory()
|
|
.then((list) => {
|
|
loading.value = false;
|
|
const eks: number[] = [];
|
|
list.forEach((d) => {
|
|
d.key = d.categoryId;
|
|
d.value = d.categoryId;
|
|
if (typeof d.categoryId === 'number') {
|
|
eks.push(d.categoryId);
|
|
}
|
|
});
|
|
expandedRowKeys.value = eks;
|
|
data.value = toTreeData({
|
|
data: list.map((d) => {
|
|
d.disabled = d.type != 0;
|
|
return d;
|
|
}),
|
|
idField: 'categoryId',
|
|
parentIdField: 'parentId'
|
|
});
|
|
if (list.length) {
|
|
if (typeof list[0].categoryId === 'number') {
|
|
selectedRowKeys.value = [list[0].categoryId];
|
|
}
|
|
current.value = list[0];
|
|
} else {
|
|
selectedRowKeys.value = [];
|
|
current.value = null;
|
|
}
|
|
})
|
|
.catch((e) => {
|
|
loading.value = false;
|
|
message.error(e.message);
|
|
});
|
|
// 规格列表
|
|
listSpec({}).then(list => {
|
|
specDict.value = list.map((d,i) => {
|
|
return {
|
|
specId: d.specId,
|
|
name: d.specName,
|
|
value: d.specName,
|
|
<<<<<<< HEAD
|
|
list: d.specValues?.map(v => {
|
|
=======
|
|
list: d.specValue?.map(v => {
|
|
>>>>>>> origin/master
|
|
return {
|
|
specId: v.specId,
|
|
specValueId: v.specValueId,
|
|
name: v.specValue,
|
|
value: v.specValue
|
|
}
|
|
})
|
|
};
|
|
})
|
|
})
|
|
// 读取商品信息
|
|
if (form.goodsId) {
|
|
images.value = [];
|
|
files.value = [];
|
|
category.value = [];
|
|
content.value = '';
|
|
getGoods(form.goodsId).then((data) => {
|
|
assignFields(data);
|
|
if (data.image) {
|
|
const arr = JSON.parse(data.image);
|
|
arr.map((img) => {
|
|
images.value.push({
|
|
uid: uuid(),
|
|
url: img,
|
|
status: 'done'
|
|
});
|
|
});
|
|
}
|
|
if (data.files) {
|
|
const arr = JSON.parse(data.files);
|
|
arr.map((img) => {
|
|
files.value.push({
|
|
uid: uuid(),
|
|
url: img,
|
|
status: 'done'
|
|
});
|
|
});
|
|
}
|
|
if(data.content){
|
|
content.value = data.content;
|
|
}
|
|
if(data.goodsSpecs){
|
|
specList.value = data.goodsSpecs.map(d => {
|
|
d.valueList = [];
|
|
const split = d.specValues?.split(',');
|
|
split?.map(text => {
|
|
d.valueList?.push({
|
|
specValue: text
|
|
})
|
|
})
|
|
return d;
|
|
});
|
|
}
|
|
|
|
setTimeout(() => {
|
|
if(data.categoryParent){
|
|
console.log(category.value);
|
|
category.value.push(data.categoryParent);
|
|
}
|
|
if(data.categoryChildren){
|
|
category.value.push(data.categoryChildren);
|
|
}
|
|
if(data.goodsSkus){
|
|
skuList.value = data.goodsSkus;
|
|
generateSku();
|
|
}
|
|
},300)
|
|
});
|
|
}
|
|
};
|
|
|
|
const { validate, resetFields, validateInfos } = useForm(form, rules);
|
|
|
|
/* 保存编辑 */
|
|
const save = () => {
|
|
if (!formRef.value) {
|
|
return;
|
|
}
|
|
formRef.value
|
|
.validate()
|
|
.then(() => {
|
|
loading.value = true;
|
|
images.value.map((d) => {
|
|
imgList.value.push(d.url);
|
|
});
|
|
files.value.map((d) => {
|
|
fileList.value.push(d.url);
|
|
});
|
|
skuList.value.map(d => {
|
|
d.images = undefined
|
|
})
|
|
if(getMerchantId()){
|
|
form.merchantId = getMerchantId();
|
|
form.merchantName = getMerchantName();
|
|
}
|
|
const formData = {
|
|
...form,
|
|
content: content.value,
|
|
image: JSON.stringify(imgList.value),
|
|
files: JSON.stringify(fileList.value),
|
|
goodsSpecs: specList.value,
|
|
goodsSkus: skuList.value
|
|
};
|
|
const saveOrUpdate = isUpdate.value ? updateGoods : addGoods;
|
|
saveOrUpdate(formData)
|
|
.then((msg) => {
|
|
loading.value = false;
|
|
imgList.value = []
|
|
fileList.value = []
|
|
category.value = []
|
|
resetFields();
|
|
message.success(msg);
|
|
setTimeout(() => {
|
|
router.go(-1)
|
|
},1000)
|
|
})
|
|
.catch((e) => {
|
|
loading.value = false;
|
|
message.error(e.message);
|
|
});
|
|
})
|
|
.catch(() => {});
|
|
};
|
|
|
|
watch(
|
|
currentRoute,
|
|
(route) => {
|
|
const { query } = unref(route);
|
|
const { goodsId } = query;
|
|
if (goodsId) {
|
|
form.goodsId = Number(goodsId);
|
|
isUpdate.value = true;
|
|
reload();
|
|
} else {
|
|
isUpdate.value = false;
|
|
imgList.value = []
|
|
fileList.value = []
|
|
// images.value = [];
|
|
// skuList.value = [];
|
|
// specList.value = [];
|
|
// category.value = [];
|
|
resetFields();
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
export default {
|
|
name: 'GoodsAdd'
|
|
};
|
|
</script>
|
|
<style lang="less" scoped>
|
|
.goods-form {
|
|
width: 1020px;
|
|
margin-bottom: 10px;
|
|
.spec-card {
|
|
margin-top: 20px;
|
|
background-color: #f8fcff;
|
|
.spec-list {
|
|
margin-left: 52px;
|
|
.spec-value-list {
|
|
margin-bottom: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
.image {
|
|
margin-top: 10px;
|
|
margin-left: 10px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.body-bottom {
|
|
height: 100px;
|
|
}
|
|
.bath-set {
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin-top: 16px;
|
|
.list {
|
|
width: 100%;
|
|
margin: 10px 0;
|
|
display: flex;
|
|
}
|
|
}
|
|
.sku-table {
|
|
.goods-list {
|
|
.head {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
border-bottom: 1px solid #eeeeee;
|
|
div {
|
|
padding: 6px;
|
|
width: 25%;
|
|
}
|
|
}
|
|
.table-td {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
div {
|
|
width: 25%;
|
|
padding: 6px;
|
|
}
|
|
}
|
|
}
|
|
.desc {
|
|
margin-top: 20px;
|
|
padding: 10px 0;
|
|
}
|
|
}
|
|
.preview-ui {
|
|
position: fixed;
|
|
top: 155px;
|
|
right: 30px;
|
|
background: url('@/assets/img/app-ui.png');
|
|
background-repeat: no-repeat;
|
|
background-position: top;
|
|
background-size: 100%;
|
|
width: 390px;
|
|
height: 844px;
|
|
}
|
|
.ele-text-placeholder{
|
|
line-height: 2em;
|
|
}
|
|
</style>
|