vue 美食杰 发布菜谱


在这里插入图片描述

header.vue

            <router-link to="/create" class="collection">发布菜谱</router-link>

create.vue

<template>
  <div class="create">
    <h2>欢迎发布新菜谱,先介绍一下你的大作!</h2>
    <section class="create-introduce">
      <h5>标题</h5>
      {{backData.title}}
      <el-input class="create-input" placeholder="请输入内容" v-model="backData.title"></el-input>
      <h5>属性</h5>
      <div>
        <el-select
          v-for="item in properties"
          :key="item.parent_type"
          :placeholder="item.parent_name"
          v-model="backData.property[item.title]"
        >
          <el-option
            v-for="option in item.list"
            :key="option.type"
            :label="option.name"
            :value="option.type"
          >
          </el-option>
        </el-select>
      </div>
      <h5>菜谱分类</h5>
      <div>
        <el-select placeholder="请选择菜谱分类" v-model="backData.classify">
          <el-option-group
            v-for="group in classifies"
            :key="group.parent_type"
            :label="group.parent_name">
            <el-option
              v-for="item in group.list"
              :key="item.type"
              :label="item.name"
              :value="item.type">
            </el-option>
          </el-option-group>
        </el-select>
      </div>
      <h5>成品图 (328*440)</h5>
      <div class="upload-img-box clearfix">
        <div class="upload-img">
          <upload-img
            action="/api/upload?type=product"
            :image-url="backData.product_pic_url"
            @res-url="(data) => {backData.product_pic_url = data.resImgUrl}"
          ></upload-img>
        </div>
        <el-input
          class="introduce-text"
          type="textarea"
          :rows="10"
          placeholder="请输入内容"
          v-model="backData.product_story"
        >
        </el-input>
      </div>
    </section>

    <h2>记录所有原材料</h2>
    <section class="create-introduce">
      <h5>主料</h5>
      <!--[ { "name": "", "specs": "" }, { "name": "", "specs": "" }, { "name": "", "specs": "" } ]-->
      <Stuff 
        v-model="backData.raw_material.main_material"
      ></Stuff>
      <h5>辅料</h5>
      <Stuff 
        v-model="backData.raw_material.accessories_material"
      ></Stuff>
    </section>

    <h2>开始写步骤了!能否简单易学就看你怎么写了,加油!</h2>
    <section class="create-introduce">
      <Upload 
        v-for="(item,index) in backData.steps" 
        :key="item.customeId"
        :n="index+1"
        v-model="backData.steps[index]"
        @remove="removeStep"
      ></Upload>
      <el-button 
        class="eaeaea add-step-button" 
        type="primary" 
        size="medium" 
        icon="el-icon-plus"
        @click="addStep"
      >增加一步</el-button>
      <h5>烹饪小技巧</h5>
      <el-input
        class="introduce-text"
        type="textarea"
        :rows="8"
        placeholder="分享下你做这道菜的过程中的心得和小技巧吧!"
        v-model="backData.skill"
      >
      </el-input>
    </section>

    <el-button 
      class="send" 
      type="primary" 
      size="medium" 
      :icon="icon"
      @click="send"
    >搞定,提交审核</el-button>

  </div>
</template>
<script>
import Stuff from './stuff'
import Upload from './step-upload'
import UploadImg from '@/components/upload-img'
import {getProperty, getClassify, publish} from '@/service/api'
// 向后端发送的数据结构
const backData = {
  title: '',  // 标题
  product_pic_url: '', // 成品图URL
  product_story: '', // 成品图故事
  property: {
    craft: 0,  // 工艺 enum: [1,2,3,4],
    flavor: 0,  // 口味  enum: [1,2,3,4],
    hard: 0,   // 难度 enum: [1,2,3,4],
    pepole: 0  // pepole 人数: [1,2,3,4],
  },  // 属性
  raw_material: { // 料
    main_material: [{name: '',specs: ''}], // 主料
    accessories_material: [{name: '',specs: ''}], // 辅料
  },
  steps: [{img_url: '',describe: '',}], // 步骤
  classify: '', // 菜谱分类
  skill: '',
}
// 用料的数据结构
const raw_material_struct = {
  name: '',specs: ''
}
// 步骤中, 每一步的数据结构
const steps_struct = {img_url: '',describe: '',}
// 步骤中,存储序号
let n = 1;
export default {
  name: 'create',
  components: {Stuff,Upload,UploadImg},
  data(){
    return {
      properties: [],  // 页面展示的数据
      classifies: [], 
      icon: '',
      backData: {
         // 标题
        title: "",
        // 属性
        property: {
          craft: "", // 工艺 enum: [1,2,3,4],
          flavor: "", // 口味  enum: [1,2,3,4],
          hard: "", // 难度 enum: [1,2,3,4],
          people: "", // pepole 人数: [1,2,3,4],
        },
        // 菜谱分类
        classify: "",
        // 成品图URL
        product_pic_url:
          "https://s1.c.meishij.net/n/images/upload_big_img.png?_=1561906961",
        // 成品图故事
        product_story: "",
        // 料
        raw_material: {
          main_material: [{ name: "", specs: "" }], // 主料
          accessories_material: [{ name: "", specs: "" }], // 辅料
        },
        // 步骤  { img_url: "", describe: "" }
        steps: Array(3)
          .fill(1)
          .map(() => ({ ...steps_struct, customeId: this.uuid() })),
        skill: "",
      },
      icon: "",
      }
    }
  },
  mounted(){
    getProperty().then(({data}) => {
      this.properties = data;
      this.backData.property = data.reduce((o,item) => {
        o[item.title] = '';
        return o;
      },{});
    })
    getClassify().then(({data}) => {
      console.log(data);
      this.classifies = data;
    })
  },
  methods:{
    uuid(){
      n++;
      return Date.now() + n;
    },
    addStep(){
      this.backData.steps.push({
        ...steps_struct,
        customeId: this.uuid()
      })
    },
    removeStep(index){
      this.backData.steps.splice(index-1, 1);
    },
    send(){
      this.icon = 'el-icon-loading';
      let param = this.backData;
      // 验证
      // 删除字段 删除字段后,当前页面需要用到这个字段的地方可能会有问题
      // 提取出需要的字段
      param.steps = param.steps.map((item) => {
        return {
          img_url: item.img_url,
          describe: item.describe,
        }
      })
      // 1. 测试过程中不跳转,手动去打开指定的跳转的页面去看数据对不对
      // 2. mock数据,模拟一套数据,预先准备一套

      //console.log(JSON.stringify(param, null, 2)); 这个2 看懵了吧? 这是转字符串输出, 2个间距,有空多看看基础吧
      publish(param).then((data) => {
        console.log(data);
        this.$router.push({
          name: 'space'
        })
      })
    }
  }
}
</script>
<style lang="stylus">

.create-introduce 
  background-color #fff
  padding 20px

  .add-step-button
    margin-left 100px

.create
  width 100%
  h2
    text-align center
    margin 20px 0
  .send
    // ff3232()
    height: 70px;
    width: 220px;
    background #ff3232
    color #fff
    border none
    margin 20px auto
    display block
  h5 
    margin 20px 0

.create-input input
  width 446px
  line-height 22px
.upload-img-box 
  .upload-img
    float left
  .introduce-text
    float left
  .el-textarea
    width 60%
    margin-left 10px
</style>

stuff.vue

<template>
  <div class="stuff">
    <div class="clearfix">
      <div class="raw-item" v-for="(item, index) in value" :key="index">
        <el-input
          placeholder="请输入内容"
          v-model="item.name"
          style="width: 200px"
        ></el-input>
        <el-input
          placeholder="请输入内容"
          v-model="item.specs"
          style="width: 100px"
        ></el-input>
        <i class="delete-icon el-icon-close" v-show="value.length!=1" @click="deletes(index)"></i>
      </div>
    </div>
    <el-button class="eaeaea" @click="medium" type="primary" size="medium" icon="el-icon-plus"
      >增加一项</el-button
    >
  </div>
</template>
<script>
// v-model 在组件上面双向绑定 value 发布事件input
export default {
  props: {
    value: {
      type: Array,
      default: () => [],
    },
  },
  methods: {
    deletes(i){
      this.value.splice(i,1)
      
      this.$emit("input",[
        ...this.value
      ])
    },
    medium(){
      this.$emit("input",[
        ...this.value,{ name: "", specs: "" }
      ])
    }
  },
};
</script>
<style lang="stylus">
.delete-icon
  background-color #ccc
  border-radius 50%
  color #fff
  :hover
    background: #ff3232;
    color: #fff;
.raw-item
  float left
  margin-right 65px
  margin-bottom 20px
  .el-input
    margin-right 5px
</style>

step-upload.vue

<template>
  <div class="step clearfix">
    <div class="step-number">{{ index }}.</div>
    <div class="upload-box">
      <upload-img
        :img-max-width="184"
        action="/api/upload?type=product"
        :imageUrl="info.img_url?info.img_url:'https://s1.c.meishij.net/n/images/upload_big_img.png?_=1561906961'"
        @imageUrl="
          (data) => {
            info.img_url = data.resImgUrl;
          }
        "
      ></upload-img>
    </div>
    <el-input
      class="introduce-text"
      type="textarea"
      :rows="8"
      v-model="info.describe"
      placeholder="请输入内容"
    >
    </el-input>
    <i class="delete-icon el-icon-close" v-show="length != 1" @click="icon(index)"></i>
    
  </div>
</template>
<script>
import UploadImg from "@/components/upload-img";
export default {
  components: { UploadImg },
  imageUrl: "https://s1.c.meishij.net/n/images/upload_step_img.png",
  props: {
    info: {
      type: Object,
      default: () => [],
    },
    index: {
      type: Number,
      default: 1,
    },
    length: {
      type: Number,
      default: 0,
    },
  },
  methods: {
    icon(i) {
      // console.log(i-1);
      this.$emit("input", i);
    },
  },
};
</script>

<style lang="stylus">
.step
  margin-bottom 20px
  > div
    float left
  .step-number
    height 180px
    width 100px
    font-size 60px
    color #aaa
    font-family Arial, Helvetica, sans-serif
    font-weight bold
    vertical-align top
    line-height 180px
  .introduce-text
    width 60%
    margin-left 40px
  .upload-box
    img
      vertical-align top
</style>

upload-img.vue

  <el-upload
    class="avatar-uploader"
    :action="action"
    :show-file-list="false"
    :on-success="handleAvatarSuccess"
    :before-upload="beforeAvatarUpload"
  >
    <img :src="url"  :style="{ width: imgMaxWidth + 'px' }"/>
  </el-upload>
</template>
<script>
export default {
  props: {
    action: String,
    imageUrl: {
      type: String,
    },
    imgMaxWidth:{
      type:  [Number, String],
      default:"210"

    }
  },
  data() {
    return {
      url: this.imageUrl,
    };
  },
  methods: {
    handleAvatarSuccess(res, file) {
      console.log("avatarSuccess",res, file);
       if (res.code === 1) {
        this.$message({
          message: res.mes,
          type: "watning",
        });
        return;
      }


      this.url = URL.createObjectURL(file.raw);
      this.$emit("imageUrl",{
        resImgUrl:this.url
      })
    },
    beforeAvatarUpload(file) {
      console.log(file, "beforeAvatarUpload");
      const isJPG = file.type === "image/jpeg" ;
      const isLt2M = file.size / 1024 / 1024 < 2;

      if (!isJPG) {
        this.$message.error("上传头像图片只能是 JPG 格式!");
      }
      if (!isLt2M) {
        this.$message.error("上传头像图片大小不能超过 2MB!");
      }
      return isJPG && isLt2M;
    },
  },
};
</script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值