Files
guofu-admin/src/views/shop/demo/add/index.vue~merged_0
2024-10-19 10:51:09 +08:00

1051 lines
31 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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