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>
|