Parcourir la source

feature:增加报价单上传字段、修改升级接口

liuzl il y a 2 ans
Parent
commit
3968d70170

+ 11 - 0
js_sdk/xp-multipart/package.json

@@ -0,0 +1,11 @@
+{
+    "id": "xp-multipart",
+    "name": "微信小程序! 一次http请求可上传多张图片、无依赖",
+    "version": "1.2.0",
+    "description": "uni.uploadFile一次只能上传一个图片,xp-multipart 解决了单图片限制",
+    "keywords": [
+        "request",
+        "多图片上传",
+        "upload"
+    ]
+}

+ 182 - 0
js_sdk/xp-multipart/xp-multipart/xp-multipart.js

@@ -0,0 +1,182 @@
+import mines from "mime-db"
+
+const br = '\r\n'
+const br2 = '\r\n\r\n'
+const boundary = generateBoundary()
+const splitBoundary = "--" + boundary
+const commonTypes = {
+	// 空间复杂度 换取 时间复杂度
+	// 避免每次上传图片时 getType方法 大量遍历MIME标准,提高性能
+}
+export let uploadTask = null
+export async function multipartUpload({
+	url,
+	header,
+	fields,
+	files,
+	success,
+	fail,
+	complete
+}) {
+	let data = await toBuffer(fields, files)
+	uploadTask = wx.request({
+		url,
+		data,
+		method: "POST",
+		header: {
+			...header,
+			'Content-Type': 'multipart/form-data; boundary=' + boundary
+		},
+		success(s) {
+			success(s)
+		},
+		fail(f) {
+			fail && fail(f)
+		},
+		complete(c) {
+			complete && complete(c)
+		}
+	})
+}
+
+/**
+ * 转换成 ArrayBuffer
+ * @param {Object} fields
+ * @param {Object} files
+ */
+async function toBuffer(fields, files) {
+	const mixUints = []
+	let fieldHeader = ''
+	for (const key in fields) {
+		fieldHeader += getFieldHeader(key, fields[key])
+	}
+	mixUints.push(str2Uint8Arr(fieldHeader))
+
+	for (const key in files) {
+		const filePath = files[key]
+
+		const fileHeader = getFileHeader(key, filePath)
+		if (fileHeader == null) continue
+		mixUints.push(str2Uint8Arr(fileHeader))
+
+		const fileUint8Arr = await file2Uint8Arr(filePath)
+		mixUints.push(fileUint8Arr)
+		mixUints.push(str2Uint8Arr(br))
+	}
+
+	mixUints.push(str2Uint8Arr(splitBoundary + "--")) //结尾
+
+	return convert2Buffer(mixUints)
+}
+/**
+ * mix数组转换成 arraybuffer
+ * @param {Object} mixUints
+ */
+function convert2Buffer(mixUints) {
+	const len = mixUints.reduce((prev, cur) => {
+		return prev + cur.length
+	}, 0)
+	const arrayBuffer = new ArrayBuffer(len)
+	const buffer = new Uint8Array(arrayBuffer)
+	let sum = 0
+	for (let i = 0; i < mixUints.length; i++) {
+		for (let j = 0; j < mixUints[i].length; j++) {
+			buffer[sum + j] = mixUints[i][j]
+		}
+		sum += mixUints[i].length
+	}
+	return arrayBuffer
+}
+/**
+ * 生成普通字段boundary串
+ * @param {Object} key
+ * @param {Object} val
+ */
+function getFieldHeader(key, val) {
+	return `${splitBoundary}${br}Content-Disposition: form-data; name="${key}"${br2}${val}${br}`
+}
+
+/**
+ * 生成图片字段boundary串
+ * @param {Object} name
+ * @param {Object} filePath
+ */
+function getFileHeader(name, filePath) {
+	const contentType = getType(filePath)
+	if (contentType == null) return null;
+	const filename = filePath.replace(/^(.*)\/(.*)/, "$2")
+	return `${splitBoundary}${br}Content-Disposition: form-data; name="${name}"; filename="${filename}"${br}Content-Type: ${contentType}${br2}`
+}
+/**
+ * 生成boundary
+ */
+function generateBoundary() {
+	let boundary = "----WebKitFormBoundary"
+	const chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
+	const len = chars.length
+	for (let i = 0; i < 16; i++) {
+		boundary += chars.charAt(~~(Math.random() * len));
+	}
+	return boundary;
+}
+/**
+ * 字符串转 Uint8Array
+ * @param {String} str
+ */
+function str2Uint8Arr(str) {
+	let bytes = [];
+	for (let i = 0; i < str.length; i++) {
+		let code = str.charCodeAt(i);
+		if (0x00 <= code && code <= 0x7f) {
+			bytes.push(code);
+		} else if (0x80 <= code && code <= 0x7ff) {
+			bytes.push((192 | (31 & (code >> 6))));
+			bytes.push((128 | (63 & code)))
+		} else if ((0x800 <= code && code <= 0xd7ff) || (0xe000 <= code && code <= 0xffff)) {
+			bytes.push((224 | (15 & (code >> 12))));
+			bytes.push((128 | (63 & (code >> 6))));
+			bytes.push((128 | (63 & code)))
+		}
+	}
+	for (let i = 0; i < bytes.length; i++) {
+		bytes[i] &= 0xff;
+	}
+	return bytes
+}
+/**
+ * 文件转 Uint8Array
+ * @param {String} filePath
+ */
+function file2Uint8Arr(filePath) {
+	return new Promise(resolve => {
+		wx.getFileSystemManager().readFile({
+			filePath,
+			success(res) {
+				resolve(new Uint8Array(res.data))
+			},
+			fail(err) {
+				console.error(err.errMsg)
+			}
+		})
+	})
+}
+/**
+ * 获取文件类型
+ * @param {Object} url
+ */
+function getType(url) {
+	if (!url) return null
+	const index = url.lastIndexOf(".");
+	const ext = url.substr(index + 1);
+	if (commonTypes.hasOwnProperty(ext)) {
+		return commonTypes[ext]
+	}
+	for (let k in mines) {
+		if (mines[k].extensions === undefined) continue
+		if (mines[k].extensions.indexOf(ext) !== -1) {
+			commonTypes[ext] = k
+			return k
+		}
+	}
+	return null
+}

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
js_sdk/xp-multipart/xp-multipart/xp-multipart.min.js


+ 154 - 84
pages/project/upgrade.vue

@@ -2,7 +2,7 @@
  * @Author: liuzhenlin 461480418@qq.ocm
  * @Date: 2023-01-12 11:57:48
  * @LastEditors: liuzhenlin
- * @LastEditTime: 2023-03-08 15:15:33
+ * @LastEditTime: 2023-05-08 10:24:04
  * @Description: file content
  * @FilePath: \oms\pages\project\upgrade.vue
 -->
@@ -20,12 +20,13 @@
     </view>
     <view class="main">
       <u-form :model="addForm" :rules="rules" ref="addForm" label-width="0">
+        <!-- 项目级别 -->
         <u-form-item prop="nboType" borderBottom customStyle="padding:40rpx 0 30rpx">
           <view class="form-label flex_l">
             <view class="label-tag"></view>
             项目级别
           </view>
-          <u-radio-group v-model="addForm.nboType" placement="row">
+          <u-radio-group v-model="addForm.nboType" placement="row" @change="changeLevel">
             <u-radio
               customStyle="margin-right:40rpx"
               :disabled="projectDetails.nboType === '10'"
@@ -44,6 +45,7 @@
             <!-- <u-radio v-if="projectDetails.nboType === '10'" label="储备" name="50"></u-radio> -->
           </u-radio-group>
         </u-form-item>
+        <!-- A/B -->
         <view v-if="addForm.nboType !== '30'">
           <u-form-item prop="nboBudget" borderBottom customStyle="padding:40rpx 0 30rpx">
             <view class="form-label flex_l">
@@ -52,7 +54,7 @@
             </view>
             <u-input
               placeholder="输入项目预算"
-              v-model="addForm.nboBudget"
+              v-model.number="addForm.nboBudget"
               border="none"
               type="number"
               suffixIconStyle="color:#CDCDCD"
@@ -85,51 +87,44 @@
               clearable
               customStyle="padding: 0 30rpx 0 12rpx"></u-input>
           </u-form-item>
-        </view>
-        <u-form-item borderBottom customStyle="padding:40rpx 0 30rpx" v-if="addForm.nboType !== '30'">
-          <view class="form-label flex_l">技术支持人员</view>
-          <u-input
-            placeholder="输入技术支持人员"
-            v-model="addForm.technicalSupportName"
-            border="none"
-            suffixIconStyle="color:#CDCDCD"
-            clearable
-            customStyle="padding: 0 30rpx 0 12rpx"></u-input>
-        </u-form-item>
-        <u-form-item
-          prop="technicalSupportContent"
-          borderBottom
-          customStyle="padding:40rpx 0 30rpx"
-          v-if="addForm.nboType !== '30'">
-          <view class="form-label flex_l">
-            <view class="label-tag"></view>
-            技术支持内容
-          </view>
-          <u-input
-            placeholder="输入技术支持内容"
-            v-model="addForm.technicalSupportContent"
-            border="none"
-            suffixIconStyle="color:#CDCDCD"
-            clearable
-            customStyle="padding: 0 30rpx 0 12rpx"></u-input>
-        </u-form-item>
-        <u-form-item
-          borderBottom
-          customStyle="padding:40rpx 0 30rpx"
-          @click="showSupportDate = true"
-          v-if="addForm.nboType !== '30'">
-          <view class="form-label flex_l">技术支持时间</view>
-          <u-input
-            :readonly="true"
-            placeholder="请选择技术支持时间"
-            v-model="addForm.technicalSupportTime"
-            border="none"
-            suffixIcon="arrow-down"
-            suffixIconStyle="color:#CDCDCD"
-            clearable
-            customStyle="padding: 0 30rpx 0 12rpx"></u-input>
-        </u-form-item>
-        <view v-if="addForm.nboType !== '30'">
+          <u-form-item prop="technicalSupportName" borderBottom customStyle="padding:40rpx 0 30rpx">
+            <view class="form-label flex_l">
+              <view class="label-tag"></view>
+              技术支持人员
+            </view>
+            <u-input
+              placeholder="输入技术支持人员"
+              v-model="addForm.technicalSupportName"
+              border="none"
+              suffixIconStyle="color:#CDCDCD"
+              clearable
+              customStyle="padding: 0 30rpx 0 12rpx"></u-input>
+          </u-form-item>
+          <u-form-item prop="technicalSupportContent" borderBottom customStyle="padding:40rpx 0 30rpx">
+            <view class="form-label flex_l">
+              <view class="label-tag"></view>
+              技术支持内容
+            </view>
+            <u-input
+              placeholder="输入技术支持内容"
+              v-model="addForm.technicalSupportContent"
+              border="none"
+              suffixIconStyle="color:#CDCDCD"
+              clearable
+              customStyle="padding: 0 30rpx 0 12rpx"></u-input>
+          </u-form-item>
+          <u-form-item borderBottom customStyle="padding:40rpx 0 30rpx" @click="showSupportDate = true">
+            <view class="form-label flex_l">技术支持时间</view>
+            <u-input
+              :readonly="true"
+              placeholder="请选择技术支持时间"
+              v-model="addForm.technicalSupportTime"
+              border="none"
+              suffixIcon="arrow-down"
+              suffixIconStyle="color:#CDCDCD"
+              clearable
+              customStyle="padding: 0 30rpx 0 12rpx"></u-input>
+          </u-form-item>
           <u-form-item borderBottom customStyle="padding:40rpx 0 30rpx" @click="showBudgetDate = true">
             <view class="form-label flex_l">项目预算期限</view>
             <u-input
@@ -162,7 +157,26 @@
               clearable
               customStyle="padding: 0 30rpx 0 12rpx"></u-input>
           </u-form-item>
+          <!-- 上传报价单文件 -->
+          <u-form-item prop="quotationFile" borderBottom customStyle="padding:40rpx 0 30rpx">
+            <view class="form-label flex_l">
+              <view class="label-tag"></view>
+              报价单
+            </view>
+            <uni-file-picker
+              file-mediatype="all"
+              mode="grid"
+              @select="chooseFile($event, 'baojia')"
+              @delete="delQuotationFile"
+              ref="upload"
+              limit="1">
+              <view class="upload-btn">
+                <u-icon name="plus-circle-fill" color="blue" size="28"></u-icon>
+              </view>
+            </uni-file-picker>
+          </u-form-item>
         </view>
+        <!-- A -->
         <view v-if="addForm.nboType === '10'">
           <u-form-item
             prop="purchasingWayVal"
@@ -184,7 +198,7 @@
               customStyle="padding: 0 30rpx 0 12rpx"></u-input>
           </u-form-item>
           <u-form-item
-            prop="technicalSupportTime"
+            prop="purchasingTime"
             borderBottom
             customStyle="padding:40rpx 0 30rpx"
             @click="showPurchasingDate = true">
@@ -195,7 +209,7 @@
             <u-input
               :readonly="true"
               placeholder="请选择采购时间"
-              v-model="addForm.technicalSupportTime"
+              v-model="addForm.purchasingTime"
               border="none"
               suffixIcon="arrow-down"
               suffixIconStyle="color:#CDCDCD"
@@ -264,8 +278,9 @@
               <u-radio customStyle="margin-right:40rpx" label="否" name="20"></u-radio>
             </u-radio-group>
           </u-form-item>
+          <!-- 上传大数参数文件 -->
           <u-form-item
-            prop="file"
+            prop="dashooParamFile"
             borderBottom
             customStyle="padding:40rpx 0 30rpx"
             v-if="addForm.isAdoptDashoo == '10'">
@@ -273,7 +288,13 @@
               <view class="label-tag"></view>
               文件
             </view>
-            <uni-file-picker file-mediatype="all" mode="grid" @select="chooseFile($event)" ref="upload" limit="1">
+            <uni-file-picker
+              file-mediatype="all"
+              mode="grid"
+              @delete="deldashooParamFile"
+              @select="chooseFile($event)"
+              ref="upload"
+              limit="1">
               <view class="upload-btn">
                 <u-icon name="plus-circle-fill" color="blue" size="28"></u-icon>
               </view>
@@ -355,6 +376,12 @@
   </view>
 </template>
 <script>
+  import {
+    multipartUpload as upload,
+    uploadTask as task, //上传任务对象,同requestTask js_sdk\xp-multipart\xp-multipart\xp-multipart.js
+
+    // D:\work\oms\js_sdk\xp-multipart\xp-multipart\xp-multipart.js
+  } from '@/js_sdk/xp-multipart/xp-multipart/xp-multipart'
   import projectApi from '../../api/project'
   import to from 'await-to-js'
   import SelectDealer from 'components/SelectDealer'
@@ -396,8 +423,10 @@
           remark: '', //转化原因
           projConversionReason: '', //升级原因
           id: 0, //项目id
-          file: '',
-          tempFilePaths: '',
+          dashooParamFile: '',
+          dashooParamFilePaths: '',
+          quotationFilePaths: '',
+          quotationFile: '',
         },
         showModal: false,
         projectDetails: {},
@@ -408,6 +437,12 @@
             message: '请选择项目级别',
             trigger: ['blur'],
           },
+          technicalSupportName: {
+            type: 'string',
+            required: true,
+            message: '请输入技术支持人员',
+            trigger: ['blur'],
+          },
           technicalSupportContent: {
             type: 'string',
             required: true,
@@ -438,12 +473,18 @@
             message: '请选择是否采纳大数技术参数',
             trigger: ['blur'],
           },
-          file: {
+          dashooParamFile: {
             type: 'object',
             required: true,
             message: '请上传文件',
             trigger: ['change'],
           },
+          quotationFile: {
+            type: 'object',
+            required: true,
+            message: '请上传报价单',
+            trigger: ['change'],
+          },
           remark: {
             type: 'string',
             required: true,
@@ -489,6 +530,17 @@
           this.addForm.remark = ''
         }
       },
+      changeLevel(e) {
+        if (e == '20') {
+          this.addForm.dashooParamFile = ''
+          this.addForm.dashooParamFilePaths = ''
+        } else if (e == '30') {
+          this.addForm.dashooParamFile = ''
+          this.addForm.dashooParamFilePaths = ''
+          this.addForm.quotationFile = ''
+          this.addForm.quotationFilePaths = ''
+        }
+      },
       // 选择采购方式
       pickModel(e) {
         this.addForm.purchasingWay = e.value[0].key
@@ -548,46 +600,71 @@
       // 选择是否采用大数参数
       chooseRadio(e) {
         if (e == '20') {
-          this.addForm.file = ''
+          this.addForm.dashooParamFile = ''
+          this.addForm.dashooParamFilePaths = ''
         }
       },
       // // 选择上传触发函数
-      chooseFile(e) {
-        this.addForm.file = e.tempFiles[0].file
-        this.addForm.tempFilePaths = e.tempFilePaths[0]
+      chooseFile(e, type = null) {
+        if (type == 'baojia') {
+          this.addForm.quotationFile = e.tempFiles[0].file
+          this.addForm.quotationFilePaths = e.tempFilePaths[0]
+        } else {
+          this.addForm.dashooParamFile = e.tempFiles[0].file
+          this.addForm.dashooParamFilePaths = e.tempFilePaths[0]
+        }
+      },
+      // 删除报价单
+      delQuotationFile() {
+        this.addForm.quotationFile = ''
+        this.addForm.quotationFilePaths = ''
+      },
+      // 删除大数参数文件
+      deldashooParamFile() {
+        this.addForm.dashooParamFile = ''
+        this.addForm.dashooParamFilePaths = ''
       },
       // 上传函数
-      async upgradeA(data) {
+      async upgradeAorB(data) {
         const basePath = process.uniEnv.VUE_APP_MicroSrvProxy_API + process.uniEnv.VUE_APP_ParentPath
-        uni.uploadFile({
-          url: basePath,
-          filePath: data.tempFilePaths,
-          name: 'file',
-          formData: data,
+        upload({
+          url: basePath, //后端接口
+          fields: data,
+          files: {
+            quotationFile: this.addForm.quotationFilePaths,
+            dashooParamFile: this.addForm.dashooParamFilePaths,
+          },
           header: {
             Tenant: process.uniEnv.VUE_APP_TENANT,
             Authorization: 'Bearer ' + uni.getStorageSync('opms_token') || '',
             'X-RPCX-SerializeType': '1',
             'X-RPCX-ServicePath': 'BusinessHandler',
-            'X-RPCX-ServiceMethod': 'BusinessUpgradeA',
+            'X-RPCX-ServiceMethod': 'BusinessUpgradeAorB',
             SrvEnv: 'dev',
           },
           success: (res) => {
-            this.$refs.uToast.show({
-              type: 'success',
-              message: '提交成功',
-              complete: () => {
-                this.goBack()
-              },
-            })
+            if (res.data.code == 200) {
+              this.$refs.uToast.show({
+                type: 'success',
+                message: '提交成功',
+                complete: () => {
+                  this.goBack()
+                },
+              })
+            } else {
+              this.$refs.uToast.show({
+                type: 'warning',
+                message: res.data.msg,
+              })
+            }
           },
           fail: (err) => {
             this.$refs.uToast.show({
-              type: 'success',
+              type: 'error',
               message: '提交失败',
             })
           },
-          complete: () => {
+          complete: (ret) => {
             this.showModal = false
           },
         })
@@ -610,8 +687,8 @@
       async handleUpgradeApi() {
         let params = this.addForm
         params.nboBudget = Number(params.nboBudget)
-        if (this.addForm.nboType === '10' && this.addForm.isAdoptDashoo == '10') {
-          this.upgradeA(params)
+        if (this.addForm.nboType === '10' || this.addForm.nboType === '20') {
+          this.upgradeAorB(params)
         } else {
           const [err, res] = await to(projectApi.upgrade(params))
           this.showModal = false
@@ -627,13 +704,6 @@
           }
         }
       },
-      // closeUser(user) {
-      //   if (user) {
-      //     console.log(user)
-      //     this.addForm.userId = user.id
-      //     this.addForm.userName = user.label
-      //   }
-      // },
       goBack() {
         uni.navigateBack({
           //关闭当前页面,返回上一页面或多级页面。

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff