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
            }
        ]
    }
]
相关推荐
ZJ_.2 分钟前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
GIS开发特训营7 分钟前
Vue零基础教程|从前端框架到GIS开发系列课程(七)响应式系统介绍
前端·vue.js·前端框架·gis开发·webgis·三维gis
Cachel wood33 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
学代码的小前端34 分钟前
0基础学前端-----CSS DAY9
前端·css
joan_8538 分钟前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui
还是大剑师兰特1 小时前
什么是尾调用,使用尾调用有什么好处?
javascript·大剑师·尾调用
m0_748236111 小时前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
Watermelo6171 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_748248941 小时前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_748235612 小时前
从零开始学前端之HTML(三)
前端·html