diff --git a/pages.json b/pages.json
index 4cde9e2..521f428 100644
--- a/pages.json
+++ b/pages.json
@@ -277,7 +277,7 @@
}
},
{
- "path": "pages/feedback/index",
+ "path": "pages/feedback/evaluate",
"style": {
"navigationBarTitleText": "投诉建议"
}
diff --git a/pages/feedback/index.vue b/pages/feedback/evaluate.vue
similarity index 97%
rename from pages/feedback/index.vue
rename to pages/feedback/evaluate.vue
index 5f6ae75..8e69bd1 100644
--- a/pages/feedback/index.vue
+++ b/pages/feedback/evaluate.vue
@@ -28,7 +28,7 @@
图片(选填)
-
+
-
-
\ No newline at end of file
diff --git a/pages/login.vue b/pages/login.vue
index f8ffee0..d8affed 100644
--- a/pages/login.vue
+++ b/pages/login.vue
@@ -88,7 +88,7 @@ export default {
}
},
onShow() {
- uni.reLaunch({ url: '/pages/feedback/index' })
+ // uni.reLaunch({ url: '/pages/feedback/evaluate' })
// setTimeout(()=>{
if(Cookies.get('remember')){
this.remember = [Cookies.get('remember')] || [];
diff --git a/pages/mine/index.vue b/pages/mine/index.vue
index e6991f1..307aba7 100644
--- a/pages/mine/index.vue
+++ b/pages/mine/index.vue
@@ -41,7 +41,7 @@
-
+
diff --git a/pages/mine/me/bindingPhone.vue b/pages/mine/me/bindingPhone.vue
index e3b6009..84303ba 100644
--- a/pages/mine/me/bindingPhone.vue
+++ b/pages/mine/me/bindingPhone.vue
@@ -42,14 +42,16 @@
+
+
+
+
+
+
diff --git a/uni_modules/xe-upload/package.json b/uni_modules/xe-upload/package.json
new file mode 100644
index 0000000..b93f8b7
--- /dev/null
+++ b/uni_modules/xe-upload/package.json
@@ -0,0 +1,80 @@
+{
+ "id": "xe-upload",
+ "displayName": "文件选择、文件上传组件(图片,视频,文件等)",
+ "version": "1.0.2",
+ "description": "H5、微信小程序、App端支持图片,视频,文件选择上传;其他端暂不支持文件选择上传",
+ "keywords": [
+ "App、H5、微信小程序、图片,视频,文件上传"
+],
+ "repository": "",
+"engines": {
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "u"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "u"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "u",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "y",
+ "快手": "y",
+ "飞书": "y",
+ "京东": "y"
+ },
+ "快应用": {
+ "华为": "y",
+ "联盟": "y"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/xe-upload/readme.md b/uni_modules/xe-upload/readme.md
new file mode 100644
index 0000000..9002295
--- /dev/null
+++ b/uni_modules/xe-upload/readme.md
@@ -0,0 +1,78 @@
+# xe-upload
+
+## 说明
+
+不占用页面位置的上传组件;
+
+H5、APP、微信小程序中可上传图片,视频和文件;其他端暂时只能上传图片和视频
+
+> 上传图片通过[chooseMedia](https://uniapp.dcloud.net.cn/api/media/video.html#choosemedia)及[chooseImage](https://uniapp.dcloud.net.cn/api/media/image.html#chooseimage)实现
+
+> 上传视频通过[chooseMedia](https://uniapp.dcloud.net.cn/api/media/video.html#choosemedia)及[chooseVideo](https://uniapp.dcloud.net.cn/api/media/video.html#choosevideo)实现
+
+> H5端上传文件通过[chooseFile](https://uniapp.dcloud.net.cn/api/media/file.html#wx-choosemessagefile)实现
+
+> APP上传文件通过[renderjs](https://uniapp.dcloud.net.cn/tutorial/renderjs.html#renderjs)实现
+
+> 微信小程序上传文件通过[chooseMessageFile](https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.chooseMessageFile.html)实现
+
+
+## 使用
+
+Attributes
+
+| 参数 | 说明 | 类型 | 默认值 |
+| ----------- | ----------- | ----------- | ----------- |
+| options | 请求配置(参数与uni.uploadFile的参数一致) | object | { name: 'file' } |
+
+Events
+
+| 事件名 | 说明 | 参数 |
+| ----------- | ----------- | ----------- |
+| callback | 接收数据 | { type, data } |
+
+callback type
+
+| 参数 | 说明 |
+| ----------- | ----------- |
+| warning | 提示信息(下文称warning回调) |
+| success | 上传成功(下文称success回调) |
+| choose | 选择文件(下文称choose回调) |
+
+callback data
+
+```
+'callback.type === success' : [
+ {
+ "size": 176579, // 选择的文件的大小
+ "name": "Kafka.pdf", // 选择的文件的名称(小程序端可能会没有)
+ "type": "application/pdf",
+ "tempFilePath": "blob:http://192.168.137.1:8080/2585769b-3195-4f3d-b9f8-d9e99f55deec", // 临时路路径
+ "fileType": "file", // 文件类型[image, video, file]
+ "response": {
+ "result": {
+ "fileName": "Kafka.pdf",
+ "filePath": `http://localhost:3000/upload/e51d814b649122fc64892d0bc6383d07.pdf`,
+ },
+ "success": true,
+ }, // 上传返回的信息
+ }
+]
+
+'callback.type === choose' : [
+ {
+ "size": 176579, // 选择的文件的大小
+ "name": "Kafka.pdf", // 选择的文件的名称(小程序端可能会没有)
+ "type": "application/pdf",
+ "tempFilePath": "blob:http://192.168.137.1:8080/4204e460-f185-4fc9-9f4d-1bc50ab06981", // 文件临时路径
+ "fileType": "file", // 文件类型[image, video, file]
+ }
+]
+```
+
+## 注意事项
+#### 1、options入参中如果url为空,则choose回调的data列表中只有选择文件能得到的信息和临时路径,临时路径可用于自定义上传方法(APP除外);传入url选择文件后会自动上传到服务器,此时choose回调不会触发,而是执行success回调,success回调的data列表会包括选择文件能得到的信息
+#### 2、APP端文件建议直接上传到服务器,拿到文件上传后的地址再进行其他操作(目前测试APP端file转换后的Blob Url无法用于uni.uploadFile,所以建议APP文件直接上传)
+#### 3、APP端文件暂时支持单个上传
+#### 4、当uni.chooseMedia可用时,会优先使用uni.chooseMedia
+#### 5、具体使用可下载示例项目运行查看完整示例
diff --git a/uni_modules/xe-upload/tools/apis.js b/uni_modules/xe-upload/tools/apis.js
new file mode 100644
index 0000000..6863c1a
--- /dev/null
+++ b/uni_modules/xe-upload/tools/apis.js
@@ -0,0 +1,177 @@
+// eslint-disable
+import { awaitWrap } from './tools';
+/**
+ * 从本地相册选择图片或使用相机拍照
+ * @param {object} config 参数详情 => https://uniapp.dcloud.net.cn/api/media/image.html#chooseimage
+ * @returns
+ */
+export const chooseImage = (config) => {
+ return awaitWrap(
+ new Promise((r, j) => {
+ uni.chooseImage({
+ ...config,
+ success: (res) => {
+ const tmpFiles = res?.tempFiles.map((e) => ({
+ tempFilePath: e.path,
+ tempFile: e,
+ size: e.size,
+ name: e.name,
+ type: e.type,
+ fileType: 'image',
+ }));
+ return r({ type: 'image', ...res, tempFiles: tmpFiles });
+ },
+ fail: (err) => j({ mode: 'chooseImage', data: err }),
+ });
+ })
+ );
+};
+
+/**
+ * 拍摄视频或从手机相册中选视频,返回视频的临时文件路径
+ * @param {object} config 参数详情 => https://uniapp.dcloud.net.cn/api/media/video.html#choosevideo
+ * @returns
+ */
+export const chooseVideo = (config) => {
+ return awaitWrap(
+ new Promise((r, j) => {
+ uni.chooseVideo({
+ ...config,
+ success: (res) => {
+ const tmpFiles = [{
+ ...res,
+ tempFilePath: res.tempFilePath,
+ tempFile: res.tempFile ?? {},
+ size: res.size,
+ name: res.name,
+ type: res.tempFile?.type,
+ fileType: 'video',
+ }];
+ return r({ type: 'video', tempFiles: tmpFiles });
+ },
+ fail: (err) => j({ mode: 'chooseVideo', data: err }),
+ });
+ })
+ );
+};
+
+/**
+ * 拍摄或从手机相册中选择图片或视频
+ * @param {object} config 参数详情 => https://uniapp.dcloud.net.cn/api/media/video.html#choosemedia
+ * @returns
+ */
+export const chooseMedia = (type, config) => {
+ if (!type) return console.error('chooseMedia type cannot be empty');
+ if (!uni.chooseMedia && type === 'image') return chooseImage(config);
+ if (!uni.chooseMedia && type === 'video') return chooseVideo(config);
+ return awaitWrap(
+ new Promise((r, j) => {
+ uni.chooseMedia({
+ ...config,
+ mediaType: [type],
+ success: (res) => r(res),
+ fail: (err) => j({ mode: 'chooseMedia', data: err }),
+ });
+ })
+ );
+};
+
+/**
+ * 从本地选择文件(h5)
+ * @param {object} config 参数详情 => https://uniapp.dcloud.net.cn/api/media/file.html#wx-choosemessagefile
+ * @returns
+ */
+export const chooseFile = (config) => {
+ return awaitWrap(
+ new Promise((r, j) => {
+ uni.chooseFile({
+ ...config,
+ success: (res) => {
+ const tmpFiles = res?.tempFiles.map((e) => {
+ let tmpType = 'file';
+ if (e.type.includes('image')) {
+ tmpType = 'image';
+ }
+ if (e.type.includes('video')) {
+ tmpType = 'video';
+ }
+ return {
+ tempFilePath: e.path,
+ tempFile: e,
+ size: e.size,
+ name: e.name,
+ type: e.type,
+ fileType: tmpType,
+ };
+ });
+ return r({ type: 'file', ...res, tempFiles: tmpFiles });
+ },
+ fail: (err) => j({ mode: 'chooseFile', data: err }),
+ });
+ })
+ );
+};
+
+/**
+ * 从本地选择文件(微信小程序)
+ * @param {object} config 参数详情 => https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.chooseMessageFile.html
+ * @returns
+ */
+export const chooseMessageFile = (config) => {
+ return awaitWrap(
+ new Promise((r, j) => {
+ wx.chooseMessageFile({
+ ...config,
+ success: (res) => {
+ const tmpFiles = res?.tempFiles.map((e) => ({
+ ...e,
+ tempFilePath: e.path,
+ fileType: e.type ?? 'file',
+ }));
+ return r({ type: 'file', ...res, tempFiles: tmpFiles });
+ },
+ fail: (err) => j({ mode: 'chooseMessageFile', data: err }),
+ });
+ })
+ );
+};
+
+/**
+ * 上传
+ * @param {object} config 参数详情 => https://uniapp.dcloud.net.cn/api/request/network-file.html#uploadfile
+ * @param {object} exts 选择的文件的数据
+ * @returns {object} exts + response
+ */
+export const uploadFile = (config, exts = {}) => {
+ return new Promise((r, j) => {
+ uni.uploadFile({
+ ...config,
+ success: (res) => r({ ...exts, response: JSON.parse(res.data) }),
+ fail: (err) => j({ mode: 'uploadFile', data: err }),
+ });
+ });
+};
+
+export const appUploadFile = (config, exts = {}, onprogress) => {
+ const { url, header, formData } = config;
+ return new Promise((r, j) => {
+ const xhr = new XMLHttpRequest();
+ xhr.open('POST', url, true);
+ for (let key in header) {
+ xhr.setRequestHeader(key, header[key]);
+ }
+ if (onprogress) {
+ xhr.upload.onprogress = onprogress;
+ }
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === 4) {
+ if (xhr.status === 200) {
+ r({ ...exts, response: JSON.parse(xhr.responseText) });
+ } else {
+ j({ mode: 'uploadFile', data: { data: xhr.responseText, errMsg: 'uploadFile fail.' } });
+ }
+ }
+ }
+ xhr.send(formData);
+ });
+};
diff --git a/uni_modules/xe-upload/tools/tools.js b/uni_modules/xe-upload/tools/tools.js
new file mode 100644
index 0000000..28009b7
--- /dev/null
+++ b/uni_modules/xe-upload/tools/tools.js
@@ -0,0 +1,180 @@
+// eslint-disable
+export const isObject = (obj) => {
+ return obj
+ ? Object.prototype.toString.call(obj) === "[object Object]"
+ : false;
+};
+export const isArray = (arr) => {
+ return arr ? Array.isArray(arr) : false;
+};
+/**
+ * handle async await
+ * @param {*} promise promise
+ */
+export const awaitWrap = (promise) =>
+ promise.then((res) => [null, res]).catch((err) => [err, {}]);
+/**
+ * 深拷贝
+ * @param {*} source
+ */
+export const deepClone = (source) => {
+ if (!isObject(source) && !isArray(source)) return source;
+ const targetObj = isArray(source) ? [] : {}; // 判断复制的目标是数组还是对象
+ for (let keys in source) {
+ // 遍历目标
+ if (source.hasOwnProperty(keys)) {
+ if (source[keys] && typeof source[keys] === "object") {
+ // 如果值是对象,就递归一下
+ targetObj[keys] = isArray(source[keys]) ? [] : {};
+ targetObj[keys] = deepClone(source[keys]);
+ } else {
+ // 如果不是,就直接赋值
+ targetObj[keys] = source[keys];
+ }
+ }
+ }
+ return targetObj;
+};
+/**
+ * @description JS对象深度合并
+ * @param {object} target 需要拷贝的对象
+ * @param {object} source 拷贝的来源对象
+ * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象)
+ */
+export const deepMerge = (target = {}, source = {}) => {
+ target = deepClone(target);
+ if (typeof target !== "object" || typeof source !== "object") return false;
+ for (const prop in source) {
+ if (!source.hasOwnProperty(prop)) continue;
+ if (prop in target) {
+ if (typeof target[prop] !== "object") {
+ target[prop] = source[prop];
+ } else if (typeof source[prop] !== "object") {
+ target[prop] = source[prop];
+ } else if (target[prop].concat && source[prop].concat) {
+ target[prop] = target[prop].concat(source[prop]);
+ } else {
+ target[prop] = deepMerge(target[prop], source[prop]);
+ }
+ } else {
+ target[prop] = source[prop];
+ }
+ }
+ return target;
+};
+/**
+ * 将File对象转为 Blob Url
+ * @param {File} File对象
+ * @returns Blob Url
+ */
+export const fileToBlob = (file) => {
+ if (!file) return;
+ const fileType = file.type;
+ const blob = new Blob([file], { type: fileType || 'application/*' });
+ const blobUrl = window.URL.createObjectURL(blob);
+ return blobUrl;
+};
+/**
+ * 将File对象转为 base64
+ * @param {File} File对象
+ * @returns base64
+ */
+export const fileToBase64 = (file) => {
+ if (!file) return;
+ return new Promise((r, j) => {
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ const base64String = reader.result;
+ r(base64String);
+ };
+ reader.onerror = () => {
+ j({ mode: 'fileToBase64', data: { errMsg: 'File to base64 fail.' } });
+ };
+ reader.readAsDataURL(file);
+ });
+};
+/**
+ * base64转临时路径(改自https://github.com/zhetengbiji/image-tools/blob/master/index.js)
+ * @param base64
+ * @returns
+ */
+function dataUrlToBase64(str) {
+ var array = str.split(',');
+ return array[array.length - 1];
+};
+function biggerThan(v1, v2) {
+ var v1Array = v1.split('.');
+ var v2Array = v2.split('.');
+ var update = false;
+ for (var index = 0; index < v2Array.length; index++) {
+ var diff = v1Array[index] - v2Array[index];
+ if (diff !== 0) {
+ update = diff > 0;
+ break;
+ }
+ }
+ return update;
+};
+var index = 0;
+function getNewFileId() {
+ return Date.now() + String(index++);
+};
+export const base64ToPath = (base64, name = '') => {
+ return new Promise((r, j) => {
+ if (typeof plus !== 'object') {
+ return j(new Error('not support'));
+ }
+ var fileName = '';
+ if (name) {
+ const names = name.split('.');
+ const extName = names.splice(-1);
+ fileName = `${names.join('.')}-${getNewFileId()}.${extName}`;
+ } else {
+ const names = base64.split(',')[0].match(/data\:\S+\/(\S+);/);
+ if (!names) {
+ j(new Error('base64 error'));
+ }
+ const extName = names[1];
+ fileName = `${getNewFileId()}.${extName}`;
+ }
+ var basePath = '_doc';
+ var dirPath = 'uniapp_temp';
+ var filePath = `${basePath}/${dirPath}/${fileName}`;
+ if (!biggerThan(plus.os.name === 'Android' ? '1.9.9.80627' : '1.9.9.80472', plus.runtime.innerVersion)) {
+ plus.io.resolveLocalFileSystemURL(basePath, function (entry) {
+ entry.getDirectory(dirPath, {
+ create: true,
+ exclusive: false,
+ }, function (entry) {
+ entry.getFile(fileName, {
+ create: true,
+ exclusive: false,
+ }, function (entry) {
+ entry.createWriter(function (writer) {
+ writer.onwrite = function () {
+ r(filePath);
+ }
+ writer.onerror = j;
+ writer.seek(0);
+ writer.writeAsBinary(dataUrlToBase64(base64));
+ }, j)
+ }, j)
+ }, j)
+ }, j)
+ return;
+ }
+ var bitmap = new plus.nativeObj.Bitmap(fileName);
+ bitmap.loadBase64Data(base64, function () {
+ bitmap.save(filePath, {}, function () {
+ bitmap.clear();
+ r(filePath);
+ }, function (error) {
+ bitmap.clear();
+ j(error);
+ });
+ }, function (error) {
+ bitmap.clear();
+ j(error);
+ });
+ });
+};