mp:增加图文草稿箱的修改;优化前端代码

This commit is contained in:
YunaiV 2023-01-14 09:17:01 +08:00
parent 28884ee638
commit 90ffe5adb7
8 changed files with 365 additions and 348 deletions

View File

@ -46,6 +46,7 @@ public interface ErrorCodeConstants {
// ========== 公众号草稿 1006007000============
ErrorCode DRAFT_LIST_FAIL = new ErrorCode(1006007000, "获得草稿列表失败,原因:{}");
ErrorCode DRAFT_CREATE_FAIL = new ErrorCode(1006007001, "创建草稿失败,原因:{}");
ErrorCode DRAFT_UPDATE_FAIL = new ErrorCode(1006007002, "更新草稿失败,原因:{}");
// TODO 要处理下
ErrorCode MENU_NOT_EXISTS = new ErrorCode(1006001002, "菜单不存在");

View File

@ -7,12 +7,18 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 公众号素材的分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MpMaterialPageReqVO extends PageParam {
@ApiModelProperty(value = "公众号账号的编号", required = true, example = "2048")
@NotNull(message = "公众号账号的编号不能为空")
private Long accountId;
@ApiModelProperty(value = "是否永久", example = "true")
private Boolean permanent;

View File

@ -30,3 +30,25 @@ tenant-id: {{adminTenentId}}
}
]
}
### 请求 /mp/draft/create 接口 => 成功
PUT {{baseUrl}}/mp/draft/update?accountId=1&mediaId=r6ryvl6LrxBU0miaST4Y-q-G9pdsmZw0OYG4FzHQkKfpLfEwIH51wy2bxisx8PvW
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
[{
"title": "我是标题OOO",
"author": "我是作者",
"digest": "我是摘要",
"content": "我是内容",
"contentSourceUrl": "https://www.iocoder.cn",
"thumbMediaId": "r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn"
}, {
"title": "我是标题XXX",
"author": "我是作者",
"digest": "我是摘要",
"content": "我是内容",
"contentSourceUrl": "https://www.iocoder.cn",
"thumbMediaId": "r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn"
}]

View File

@ -7,21 +7,20 @@ import cn.iocoder.yudao.module.mp.controller.admin.news.vo.MpDraftPageReqVO;
import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.draft.WxMpAddDraft;
import me.chanjar.weixin.mp.bean.draft.WxMpDraftItem;
import me.chanjar.weixin.mp.bean.draft.WxMpDraftList;
import me.chanjar.weixin.mp.bean.draft.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.DRAFT_CREATE_FAIL;
import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.DRAFT_LIST_FAIL;
import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*;
// TODO 芋艿权限
@Api(tags = "管理后台 - 公众号草稿")
@ -54,14 +53,37 @@ public class MpDraftController {
@ApiImplicitParam(name = "accountId", value = "公众号账号的编号", required = true,
example = "1024", dataTypeClass = Long.class)
public CommonResult<String> createDraft(@RequestParam("accountId") Long accountId,
@RequestBody WxMpAddDraft reqVO) {
@RequestBody WxMpAddDraft draft) {
WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId);
try {
String mediaId = mpService.getDraftService().addDraft(reqVO);
String mediaId = mpService.getDraftService().addDraft(draft);
return success(mediaId);
} catch (WxErrorException e) {
throw exception(DRAFT_CREATE_FAIL, e.getError().getErrorMsg());
}
}
@PutMapping("/update")
@ApiOperation("更新草稿")
@ApiImplicitParams({
@ApiImplicitParam(name = "accountId", value = "公众号账号的编号", required = true,
example = "1024", dataTypeClass = Long.class),
@ApiImplicitParam(name = "mediaId", value = "草稿素材的编号", required = true,
example = "xxx", dataTypeClass = String.class),
})
public CommonResult<Boolean> createDraft(@RequestParam("accountId") Long accountId,
@RequestParam("mediaId") String mediaId,
@RequestBody List<WxMpDraftArticles> articles) {
WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId);
try {
for (int i = 0; i < articles.size(); i++) {
WxMpDraftArticles article = articles.get(i);
mpService.getDraftService().updateDraft(new WxMpUpdateDraft(mediaId, i, article));
}
return success(true);
} catch (WxErrorException e) {
throw exception(DRAFT_UPDATE_FAIL, e.getError().getErrorMsg());
}
}
}

View File

@ -20,6 +20,7 @@ public interface MpMaterialMapper extends BaseMapperX<MpMaterialDO> {
default PageResult<MpMaterialDO> selectPage(MpMaterialPageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<MpMaterialDO>()
.eq(MpMaterialDO::getAccountId, pageReqVO.getAccountId())
.eqIfPresent(MpMaterialDO::getPermanent, pageReqVO.getPermanent())
.eqIfPresent(MpMaterialDO::getType, pageReqVO.getType()));
}

View File

@ -19,3 +19,12 @@ export function createDraft(accountId, articles) {
}
})
}
// 更新草稿
export function updateDraft(accountId, mediaId, articles) {
return request({
url: '/mp/draft/update?accountId=' + accountId + '&mediaId=' + mediaId,
method: 'put',
data: articles
})
}

View File

@ -117,7 +117,7 @@
},
props: {
objData: {
type: Object,
type: Object, // type - accountId -
required: true
},
newsType:{ // 12稿
@ -148,6 +148,11 @@
selectMaterial(item) {
this.$emit('selectMaterial', item)
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1
this.getPage()
},
getPage() {
this.loading = true
if (this.objData.type === 'news' && this.newsType === '1') { // +

View File

@ -20,6 +20,8 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
芋道源码
优化代码和项目的代码保持一致
-->
<template>
<div class="app-container">
@ -54,7 +56,7 @@ SOFTWARE.
<!-- TODO 芋艿权限样式搜索框之类的 -->
<el-row class="ope-row">
<el-button type="success" circle @click="handlePublishNews(item)">发布</el-button>
<el-button type="primary" icon="el-icon-edit" circle @click="handleEditNews(item)"></el-button>
<el-button type="primary" icon="el-icon-edit" circle @click="handleUpdate(item)"></el-button>
<el-button type="danger" icon="el-icon-delete" circle @click="delMaterial(item)"></el-button>
</el-row>
</div>
@ -66,14 +68,10 @@ SOFTWARE.
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- TODO 芋艿位置调整 -->
<!-- 添加或修改草稿对话框 -->
<el-dialog :title="operateMaterial === 'add' ? '新建图文' : '修改图文'"
append-to-body
:before-close="dialogNewsClose"
:close-on-click-modal="false"
:visible.sync="dialogNewsVisible"
width="80%"
top="20px">
append-to-body width="80%" top="20px" :visible.sync="dialogNewsVisible"
:before-close="dialogNewsClose" :close-on-click-modal="false">
<div class="left">
<div class="select-item">
<div v-for="(news, index) in articlesAdd" :key='news.id'>
@ -115,12 +113,11 @@ SOFTWARE.
</div>
</div>
<div class="right" v-loading="addMaterialLoading" v-if="articlesAdd.length > 0">
<!--富文本编辑器组件-->
<el-row>
<wx-editor v-model="articlesAdd[isActiveAddNews].content" :account-id="this.uploadData.accountId"
v-if="hackResetEditor"/>
</el-row>
<br><br><br><br>
<br /> <br /> <br /> <br />
<!-- 标题作者原文地址 -->
<el-input v-model="articlesAdd[isActiveAddNews].title" placeholder="请输入标题(必填)" />
<el-input v-model="articlesAdd[isActiveAddNews].author" placeholder="请输入作者" style="margin-top: 5px;" />
<el-input v-model="articlesAdd[isActiveAddNews].contentSourceUrl" placeholder="请输入原文地址" style="margin-top: 5px;" />
<!-- 封面和摘要 -->
<div class="input-tt">封面和摘要</div>
<div>
@ -137,20 +134,20 @@ SOFTWARE.
<div slot="tip" class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式大小不超过 2M</div>
</el-upload>
</div>
<el-dialog title="选择图片" :visible.sync="dialogImageVisible" width="80%" append-to-body>
<wx-material-select ref="materialSelect" :objData="{type: 'image', accountId: this.queryParams.accountId}"
@selectMaterial="selectMaterial" />
</el-dialog>
</div>
<el-dialog title="选择图片" :visible.sync="dialogImageVisible" width="80%" append-to-body>
<WxMaterialSelect :objData="{repType:'image'}" @selectMaterial="selectMaterial" />
</el-dialog>
<el-input :rows="8" type="textarea" v-model="articlesAdd[isActiveAddNews].digest" placeholder="请输入摘要"
class="digest" maxlength="120"></el-input>
class="digest" maxlength="120" style="float: right" />
</div>
<!-- 标题作者原文地址 -->
<div class="input-tt">标题</div>
<el-input v-model="articlesAdd[isActiveAddNews].title" placeholder="请输入标题"></el-input>
<div class="input-tt">作者</div>
<el-input v-model="articlesAdd[isActiveAddNews].author" placeholder="请输入作者"></el-input>
<div class="input-tt">原文地址</div>
<el-input v-model="articlesAdd[isActiveAddNews].contentSourceUrl" placeholder="请输入原文地址"></el-input>
<!--富文本编辑器组件-->
<el-row>
<wx-editor v-model="articlesAdd[isActiveAddNews].content" :account-id="this.uploadData.accountId"
v-if="hackResetEditor"/>
</el-row>
<!-- 原文地址 -->
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogNewsVisible = false"> </el-button>
@ -166,7 +163,7 @@ import WxEditor from '@/views/mp/components/wx-editor/WxEditor.vue';
import WxNews from '@/views/mp/components/wx-news/main.vue';
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
import { getAccessToken } from '@/utils/auth'
import { createDraft, getDraftPage } from "@/api/mp/draft";
import {createDraft, getDraftPage, updateDraft} from "@/api/mp/draft";
import { getSimpleAccounts } from "@/api/mp/account";
export default {
@ -192,18 +189,12 @@ export default {
accountId: undefined,
},
page1: {
total: 0, //
currentPage: 1, //
pageSize: 10 //
},
// ========== ==========
actionUrl: process.env.VUE_APP_BASE_API + "/admin-api/mp/material/upload-permanent", //
headers: { Authorization: "Bearer " + getAccessToken() }, //
fileList: [],
uploadData: {
"type": 'image', // TODO thumb
"type": 'image',
// "accountId": 1,
},
@ -213,8 +204,6 @@ export default {
articlesAdd: [],
isActiveAddNews: 0,
dialogImageVisible: false,
imageListData: [],
tableLoading1: false,
operateMaterial: 'add',
articlesMediaId: '',
hackResetEditor: false,
@ -236,6 +225,7 @@ export default {
})
},
methods: {
// ======================== ========================
/** 设置账号编号 */
setAccountId(accountId) {
this.queryParams.accountId = accountId;
@ -267,6 +257,10 @@ export default {
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1
//
if (this.queryParams.accountId) {
this.setAccountId(this.queryParams.accountId)
}
this.getList()
},
/** 重置按钮操作 */
@ -279,62 +273,24 @@ export default {
this.handleQuery()
},
delMaterial(item){
this.$confirm('此操作将永久删除该草稿, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.loading = true
delObj({
id:item.mediaId
}).then(response => {
this.loading = false
if(response.code == 200){
this.getList(this.queryParams)
}else{
this.loading = false
this.$message.error('删除出错:' + response.msg)
}
}).catch(() => {
this.loading = false
})
})
// ======================== /稿 ========================
/** 新增按钮操作 */
handleAdd() {
this.resetEditor();
this.reset();
//
this.operateMaterial = 'add'
this.dialogNewsVisible = true
},
getPage1(){
this.tableLoading1 = true
getPage1({
current: this.page1.currentPage,
size: this.page1.pageSize,
type:'image'
}).then(response => {
this.tableLoading1 = false
this.imageListData = response.data.items
this.page1.total = response.data.totalCount
}).catch(() => {
this.tableLoading1 = false
})
},
openMaterial(){
this.imageListData = null
this.page1.currentPage = 1
this.getPage1()
this.dialogImageVisible = true
},
dialogNewsClose(done){
this.$confirm('修改内容可能还未保存,确定关闭吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.hackResetEditor = false//
this.$nextTick(() => {
this.hackResetEditor = true//
})
this.isActiveAddNews = 0
done()
}).catch(() => {
})
/** 更新按钮操作 */
handleUpdate(item){
this.resetEditor();
this.reset();
this.articlesMediaId = item.mediaId
this.articlesAdd = JSON.parse(JSON.stringify(item.content.newsItem))
//
this.operateMaterial = 'edit'
this.dialogNewsVisible = true
},
/** 提交按钮 */
submitForm() {
@ -347,59 +303,23 @@ export default {
}).finally(() => {
this.addMaterialLoading = false
})
}
if(this.operateMaterial == 'edit'){
putObj({
articles:this.articlesAdd,
mediaId:this.articlesMediaId
}).then(response => {
this.addMaterialLoading = false
this.dialogNewsVisible = false
if(response.code == 200){
this.isActiveAddNews = 0
this.articlesAdd = [
{
"title": '',
"thumbMediaId": '',
"author": '',
"digest": '',
"showCoverPic": '',
"content": '',
"contentSourceUrl": '',
"needOpenComment":'',
"onlyFansCanComment":'',
"thumbUrl":''
}
]
this.getList(this.queryParams)
}else{
this.$message.error('修改图文出错:' + response.msg)
}
}).catch(() => {
} else {
updateDraft(this.queryParams.accountId, this.articlesMediaId, this.articlesAdd).then(response => {
this.$modal.msgSuccess("更新成功");
this.dialogNewsVisible = false;
this.getList()
}).finally(() => {
this.addMaterialLoading = false
})
}
},
handleEditNews(item){
this.hackResetEditor = false //
this.$nextTick(() => {
this.hackResetEditor = true //
})
if (this.operateMaterial == 'add') {
this.isActiveAddNews = 0
}
this.operateMaterial = 'edit'
this.articlesAdd = JSON.parse(JSON.stringify(item.content.newsItem))
this.articlesMediaId = item.mediaId
this.dialogNewsVisible = true
},
/** 新增按钮操作 */
handleAdd() {
this.resetEditor();
this.reset();
//
this.operateMaterial = 'add'
this.dialogNewsVisible = true
//
dialogNewsClose(done) {
this.$modal.confirm('修改内容可能还未保存,确定关闭吗?').then(() => {
this.reset()
this.resetEditor()
done()
}).catch(() => {})
},
//
reset() {
@ -498,11 +418,20 @@ export default {
this.articlesAdd[this.isActiveAddNews].thumbMediaId = response.data.mediaId
this.articlesAdd[this.isActiveAddNews].thumbUrl = response.data.url
},
selectMaterial(item){
// or 稿
selectMaterial(item) {
this.dialogImageVisible = false
this.articlesAdd[this.isActiveAddNews].thumbMediaId = item.mediaId
this.articlesAdd[this.isActiveAddNews].thumbUrl = item.url
},
//
openMaterial() {
this.dialogImageVisible = true
try {
this.$refs['materialSelect'].queryParams.accountId = this.queryParams.accountId // accountId
this.$refs['materialSelect'].handleQuery(); //
} catch (e) {}
},
// ======================== 稿 ========================
handlePublishNews(item){
@ -521,248 +450,270 @@ export default {
})
}).catch(() => {
})
}
},
delMaterial(item){
this.$confirm('此操作将永久删除该草稿, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.loading = true
delObj({
id:item.mediaId
}).then(response => {
this.loading = false
if(response.code == 200){
this.getList(this.queryParams)
}else{
this.loading = false
this.$message.error('删除出错:' + response.msg)
}
}).catch(() => {
this.loading = false
})
})
},
}
}
</script>
<style lang="scss" scoped>
.pagination {
float: right;
margin-right: 25px;
}
.pagination {
float: right;
margin-right: 25px;
}
.add_but {
padding: 10px;
}
.add_but {
padding: 10px;
}
.ope-row {
margin-top: 5px;
text-align: center;
border-top: 1px solid #eaeaea;
padding-top: 5px;
}
.ope-row {
margin-top: 5px;
text-align: center;
border-top: 1px solid #eaeaea;
padding-top: 5px;
}
.item-name {
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.item-name {
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.el-upload__tip {
margin-left: 5px;
}
.el-upload__tip {
margin-left: 5px;
}
/*新增图文*/
.left {
display: inline-block;
width: 35%;
vertical-align: top;
margin-top: 200px;
}
/*新增图文*/
.left {
display: inline-block;
width: 35%;
vertical-align: top;
margin-top: 200px;
}
.right {
display: inline-block;
width: 60%;
margin-top: -40px;
}
.right {
display: inline-block;
width: 60%;
margin-top: -40px;
}
.avatar-uploader {
width: 20%;
display: inline-block;
}
.avatar-uploader {
width: 20%;
display: inline-block;
}
.avatar-uploader .el-upload {
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
text-align: unset !important;
}
.avatar-uploader .el-upload {
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
text-align: unset !important;
}
.avatar-uploader .el-upload:hover {
border-color: #165dff;
}
.avatar-uploader .el-upload:hover {
border-color: #165dff;
}
.avatar-uploader-icon {
border: 1px solid #d9d9d9;
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
}
.avatar-uploader-icon {
border: 1px solid #d9d9d9;
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
}
.avatar {
width: 230px;
height: 120px;
}
.avatar {
width: 230px;
height: 120px;
}
.avatar1 {
width: 120px;
height: 120px;
}
.avatar1 {
width: 120px;
height: 120px;
}
.digest {
width: 60%;
display: inline-block;
vertical-align: top;
}
.digest {
width: 60%;
display: inline-block;
vertical-align: top;
}
/*新增图文*/
/*瀑布流样式*/
/*新增图文*/
/*瀑布流样式*/
.waterfall {
width: 100%;
column-gap: 10px;
column-count: 5;
margin: 0 auto;
}
.waterfall-item {
padding: 10px;
margin-bottom: 10px;
break-inside: avoid;
border: 1px solid #eaeaea;
}
p {
line-height: 30px;
}
@media (min-width: 992px) and (max-width: 1300px) {
.waterfall {
width: 100%;
column-gap: 10px;
column-count: 5;
margin: 0 auto;
column-count: 3;
}
.waterfall-item {
padding: 10px;
margin-bottom: 10px;
break-inside: avoid;
border: 1px solid #eaeaea;
}
p {
line-height: 30px;
color: red;
}
}
@media (min-width: 992px) and (max-width: 1300px) {
.waterfall {
column-count: 3;
}
p {
color: red;
}
@media (min-width: 768px) and (max-width: 991px) {
.waterfall {
column-count: 2;
}
p {
color: orange;
}
}
@media (min-width: 768px) and (max-width: 991px) {
.waterfall {
column-count: 2;
}
p {
color: orange;
}
@media (max-width: 767px) {
.waterfall {
column-count: 1;
}
}
@media (max-width: 767px) {
.waterfall {
column-count: 1;
}
}
/*瀑布流样式*/
.news-main {
background-color: #FFFFFF;
width: 100%;
margin: auto;
height: 120px;
}
/*瀑布流样式*/
.news-main {
background-color: #FFFFFF;
width: 100%;
margin: auto;
height: 120px;
}
.news-content {
background-color: #acadae;
width: 100%;
height: 120px;
position: relative;
}
.news-content {
background-color: #acadae;
width: 100%;
height: 120px;
position: relative;
}
.news-content-title {
display: inline-block;
font-size: 15px;
color: #FFFFFF;
position: absolute;
left: 0px;
bottom: 0px;
background-color: black;
width: 98%;
padding: 1%;
opacity: 0.65;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 25px;
}
.news-content-title {
display: inline-block;
font-size: 15px;
color: #FFFFFF;
position: absolute;
left: 0px;
bottom: 0px;
background-color: black;
width: 98%;
padding: 1%;
opacity: 0.65;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 25px;
}
.news-main-item {
background-color: #FFFFFF;
padding: 5px 0px;
border-top: 1px solid #eaeaea;
width: 100%;
margin: auto;
}
.news-main-item {
background-color: #FFFFFF;
padding: 5px 0px;
border-top: 1px solid #eaeaea;
width: 100%;
margin: auto;
}
.news-content-item {
position: relative;
margin-left: -3px
}
.news-content-item {
position: relative;
margin-left: -3px
}
.news-content-item-title {
display: inline-block;
font-size: 12px;
width: 70%;
}
.news-content-item-title {
display: inline-block;
font-size: 12px;
width: 70%;
}
.news-content-item-img {
display: inline-block;
width: 25%;
background-color: #acadae
}
.news-content-item-img {
display: inline-block;
width: 25%;
background-color: #acadae
}
.input-tt {
padding: 5px;
}
.input-tt {
padding: 5px;
}
.activeAddNews {
border: 5px solid #2bb673;
}
.activeAddNews {
border: 5px solid #2bb673;
}
.news-main-plus {
width: 280px;
text-align: center;
margin: auto;
height: 50px;
}
.news-main-plus {
width: 280px;
text-align: center;
margin: auto;
height: 50px;
}
.icon-plus {
margin: 10px;
font-size: 25px;
}
.icon-plus {
margin: 10px;
font-size: 25px;
}
.select-item {
width: 60%;
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.select-item {
width: 60%;
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.father .child {
display: none;
text-align: center;
position: relative;
bottom: 25px;
}
.father .child {
display: none;
text-align: center;
position: relative;
bottom: 25px;
}
.father:hover .child {
display: block;
}
.father:hover .child {
display: block;
}
.thumb-div {
display: inline-block;
width: 30%;
text-align: center;
}
.thumb-div {
display: inline-block;
width: 30%;
text-align: center;
}
.thumb-but {
margin: 5px;
}
.thumb-but {
margin: 5px;
}
.material-img {
width: 100%;
height: 100%;
}
.material-img {
width: 100%;
height: 100%;
}
</style>