uniapp 给画作生成画框

javascript 复制代码
<template>
  <ax-page class="privateCustom">
    <gui-page :customHeader="true" ref="guipage">
      <template #gHeader>
        <aHeader title="个性定制" :showTitle="true" back="2">
        </aHeader>
      </template>
      <template v-slot:gBody>
        <view class="privateCustom-body">
          <form @submit="submit">
            <view class="gui-bg-white gui-dark-bg-level-3 gui-padding-x">
              <view class="gui-form-item gui-border-b">
                <text class="gui-form-label"
                  >{{ pageIndex == 0 ? '上传图片' : '已选画作' }} |</text
                >
                <view class="rowContentDiv">
                  <view v-if="pageIndex == 0">
                    <view
                      @tap="uploadImgClick"
                      class="uploadDiv"
                      v-if="!formData.image"
                    >
                      <image class="addImg" :src="addPImg" mode="scaleToFill" />
                      <text>图片</text>
                    </view>
                    <image
                      v-else
                      :src="formData.image"
                      class="uploadDiv"
                      mode="aspectFit"
                      @tap="uploadImgClick"
                    ></image>
                  </view>
                  <image
                    :src="canvasSize.imgSrc || formData.image"
                    class="posterImg"
                    mode="aspectFit"
                    @tap="previewImage(canvasSize.imgSrc || formData.image)"
                  ></image>
                </view>
              </view>
              <!-- <view class="gui-form-item gui-border-b">
                <text class="gui-form-label">画作名称 |</text>
                <view class="frame-box content">
                  <input
                    type="text"
                    class="gui-form-input"
                    v-model="formData.name"
                    name="name"
                    placeholder="请输入内容"
                  />
                </view>
              </view> -->
              <view class="gui-form-item gui-border-b" v-if="pageIndex == 0">
                <text class="gui-form-label">画框选择 |</text>
                <view class="frame-box content">
                  <view
                    style="display: flex; align-items: center"
                    v-for="(item, index) in selectFrame"
                    :key="index"
                    :class="[
                      'frame-item',
                      { 'frameImg-active': index == formData.frame }
                    ]"
                    @tap="frameClick(index)"
                  >
                    <image
                      :src="$App.ossImage(item.url)"
                      class="frameImg"
                      mode="aspectFit"
                    />
                    <text>{{ item.name }}</text>
                  </view>
                </view>
              </view>
              <view class="gui-form-item gui-border-b" v-if="pageIndex == 0">
                <text class="gui-form-label">选择画纸 |</text>
                <view class="frame-box content">
                  <view
                    style="display: flex; align-items: center"
                    v-for="(item, index) in papers"
                    :key="index"
                    :class="[
                      'frame-item',
                      { 'frameImg-active': index == formData.paper }
                    ]"
                    @tap="paperClick(index)"
                  >
                    <image
                      :src="$App.ossImage(item.url)"
                      class="frameImg"
                      mode="aspectFit"
                    />
                    <text>{{ item.name }}</text>
                  </view>
                </view>
              </view>
              <view class="gui-form-item gui-border-b" v-if="pageIndex == 0">
                <text class="gui-form-label">画框尺寸 |</text>
                <view class="frame-box content">
                  <picker
                    @change="selectSize"
                    :value="currentSize"
                    range-key="key"
                    v-if="selectSizeMenu.length > 0"
                    :range="selectSizeMenu"
                  >
                    <view class="uni-input">{{
                      selectSizeMenu[currentSize].key
                    }}</view>
                  </picker>
                </view>
              </view>
              <view
                class="gui-form-item gui-border-b"
                v-if="
                  currentSize == selectSizeMenu.length - 1 && pageIndex == 0
                "
              >
                <text class="gui-form-label">自定义尺寸</text>
                <view class="frame-box content">
                  <input
                    type="number"
                    class="gui-form-input"
                    v-model="formData.size"
                    name="size"
                    placeholder="最小10"
                    @blur="getImageSize"
                    :min="1"
                  />
                </view>
              </view>

              <view class="gui-form-item gui-border-b" v-if="pageIndex == 0">
                <text class="gui-form-label">卡纸宽度 |</text>
                <view class="frame-box content">
                  <picker
                    @change="selectWidths"
                    :value="formData.width"
                    range-key="w"
                    v-if="widths.length > 0"
                    :range="widths"
                  >
                    <view class="uni-input"
                      >{{ widths[formData.width].w }}cm</view
                    >
                  </picker>
                </view>
              </view>

              <view class="gui-form-item gui-border-b" v-if="pageIndex == 0">
                <text class="gui-form-label">选择数量 |</text>
                <view class="frame-box content">
                  <gui-step-box
                    :inputClass="[
                      'gui-step-box-input',
                      'gui-border-radius',
                      'gui-bg-gray',
                      'gui-dark-bg-level-2'
                    ]"
                    @change="stepChange"
                    :value="formData.num"
                    :minNum="1"
                  ></gui-step-box>
                </view>
              </view>

              <view class="gui-form-item gui-border-b" v-if="pageIndex == 0">
                <text class="gui-form-label">指导价格 |</text>
                <view class="price-box content">
                  <image :src="pricePImg" class="pricePImg" />
                  <text class="price-text">{{
                    Utils.amountS(formData.price)
                  }}</text>
                </view>
              </view>
              <view class="gui-form-item gui-border-b" v-if="pageIndex == 1">
                <text class="gui-form-label">作品备注 |</text>
                <view class="frame-box content">
                  <input
                    type="text"
                    class="gui-form-input"
                    v-model="formData.mark"
                    name="mark"
                    placeholder="请输入备注"
                  />
                </view>
              </view>

              <view class="gui-form-item gui-border-b" v-if="pageIndex == 1">
                <text class="gui-form-label">收货地址 |</text>
                <view class="frame-box content" @tap="goAddressList">
                  <view
                    class="left-part"
                    v-if="Object.keys(formData.address).length > 0"
                  >
                    <view class="name">
                      {{ formData.address.recipient }}
                      {{ formData.address.phone }}
                    </view>
                    <view class="address-detail">
                      {{
                        `${formData.address.address}${formData.address.detail}`
                      }}
                    </view>
                  </view>
                  <view class="left-part" v-else>
                    <view class="name"> 请选择收货地址 </view>
                  </view>
                  <!-- <view class="right-part">
                    <text
                      class="gui-list-arrow-right gui-icons gui-color-gray-light"
                      >&#xe601;</text
                    >
                  </view> -->
                </view>
              </view>
              <view class="gui-form-item gui-border-b" v-if="pageIndex == 1">
                <text class="gui-form-label">提示 |</text>
                <view class="frame-box content" style="color: red">
                  定制商品,非商品自身问题,暂不支持退换服务,请您在确认订单前仔细核对商品信息,如有任何疑问,请及时联系我们。感谢您的理解与支持!
                </view>
              </view>
            </view>

            <button
              class="gui-button gui-bg-primary gui-noborder submitBtn"
              formType="submit"
            >
              <text class="gui-color-white gui-button-text">{{
                pageIndex == 0 ? '去下单' : '确认下单'
              }}</text>
            </button>
            <view style="height: 60rpx"></view>
          </form>
          <view class="canvas-in">
            <canvas
              v-if="canvasSize.heightIn > 0"
              :style="{
                width: canvasSize.widthIn + 'px',
                height: canvasSize.heightIn + 'px',
                opacity: 0
              }"
              canvas-id="graceCanvas"
              class="grace-canvas"
            ></canvas>
          </view>
        </view>
      </template>
    </gui-page>
  </ax-page>
</template>
<script setup lang="ts">
import App from '@/script/module/App'
import PublicImg from '@/components/publicImage.vue'
import User from '@/script/module/User'
import { axj } from '@/script/sdk/ms-store'
import axConfig from '@/script/AxConfig'
import {
  onBackPress,
  onLoad,
  onPageScroll,
  onReachBottom,
  onShow,
  onUnload
} from '@dcloudio/uni-app'
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
import Page from '@/script/module/Page'
import Utils from '@/script/util/Utils'
import Artist from '@/script/module/Artist'
import addPImg from '../images/addP.png'
import pricePImg from '../images/priceP.png'
import SdkActs from '@/script/module/SdkActs'

const pageIndex = ref(0)
const selectSizeMenu = ref([])
const sizeList = ref([])
const selectFrame = ref([])
const papers = ref([])
const widths = ref([])

const formData = reactive({
  name: '',
  mark: '',
  image: '',
  frame: 0,
  num: 1,
  price: <number | string>'',
  address: User.state.address,
  size: <any>10,
  width: 0,
  paper: 0
})
const currentSize = ref(0)
const customOrderCfg = ref(<axj.DtDCustomOrderCfg>undefined)
const imgInfo = ref({
  width: 0,
  height: 0,
  aspectRatio: 0
})

const canvasSize = reactive({
  widthIn: 0, // 自动计算转换为 px
  heightIn: 0, //  自动计算转换为 px
  bgColor: '#ffffff', // 背景颜色
  bgImg: '',
  contentImg: '',
  imgSrc: '',
  multiple: 1, // 将画布放大 2.0 - 2.9 倍(支持小数,过大app端会出现无法渲染的问题),保存的图片更清晰
  bw: 0,
  iw: 0,
  ih: 0,
  w: 0,
  h: 0
})
const context = ref(<any>null)

const imagesPath = ref(<any>[])

// 选择图片
const uploadImgClick = () => {
  uni.chooseImage({
    count: 1,
    extension: ['jpeg', 'jpg', 'png'],
    success: (res) => {
      console.log('[  getImageInfo  ] >', res)
      formData.image = res.tempFilePaths[0]
      getImageInfo(res.tempFilePaths[0])
      formData.name = res?.tempFiles[0]?.name || ''
    }
  })
}

// 获取图片信息
const getImageInfo = (tempFilePath) => {
  uni.getImageInfo({
    src: tempFilePath,
    success: (info) => {
      const width = info.width
      const height = info.height
      // 获取宽高比,用于自定义尺寸时候计算宽度
      const aspectRatio = width / height
      imgInfo.value = {
        width: width,
        height: height,
        aspectRatio: aspectRatio
      }
      initSize()
      customOrder()
    }
  })
}

// 自定义尺寸重绘画布
const getImageSize = () => {
  customOrder()
  initSize()
}

// 上传图片
const uploadFun = (
  filePath,
  name: string = new Date().getTime() + 'image',
  back: () => any
) => {
  Artist.fileUpload(filePath, name, 'png', (url) => {
    uni.hideLoading()
    formData.image = url
    if (back) {
      back()
    }
  })
}
// 选择画框
const frameClick = (index) => {
  formData.frame = index
  initSize()
  customOrder()
}

const paperClick = (index) => {
  formData.paper = index
  customOrder()
}

// 定做数量
const stepChange = (e) => {
  if (e && e[0]) {
    formData.num = e[0]
    customOrder()
  }
}

// 选择尺寸
const selectSize = (e) => {
  // console.log(e)
  currentSize.value = e.detail.value
  if (currentSize.value == selectSizeMenu.value.length - 1) {
    formData.size = selectSizeMenu.value[0].w
  } else {
    formData.size = selectSizeMenu.value[e.detail.value].w
    initSize()
    customOrder()
  }
}

// 选择卡纸宽度
const selectWidths = (e) => {
  formData.width = e.detail.value
  initSize()
  customOrder()
}

// 选择地址
const goAddressList = () => {
  SdkActs.address(null, null, null)
}

// 提交订单
const submit = (e) => {
  if (formData.image.length == 0) {
    App.toast('请上传图片')
    return
  }

  if (pageIndex.value == 1) {
    if (!formData.address) {
      App.toast('请选择收货地址')
      return
    }
    uni.showModal({
      title: '提示',
      content: '确认要提交订单吗?',
      success: (res) => {
        if (res.confirm) {
          // 上传图片后回调提交订单
          uploadFun(formData.image, formData.name, () => {
            customOrder(true)
          })
        } else if (res.cancel) {
          // 用户点击取消按钮
          console.log('用户点击取消')
        }
      }
    })
  } else {
    pageIndex.value = 1
  }
}

/**
 * 自定义画框订单(包括预览逻辑)  confirm 	确认订单(下单)
 * 流程 : 提交订单-订单详情-支付
 */
const customOrder = (confirm?: boolean) => {
  if (formData.image.length == 0) {
    App.toast('请上传图片')
    return
  }
  // if (!formData.name) {
  //   App.toast('请输入画作名称')
  //   return
  // }

  let sizes = selectSizeMenu.value[currentSize.value]
  let params = {
    image: formData?.image, //	  画芯
    // name: formData?.name, //		画作名称
    frameIdx: formData.frame, //		画框索引
    paperIdx: formData.paper, //		画纸索引
    widthIdx: formData.width, //		卡纸宽度索引
    // attName: selectFrame.value[formData.frame]?.name, //		画框名称
    w: sizes?.w, //		宽(cm)
    h: sizes?.h, //		高(cm)
    num: formData.num, //		数量
    mark: formData.mark, //		备注
    confirm: confirm || false, //		确认订单(下单)
    address: formData.address //		收货地址
  }
  App.client.store.Api_order.customOrder(
    1,
    [params],
    (err: any, res: axj.DtDOrderRep) => {
      console.log('[ customOrder ] >', err, res)
      formData.price = res.amount
      if (res?.orderId && confirm) {
        // uni.redirectTo({
        //   url: `/pagesShop/shop/cashierDesk?orderId=${res.orderId}`
        // })
        Page.navUri(`/pagesShop/order/orderDetail?orderId=${res.orderId}`)
      } else {
        // initSize()
      }
    }
  )
}
// 获取初始化信息
const getCustomOrderCfg = () => {
  App.client.store.Api_order.customOrderCfg(
    1,
    [],
    (err: any, res: axj.DtDCustomOrderCfg) => {
      customOrderCfg.value = res
      if (res) {
        if (res.sizes) {
          // selectSizeMenu.value = ['其他']
          // 拼接尺寸显示内容
          for (let i = 0; i < res.sizes.length; i++) {
            const el = res.sizes[i]
            if (el.open) {
              el.key = el.w + 'cm*' + el.h + 'cm'
              // selectSizeMenu.value.unshift(size)
            }
          }

          selectSizeMenu.value = res.sizes
          // 增加自定义尺寸选项
          let obj = {
            name: '其他',
            w: res.sizes[0].w || 10,
            h: res.sizes[0].h || 10,
            open: true,
            key: '其他'
          }
          formData.size = obj.w
          //@ts-ignore
          selectSizeMenu.value.push(obj)
        }
        if (res.frames) {
          selectFrame.value = res.frames
        }
        if (res.papers) {
          papers.value = res.papers
        }
        if (res.widths) {
          widths.value = res.widths
        }
      }
    }
  )
}

// 判断是否存在相同图片,存在则直接显示,不存在则生成
const checkNameExists = (back: (res: any) => any) => {
  let imgPath = imagesPath.value
  let name =
    selectSizeMenu.value[currentSize.value].key +
    '_' +
    selectSizeMenu.value[currentSize.value].w +
    '_' +
    formData.name +
    selectFrame.value[formData.frame].name +
    widths.value[formData.width].w

  const existingImage = imgPath.find((image) => image.name === name)

  if (existingImage) {
    canvasSize.imgSrc = existingImage.url
    back(existingImage)
  } else {
    back(null)
  }
}

// w h 图片+画框的宽高  ,bw 画框尺寸 ,iw ih 图片宽高
const initSize = () => {
  if (!formData.image) {
    return
  }
  // 如果是自定义宽度,根据图片宽高比,计算高度
  if (currentSize.value == selectSizeMenu.value.length - 1) {
    formData.size = parseInt(formData.size)
    formData.size = Math.max(formData.size, 10)
    if (formData.size) {
      const width = formData.size
      const height = width / (imgInfo.value.aspectRatio || 1)
      selectSizeMenu.value[selectSizeMenu.value.length - 1].w = width.toFixed(2)
      selectSizeMenu.value[selectSizeMenu.value.length - 1].h =
        height.toFixed(2)
    }
  }

  checkNameExists((res) => {
    if (!res) {
      let width = selectSizeMenu.value[currentSize.value].w
      let height = selectSizeMenu.value[currentSize.value].h

      let imgWidth = imgInfo.value.width
      let imgHeight = imgInfo.value.height
      let cardBoardWidth = widths.value[formData.width].w

      if (imgWidth >= imgHeight != width >= height) {
        // 画框方向和画面方向一致
        let swap = width
        width = height
        height = swap
      }

      let imgW_H = imgWidth / imgHeight
      if (imgW_H >= width / height) {
        imgWidth = width - (cardBoardWidth + 2) * 2
        imgHeight = imgWidth / imgW_H
      } else {
        imgHeight = height - (cardBoardWidth + 2) * 2
        imgWidth = imgHeight * imgW_H
      }

      let w = width
      let h = height
      let bw = 2
      let iw = imgWidth
      let ih = imgHeight

      console.log('[  initSize  ] >', w, h, iw, bw, ih)
      if (w >= h !== iw >= ih) {
        var t = w
        w = h
        h = t
      }
      var s = 750 / w
      if (!s) {
        return
      }

      canvasSize.w = 750
      canvasSize.h = h * s
      canvasSize.bw = bw * s
      canvasSize.iw = iw * s
      canvasSize.ih = ih * s
      canvasSize.widthIn = canvasSize.w
      canvasSize.heightIn = canvasSize.h
      setTimeout(() => {
        draw()
      }, 1000)
    }
  })
}

// 绘制画框图
const draw = () => {
  uni.showLoading({ title: '正在生成画框图...' })
  step01()
  let img = selectFrame.value[formData.frame].url
  if (formData.image != '') {
    drawBGIMG(App.ossImage(img), () => {
      step03()
    })
  } else {
    step03()
  }
}

const step01 = () => {
  context.value.setFillStyle(canvasSize.bgColor)
  context.value.fillRect(0, 0, canvasSize.widthIn, canvasSize.heightIn)
}
// 绘制边框
const drawBGIMG = (img, callback) => {
  uni.downloadFile({
    url: img,
    success: (res) => {
      if (res.statusCode == 200) {
        // 绘制
        uni.getImageInfo({
          src: res.tempFilePath,
          success: (res2) => {
            var pattern = context.value.createPattern(
              res.tempFilePath,
              'repeat'
            )
            context.value.fillStyle = pattern
            var scale = canvasSize.bw / res2.height
            var w_2 = canvasSize.w * 0.5
            var h_2 = canvasSize.h * 0.5
            // 顶部边框
            context.value.save()
            context.value.rotate(0)
            context.value.scale(scale, scale)
            context.value.fillRect(
              0,
              0,
              canvasSize.w / scale,
              canvasSize.bw / scale
            )
            context.value.restore()

            // 底部边框
            context.value.save()
            context.value.translate(w_2, h_2)
            context.value.rotate(Math.PI)
            context.value.translate(-w_2, -h_2)
            context.value.scale(scale, scale)
            context.value.fillRect(
              0,
              0,
              canvasSize.w / scale,
              canvasSize.bw / scale
            )
            context.value.restore()

            // 右侧边框
            context.value.save()
            context.value.translate(w_2, h_2)
            context.value.rotate(Math.PI * 0.5)
            context.value.translate(-h_2, -w_2)
            context.value.scale(scale, scale)

            // 开始绘制梯形路;
            context.value.beginPath()
            context.value.moveTo(0, 0)
            context.value.lineTo(canvasSize.h / scale, 0)
            context.value.lineTo(
              canvasSize.h / scale - canvasSize.bw / scale,
              canvasSize.bw / scale
            )
            context.value.lineTo(canvasSize.bw / scale, canvasSize.bw / scale)
            context.value.closePath()
            // 填充梯形区域
            context.value.fill()
            context.value.restore()

            // 左侧边框
            context.value.save()
            context.value.translate(w_2, h_2)
            context.value.rotate(-Math.PI * 0.5)
            context.value.translate(-h_2, -w_2)
            context.value.scale(scale, scale)
            // 开始绘制梯形路;
            context.value.beginPath()
            context.value.moveTo(0, 0)
            context.value.lineTo(canvasSize.h / scale, 0)
            context.value.lineTo(
              canvasSize.h / scale - canvasSize.bw / scale,
              canvasSize.bw / scale
            )
            context.value.lineTo(canvasSize.bw / scale, canvasSize.bw / scale)
            context.value.closePath()
            // 填充梯形区域
            context.value.fill()
            context.value.restore()

            callback()
          }
        })
      }
    },
    fail: function (e) {
      uni.hideLoading()
      console.log(e)
    }
  })
}
// 绘制图片
const step03 = () => {
  uni.downloadFile({
    url: formData.image.startsWith('tmp/')
      ? App.ossImage(formData.image)
      : formData.image,
    success: (res) => {
      if (res.statusCode === 200) {
        context.value.drawImage(
          res.tempFilePath,
          (canvasSize.w - canvasSize.iw) / 2,
          (canvasSize.h - canvasSize.ih) / 2,
          canvasSize.iw,
          canvasSize.ih
        )
        // 在最后一步执行 drawIt 完整最终的绘制
        drawIt()
      }
    },
    fail: function (e) {
      console.log(e)
    }
  })
}

const drawIt = () => {
  context.value.draw(true, () => {
    setTimeout(() => {
      uni.canvasToTempFilePath({
        x: 0,
        y: 0,
        width: canvasSize.widthIn,
        height: canvasSize.heightIn,
        destWidth: canvasSize.widthIn,
        destHeight: canvasSize.heightIn,
        canvasId: 'graceCanvas',
        success: (res) => {
          // 在H5平台下,tempFilePath 为 base64
          canvasSize.imgSrc = res.tempFilePath
          let name =
            selectSizeMenu.value[currentSize.value].key +
            '_' +
            selectSizeMenu.value[currentSize.value].w +
            '_' +
            formData.name +
            selectFrame.value[formData.frame].name +
            widths.value[formData.width].w
          checkNameExists((res) => {
            if (!res) {
              imagesPath.value.push({
                name: name,
                url: canvasSize.imgSrc
              })
            }
          })

          uni.hideLoading()
        }
      })
    }, 1000)
  })
}
const previewImage = (url) => {
  ;(url = url.startsWith('tmp/') ? App.ossImage(url) : url),
    uni.previewImage({
      urls: [url],
      current: 0
    })
}

const kf = () => {
  
  Page.openHelp(JSON.stringify(data))
}

onShow(() => {
  // 必须放在show防止登录后返回不触发
  getCustomOrderCfg()
})

onLoad((option) => {
  if (!User.state?.logined) {
    Page.navLogin()
  }
  // 监听选择地址返回数据
  uni.$on('addressSelectBack', (res) => {
    formData.address = res
  })
  context.value = uni.createCanvasContext('graceCanvas')
})

const goBack = () => {
  if (pageIndex.value == 1) {
    pageIndex.value = 0
  } else {
    // 物理返回
    nextTick(() => {
      Page.navBack()
    })
  }
}
onBackPress((e: any) => {
  if (e && e.from === 'backbutton') {
    goBack()
    return true
  }
})
onUnload(() => {
  uni.$off('addressSelectBack')
})
</script>
<style scoped lang="scss">
.privateCustom {
  background: #fbfbfb;
  position: relative;
  .privateCustom-body {
    position: relative;

    //   margin-top: 38rpx;
  }
  // .prvate-line {
  //   width: 2rpx;
  //   height: 20rpx;
  //   background: #000;
  //   margin-right: 44rpx;
  //   margin-left: 15rpx;
  //   margin-top: 6rpx;
  // }
  .content {
    width: 500rpx;
  }

  .rowContentDiv {
    display: flex;
    justify-content: space-around;
    width: 500rpx;
  }
  .posterImg {
    height: 200rpx;
    width: 200rpx;
  }
  .uploadDiv {
    height: 200rpx;
    width: 200rpx;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    font-weight: 400;
    font-size: 26rpx;
    color: #a1a1a1;
    border: 2rpx dashed black;
    .addImg {
      width: 46rpx;
      height: 46rpx;
      flex: none;
    }
  }
  :deep(.gui-form-label) {
    font-weight: 400;
    font-size: 24rpx;
    color: #323232;
    margin-right: 20rpx !important;
    // line-height: normal !important;
  }
  :deep(.gui-form-item) {
    // align-items: flex-start !important;
    height: auto !important;
    // line-height: normal !important;
    width: auto !important;
    // margin-top: 80rpx;
    // margin-top: 55rpx;
    margin-top: 15rpx;
  }
  .frame-box {
    flex-wrap: wrap;
    display: flex;
    .frame-item {
      border: 2rpx solid #fff;
      padding: 4rpx;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      margin-right: 4px;
    }
    .frameImg {
      width: 50rpx;
      height: 50rpx;
      margin-bottom: 18rpx;
    }
    // .frameImg:nth-child(2n - 1) {
    //   margin-right: 20rpx;
    // }
    .frameImg-active {
      border-color: #961912 !important;
    }
  }
  .price-box {
    display: flex;
    font-weight: 400;
    font-size: 37rpx;
    color: #ff0000;
    line-height: 37rpx;
    height: 48rpx;
    align-items: center;
    .pricePImg {
      width: 48rpx;
      height: 48rpx;
    }
    text {
      margin-left: 9rpx;
    }
  }
}

.submitBtn {
  width: 702rpx;
  height: 90rpx;
  background-color: #e4a428 !important;
  font-weight: 400;
  font-size: 52rpx;
  color: #ffffff;
  line-height: 90rpx;
  margin-left: 24rpx !important;
  margin-top: 40rpx !important;
}
.canvas-in {
  width: 750rpx;
  overflow: hidden;
  position: absolute;
  z-index: 1;
  left: 0;
  top: -5000px;
}
</style>

customOrderCfg数据格式

javascript 复制代码
[
    1,
    {
        "frames": [
            {
                "price": 1000,
                "name": "画框一",
                "bName": "这两个是红色的画框",
                "url": "ms-admin/2024/08/15/00i7ihmuzlxdh0mh.png",
                "open": true,
                "bname": "这两个是红色的画框"
            },
            {
                "price": 1000,
                "name": "画框二",
                "url": "ms-admin/2024/08/15/00u8ihmuzlrf8nzr.png",
                "open": true
            },
            {
                "price": 1000,
                "name": "画框三",
                "url": "ms-admin/2024/08/15/00x9ihmuzlj3ey2q.png",
                "open": true
            }
        ],
        "papers": [
            {
                "price": 10000,
                "name": "宣纸",
                "url": "ms-admin/2024/08/15/00uxedxuzlba8ebp.png",
                "open": true
            },
            {
                "price": 20000,
                "name": "油画纸",
                "url": "ms-admin/2024/08/15/0040fdxuzlcxwifk.png",
                "open": true
            }
        ],
        "sizes": [
            {
                "name": "尺寸一",
                "w": 43.0,
                "h": 77.0,
                "open": true
            },
            {
                "name": "尺寸二",
                "w": 50.0,
                "h": 50.0,
                "open": true
            },
            {
                "name": "尺寸三",
                "w": 36.0,
                "h": 96.0,
                "open": true
            },
            {
                "name": "尺寸四",
                "w": 77.0,
                "h": 43.0,
                "open": true
            }
        ],
        "widths": [
            {
                "name": "大框大画",
                "w": 1.0,
                "open": true
            },
            {
                "name": "大框中画",
                "w": 10.0,
                "open": true
            },
            {
                "name": "大框小画",
                "w": 15.0,
                "open": true
            }
        ]
    }
]
相关推荐
mldong7 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排8 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60618 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角9 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!10 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰10 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi11 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen12 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马13 小时前
AI源码分析:拆解模型底层实现逻辑
前端
BillKu13 小时前
全局样式变量:CSS 自定义属性(--border-color)和SCSS 变量($border-color)的说明
javascript·css·scss