IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> (小程序) 客户签名及签名后页面整体转图片后上传 -> 正文阅读

[移动开发](小程序) 客户签名及签名后页面整体转图片后上传

先说明下项目需求:

? ? ? ? 1、销货清单 需要门店老板 签名

? ? ? ? 2、签名后 将签名以及铺货清单整体转为图片上传到服务器

图片展示:

?图片说明:1:页面结构 上面为清单 下面为客户签名展示区域

? ? ? ? ? ? ? ? ? ? 2:点击图1 客户签名区域 弹出签名面板

? ? ? ? ? ? ? ? ? ? 3: 图2 签名完成后? 页面展示签名;点击保存后? 将整个页面转为图片上传到服务器?

? ? ? ? ? ? ? ? ? ? ?4: 上传到服务器后返回的图片

项目使用插件说明:

? ? ? ? 1、客户签名? 使用?handwriting??

????????????????小程序插件-手写签名 - 简书

? ? ? ? 2、页面转图片? ?使用??wxml-to-canvas

????????????????wxml-to-canvas | 微信开放文档

?代码展示:

看代码之前先熟悉下两个插件,两个插件的代码没有贴上来。

整个功能主要就是两个插件的引用。

?wxml:

<view class="page-title">
  <view class="h1-view">***配送中心</view>
  <view class="h3-view">{{sale.type === 10 ?'销货':'退货'}}清单</view>
</view>

<view class="shop-name-view marginB10 flex flex_LT flex_column_center">
  <view>客户:{{sale.distributor_name}}</view>
  <view>{{sale.created_at_text}}</view>
</view>

<view class="page-main">
  <view class="table-row flex flex_LT flex_column_center">
    <view class="idx-view">序号</view>
    <view class="name-view">名称及规格</view>
    <view class="unit-view">单位</view>
    <view class="qty-view">数量</view>
    <view class="price-view">单价</view>
    <view class="amount-view">金额</view>
  </view>
  <view>
    <block wx:for="{{sale.goods}}" wx:key="index">
      <view class="table-row flex flex_LT flex_column_center">
        <view class="idx-view">{{index+1}}</view>
        <view class="name-view">{{item.goods_name}}</view>
        <view class="unit-view">{{item.unit_name}}</view>
        <view class="qty-view">{{item.qty}}</view>
        <view class="price-view">{{item.price_text}}</view>
        <view class="amount-view">{{item.amount_text}}</view>
      </view>
    </block>
    <block wx:if="{{sale.goods.length < 6}}">
      <block wx:for="{{6 -sale.goods.length}}" wx:key="index">
        <view class="table-row flex flex_LT flex_column_center">
          <view class="idx-view">{{6-(6-sale.goods.length)+1+index}}</view>
          <view class="name-view"></view>
          <view class="unit-view"></view>
          <view class="qty-view"></view>
          <view class="price-view"></view>
          <view class="amount-view"></view>
        </view>
      </block>
    </block>
  </view>

  <view class="count-view table-row flex flex_column_center">
    <text class="count-label">合计:</text>
    <text class="count-amount">{{sale.sum_amount_text}}</text>
    <text class="count-end-label">元</text>
  </view>
</view>

<view class="page-footer">
  <view class="address">地址:******************</view>
  <view class="created-by">制单: {{sale.created_by}}</view>
  <view class="customer-name flex">
    <text class="customer-label">客户签名:</text>
    <view class="handwriting">
      <handwriting bindOnEdit="onHnadwritingEdit" bindOnClose="onHnadwritingClose" bindOnComplete="onHnadwritingComplete" />
    </view>
  </view>
</view>

<view class="footer" >
  <button bind:tap="onHnadwritingToggle" class="btn-radius footer-btn app-bg-color">保 存</button>
</view>

<wxml-to-canvas hidden class="widget" width="{{width}}" height="{{height}}"></wxml-to-canvas>

wxss:

page {
  background: #fff;
  padding: 20rpx;

}

.footer {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 710rpx;
  padding: 20rpx;
  height: 100rpx;
  background: #fff;
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
  box-sizing: content-box;
  box-shadow: 0 0 10rpx #ddd;
}

.btn-radius-block {
  width: 80%;
}

.footer-btn {
  border: 0;
  outline: 0;
  font-size: 30rpx;
  border-radius: 80rpx;
  height: 80rpx;
  color: #fff;
}


.page-title {
  padding-bottom: 20rpx;
}

.h1-view {
  font-size: 50rpx;
  text-align: center;
  margin-bottom: 20rpx;
}

.h3-view {
  text-align: center;
  margin-bottom: 20rpx;
  font-size: 40rpx;
  padding: 0 20rpx;
}

.page-main {
  width: 710rpx;
}

.table-row {
  border-bottom: 1px solid #999;
}

.table-row view {
  text-align: center;
  line-height: 60rpx;
  height: 60rpx;
  font-size:24rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  word-wrap: normal;
}

.idx-view {
  width: 61rpx;
}

.name-view {
  width: 324rpx;
}

.unit-view {
  width: 61rpx;
}

.qty-view {
  width: 61rpx;
}

.price-view {
  width: 101rpx;
}

.amount-view {
  width: 100rpx;
}

.count-view {
  padding: 10rpx 0;
}

.count-label {
  margin-left: 20rpx;
  margin-right: 20rpx;
}

.count-amount {
  width: 200rpx;
  height: 50rpx;
  margin-right: 20rpx;
  text-align: center;
  line-height: 50rpx;
  color: #f00;
  font-weight: bold;
}

.address {
  margin-bottom: 30rpx;
  margin-top: 10rpx;
}

.customer-label {
  width: 150rpx;
}

.handwriting {
  width: 560rpx;
}

.customer-name {
  margin-top: 30rpx;
}

.page-footer {
  padding-bottom: 200rpx;
}

.widget-view {
  position: fixed;
  left: 0;
  top: 0;
  width: 750rpx;
  height: 100%;
  background: rgba(0, 0, 0, 0.6);
}

.widget {
  position: absolute;
  left: 0;
  top: 0;
  width:0;
  height:0;
  z-index: -1;
  opacity: 0;
}
.flex{
  display: flex
}
.flex-item{
  flex:1;
}
.flex_column{
  flex-direction:column
}
.flex_LT{
  justify-content : space-between
}
.flex_R{
  justify-content : flex-end
}

.flex_PJ{
  justify-content :  space-around
}

.flex_center{
  justify-content : center
}
.flex_column_center{
  align-items : center
}

json:

{
  "usingComponents": {
    "handwriting": "/component/handwriting/index",
    "wxml-to-canvas": "wxml-to-canvas"
  },
  "navigationBarTitleText": "***配送中心"
}

js:

const app = getApp();
const api = require("../../../../utils/api");
const utils = require("../../../../utils/util");
const sMD5 = require('../../../../utils/spark-md5.js')

let {
  temp,
  style
} = require('./wxmlAndStyle.js')


Page({

  data: {
    sale: {},
    shopId: '',
    showFooterBtn: true,
    writingImge: '',
    width: wx.getSystemInfoSync().windowWidth,
    height: wx.getSystemInfoSync().windowHeight,
  },


  onLoad: function (options) {
    let id = options.id;
    this.data.shopId = options.shopId
    this.initData(id);
  },

  //id为销售单的id  这里用来查询出数据 显示在页面上 可以自己定义一个json代替数据测试
  initData: function (id) {
    wx.promise.request({
      url: api.API_ADMINS_SALES_SHOW,
      data: {
        id: id
      },
      header: app.defaultAdminHeader()
    }).then(res => {
      this.setData({
        sale: this.formatData(res)
      })
    }).catch(err => {
      utils.showMsg("加载销售单详情失败:" + err.errMsg)
    })
  },
  formatData: function (data) {
    data.created_at_text = utils.formatDate_3(data.created_at);
    data.state_text = this.getStateText(data.state);
    data.last_receipt_at_text = utils.formatDate_(data.last_receipt_at);
    data.receipt_amount_text = utils.formatAmount_(data.receipt_amount, 0.01);
    data.sum_amount_text = utils.formatAmount_(data.sum_amount, 0.01);
    data.receipt_state_text = data.receipt_state === 0 ? '未结清' : '已结清';
    data.type_text = data.type === 10 ? '销售单' : '退货单';
    data.goods = this.formatGoods(data.goods);

    return data;
  },
  formatGoods: function (goods) {
    goods.forEach(item => {
      item.price_text = utils.formatAmount_(item.price, 0.01);
      item.amount_text = utils.formatAmount_(item.amount, 0.01);
    })
    return goods;
  },

  getStateText: function (state) {
    switch (state) {
      case -1:
        return '已删除';
      case 0:
        return '已开单';
      case 1:
        return '已确认';
      case 2:
        return '已出库';
      case 3:
        return '已退货'
      default:
        return '';
    }
  },
  onBackOrder: function () {
        wx.showModal({
          title: '温馨提示',
          content: '确定返单吗?',
          showCancel: true,
          confirmColor: "#84bc25",
          success: (res) => {
            if (res.confirm) {
              this.onCancelOut()
            }
          }
        })
  },
  
  onCancelOut: function () {
    wx.promise.request({
      url: api.API_SALES_CANCEL_OUT_APP,
      method: 'POST',
      data: {
        id: this.data.sale.id
      },
      header: app.defaultAdminHeader()
    }).then(res => {
      wx.showToast({
        title: '操作成功!',
        icon: 'none',
        duration: 2000
      })
      this.initData(this.data.sale.id)
    }).catch(err => {
      utils.showMsg("返单失败:" + err.errMsg)
    })
  },
  onHnadwritingComplete: function (e) {
    console.log(e.detail)
    this.setData({
      showFooterBtn: true,
    })
    wx.getImageInfo({
      src: e.detail,
      success: (e) => {
        console.log(e)
        this.uploadImg(e)
      }
    })
  },
  uploadImg: function (tempFile) {
    wx.getFileSystemManager().readFile({
      filePath: tempFile.path, //选择图片返回的相对路径
      // encoding: 'binary', //编码格式
      success: res => {
        let spark = new sMD5.ArrayBuffer();
        spark.append(res.data);
        let hexHash = spark.end(false);
        wx.promise.uploadFile({
          url: api.API_ADMINS_UPLOAD_IMAGES_CREATE,
          filePath: tempFile.path,
          name: 'image',
          formData: {
            'image': tempFile,
            'md5': hexHash
          },
          header: {
            'content-type': 'application/x-www-form-urlencoded',
            'Authorization': app.globalData.adminToken
          }
        }).then(res => {
          console.log(res);
          if (res.code === 20000 && res.data) {
            this.setData({
              writingImge: res.data.url
            })
          }
        }).catch(err => {
          utils.showMsg("上传图片失败:" + err.errMsg)
        })
      }
    })
  },
  onHnadwritingEdit: function () {
    this.setData({
      showFooterBtn: false
    })
  },
  onHnadwritingClose: function () {
    this.setData({
      showFooterBtn: true
    })
  },
  onHnadwritingToggle: function () {

    if (!this.data.writingImge) {
      utils.showMsg("请客户签名后再提交!");
      return;
    }

    this.widget = this.selectComponent('.widget')
    if (this.data.writingImge) {
      temp.setUrl(this.data.writingImge)
    }
    temp.init(this.data.sale.id);
    wx.showLoading({
      title: '正在加载...',
    })
    setTimeout(() => {
      wx.hideLoading()
      this.renderToCanvas();
    }, 1000)
  },
  renderToCanvas() {
    let wxml = temp.getWxml();
    const p1 = this.widget.renderToCanvas({
      wxml,
      style
    })
    p1.then((res) => {
      console.log('container', res.layoutBox)
      this.container = res;
      this.extraImage();
    })
  },
  extraImage() {
    const p2 = this.widget.canvasToTempFilePath()
    p2.then(res => {
      console.log(res);
      wx.getImageInfo({
        src: res.tempFilePath,
        success: (e) => {
          console.log(e)
          this.onUploadApplyImage(e)
        }
      })
    })
  },

  onUploadApplyImage: function (tempFile) {
    wx.getFileSystemManager().readFile({
      filePath: tempFile.path, //选择图片返回的相对路径
      // encoding: 'binary', //编码格式
      success: res => {
        let spark = new sMD5.ArrayBuffer();
        spark.append(res.data);
        let hexHash = spark.end(false);
        wx.showLoading({
          title: '正在上传图片...',
        })
        wx.promise.uploadFile({
          url: api.API_ADMINS_UPLOAD_IMAGES_CREATE,
          filePath: tempFile.path,
          name: 'image',
          formData: {
            'image': tempFile,
            'md5': hexHash
          },
          header: {
            'content-type': 'application/x-www-form-urlencoded',
            'Authorization': app.globalData.adminToken
          }
        }).then(res => {
          wx.hideLoading()
          console.log(res);
          if (res.code === 20000 && res.data) {
            this.onUploadApply(res.data.id)
          }
        }).catch(err => {
          wx.hideLoading()
          utils.showMsg("上传图片失败:" + err.errMsg)
        })
      }
    })
  },
  onUploadApply: function (image_id) {
    wx.showLoading({
      title: '正在保存图片...',
    })
    wx.promise.request({
      url: api.API_ADMINS_SALE_APPLY_IMAGE,
      method: 'POST',
      data: {
        id: this.data.sale.id,
        image_id: image_id
      },
      header: app.defaultAdminHeader()
    }).then(res => {
      wx.hideLoading()
      wx.showToast({
        title: '保存图片成功!',
        icon: 'none',
        duration: 2000
      })
      wx.navigateBack()
    }).catch(err => {
      wx.hideLoading()
      utils.showMsg("保存图片失败:" + err.errMsg)
    })
  }
})

wxmlAndStyle.js:

const app = getApp();
const api = require("../../../../utils/api");
const utils = require("../../../../utils/util");

let wWidth = wx.getSystemInfoSync().windowWidth;
let wHeight = wx.getSystemInfoSync().windowHeight ;

let style = {
  pageview: {
    backgroundColor: "#fff",
    padding: 10,
    paddingTop:40,
    width: wWidth,
    height: wHeight
  },
  pagetitle: {
    width: wWidth,
    height: 60,
    flexDirection: 'row',
    justifyContent: 'space-around',
    alignItems: 'center',
  },
  h1view: {
    width: wWidth,
    height: 30,
    textAlign: 'center',
  },
  h3view: {
    width: wWidth,
    height: 30,
    textAlign: 'center',
  },
  t1: {
    width: wWidth,
    height: 30,
    fontSize: 22,
    lineHeight: 30,
    fontWeight: 'bold',
    color: "#000000"
  },
  t2: {
    width: wWidth,
    height: 30,
    fontSize: 18,
    lineHeight: 30,
    fontWeight: 'bold',
    color: "#000000",
    borderBottom: '1px solid #000'
  },

  shopnameview: {
    width: wWidth,
    height: 14,
  },
  distributorName: {
    width: wWidth / 2.1,
    height: 14,
    fontSize:12,
    lineHeight: 14,
    color: "#999",
  },


  pagemain: {
    width: wWidth-20,
    backgroundColor:"#999"
  },

  tablerow: {
    width: wWidth-10,
    backgroundColor:"#fff",
    marginBottom:1,
  },

  rowitem: {
    textAlign: 'center',
    lineHeight: 28,
    height: 28,
    fontSize:12,
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    whiteSpace: 'nowrap',
    wordWrap: 'normal',
  },

  idxview: {
    width: (wWidth -20) * (1/8)   //30
  },

  nameview: {
    width: (wWidth -20) * (3/8)//165
  },

  unitview: {
    width: (wWidth -20) * (1/8)//40
  },

  qtyview: {
    width: (wWidth -20) * (1/8)//40
  },

  priceview: {
    width: (wWidth -20) * (1/8)//40,
  },

  amountview: {
    width: (wWidth -20) * (1/8)//40,
  },

  countlabel: {
    width: 60
  },

  countamount: {
    width: 100,
    height: 25,
    textAlign: "center",
    lineHeight: 25,
    color: "red",
    fontWeight: "bold",
  },


  address: {
    width:wWidth,
    lineHeight: 28,
    height: 28,
    fontSize:13,
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    whiteSpace: 'nowrap',
    wordWrap: 'normal',
    borderWidth: 1
  },

  customerlabel: {
    width: 75,
    lineHeight: 28,
    height: 28,
    fontSize:13,
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    whiteSpace: 'nowrap',
    wordWrap: 'normal',
    borderWidth: 1
  },

  customername: {
    width:wWidth,
  },

  customerImg:{
    width:200,
    height:100
  },

  pagefooter: {
    width:wWidth
  },

  createdby:{
    width:wWidth,
    textAlign:'left',
    lineHeight: 30,
    height: 30,
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    whiteSpace: 'nowrap',
    wordWrap: 'normal',
    borderWidth: 1
  },





  flexcolumn: {
    flexDirection: 'column'
  },
  flexlt: {
    flexDirection: 'row',
    justifyContent: 'space-between'
  },
  flexr: {
    flexDirection: 'row',
    justifyContent: 'flex-end'
  },
  flexl: {
    flexDirection: 'row',
    justifyContent: 'flex-start'
  },
  flexpj: {
    flexDirection: 'row',
    justifyContent: 'space-around'
  },

  flexcenter: {
    flexDirection: 'row',
    justifyContent: 'center'
  },
  flexcolumncenter: {
    alignItems: 'center'
  },
  marginB10: {
    marginBottom: 10
  },
  textR: {
    textAlign: 'right'
  }
}

let temp = {
  url: '',
  wxml: '',
  sale: {},
  init: function (id) {
    wx.promise.request({
      url: api.API_ADMINS_SALES_SHOW,
      data: {
        id: id
      },
      header: app.defaultAdminHeader()
    }).then(res => {
      temp.sale = temp.formatData(res)
      temp.initWxml(temp.sale)
    }).catch(err => {
      utils.showMsg("加载销售单详情失败:" + err.errMsg)
    })
  },
  setUrl: function (url) {
    temp.url = url
  },
  formatData: function (data) {
    data.created_at_text = utils.formatDate_3(data.created_at);
    data.state_text = temp.getStateText(data.state);
    data.last_receipt_at_text = utils.formatDate_(data.last_receipt_at);
    data.receipt_amount_text = utils.formatAmount_(data.receipt_amount, 0.01);
    data.sum_amount_text = utils.formatAmount_(data.sum_amount, 0.01);
    data.receipt_state_text = data.receipt_state === 0 ? '未结清' : '已结清';
    data.type_text = data.type === 10 ? '销售单' : '退货单';
    data.goods = temp.formatGoods(data.goods);
    return data;
  },
  formatGoods: function (goods) {
    goods.forEach(item => {
      item.price_text = utils.formatAmount_(item.price, 0.01);
      item.amount_text = utils.formatAmount_(item.amount, 0.01);
    })
    return goods;
  },
  getStateText: function (state) {
    switch (state) {
      case -1:
        return '已删除';
      case 0:
        return '已开单';
      case 1:
        return '已确认';
      case 2:
        return '已出库';
      case 3:
        return '已退货'
      default:
        return '';
    }
  },
  initWxml: function (sale) {

    const title = `
    <view class="pagetitle flexcolumn" >
      <view class="h1view flexcenter"><text class="t1">****配送中心</text></view>
      <view class="h3view"><text class="t2">${sale.type === 10 ? '销售' : '退货'}清单</text></view>
    </view>
    `;
    const shopName = `
    <view class="marginB10 flexl flexcolumncenter shopnameview">
      <text class="distributorName">客户:${sale.distributor_name}</text>
      <text class="distributorName textR">${sale.created_at_text}</text>
    </view>
    `;
    const tableHeader = `
    <view class="tablerow flexl flexcolumncenter">
      <text class="idxview rowitem">序号</text>
      <text class="nameview rowitem">名称及规格</text>
      <text class="unitview rowitem">单位</text>
      <text class="qtyview rowitem">数量</text>
      <text class="priceview rowitem">单价</text>
      <text class="amountview rowitem">金额</text>
    </view>
    `;
    let tableBody = ''
    sale.goods.forEach((item, index) => {
      tableBody += `
        <view class="tablerow flexl flexcolumncenter">
          <text class="idxview rowitem">${index+1}</text>
          <text class="nameview rowitem">${item.goods_name}</text>
          <text class="unitview rowitem">${item.unit_name}</text>
          <text class="qtyview rowitem">${item.qty}</text>
          <text class="priceview rowitem">${item.price_text}</text>
          <text class="amountview rowitem">${item.amount_text}</text>
        </view>
      `
    })
    if (sale.goods.length < 6) {
      let count = 6 - sale.goods.length;
      for (let i = 0; i < count; i++) {
        tableBody += `
        <view class="tablerow flexl  flexcolumncenter">
          <text class="idxview rowitem">${15-(15-sale.goods.length)+1+i}</text>
          <text class="nameview rowitem"></text>
          <text class="unitview rowitem"></text>
          <text class="qtyview rowitem"></text>
          <text class="priceview rowitem"></text>
          <text class="amountview rowitem"></text>
        </view>
        `
      }
    }
    let tableFooter = `
    <view class="tablerow flexl flexcolumncenter">
      <text class="countlabel rowitem">合计:</text>
      <text class="countamount rowitem">${sale.sum_amount_text}</text>
      <text class="countlabel rowitem">元</text>
    </view>
    `;
    let imgWxml = temp.url ? `<image class="customerImg" src="${temp.url}"></image>` : '';
    let pageFooter = `
    <view class="pagefooter flexcolumn">
      <text class="address">地址:**********************</text>
      <text class="createdby">制单: ${sale.created_by}</text>
      <view class="customername flexl">
        <text class="customerlabel">客户签名:</text>
        ${imgWxml}
      </view>
    </view>
    `
    let tableWxml = `<view class="pagemain">${tableHeader}${tableBody}${tableFooter}</view>`
    this.wxml = `<view class="pageview">${title}${shopName}${tableWxml}${pageFooter}</view>`;

  },
  getWxml: function () {
    return this.wxml
  },
}


module.exports = {
  temp,
  style
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-11-18 11:17:50  更:2021-11-18 11:19:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:45:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码