diff --git a/package.json b/package.json
index 68f3e363..4240893a 100644
--- a/package.json
+++ b/package.json
@@ -58,12 +58,12 @@
"luch-request": "^3.0.8",
"pinia": "^2.0.33",
"pinia-plugin-persist-uni": "^1.2.0",
- "vue": "^3.4.21",
- "vue-i18n": "^9.1.9",
- "weixin-js-sdk": "^1.6.0",
"pinyin-pro": "^3.23.1",
"quill": "^1.3.7",
- "quill-image-resize-mp": "^3.0.1"
+ "quill-image-resize-mp": "^3.0.1",
+ "vue": "^3.4.21",
+ "vue-i18n": "^9.1.9",
+ "weixin-js-sdk": "^1.6.0"
},
"devDependencies": {
"@dcloudio/types": "^3.4.8",
diff --git a/src/.env.js b/src/.env.js
deleted file mode 100644
index c9e42cfd..00000000
--- a/src/.env.js
+++ /dev/null
@@ -1,25 +0,0 @@
-//设置环境(打包前修改此变量)
-const ENV = "DEV";
-const UNI_APP = {};
-// 表情包路径
-UNI_APP.EMO_URL = "/static/emoji/";
-// #ifdef MP-WEIXIN
-// 微信小程序的本地表情包经常莫名失效,建议将表情放到服务器中
-// UNI_APP.EMO_URL = "https://www.boxim.online/emoji/";
-// #endif
-
-if (ENV == "DEV") {
- UNI_APP.BASE_URL = "http://chatsever.zianxin.com";
- // UNI_APP.BASE_URL = "https://www.boxim.online/api";
- UNI_APP.WS_URL = "ws://chatapp.zianxin.com/im";
- // UNI_APP.WS_URL = "wss://www.boxim.online/im";
- // H5 走本地代理解决跨域问题
- // #ifdef H5
- UNI_APP.BASE_URL = "/api";
- // #endif
-}
-if (ENV == "PROD") {
- UNI_APP.BASE_URL = "https://www.boxim.online/api";
- UNI_APP.WS_URL = "wss://www.boxim.online/im";
-}
-export default UNI_APP;
diff --git a/src/App.vue b/src/App.vue
index b10ac82c..d4a3e3f6 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -2,13 +2,16 @@
import { onLaunch, onShow, onError } from '@dcloudio/uni-app';
import { ShoproInit } from './sheep';
import RecentApi from './sheep/api/member/recent';
+
+ // 与聊天相关的导入
import http from './common/request';
import * as msgType from './common/messageType';
import * as enums from './common/enums';
import * as wsApi from './common/wssocket';
- import UNI_APP from '@/.env.js';
+ import CHAT_ENV from '@/chat-env.js';
export default {
+ /** ↓↓↓ 以下为app启动聊天相关模块初始化,WebSocket链接,加载聊天消息,加载离线消息,加载群组消息,初始化聊天相关的全局状态仓库 */
data() {
return {
isExit: false, // 是否已退出
@@ -20,8 +23,6 @@
},
methods: {
init() {
- console.log('xxxc');
-
this.reconnecting = false;
this.isExit = false;
// 加载数据
@@ -37,7 +38,7 @@
},
initWebSocket() {
let loginInfo = uni.getStorageSync('loginInfo');
- wsApi.connect(UNI_APP.WS_URL, loginInfo.accessToken);
+ wsApi.connect(CHAT_ENV.WS_URL, loginInfo.accessToken);
wsApi.onConnect(() => {
if (this.reconnecting) {
// 重连成功
@@ -87,8 +88,6 @@
});
},
loadStore() {
- console.log(this.userStore);
-
return this.userStore.loadUser().then(() => {
const promises = [];
promises.push(this.friendStore.loadFriend());
@@ -200,9 +199,9 @@
// 单人视频信令
if (msgType.isRtcPrivate(msg.type)) {
// #ifdef MP-WEIXIN
- // 小程序不支持音视频
- return;
+ return; // 小程序不支持音视频
// #endif
+
// 被呼叫,弹出视频页面
let delayTime = 100;
if (
@@ -315,9 +314,9 @@
// 群视频信令
if (msgType.isRtcGroup(msg.type)) {
// #ifdef MP-WEIXIN
- // 小程序不支持音视频
- return;
+ return; // 小程序不支持音视频
// #endif
+
// 被呼叫,弹出视频页面
let delayTime = 100;
if (msg.type == enums.MESSAGE_TYPE.RTC_GROUP_SETUP) {
@@ -404,6 +403,7 @@
// this.audioTip.src = "/static/audio/tip.wav";
// this.audioTip.play();
},
+ // 刷新token
refreshToken(loginInfo) {
return new Promise((resolve, reject) => {
if (!loginInfo || !loginInfo.refreshToken) {
@@ -443,7 +443,7 @@
});
// 重新连接
let loginInfo = uni.getStorageSync('loginInfo');
- wsApi.reconnect(UNI_APP.WS_URL, loginInfo.accessToken);
+ wsApi.reconnect(CHAT_ENV.WS_URL, loginInfo.accessToken);
})
.catch(() => {
// 5s后重试
@@ -483,6 +483,8 @@
// #endif
},
},
+ /** ↑↑↑ 以上为app启动聊天相关模块初始化,WebSocket链接,加载聊天消息,加载离线消息,加载群组消息,初始化聊天相关的全局状态仓库 */
+
onLaunch() {
// 隐藏原生导航栏 使用自定义底部导航
// uni.hideTabBar({
@@ -490,7 +492,7 @@
// });
// 加载Shopro底层依赖
ShoproInit();
- // 更新用户位置
+ // app启动时更新用户位置
uni.getLocation({
success: (res) => {
const params = {
@@ -505,8 +507,7 @@
});
this.$mountStore();
- // 延迟1s,避免用户看到页面跳转
- this.closeSplashscreen(1000);
+ this.closeSplashscreen(1000); // 延迟1s,避免用户看到页面跳转
uni.setStorageSync('loginInfo', {
accessToken:
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIyIiwiZXhwIjoxNzY0MDU5ODk2LCJpbmZvIjoie1wibmlja05hbWVcIjpcInd3d1wiLFwidGVybWluYWxcIjoxLFwidXNlcklkXCI6MixcInVzZXJOYW1lXCI6XCJ3d3dcIn0ifQ.xBfQJg6FFHQGzkK_IUGZUjVN8g5tex3VtWsTr1gGjzg',
@@ -519,21 +520,13 @@
let loginInfo = uni.getStorageSync('loginInfo');
this.refreshToken(loginInfo)
.then(() => {
- // #ifdef H5
- // 跳转到聊天页
- uni.switchTab({
- url: '/pages/message/message',
- });
- // #endif
- // 初始化
+ // 聊天初始化
this.init();
this.closeSplashscreen(0);
})
.catch(() => {
// 跳转到登录页
- // uni.navigateTo({
- // url: '/pages/login/login',
- // });
+ uni.redirectTo({ url: '/pages/login/login' });
});
},
onShow() {
@@ -554,6 +547,35 @@
diff --git a/src/chat-env.js b/src/chat-env.js
new file mode 100644
index 00000000..353b4a4c
--- /dev/null
+++ b/src/chat-env.js
@@ -0,0 +1,22 @@
+const ENV = 'DEV'; //设置环境(打包前修改此变量)
+
+const CHAT_ENV = {};
+
+if (ENV == 'DEV') {
+ CHAT_ENV.BASE_URL = 'http://chatsever.zianxin.com';
+ // CHAT_ENV.BASE_URL = "https://www.boxim.online/api";
+ CHAT_ENV.WS_URL = 'ws://chatapp.zianxin.com/im';
+ // CHAT_ENV.WS_URL = "wss://www.boxim.online/im";
+
+ // H5 走本地代理解决跨域问题
+ // #ifdef H5
+ CHAT_ENV.BASE_URL = '/api';
+ // #endif
+}
+
+if (ENV == 'PROD') {
+ CHAT_ENV.BASE_URL = 'https://www.boxim.online/api';
+ CHAT_ENV.WS_URL = 'wss://www.boxim.online/im';
+}
+
+export default CHAT_ENV;
diff --git a/src/store/chatStore.js b/src/chat-store/chatStore.js
similarity index 100%
rename from src/store/chatStore.js
rename to src/chat-store/chatStore.js
diff --git a/src/store/configStore.js b/src/chat-store/configStore.js
similarity index 100%
rename from src/store/configStore.js
rename to src/chat-store/configStore.js
diff --git a/src/store/friendStore.js b/src/chat-store/friendStore.js
similarity index 100%
rename from src/store/friendStore.js
rename to src/chat-store/friendStore.js
diff --git a/src/store/groupStore.js b/src/chat-store/groupStore.js
similarity index 100%
rename from src/store/groupStore.js
rename to src/chat-store/groupStore.js
diff --git a/src/store/userStore.js b/src/chat-store/userStore.js
similarity index 100%
rename from src/store/userStore.js
rename to src/chat-store/userStore.js
diff --git a/src/common/emotion.js b/src/common/emotion.js
index f7dfe374..a79d571b 100644
--- a/src/common/emotion.js
+++ b/src/common/emotion.js
@@ -1,43 +1,98 @@
-import UNI_APP from '@/.env.js'
+const EMO_URL = '/static/emoji/';
+// #ifdef MP-WEIXIN
+// 微信小程序的本地表情包经常莫名失效,建议将表情放到服务器中
+// CHAT_ENV.EMO_URL = "https://www.boxim.online/emoji/";
+// #endif
-
-const emoTextList = ['憨笑', '媚眼', '开心', '坏笑', '可怜', '爱心', '笑哭', '拍手', '惊喜', '打气',
- '大哭', '流泪', '饥饿', '难受', '健身', '示爱', '色色', '眨眼', '暴怒', '惊恐',
- '思考', '头晕', '大吐', '酷笑', '翻滚', '享受', '鼻涕', '快乐', '雀跃', '微笑',
- '贪婪', '红心', '粉心', '星星', '大火', '眼睛', '音符', "叹号", "问号", "绿叶",
- "燃烧", "喇叭", "警告", "信封", "房子", "礼物", "点赞", "举手", "拍手", "点头",
- "摇头", "偷瞄", "庆祝", "疾跑", "打滚", "惊吓", "起跳"
+const emoTextList = [
+ '憨笑',
+ '媚眼',
+ '开心',
+ '坏笑',
+ '可怜',
+ '爱心',
+ '笑哭',
+ '拍手',
+ '惊喜',
+ '打气',
+ '大哭',
+ '流泪',
+ '饥饿',
+ '难受',
+ '健身',
+ '示爱',
+ '色色',
+ '眨眼',
+ '暴怒',
+ '惊恐',
+ '思考',
+ '头晕',
+ '大吐',
+ '酷笑',
+ '翻滚',
+ '享受',
+ '鼻涕',
+ '快乐',
+ '雀跃',
+ '微笑',
+ '贪婪',
+ '红心',
+ '粉心',
+ '星星',
+ '大火',
+ '眼睛',
+ '音符',
+ '叹号',
+ '问号',
+ '绿叶',
+ '燃烧',
+ '喇叭',
+ '警告',
+ '信封',
+ '房子',
+ '礼物',
+ '点赞',
+ '举手',
+ '拍手',
+ '点头',
+ '摇头',
+ '偷瞄',
+ '庆祝',
+ '疾跑',
+ '打滚',
+ '惊吓',
+ '起跳',
];
const regex = /\#[\u4E00-\u9FA5]{1,3}\;/gi;
let containEmoji = (content) => {
- return regex.test(content)
-}
+ return regex.test(content);
+};
let transform = (content, extClass) => {
- return content.replace(regex, (emoText)=>{
- // 将匹配结果替换表情图片
- let word = emoText.replace(/\#|\;/gi, '');
- let idx = emoTextList.indexOf(word);
- if (idx == -1) {
- return emoText;
- }
- let path = textToPath(emoText);
- let img = `
`;
- return img;
- });
-}
+ return content.replace(regex, (emoText) => {
+ // 将匹配结果替换表情图片
+ let word = emoText.replace(/\#|\;/gi, '');
+ let idx = emoTextList.indexOf(word);
+ if (idx == -1) {
+ return emoText;
+ }
+ let path = textToPath(emoText);
+ let img = `
`;
+ return img;
+ });
+};
let textToPath = (emoText) => {
- let word = emoText.replace(/\#|\;/gi, '');
- let idx = emoTextList.indexOf(word);
- return UNI_APP.EMO_URL + idx + ".gif";
-}
+ let word = emoText.replace(/\#|\;/gi, '');
+ let idx = emoTextList.indexOf(word);
+ return EMO_URL + idx + '.gif';
+};
export default {
- containEmoji,
- emoTextList,
- transform,
- textToPath
-}
\ No newline at end of file
+ containEmoji,
+ emoTextList,
+ transform,
+ textToPath,
+};
diff --git a/src/common/enums.js b/src/common/enums.js
index fa7ada08..505e9065 100644
--- a/src/common/enums.js
+++ b/src/common/enums.js
@@ -1,70 +1,64 @@
-
const MESSAGE_TYPE = {
- TEXT: 0,
- IMAGE: 1,
- FILE: 2,
- AUDIO: 3,
- VIDEO: 4,
- RECALL: 10,
- READED: 11,
- RECEIPT: 12,
- TIP_TIME: 20,
- TIP_TEXT: 21,
- LOADING: 30,
- ACT_RT_VOICE: 40,
- ACT_RT_VIDEO: 41,
- USER_BANNED: 50,
- FRIEND_NEW: 80,
- FRIEND_DEL: 81,
- FRIEND_DND: 82,
- GROUP_NEW: 90,
- GROUP_DEL: 91,
- GROUP_DND: 92,
- RTC_CALL_VOICE: 100,
- RTC_CALL_VIDEO: 101,
- RTC_ACCEPT: 102,
- RTC_REJECT: 103,
- RTC_CANCEL: 104,
- RTC_FAILED: 105,
- RTC_HANDUP: 106,
- RTC_CANDIDATE: 107,
- RTC_GROUP_SETUP: 200,
- RTC_GROUP_ACCEPT: 201,
- RTC_GROUP_REJECT: 202,
- RTC_GROUP_FAILED: 203,
- RTC_GROUP_CANCEL: 204,
- RTC_GROUP_QUIT: 205,
- RTC_GROUP_INVITE: 206,
- RTC_GROUP_JOIN: 207,
- RTC_GROUP_OFFER: 208,
- RTC_GROUP_ANSWER: 209,
- RTC_GROUP_CANDIDATE: 210,
- RTC_GROUP_DEVICE: 211
-}
+ TEXT: 0,
+ IMAGE: 1,
+ FILE: 2,
+ AUDIO: 3,
+ VIDEO: 4,
+ RECALL: 10,
+ READED: 11,
+ RECEIPT: 12,
+ TIP_TIME: 20,
+ TIP_TEXT: 21,
+ LOADING: 30,
+ ACT_RT_VOICE: 40,
+ ACT_RT_VIDEO: 41,
+ USER_BANNED: 50,
+ FRIEND_NEW: 80,
+ FRIEND_DEL: 81,
+ FRIEND_DND: 82,
+ GROUP_NEW: 90,
+ GROUP_DEL: 91,
+ GROUP_DND: 92,
+ RTC_CALL_VOICE: 100,
+ RTC_CALL_VIDEO: 101,
+ RTC_ACCEPT: 102,
+ RTC_REJECT: 103,
+ RTC_CANCEL: 104,
+ RTC_FAILED: 105,
+ RTC_HANDUP: 106,
+ RTC_CANDIDATE: 107,
+ RTC_GROUP_SETUP: 200,
+ RTC_GROUP_ACCEPT: 201,
+ RTC_GROUP_REJECT: 202,
+ RTC_GROUP_FAILED: 203,
+ RTC_GROUP_CANCEL: 204,
+ RTC_GROUP_QUIT: 205,
+ RTC_GROUP_INVITE: 206,
+ RTC_GROUP_JOIN: 207,
+ RTC_GROUP_OFFER: 208,
+ RTC_GROUP_ANSWER: 209,
+ RTC_GROUP_CANDIDATE: 210,
+ RTC_GROUP_DEVICE: 211,
+};
const USER_STATE = {
- OFFLINE: 0,
- FREE: 1,
- BUSY: 2
-}
+ OFFLINE: 0,
+ FREE: 1,
+ BUSY: 2,
+};
const TERMINAL_TYPE = {
- WEB: 0,
- APP: 1
-}
+ WEB: 0,
+ APP: 1,
+};
const MESSAGE_STATUS = {
- FAILED: -2, // 发送失败
- SENDING: -1, // 发送中(消息没到服务器)
- PENDING: 0, // 未送达(消息已到服务器,但对方没收到)
- DELIVERED: 1, // 已送达(对方已收到,但是未读消息)
- RECALL: 2, // 已撤回
- READED: 3, // 消息已读
-}
+ FAILED: -2, // 发送失败
+ SENDING: -1, // 发送中(消息没到服务器)
+ PENDING: 0, // 未送达(消息已到服务器,但对方没收到)
+ DELIVERED: 1, // 已送达(对方已收到,但是未读消息)
+ RECALL: 2, // 已撤回
+ READED: 3, // 消息已读
+};
-export {
- MESSAGE_TYPE,
- USER_STATE,
- TERMINAL_TYPE,
- MESSAGE_STATUS
-}
+export { MESSAGE_TYPE, USER_STATE, TERMINAL_TYPE, MESSAGE_STATUS };
diff --git a/src/common/messageType.js b/src/common/messageType.js
index f6b1cafc..289b6a38 100644
--- a/src/common/messageType.js
+++ b/src/common/messageType.js
@@ -1,40 +1,31 @@
-
// 是否普通消息
let isNormal = function (type) {
- return type >= 0 && type < 10;
-}
+ return type >= 0 && type < 10;
+};
// 是否状态消息
let isStatus = function (type) {
- return type >= 10 && type < 20;
-}
+ return type >= 10 && type < 20;
+};
// 是否提示消息
let isTip = function (type) {
- return type >= 20 && type < 30;
-}
+ return type >= 20 && type < 30;
+};
// 操作交互类消息
let isAction = function (type) {
- return type >= 40 && type < 50;
-}
+ return type >= 40 && type < 50;
+};
// 单人通话信令
let isRtcPrivate = function (type) {
- return type >= 100 && type < 200;
-}
+ return type >= 100 && type < 200;
+};
// 多人通话信令
let isRtcGroup = function (type) {
- return type >= 200 && type < 300;
-}
+ return type >= 200 && type < 300;
+};
-
-export {
- isNormal,
- isStatus,
- isTip,
- isAction,
- isRtcPrivate,
- isRtcGroup
-}
\ No newline at end of file
+export { isNormal, isStatus, isTip, isAction, isRtcPrivate, isRtcGroup };
diff --git a/src/common/recorder-app.js b/src/common/recorder-app.js
index f114bbb3..389e5b65 100644
--- a/src/common/recorder-app.js
+++ b/src/common/recorder-app.js
@@ -1,67 +1,61 @@
-import UNI_APP from '@/.env.js';
+import CHAT_ENV from '@/chat-env.js';
const rc = uni.getRecorderManager();
// 录音开始时间
let startTime = null;
-let checkIsEnable = ()=>{
- return true;
-}
+let checkIsEnable = () => {
+ return true;
+};
let start = () => {
- return new Promise((resolve, reject) => {
- rc.onStart(() => {
- startTime = new Date();
- resolve()
- });
- rc.onError((e) => {
- console.log(e);
- reject(e)
- })
- rc.start({
- format: 'mp3' // 录音格式,可选值:aac/mp3
- });
- })
-}
+ return new Promise((resolve, reject) => {
+ rc.onStart(() => {
+ startTime = new Date();
+ resolve();
+ });
+ rc.onError((e) => {
+ console.log(e);
+ reject(e);
+ });
+ rc.start({
+ format: 'mp3', // 录音格式,可选值:aac/mp3
+ });
+ });
+};
let close = () => {
- rc.stop();
-}
+ rc.stop();
+};
let upload = () => {
- return new Promise((resolve, reject) => {
- rc.onStop((wavFile, a, b) => {
- uni.uploadFile({
- url: UNI_APP.BASE_URL + '/file/upload',
- header: {
- accessToken: uni.getStorageSync("loginInfo").accessToken
- },
- filePath: wavFile.tempFilePath,
- name: 'file',
- success: (res) => {
- let r = JSON.parse(res.data);
- if (r.code != 200) {
- reject(r.message);
- } else {
- const duration = (new Date().getTime() - startTime.getTime()) /
- 1000
- const data = {
- duration: Math.round(duration),
- url: r.data
- }
- resolve(data);
- }
- },
- fail: (e) => {
- reject(e);
- }
- })
- });
- })
-}
+ return new Promise((resolve, reject) => {
+ rc.onStop((wavFile, a, b) => {
+ uni.uploadFile({
+ url: CHAT_ENV.BASE_URL + '/file/upload',
+ header: {
+ accessToken: uni.getStorageSync('loginInfo').accessToken,
+ },
+ filePath: wavFile.tempFilePath,
+ name: 'file',
+ success: (res) => {
+ let r = JSON.parse(res.data);
+ if (r.code != 200) {
+ reject(r.message);
+ } else {
+ const duration = (new Date().getTime() - startTime.getTime()) / 1000;
+ const data = {
+ duration: Math.round(duration),
+ url: r.data,
+ };
+ resolve(data);
+ }
+ },
+ fail: (e) => {
+ reject(e);
+ },
+ });
+ });
+ });
+};
-export {
- checkIsEnable,
- start,
- close,
- upload
-}
\ No newline at end of file
+export { checkIsEnable, start, close, upload };
diff --git a/src/common/recorder-h5.js b/src/common/recorder-h5.js
index 38559426..a82b3e8c 100644
--- a/src/common/recorder-h5.js
+++ b/src/common/recorder-h5.js
@@ -1,92 +1,89 @@
-import UNI_APP from '@/.env.js';
+import CHAT_ENV from '@/chat-env.js';
let rc = null;
let duration = 0;
let chunks = [];
let stream = null;
-let startTime = null;
+let startTime = null;
let checkIsEnable = () => {
- if (origin.indexOf('https') === -1 && origin.indexOf('localhost') === -1 &&
- origin.indexOf('127.0.0.1') === -1) {
- uni.showToast({
- title: '请在https环境中使用录音功能',
- icon: 'error'
- })
- return false;
- }
- if (!navigator.mediaDevices || !window.MediaRecorder) {
- uni.showToast({
- title: '当前浏览器不支持录音',
- icon: 'error'
- })
- return false;
- }
- return true;
-}
+ if (
+ origin.indexOf('https') === -1 &&
+ origin.indexOf('localhost') === -1 &&
+ origin.indexOf('127.0.0.1') === -1
+ ) {
+ uni.showToast({
+ title: '请在https环境中使用录音功能',
+ icon: 'error',
+ });
+ return false;
+ }
+ if (!navigator.mediaDevices || !window.MediaRecorder) {
+ uni.showToast({
+ title: '当前浏览器不支持录音',
+ icon: 'error',
+ });
+ return false;
+ }
+ return true;
+};
let start = () => {
- return navigator.mediaDevices.getUserMedia({ audio: true }).then(audioStream => {
- startTime = new Date().getTime();
- chunks = [];
- stream = audioStream;
- rc = new MediaRecorder(stream)
- rc.start()
- })
-}
+ return navigator.mediaDevices.getUserMedia({ audio: true }).then((audioStream) => {
+ startTime = new Date().getTime();
+ chunks = [];
+ stream = audioStream;
+ rc = new MediaRecorder(stream);
+ rc.start();
+ });
+};
let close = () => {
- stream.getTracks().forEach((track) => {
- track.stop()
- })
- rc.stop()
-}
-
+ stream.getTracks().forEach((track) => {
+ track.stop();
+ });
+ rc.stop();
+};
let upload = () => {
- return new Promise((resolve, reject) => {
- rc.ondataavailable = (e) => {
- chunks.push(e.data)
- }
- rc.onstop = () => {
- if(!chunks[0].size){
- chunks = [];
- return;
- }
- duration = (new Date().getTime() - startTime) / 1000;
- const newbolb = new Blob(chunks, { 'type': 'audio/mpeg' });
- const name = new Date().getDate() + '.mp3';
- const file = new File([newbolb], name)
- uni.uploadFile({
- url: UNI_APP.BASE_URL + '/file/upload',
- header: {
- accessToken: uni.getStorageSync("loginInfo").accessToken
- },
- file: file,
- name: 'file',
- success: (res) => {
- let r = JSON.parse(res.data);
- if (r.code != 200) {
- console.log(res)
- reject(r.message);
- } else {
- const data = {
- duration: parseInt(duration),
- url: r.data
- }
- resolve(data);
- }
- },
- fail: (e) => {
- reject(e);
- }
- })
- }
- })
-}
+ return new Promise((resolve, reject) => {
+ rc.ondataavailable = (e) => {
+ chunks.push(e.data);
+ };
+ rc.onstop = () => {
+ if (!chunks[0].size) {
+ chunks = [];
+ return;
+ }
+ duration = (new Date().getTime() - startTime) / 1000;
+ const newbolb = new Blob(chunks, { type: 'audio/mpeg' });
+ const name = new Date().getDate() + '.mp3';
+ const file = new File([newbolb], name);
+ uni.uploadFile({
+ url: CHAT_ENV.BASE_URL + '/file/upload',
+ header: {
+ accessToken: uni.getStorageSync('loginInfo').accessToken,
+ },
+ file: file,
+ name: 'file',
+ success: (res) => {
+ let r = JSON.parse(res.data);
+ if (r.code != 200) {
+ console.log(res);
+ reject(r.message);
+ } else {
+ const data = {
+ duration: parseInt(duration),
+ url: r.data,
+ };
+ resolve(data);
+ }
+ },
+ fail: (e) => {
+ reject(e);
+ },
+ });
+ };
+ });
+};
-export {
- checkIsEnable,
- start,
- close,
- upload
-}
\ No newline at end of file
+export { checkIsEnable, start, close, upload };
diff --git a/src/common/request.js b/src/common/request.js
index 83f8e429..38c41d86 100644
--- a/src/common/request.js
+++ b/src/common/request.js
@@ -1,4 +1,4 @@
-import UNI_APP from '@/.env.js';
+import CHAT_ENV from '@/chat-env.js';
// 请求队列
let requestList = [];
@@ -13,8 +13,7 @@ const request = (options) => {
}
return new Promise(function (resolve, reject) {
uni.request({
- url: UNI_APP.BASE_URL + options.url,
- // url: UNI_APP.BASE_URL + options.url,
+ url: CHAT_ENV.BASE_URL + options.url,
method: options.method || 'GET',
header: header,
data: options.data || {},
@@ -75,7 +74,7 @@ const reqRefreshToken = (loginInfo) => {
return new Promise(function (resolve, reject) {
uni.request({
method: 'PUT',
- url: UNI_APP.BASE_URL + '/refreshToken',
+ url: CHAT_ENV.BASE_URL + '/refreshToken',
header: {
refreshToken: loginInfo.refreshToken,
},
diff --git a/src/common/str.js b/src/common/str.js
index 3f6de923..e69fb11e 100644
--- a/src/common/str.js
+++ b/src/common/str.js
@@ -1,14 +1,14 @@
let html2Escape = (strText) => {
- return strText.replace(/[<>&"]/g, function(c) {
- return {
- '<': '<',
- '>': '>',
- '&': '&',
- '"': '"'
- } [c];
- });
-}
+ return strText.replace(/[<>&"]/g, function (c) {
+ return {
+ '<': '<',
+ '>': '>',
+ '&': '&',
+ '"': '"',
+ }[c];
+ });
+};
export default {
- html2Escape
-}
\ No newline at end of file
+ html2Escape,
+};
diff --git a/src/common/url.js b/src/common/url.js
index c95d8341..ccdf9410 100644
--- a/src/common/url.js
+++ b/src/common/url.js
@@ -1,23 +1,22 @@
-
// 使用正则表达式匹配更广泛的URL格式(此正则由deepseek生成)
-const regex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]|\bwww\.[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
-
-let containUrl = (content) => {
- return regex.test(content)
-}
+const regex =
+ /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]|\bwww\.[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi;
+let containUrl = (content) => {
+ return regex.test(content);
+};
let replaceURLWithHTMLLinks = (content, color) => {
- return content.replace(regex, (url) => {
- // 如果URL不以http(s)://开头,则添加http://前缀
- if (!url.startsWith("http")) {
- url = "http://" + url;
- }
- return `${url}`;
- });
-}
+ return content.replace(regex, (url) => {
+ // 如果URL不以http(s)://开头,则添加http://前缀
+ if (!url.startsWith('http')) {
+ url = 'http://' + url;
+ }
+ return `${url}`;
+ });
+};
export default {
- containUrl,
- replaceURLWithHTMLLinks
-}
\ No newline at end of file
+ containUrl,
+ replaceURLWithHTMLLinks,
+};
diff --git a/src/common/wssocket.js b/src/common/wssocket.js
index 5747dc55..44891d4a 100644
--- a/src/common/wssocket.js
+++ b/src/common/wssocket.js
@@ -1,4 +1,4 @@
-let accessToken = "";
+let accessToken = '';
let messageCallBack = null;
let closeCallBack = null;
let connectCallBack = null;
@@ -8,147 +8,135 @@ let lastConnectTime = new Date(); // 最后一次连接时间
let socketTask = null;
let connect = (wsurl, token) => {
- accessToken = token;
- if (isConnect) {
- return;
- }
- lastConnectTime = new Date();
- socketTask = uni.connectSocket({
- url: wsurl,
- success: (res) => {},
- fail: (e) => {
- console.log(e);
- console.log("WebSocket连接失败,10s后重连");
- setTimeout(() => {
- connect();
- }, 10000)
- }
- });
+ accessToken = token;
+ if (isConnect) {
+ return;
+ }
+ lastConnectTime = new Date();
+ socketTask = uni.connectSocket({
+ url: wsurl,
+ success: (res) => {},
+ fail: (e) => {
+ console.log(e);
+ console.log('WebSocket连接失败,10s后重连');
+ setTimeout(() => {
+ connect();
+ }, 10000);
+ },
+ });
- socketTask.onOpen((res) => {
- console.log("WebSocket连接成功");
- isConnect = true;
- // 发送登录命令
- let loginInfo = {
- cmd: 0,
- data: {
- accessToken: accessToken
- }
- };
- socketTask.send({
- data: JSON.stringify(loginInfo)
- });
- })
+ socketTask.onOpen((res) => {
+ console.log('WebSocket连接成功');
+ isConnect = true;
+ // 发送登录命令
+ let loginInfo = {
+ cmd: 0,
+ data: {
+ accessToken: accessToken,
+ },
+ };
+ socketTask.send({
+ data: JSON.stringify(loginInfo),
+ });
+ });
- socketTask.onMessage((res) => {
- let sendInfo = JSON.parse(res.data)
- if (sendInfo.cmd == 0) {
- heartCheck.start()
- connectCallBack && connectCallBack();
- console.log('WebSocket登录成功')
- } else if (sendInfo.cmd == 1) {
- // 重新开启心跳定时
- heartCheck.reset();
- } else {
- // 其他消息转发出去
- console.log("接收到消息", sendInfo);
- messageCallBack && messageCallBack(sendInfo.cmd, sendInfo.data)
- }
- })
+ socketTask.onMessage((res) => {
+ let sendInfo = JSON.parse(res.data);
+ if (sendInfo.cmd == 0) {
+ heartCheck.start();
+ connectCallBack && connectCallBack();
+ console.log('WebSocket登录成功');
+ } else if (sendInfo.cmd == 1) {
+ // 重新开启心跳定时
+ heartCheck.reset();
+ } else {
+ // 其他消息转发出去
+ console.log('接收到消息', sendInfo);
+ messageCallBack && messageCallBack(sendInfo.cmd, sendInfo.data);
+ }
+ });
- socketTask.onClose((res) => {
- console.log('WebSocket连接关闭')
- isConnect = false;
- closeCallBack && closeCallBack(res);
- })
+ socketTask.onClose((res) => {
+ console.log('WebSocket连接关闭');
+ isConnect = false;
+ closeCallBack && closeCallBack(res);
+ });
- socketTask.onError(e => {
- console.log("ws错误:", e)
- close(1006);
- isConnect = false;
- closeCallBack && closeCallBack(e);
- })
-}
+ socketTask.onError((e) => {
+ console.log('ws错误:', e);
+ close(1006);
+ isConnect = false;
+ closeCallBack && closeCallBack(e);
+ });
+};
//定义重连函数
let reconnect = (wsurl, accessToken) => {
- console.log("尝试重新连接");
- if (isConnect) {
- return;
- }
- // 延迟10秒重连 避免过多次过频繁请求重连
- let timeDiff = new Date().getTime() - lastConnectTime.getTime()
- let delay = timeDiff < 10000 ? 10000 - timeDiff : 0;
- rec && clearTimeout(rec);
- rec = setTimeout(function() {
- connect(wsurl, accessToken);
- }, delay);
+ console.log('尝试重新连接');
+ if (isConnect) {
+ return;
+ }
+ // 延迟10秒重连 避免过多次过频繁请求重连
+ let timeDiff = new Date().getTime() - lastConnectTime.getTime();
+ let delay = timeDiff < 10000 ? 10000 - timeDiff : 0;
+ rec && clearTimeout(rec);
+ rec = setTimeout(function () {
+ connect(wsurl, accessToken);
+ }, delay);
};
//设置关闭连接
let close = (code) => {
- if (!isConnect) {
- return;
- }
- socketTask.close({
- code: code,
- complete: (res) => {
- isConnect = false;
- console.log("关闭websocket连接");
- },
- fail: (e) => {
- console.log("关闭websocket连接失败", e);
- }
- });
+ if (!isConnect) {
+ return;
+ }
+ socketTask.close({
+ code: code,
+ complete: (res) => {
+ isConnect = false;
+ console.log('关闭websocket连接');
+ },
+ fail: (e) => {
+ console.log('关闭websocket连接失败', e);
+ },
+ });
};
-
// 心跳设置
let heartCheck = {
- timeout: 20000, //每段时间发送一次心跳包 这里设置为20s
- timeoutObj: null, //延时发送消息对象(启动心跳新建这个对象,收到消息后重置对象)
- start: function() {
- if (isConnect) {
- console.log('发送WebSocket心跳')
- let heartBeat = {
- cmd: 1,
- data: {}
- };
- sendMessage(JSON.stringify(heartBeat))
- }
- },
- reset: function() {
- clearTimeout(this.timeoutObj);
- this.timeoutObj = setTimeout(() => heartCheck.start(), this.timeout);
- }
+ timeout: 20000, //每段时间发送一次心跳包 这里设置为20s
+ timeoutObj: null, //延时发送消息对象(启动心跳新建这个对象,收到消息后重置对象)
+ start: function () {
+ if (isConnect) {
+ console.log('发送WebSocket心跳');
+ let heartBeat = {
+ cmd: 1,
+ data: {},
+ };
+ sendMessage(JSON.stringify(heartBeat));
+ }
+ },
+ reset: function () {
+ clearTimeout(this.timeoutObj);
+ this.timeoutObj = setTimeout(() => heartCheck.start(), this.timeout);
+ },
};
let sendMessage = (message) => {
- socketTask.send({ data: message })
-}
+ socketTask.send({ data: message });
+};
let onConnect = (callback) => {
- connectCallBack = callback;
-}
-
+ connectCallBack = callback;
+};
let onMessage = (callback) => {
- messageCallBack = callback;
-}
-
+ messageCallBack = callback;
+};
let onClose = (callback) => {
- closeCallBack = callback;
-}
-
+ closeCallBack = callback;
+};
// 将方法暴露出去
-export {
- connect,
- reconnect,
- close,
- sendMessage,
- onConnect,
- onMessage,
- onClose
-}
\ No newline at end of file
+export { connect, reconnect, close, sendMessage, onConnect, onMessage, onClose };
diff --git a/src/components/bar/arrow-bar.vue b/src/components/bar/arrow-bar.vue
index 6ecf1891..435056ca 100644
--- a/src/components/bar/arrow-bar.vue
+++ b/src/components/bar/arrow-bar.vue
@@ -1,64 +1,63 @@
-
-
- {{ title }}
-
-
+
+
+ {{ title }}
+
+
\ No newline at end of file
+ .icon {
+ margin-left: 40rpx;
+ }
+
+ .title {
+ flex: 1;
+ margin-left: 10rpx;
+ }
+
+ .arrow {
+ margin-right: 40rpx;
+ }
+ }
+
diff --git a/src/components/chat-item/chat-item.vue b/src/components/chat-item/chat-item.vue
index 94777c44..8e77b29b 100644
--- a/src/components/chat-item/chat-item.vue
+++ b/src/components/chat-item/chat-item.vue
@@ -14,13 +14,13 @@
{{ atText }}
- {{
- chat.sendNickName + ': '
- }}
+
+ {{ chat.sendNickName + ': ' }}
+
{{ chat.lastContent }}
-
+
@@ -111,8 +111,9 @@
position: relative;
padding: 18rpx 20rpx;
align-items: center;
- background-color: white;
+ background-color: $im-bg;
white-space: nowrap;
+ border-bottom: 1px solid $im-border;
&:hover {
background-color: $im-bg-active;
@@ -175,7 +176,7 @@
display: flex;
font-size: $im-font-size-smaller;
color: $im-text-color-lighter;
- padding-top: 8rpx;
+ padding: 8rpx 30rpx 0 0;
align-items: center;
.chat-at-text {
@@ -191,6 +192,7 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ margin-right: 30rpx;
}
.icon {
diff --git a/src/components/chat-record/chat-record.vue b/src/components/chat-record/chat-record.vue
index cb390d8e..28685d30 100644
--- a/src/components/chat-record/chat-record.vue
+++ b/src/components/chat-record/chat-record.vue
@@ -1,243 +1,274 @@
-
-
- {{ recording ? '正在录音' : '长按 说话' }}
-
-
-
-
-
-
-
-
-
-
- {{ recordTip }}
-
-
-
- {{ moveToCancel ? '松手取消' : '松手发送,上划取消' }}
-
-
-
+
+
+ {{ recording ? '正在录音' : '长按 说话' }}
+
+
+
+
+
+
+
+
+
+
+ {{ recordTip }}
+
+
+
+ {{
+ moveToCancel ? '松手取消' : '松手发送,上划取消'
+ }}
+
+
\ No newline at end of file
+ .red {
+ color: $im-color-danger !important;
+ }
+ }
+ }
+
diff --git a/src/components/file-upload/file-upload.vue b/src/components/file-upload/file-upload.vue
index c364ffd5..c10852c8 100644
--- a/src/components/file-upload/file-upload.vue
+++ b/src/components/file-upload/file-upload.vue
@@ -1,122 +1,130 @@
-
-
-
-
-
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/src/components/image-upload/image-upload.vue b/src/components/image-upload/image-upload.vue
index 74410da8..ba949690 100644
--- a/src/components/image-upload/image-upload.vue
+++ b/src/components/image-upload/image-upload.vue
@@ -1,100 +1,100 @@
-
-
-
+
+
+
-
\ No newline at end of file
+
diff --git a/src/components/long-press-menu/long-press-menu.vue b/src/components/long-press-menu/long-press-menu.vue
index 22b7c8a2..e8bafbb8 100644
--- a/src/components/long-press-menu/long-press-menu.vue
+++ b/src/components/long-press-menu/long-press-menu.vue
@@ -1,129 +1,145 @@
-
+
\ No newline at end of file
+ .menu-icon {
+ margin-right: 10rpx;
+ }
+ }
+ }
+ }
+
diff --git a/src/im-var.scss b/src/im-var.scss
index abd98786..0c923fe8 100644
--- a/src/im-var.scss
+++ b/src/im-var.scss
@@ -1,18 +1,18 @@
// 颜色
-$im-color-primary: #3e45d7;
-$im-color-primary-light-1: mix(#fff, $im-color-primary, 10%);
-$im-color-primary-light-2: mix(#fff, $im-color-primary, 20%);
-$im-color-primary-light-3: mix(#fff, $im-color-primary, 30%);
-$im-color-primary-light-4: mix(#fff, $im-color-primary, 40%);
-$im-color-primary-light-5: mix(#fff, $im-color-primary, 50%);
-$im-color-primary-light-6: mix(#fff, $im-color-primary, 60%);
-$im-color-primary-light-7: mix(#fff, $im-color-primary, 70%);
-$im-color-primary-light-8: mix(#fff, $im-color-primary, 80%);
-$im-color-primary-light-9: mix(#fff, $im-color-primary, 90%);
-$im-color-primary-dark-1: mix(#000, $im-color-primary, 10%);
-$im-color-primary-dark-2: mix(#000, $im-color-primary, 20%);
-$im-color-primary-dark-3: mix(#000, $im-color-primary, 30%);
-$im-color-primary-dark-4: mix(#000, $im-color-primary, 40%);
+$im-color-primary: #ffcd32;
+$im-color-primary-light-1: mix(#000, $im-color-primary, 10%);
+$im-color-primary-light-2: mix(#000, $im-color-primary, 20%);
+$im-color-primary-light-3: mix(#000, $im-color-primary, 30%);
+$im-color-primary-light-4: mix(#000, $im-color-primary, 40%);
+$im-color-primary-light-5: mix(#000, $im-color-primary, 50%);
+$im-color-primary-light-6: mix(#000, $im-color-primary, 60%);
+$im-color-primary-light-7: mix(#000, $im-color-primary, 70%);
+$im-color-primary-light-8: mix(#000, $im-color-primary, 80%);
+$im-color-primary-light-9: mix(#000, $im-color-primary, 90%);
+$im-color-primary-dark-1: mix(#fff, $im-color-primary, 10%);
+$im-color-primary-dark-2: mix(#fff, $im-color-primary, 20%);
+$im-color-primary-dark-3: mix(#fff, $im-color-primary, 30%);
+$im-color-primary-dark-4: mix(#fff, $im-color-primary, 40%);
$im-color-success: #18bc37;
$im-color-warning: #f3a73f;
@@ -20,16 +20,16 @@ $im-color-danger: #e43d33;
$im-color-info: #8f939c;
// 文字颜色
-$im-text-color: #000000;
-$im-text-color-light: #6a6a6a;
-$im-text-color-lighter: #909399;
+$im-text-color: #fff;
+$im-text-color-light: #6a6a6a;
+$im-text-color-lighter: #909399;
$im-text-color-lighter-extra: #c7c7c7;
// 边框颜色
-$im-border: #F0F0F0;
-$im-border-light: #EDEDED;
-$im-border-lighter: #DCDCDC;
-$im-border-lighter-extra: #B9B9B9;
+$im-border: #333;
+$im-border-light: #3f3f3f;
+$im-border-lighter: #474747;
+$im-border-lighter-extra: #595959;
// 文字大小
$im-font-size: 30rpx;
@@ -41,24 +41,25 @@ $im-font-size-larger: 34rpx;
$im-font-size-larger-extra: 36rpx;
// 阴影
-$im-box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
-$im-box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
-$im-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, .12);
-$im-box-shadow-dark: 0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5);
+$im-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
+$im-box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+$im-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, 0.12);
+$im-box-shadow-dark: 0px 1px 10px 2px
+ rgba(
+ $color: #444,
+ $alpha: 0.01,
+ );
// 背景
-$im-bg: #f8f9ff;
-$im-bg-linear: linear-gradient(135deg, #f8f9ff 0%, #ffffff 100%);
-$im-bg-active: #f7f8fc;
+$im-bg: #222;
+$im-bg-linear: linear-gradient(135deg, #222 0%, #333 100%);
+$im-bg-active: #333;
$im-bg-active-dark: $im-color-primary-light-9;
-$im-bg-input: #fafafa;
-
+$im-bg-input: #333;
// 标题
-$im-title-size: 26px;
-$im-title-size-1: 22px;
+$im-title-size: 26px;
+$im-title-size-1: 22px;
$im-title-size-2: 18px;
-$font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
-
-$im-nav-bar-height: 50px;
\ No newline at end of file
+$im-nav-bar-height: 50px;
diff --git a/src/main.scss b/src/main.scss
deleted file mode 100644
index b433aa75..00000000
--- a/src/main.scss
+++ /dev/null
@@ -1,27 +0,0 @@
-page {
- font-family: Noto Sans SC, Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
- line-height: 1.5;
- font-weight: 400;
-
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-:root,
-page {
- user-select: none;
- background-color: #222222;
- color: #ffffff;
- font-size: 14px;
- height: 100%;
- --wot-color-theme: #ffcd32;
- --wot-backtop-bg: #ffcd32;
- --wot-dark-background: #222;
- --wot-dark-background2: #222;
- --wot-dark-color: #ffcd32;
- --wot-picker-toolbar-title-color: #fff;
- // background-image: url(./static/page-bg.png);
- // background-repeat: no-repeat;
- // background-size: 100% 100%;
-}
diff --git a/src/main.ts b/src/main.ts
index 5a57757f..9a294c06 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,4 +1,10 @@
// @ts-nocheck
+import App from './App.vue';
+import { createSSRApp } from 'vue';
+import { setupPinia } from './sheep/store';
+import 'virtual:uno.css';
+
+/** 聊天common模块 */
import request from './common/request';
import emotion from './common/emotion.js';
import url from './common/url.js';
@@ -22,19 +28,14 @@ window.ImageResize = { default: ImageResize };
import * as recorder from './common/recorder-app';
// #endif
-import App from './App.vue';
-import { createSSRApp } from 'vue';
-// @ts-ignore
-import { setupPinia } from './sheep/store';
-import './main.scss';
-import 'virtual:uno.css';
-
-import useChatStore from '@/store/chatStore.js';
-import useFriendStore from '@/store/friendStore.js';
-import useGroupStore from '@/store/groupStore.js';
-import useConfigStore from '@/store/configStore.js';
-import useUserStore from '@/store/userStore.js';
+/** 聊天功能pinia仓库 */
+import useChatStore from '@/chat-store/chatStore.js';
+import useFriendStore from '@/chat-store/friendStore.js';
+import useGroupStore from '@/chat-store/groupStore.js';
+import useConfigStore from '@/chat-store/configStore.js';
+import useUserStore from '@/chat-store/userStore.js';
+/** 聊天页面的导航组件 */
import barGroup from '@/components/bar/bar-group';
import arrowBar from '@/components/bar/arrow-bar';
import btnBar from '@/components/bar/btn-bar';
@@ -45,6 +46,7 @@ export function createApp() {
setupPinia(app);
+ // 聊天common模块挂载为全局属性
app.config.globalProperties.$http = request;
app.config.globalProperties.$wsApi = socketApi;
app.config.globalProperties.$msgType = messageType;
@@ -55,12 +57,13 @@ export function createApp() {
app.config.globalProperties.$date = date;
app.config.globalProperties.$rc = recorder;
+ // 聊天页面的导航组件设置为全局组件
app.component('bar-group', barGroup);
app.component('arrow-bar', arrowBar);
app.component('btn-bar', btnBar);
app.component('switch-bar', switchBar);
- // 初始化时再挂载store对象
+ // $mountStore挂载为全局属性,等app初始化时再挂载chat-store到全局属性,方便页面中直接this使用
app.config.globalProperties.$mountStore = () => {
app.config.globalProperties.chatStore = useChatStore();
app.config.globalProperties.friendStore = useFriendStore();
diff --git a/src/manifest.json b/src/manifest.json
index c387765d..16de4d14 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -165,22 +165,7 @@
}
}
},
- "nativePlugins" : {
- "wf-uni-wfc-client" : {
- "__plugin_info__" : {
- "name" : "【官方】野火即时通讯IM插件",
- "description" : "野火官方发布移动端即时通讯插件,包括Android和iOS平台,拥有完善的Demo,支持单人、群组和聊天室会话,支持文字、语音、图片等常见消息,服务器和移动端免费使用,让您一步拥有完美的即时通讯软件。",
- "platforms" : "Android,iOS",
- "url" : "https://ext.dcloud.net.cn/plugin?id=7895",
- "android_package_name" : "uni.app.UNIB94390A",
- "ios_bundle_id" : "uni.app.UNIB94390A",
- "isCloud" : true,
- "bought" : 1,
- "pid" : "7895",
- "parameters" : {}
- }
- }
- }
+ "nativePlugins" : {}
},
"quickapp" : {},
"quickapp-native" : {
diff --git a/src/pages.json b/src/pages.json
index c34df6da..cbde688a 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -5,7 +5,10 @@
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
"^su-(.*)": "@/sheep/ui/su-$1/su-$1.vue",
"^s-(.*)": "@/sheep/components/s-$1/s-$1.vue",
- "^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue"
+ "^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue",
+ "^u--(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue",
+ "^up-(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue",
+ "^u-([^-].*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue"
}
},
"pages": [
@@ -840,10 +843,15 @@
],
"globalStyle": {
"navigationBarTextStyle": "black",
- "navigationBarTitleText": "交友聊天",
+ "navigationBarTitleText": "荷塘月色交友聊天",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8",
- "navigationStyle": "custom"
+ /* #ifndef MP-WEIXIN */
+ "navigationStyle": "custom",
+ /* #endif */
+ /* #ifdef MP-WEIXIN */
+ "navigationStyle": "default"
+ /* #endif */
},
"tabBar": {
"color": "#fff",
diff --git a/src/pages/message/chat-box.vue b/src/pages/message/chat-box.vue
index b492622f..ee89edbe 100644
--- a/src/pages/message/chat-box.vue
+++ b/src/pages/message/chat-box.vue
@@ -1,6 +1,18 @@
-
- {{ title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -203,7 +215,7 @@
\ No newline at end of file
+ &.right {
+ right: 30rpx;
+ }
+ }
+ }
+ }
+
diff --git a/src/pages/message/chat-private-video.vue b/src/pages/message/chat-private-video.vue
index 0677af70..22152fac 100644
--- a/src/pages/message/chat-private-video.vue
+++ b/src/pages/message/chat-private-video.vue
@@ -1,100 +1,106 @@
-
+
\ No newline at end of file
+ .chat-web-view {
+ }
+
diff --git a/src/pages/message/message.vue b/src/pages/message/message.vue
index a8df83e6..c1cc31a0 100644
--- a/src/pages/message/message.vue
+++ b/src/pages/message/message.vue
@@ -1,207 +1,228 @@
-
- 消息
-
-
- 消息接收中...
-
-
-
-
- 正在初始化...
-
-
-
-
-
-
-
-
-
-
-
- 还没有聊天
- 添加好友或创建群聊,开始精彩的对话吧
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ 消息
+ 缘友
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 消息接收中...
+
+
+
+
+ 正在初始化...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
+ .chat-loading {
+ display: block;
+ width: 100%;
+ height: 120rpx;
+ background: #222;
+ color: $im-text-color-lighter;
+
+ .loading-box {
+ position: relative;
+ }
+ }
+
+ .scroll-bar {
+ flex: 1;
+ height: 100%;
+ }
+ }
+
diff --git a/src/sheep/components/s-share-modal/canvas-poster/poster/user.js b/src/sheep/components/s-share-modal/canvas-poster/poster/user.js
index aa73922e..d0fd15ca 100644
--- a/src/sheep/components/s-share-modal/canvas-poster/poster/user.js
+++ b/src/sheep/components/s-share-modal/canvas-poster/poster/user.js
@@ -1,6 +1,6 @@
import sheep from '@/sheep';
import { formatImageUrlProtocol, getWxaQrcode } from './index';
-import { measureTextWidth } from '@/utils/textUtils'; // 引入新封装的方法
+import { measureTextWidth } from '@/sheep/util/textUtils';
const user = async (poster) => {
const width = poster.width;
const userInfo = sheep.$store('user').userInfo;
@@ -9,7 +9,9 @@ const user = async (poster) => {
return [
{
type: 'image',
- src: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.user_bg)),
+ src: formatImageUrlProtocol(
+ sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.user_bg),
+ ),
css: {
width,
position: 'fixed',
@@ -29,7 +31,7 @@ const user = async (poster) => {
fontFamily: 'sans-serif',
position: 'fixed',
top: width * 0.4,
- left: (width-widthNickName) / 2,
+ left: (width - widthNickName) / 2,
},
},
{
diff --git a/src/utils/textUtils.js b/src/sheep/util/textUtils.js
similarity index 97%
rename from src/utils/textUtils.js
rename to src/sheep/util/textUtils.js
index f6231a0d..02eb1da1 100644
--- a/src/utils/textUtils.js
+++ b/src/sheep/util/textUtils.js
@@ -1,4 +1,3 @@
-// sheep/utils/textUtils.js
export function measureTextWidth(text, fontSize = 14, fontFamily = 'sans-serif') {
// 钉钉小程序没有 uni.createCanvasContext 方法
if (typeof uni === 'undefined' || typeof uni.createCanvasContext !== 'function') {
diff --git a/src/uni_modules/lsj-upload/changelog.md b/src/uni_modules/lsj-upload/changelog.md
new file mode 100644
index 00000000..090bca01
--- /dev/null
+++ b/src/uni_modules/lsj-upload/changelog.md
@@ -0,0 +1,120 @@
+## 2.3.2(2024-06-13)
+问题修复:2.3.1版本引起的部分设备不支持findLastIndex问题
+## 2.3.1(2024-05-20)
+修复:文件不去重时返回文件列表name与组件内置列表key不一致问题。
+## 2.3.0(2024-05-20)
+优化:1:增加属性distinct【选择文件是否去重】、2:对show/hide函数增加uni.$emit事件监听,若页面存在多个上传组件时,可通过uni.$emit控制所有上传组件webview透明层是否显示。
+## 2.2.9(2023-06-01)
+优化:将是否多选与count字段解绑(原逻辑是count>1为允许多选),改为新增multiple属性控制是否多选。
+## 2.2.8(2023-06-01)
+修复上版本提交时accept测试值未删除导致h5端只能选择图片的问题。
+## 2.2.7(2023-05-06)
+应群友建议,当instantly为true时,触发change事件后延迟1000毫秒再自动上传,方便动态修改参数,其实个人还是建议想在change事件动态设置参数的伙伴将instantly设置为false,修改参数后手动调用upload()
+## 2.2.6(2023-02-09)
+修复多个文件同时选择时返回多次change回调的问题
+## 2.2.5(2022-12-27)
+1.修复多选文件时未能正常校验数量的问题;
+2.app端与H5端支持单选或多选文件,通过count数量控制,超过1开启多选。
+## 2.2.4(2022-12-27)
+1.修复多选文件时未能正常校验数量的问题;
+2.app端修复多选只取到第一个文件的问题。
+## 2.2.3(2022-12-06)
+修复手动调用show()导致count失效的问题
+## 2.2.2(2022-12-01)
+Vue3自行修改兼容
+## 2.2.1(2022-10-19)
+修复childId警告提示
+## 2.2.0(2022-10-10)
+更新app端webview窗口参数clidId,默认值添加时间戳保证唯一性
+## 2.1.9(2022-07-13)
+[修复] app端选择文件后初始化设置的文件列表被清空问题
+## 2.1.8(2022-07-13)
+[新增] ref方法初始化文件列表,用于已提交后再次编辑时需带入已上传文件:setFiles(files),可传入数组或Map对象,传入格式请与组件选择返回格式保持一致,且name为必须属性。
+## 2.1.7(2022-07-12)
+修复ios端偶现创建webview初始化参数未生效的问题
+## 2.1.6(2022-07-11)
+[修复]:修复上个版本更新导致nvue窗口组件不能选择文件的问题;
+[新增]:
+1.应群友建议(填写禁止格式太多)格式限制formats由原来填写禁止选择的格式改为填写允许被选择的格式;
+2.应群友建议(增加上传结束回调事件),上传结束回调事件@uploadEnd
+3.如能帮到你请留下你的免费好评,组件使用过程中有问题可以加QQ群交流,至于Map对象怎么使用这类前端基础问题请自行百度
+## 2.1.5(2022-07-01)
+app端组件销毁时添加自动销毁webview功能,避免v-if销毁组件的情况控件还能被点击的问题
+## 2.1.4(2022-07-01)
+修复小程序端回显问题
+## 2.1.3(2022-06-30)
+回调事件返回参数新增path字段(文件临时地址),用于回显
+## 2.1.2(2022-06-16)
+修复APP端Tabbar窗口无法选择文件的问题
+## 2.1.1(2022-06-16)
+优化:
+1.组件优化为允许在v-if中使用;
+2.允许option直接在data赋值,不再强制在onRead中初始化;
+## 2.1.0(2022-06-13)
+h5 pc端更改为单次可多选
+## 2.0.9(2022-06-10)
+更新演示内容,部分同学不知道怎么获取服务端返回的数据
+## 2.0.8(2022-06-09)
+优化动态更新上传参数函数,具体查看下方说明:动态更新参数演示
+## 2.0.7(2022-06-07)
+新增wxFileType属性,用于小程序端选择附件时可选文件类型
+## 2.0.6(2022-06-07)
+修复小程序端真机选择文件提示失败的问题
+## 2.0.5(2022-06-02)
+优化小程序端调用hide()后未阻止触发文件选择问题
+## 2.0.4(2022-06-01)
+优化APP端选择器初始定位
+## 2.0.3(2022-05-31)
+修复nvue窗口选择文件报错问题
+## 2.0.2(2022-05-20)
+修复ios端opiton设置过早未传入webview导致不自动上传问题
+## 2.0.1(2022-05-19)
+修复APP端子窗口点击选择文件不响应问题
+## 2.0.0(2022-05-18)
+此次组件更新至2.0版本,与1.0版本使用上略有差异,已使用1.0的同学请自行斟酌是否需要升级!
+部分差异:
+一、 2.0新增异步触发上传功能;
+二、2.0新增文件批量上传功能;
+三、2.0优化option,剔除属性,只保留上传接口所需字段,且允许异步更改option的值;
+四、组件增加size(文件大小限制)、count(文件个数限制)、formats(文件后缀限制)、accept(文件类型限制)、instantly(是否立即自动上传)、debug(日志打印)等属性;
+五、回调事件取消input事件、callback事件,新增change事件和progress事件;
+六、ref事件新增upload事件、clear事件;
+七、优化组件代码,show和hide函数改为显示隐藏,不再重复开关webview;
+
+## 1.2.3(2022-03-22)
+修复Demo里传入待完善功能[手动上传属性manual=true]导致不自动上传的问题,手动提交上传待下个版本更新
+## 1.2.2(2022-02-21)
+修复上版本APP优化导致H5和小程序端不自动初始化的问题,此次更新仅修复此问题。异步提交功能下个版本更新~
+## 1.2.1(2022-01-25)
+QQ1群已满,已开放2群:469580165
+## 1.2.0(2021-12-09)
+优化APP端页面中DOM重排后每次需要重新定位的问题
+## 1.1.1(2021-12-09)
+优化,与上版本使用方式有改变,请检查后确认是否需要更新,create更名为show, close更名为hide,取消初始化时手动create, 传参方式改为props=>option
+## 1.1.0(2021-12-09)
+新增refresh方法,用于DOM发生重排时重新定位控件(APP端)
+## 1.0.9(2021-07-15)
+修复上传进度未同步渲染,直接返回100%的BUG
+## 1.0.8(2021-07-12)
+修复H5端传入height和width未生效的bug
+## 1.0.7(2021-07-07)
+修复h5和小程序端上传完成callback未返回fileName字段问题
+## 1.0.6(2021-07-07)
+修复h5端提示信息debug
+## 1.0.5(2021-06-29)
+感谢小伙伴找出bug,上传成功回调success未置为true,已修复
+## 1.0.4(2021-06-28)
+新增兼容APP,H5,小程序手动关闭控件,关闭后不再弹出文件选择框,需要重新create再次开启
+## 1.0.3(2021-06-28)
+close增加条件编译,除app端外不需要close
+## 1.0.2(2021-06-28)
+1.修复页面滚动位置后再create控件导致控件位置不正确的问题;
+2.修复nvue无法create控件;
+3.示例项目新增nvue使用案例;
+## 1.0.1(2021-06-28)
+因为有的朋友不清楚app端切换tab时应该怎么处理webview,现重新上传一版示例项目,需要做tab切换的朋友可以导入示例项目查看
+## 1.0.0(2021-06-25)
+此插件为l-file插件中上传功能改版,更新内容为:
+1. 按钮内嵌入页面,不再强制固定底部,可跟随页面滚动
+2.无需再单独弹框点击上传,减去中间层
+3.通过slot自定义按钮样式
diff --git a/src/uni_modules/lsj-upload/components/lsj-upload/LsjFile.js b/src/uni_modules/lsj-upload/components/lsj-upload/LsjFile.js
new file mode 100644
index 00000000..a86233e3
--- /dev/null
+++ b/src/uni_modules/lsj-upload/components/lsj-upload/LsjFile.js
@@ -0,0 +1,414 @@
+export class LsjFile {
+ constructor(data) {
+ this.dom = null;
+ // files.type = waiting(等待上传)|| loading(上传中)|| success(成功) || fail(失败)
+ this.files = new Map();
+ this.debug = data.debug || false;
+ this.id = data.id;
+ this.width = data.width;
+ this.height = data.height;
+ this.option = data.option;
+ this.instantly = data.instantly;
+ this.prohibited = data.prohibited;
+ this.onchange = data.onchange;
+ this.onprogress = data.onprogress;
+ this.uploadHandle = this._uploadHandle;
+ // #ifdef MP-WEIXIN
+ this.uploadHandle = this._uploadHandleWX;
+ // #endif
+ }
+
+
+ /**
+ * 创建File节点
+ * @param {string}path webview地址
+ */
+ create(path) {
+ if (!this.dom) {
+ // #ifdef H5
+ let dom = document.createElement('input');
+ dom.type = 'file'
+ dom.value = ''
+ dom.style.height = this.height
+ dom.style.width = this.width
+ dom.style.position = 'absolute'
+ dom.style.top = 0
+ dom.style.left = 0
+ dom.style.right = 0
+ dom.style.bottom = 0
+ dom.style.opacity = 0
+ dom.style.zIndex = 999
+ dom.accept = this.prohibited.accept;
+ if (this.prohibited.multiple) {
+ dom.multiple = 'multiple';
+ }
+ dom.onchange = event => {
+ for (let file of event.target.files) {
+ if (this.files.size >= this.prohibited.count) {
+ this.toast(`只允许上传${this.prohibited.count}个文件`);
+ this.dom.value = '';
+ break;
+ }
+ this.addFile(file);
+ }
+
+ this._uploadAfter();
+
+ this.dom.value = '';
+ };
+ this.dom = dom;
+ // #endif
+
+ // #ifdef APP-PLUS
+ let styles = {
+ top: '-200px',
+ left: 0,
+ width: '1px',
+ height: '200px',
+ background: 'transparent'
+ };
+ let extras = {
+ debug: this.debug,
+ instantly: this.instantly,
+ prohibited: this.prohibited,
+ }
+ this.dom = plus.webview.create(path, this.id, styles,extras);
+ this.setData(this.option);
+ this._overrideUrlLoading();
+ // #endif
+ return this.dom;
+ }
+ }
+
+
+ /**
+ * 设置上传参数
+ * @param {object|string}name 上传参数,支持a.b 和 a[b]
+ */
+ setData() {
+ let [name,value = ''] = arguments;
+ if (typeof name === 'object') {
+ Object.assign(this.option,name);
+ }
+ else {
+ this._setValue(this.option,name,value);
+ }
+
+ this.debug&&console.log(JSON.stringify(this.option));
+
+ // #ifdef APP-PLUS
+ this.dom.evalJS(`vm.setData('${JSON.stringify(this.option)}')`);
+ // #endif
+ }
+
+ /**
+ * 上传
+ * @param {string}name 文件名称
+ */
+ async upload(name='') {
+ if (!this.option.url) {
+ throw Error('未设置上传地址');
+ }
+
+ // #ifndef APP-PLUS
+ if (name && this.files.has(name)) {
+ await this.uploadHandle(this.files.get(name));
+ }
+ else {
+ for (let item of this.files.values()) {
+ if (item.type === 'waiting' || item.type === 'fail') {
+ await this.uploadHandle(item);
+ }
+ }
+ }
+ // #endif
+
+ // #ifdef APP-PLUS
+ this.dom&&this.dom.evalJS(`vm.upload('${name}')`);
+ // #endif
+ }
+
+ // 选择文件change
+ addFile(file,isCallChange) {
+
+ let name = file.name;
+ this.debug&&console.log('文件名称',name,'大小',file.size);
+
+ if (file) {
+ // 限制文件格式
+ let path = '';
+ let suffix = name.substring(name.lastIndexOf(".")+1).toLowerCase();
+ let formats = this.prohibited.formats.toLowerCase();
+
+ // #ifndef MP-WEIXIN
+ path = URL.createObjectURL(file);
+ // #endif
+ // #ifdef MP-WEIXIN
+ path = file.path;
+ // #endif
+ if (formats&&!formats.includes(suffix)) {
+ this.toast(`不支持上传${suffix.toUpperCase()}格式文件`);
+ return false;
+ }
+ // 限制文件大小
+ if (file.size > 1024 * 1024 * Math.abs(this.prohibited.size)) {
+ this.toast(`附件大小请勿超过${this.prohibited.size}M`)
+ return false;
+ }
+
+ try{
+ if (!this.prohibited.distinct) {
+ let homonymIndex = [...this.files.keys()].findIndex(item=>{
+ return (item.substring(0,item.lastIndexOf("("))||item.substring(0,item.lastIndexOf("."))) == name.substring(0,name.lastIndexOf(".")) &&
+ item.substring(item.lastIndexOf(".")+1).toLowerCase() === suffix;
+ })
+ if (homonymIndex > -1) {
+ name = `${name.substring(0,name.lastIndexOf("."))}(${homonymIndex+1}).${suffix}`;
+ }
+ }
+ }catch(e){
+ //TODO handle the exception
+ }
+
+ this.files.set(name,{file,path,name: name,size: file.size,progress: 0,type: 'waiting'});
+ return true;
+ }
+ }
+
+ /**
+ * 移除文件
+ * @param {string}name 不传name默认移除所有文件,传入name移除指定name的文件
+ */
+ clear(name='') {
+ // #ifdef APP-PLUS
+ this.dom&&this.dom.evalJS(`vm.clear('${name}')`);
+ // #endif
+
+ if (!name) {
+ this.files.clear();
+ }
+ else {
+ this.files.delete(name);
+ }
+ return this.onchange(this.files);
+ }
+
+ /**
+ * 提示框
+ * @param {string}msg 轻提示内容
+ */
+ toast(msg) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ }
+
+ /**
+ * 微信小程序选择文件
+ * @param {number}count 可选择文件数量
+ */
+ chooseMessageFile(type,count) {
+ wx.chooseMessageFile({
+ count: count,
+ type: type,
+ success: ({ tempFiles }) => {
+ for (let file of tempFiles) {
+ this.addFile(file);
+ }
+ this._uploadAfter();
+ },
+ fail: () => {
+ this.toast(`打开失败`);
+ }
+ })
+ }
+
+ _copyObject(obj) {
+ if (typeof obj !== "undefined") {
+ return JSON.parse(JSON.stringify(obj));
+ } else {
+ return obj;
+ }
+ }
+
+ /**
+ * 自动根据字符串路径设置对象中的值 支持.和[]
+ * @param {Object} dataObj 数据源
+ * @param {String} name 支持a.b 和 a[b]
+ * @param {String} value 值
+ * setValue(dataObj, name, value);
+ */
+ _setValue(dataObj, name, value) {
+ // 通过正则表达式 查找路径数据
+ let dataValue;
+ if (typeof value === "object") {
+ dataValue = this._copyObject(value);
+ } else {
+ dataValue = value;
+ }
+ let regExp = new RegExp("([\\w$]+)|\\[(:\\d)\\]", "g");
+ const patten = name.match(regExp);
+ // 遍历路径 逐级查找 最后一级用于直接赋值
+ for (let i = 0; i < patten.length - 1; i++) {
+ let keyName = patten[i];
+ if (typeof dataObj[keyName] !== "object") dataObj[keyName] = {};
+ dataObj = dataObj[keyName];
+ }
+ // 最后一级
+ dataObj[patten[patten.length - 1]] = dataValue;
+ this.debug&&console.log('参数更新后',JSON.stringify(this.option));
+ }
+
+ _uploadAfter() {
+ this.onchange(this.files);
+ setTimeout(()=>{
+ this.instantly&&this.upload();
+ },1000)
+ }
+
+ _overrideUrlLoading() {
+ this.dom.overrideUrlLoading({ mode: 'reject' }, e => {
+ let {retype,item,files,end} = this._getRequest(
+ e.url
+ );
+ let _this = this;
+ switch (retype) {
+ case 'updateOption':
+ this.dom.evalJS(`vm.setData('${JSON.stringify(_this.option)}')`);
+ break
+ case 'change':
+ try {
+ _this.files = new Map([..._this.files,...JSON.parse(unescape(files))]);
+ } catch (e) {
+ return console.error('出错了,请检查代码')
+ }
+ _this.onchange(_this.files);
+ break
+ case 'progress':
+ try {
+ item = JSON.parse(unescape(item));
+ } catch (e) {
+ return console.error('出错了,请检查代码')
+ }
+ _this._changeFilesItem(item,end);
+ break
+ default:
+ break
+ }
+ })
+ }
+
+ _getRequest(url) {
+ let theRequest = new Object()
+ let index = url.indexOf('?')
+ if (index != -1) {
+ let str = url.substring(index + 1)
+ let strs = str.split('&')
+ for (let i = 0; i < strs.length; i++) {
+ theRequest[strs[i].split('=')[0]] = unescape(strs[i].split('=')[1])
+ }
+ }
+ return theRequest
+ }
+
+ _changeFilesItem(item,end=false) {
+ this.debug&&console.log('onprogress',JSON.stringify(item));
+ this.onprogress(item,end);
+ this.files.set(item.name,item);
+ }
+
+ _uploadHandle(item) {
+ item.type = 'loading';
+ delete item.responseText;
+ return new Promise((resolve,reject)=>{
+ this.debug&&console.log('option',JSON.stringify(this.option));
+ let {url,name,method='POST',header,formData} = this.option;
+ let form = new FormData();
+ for (let keys in formData) {
+ form.append(keys, formData[keys])
+ }
+ form.append(name, item.file);
+ let xmlRequest = new XMLHttpRequest();
+ xmlRequest.open(method, url, true);
+ for (let keys in header) {
+ xmlRequest.setRequestHeader(keys, header[keys])
+ }
+
+ xmlRequest.upload.addEventListener(
+ 'progress',
+ event => {
+ if (event.lengthComputable) {
+ let progress = Math.ceil((event.loaded * 100) / event.total)
+ if (progress <= 100) {
+ item.progress = progress;
+ this._changeFilesItem(item);
+ }
+ }
+ },
+ false
+ );
+
+ xmlRequest.ontimeout = () => {
+ console.error('请求超时')
+ item.type = 'fail';
+ this._changeFilesItem(item,true);
+ return resolve(false);
+ }
+
+ xmlRequest.onreadystatechange = ev => {
+ if (xmlRequest.readyState == 4) {
+ if (xmlRequest.status == 200) {
+ this.debug&&console.log('上传完成:' + xmlRequest.responseText)
+ item['responseText'] = xmlRequest.responseText;
+ item.type = 'success';
+ this._changeFilesItem(item,true);
+ return resolve(true);
+ } else if (xmlRequest.status == 0) {
+ console.error('status = 0 :请检查请求头Content-Type与服务端是否匹配,服务端已正确开启跨域,并且nginx未拦截阻止请求')
+ }
+ console.error('--ERROR--:status = ' + xmlRequest.status)
+ item.type = 'fail';
+ this._changeFilesItem(item,true);
+ return resolve(false);
+ }
+ }
+ xmlRequest.send(form)
+ });
+ }
+
+ _uploadHandleWX(item) {
+ item.type = 'loading';
+ delete item.responseText;
+ return new Promise((resolve,reject)=>{
+ this.debug&&console.log('option',JSON.stringify(this.option));
+ let form = {filePath: item.file.path,...this.option };
+ form['fail'] = ({ errMsg = '' }) => {
+ console.error('--ERROR--:' + errMsg)
+ item.type = 'fail';
+ this._changeFilesItem(item,true);
+ return resolve(false);
+ }
+ form['success'] = res => {
+ if (res.statusCode == 200) {
+ this.debug&&console.log('上传完成,微信端返回不一定是字符串,根据接口返回格式判断是否需要JSON.parse:' + res.data)
+ item['responseText'] = res.data;
+ item.type = 'success';
+ this._changeFilesItem(item,true);
+ return resolve(true);
+ }
+ item.type = 'fail';
+ this._changeFilesItem(item,true);
+ return resolve(false);
+ }
+
+ let xmlRequest = uni.uploadFile(form);
+ xmlRequest.onProgressUpdate(({ progress = 0 }) => {
+ if (progress <= 100) {
+ item.progress = progress;
+ this._changeFilesItem(item);
+ }
+ })
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/uni_modules/lsj-upload/components/lsj-upload/lsj-upload.vue b/src/uni_modules/lsj-upload/components/lsj-upload/lsj-upload.vue
new file mode 100644
index 00000000..46ac2969
--- /dev/null
+++ b/src/uni_modules/lsj-upload/components/lsj-upload/lsj-upload.vue
@@ -0,0 +1,338 @@
+
+
+
+ 附件上传
+
+
+
+
+
+
+
diff --git a/src/uni_modules/lsj-upload/hybrid/html/js/vue.min.js b/src/uni_modules/lsj-upload/hybrid/html/js/vue.min.js
new file mode 100644
index 00000000..97ac4cf9
--- /dev/null
+++ b/src/uni_modules/lsj-upload/hybrid/html/js/vue.min.js
@@ -0,0 +1,8 @@
+/*!
+ * Vue.js v2.3.0
+ * (c) 2014-2017 Evan You
+ * Released under the MIT License.
+ */
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Vue=t()}(this,function(){"use strict";function e(e){return void 0===e||null===e}function t(e){return void 0!==e&&null!==e}function n(e){return!0===e}function r(e){return"string"==typeof e||"number"==typeof e}function i(e){return null!==e&&"object"==typeof e}function o(e){return"[object Object]"===Ai.call(e)}function a(e){return"[object RegExp]"===Ai.call(e)}function s(e){return null==e?"":"object"==typeof e?JSON.stringify(e,null,2):String(e)}function c(e){var t=parseFloat(e);return isNaN(t)?e:t}function u(e,t){for(var n=Object.create(null),r=e.split(","),i=0;i-1)return e.splice(n,1)}}function f(e,t){return Si.call(e,t)}function p(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}function d(e,t){function n(n){var r=arguments.length;return r?r>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n}function v(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function h(e,t){for(var n in t)e[n]=t[n];return e}function m(e){for(var t={},n=0;n=0&&$o[n].id>e.id;)n--;$o.splice(Math.max(n,Ao)+1,0,e)}else $o.push(e);Co||(Co=!0,ro($e))}}function Ae(e){To.clear(),Oe(e,To)}function Oe(e,t){var n,r,o=Array.isArray(e);if((o||i(e))&&Object.isExtensible(e)){if(e.__ob__){var a=e.__ob__.dep.id;if(t.has(a))return;t.add(a)}if(o)for(n=e.length;n--;)Oe(e[n],t);else for(r=Object.keys(e),n=r.length;n--;)Oe(e[r[n]],t)}}function Se(e,t,n){Eo.get=function(){return this[t][n]},Eo.set=function(e){this[t][n]=e},Object.defineProperty(e,n,Eo)}function Te(e){e._watchers=[];var t=e.$options;t.props&&Ee(e,t.props),t.methods&&Me(e,t.methods),t.data?je(e):E(e._data={},!0),t.computed&&Le(e,t.computed),t.watch&&Pe(e,t.watch)}function Ee(e,t){var n=e.$options.propsData||{},r=e._props={},i=e.$options._propKeys=[],o=!e.$parent;lo.shouldConvert=o;for(var a in t)!function(o){i.push(o);var a=U(o,t,n,e);j(r,o,a),o in e||Se(e,"_props",o)}(a);lo.shouldConvert=!0}function je(e){var t=e.$options.data;t=e._data="function"==typeof t?Ne(t,e):t||{},o(t)||(t={});for(var n=Object.keys(t),r=e.$options.props,i=n.length;i--;)r&&f(r,n[i])||$(n[i])||Se(e,"_data",n[i]);E(t,!0)}function Ne(e,t){try{return e.call(t)}catch(e){return C(e,t,"data()"),{}}}function Le(e,t){var n=e._computedWatchers=Object.create(null);for(var r in t){var i=t[r],o="function"==typeof i?i:i.get;n[r]=new So(e,o,g,jo),r in e||Ie(e,r,i)}}function Ie(e,t,n){"function"==typeof n?(Eo.get=De(t),Eo.set=g):(Eo.get=n.get?!1!==n.cache?De(t):n.get:g,Eo.set=n.set?n.set:g),Object.defineProperty(e,t,Eo)}function De(e){return function(){var t=this._computedWatchers&&this._computedWatchers[e];if(t)return t.dirty&&t.evaluate(),oo.target&&t.depend(),t.value}}function Me(e,t){e.$options.props;for(var n in t)e[n]=null==t[n]?g:d(t[n],e)}function Pe(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;i=0||n.indexOf(e[i])<0)&&r.push(e[i]);return r}return e}function pt(e){this._init(e)}function dt(e){e.use=function(e){if(!e.installed){var t=v(arguments,1);return t.unshift(this),"function"==typeof e.install?e.install.apply(e,t):"function"==typeof e&&e.apply(null,t),e.installed=!0,this}}}function vt(e){e.mixin=function(e){this.options=B(this.options,e)}}function ht(e){e.cid=0;var t=1;e.extend=function(e){e=e||{};var n=this,r=n.cid,i=e._Ctor||(e._Ctor={});if(i[r])return i[r];var o=e.name||n.options.name,a=function(e){this._init(e)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=t++,a.options=B(n.options,e),a.super=n,a.options.props&&mt(a),a.options.computed&>(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,Di.forEach(function(e){a[e]=n[e]}),o&&(a.options.components[o]=a),a.superOptions=n.options,a.extendOptions=e,a.sealedOptions=h({},a.options),i[r]=a,a}}function mt(e){var t=e.options.props;for(var n in t)Se(e.prototype,"_props",n)}function gt(e){var t=e.options.computed;for(var n in t)Ie(e.prototype,n,t[n])}function yt(e){Di.forEach(function(t){e[t]=function(e,n){return n?("component"===t&&o(n)&&(n.name=n.name||e,n=this.options._base.extend(n)),"directive"===t&&"function"==typeof n&&(n={bind:n,update:n}),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}})}function _t(e){return e&&(e.Ctor.options.name||e.tag)}function bt(e,t){return"string"==typeof e?e.split(",").indexOf(t)>-1:!!a(e)&&e.test(t)}function $t(e,t,n){for(var r in e){var i=e[r];if(i){var o=_t(i.componentOptions);o&&!n(o)&&(i!==t&&xt(i),e[r]=null)}}}function xt(e){e&&e.componentInstance.$destroy()}function wt(e){for(var n=e.data,r=e,i=e;t(i.componentInstance);)i=i.componentInstance._vnode,i.data&&(n=Ct(i.data,n));for(;t(r=r.parent);)r.data&&(n=Ct(n,r.data));return kt(n)}function Ct(e,n){return{staticClass:At(e.staticClass,n.staticClass),class:t(e.class)?[e.class,n.class]:n.class}}function kt(e){var n=e.class,r=e.staticClass;return t(r)||t(n)?At(r,Ot(n)):""}function At(e,t){return e?t?e+" "+t:e:t||""}function Ot(n){if(e(n))return"";if("string"==typeof n)return n;var r="";if(Array.isArray(n)){for(var o,a=0,s=n.length;a-1?ua[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:ua[e]=/HTMLUnknownElement/.test(t.toString())}function Et(e){if("string"==typeof e){var t=document.querySelector(e);return t||document.createElement("div")}return e}function jt(e,t){var n=document.createElement(e);return"select"!==e?n:(t.data&&t.data.attrs&&void 0!==t.data.attrs.multiple&&n.setAttribute("multiple","multiple"),n)}function Nt(e,t){return document.createElementNS(ia[e],t)}function Lt(e){return document.createTextNode(e)}function It(e){return document.createComment(e)}function Dt(e,t,n){e.insertBefore(t,n)}function Mt(e,t){e.removeChild(t)}function Pt(e,t){e.appendChild(t)}function Rt(e){return e.parentNode}function Ft(e){return e.nextSibling}function Bt(e){return e.tagName}function Ht(e,t){e.textContent=t}function Ut(e,t,n){e.setAttribute(t,n)}function Vt(e,t){var n=e.data.ref;if(n){var r=e.context,i=e.componentInstance||e.elm,o=r.$refs;t?Array.isArray(o[n])?l(o[n],i):o[n]===i&&(o[n]=void 0):e.data.refInFor?Array.isArray(o[n])&&o[n].indexOf(i)<0?o[n].push(i):o[n]=[i]:o[n]=i}}function zt(e,n){return e.key===n.key&&e.tag===n.tag&&e.isComment===n.isComment&&t(e.data)===t(n.data)&&Jt(e,n)}function Jt(e,n){if("input"!==e.tag)return!0;var r;return(t(r=e.data)&&t(r=r.attrs)&&r.type)===(t(r=n.data)&&t(r=r.attrs)&&r.type)}function Kt(e,n,r){var i,o,a={};for(i=n;i<=r;++i)o=e[i].key,t(o)&&(a[o]=i);return a}function qt(e,t){(e.data.directives||t.data.directives)&&Wt(e,t)}function Wt(e,t){var n,r,i,o=e===pa,a=t===pa,s=Zt(e.data.directives,e.context),c=Zt(t.data.directives,t.context),u=[],l=[];for(n in c)r=s[n],i=c[n],r?(i.oldValue=r.value,Yt(i,"update",t,e),i.def&&i.def.componentUpdated&&l.push(i)):(Yt(i,"bind",t,e),i.def&&i.def.inserted&&u.push(i));if(u.length){var f=function(){for(var n=0;n=0&&" "===(m=e.charAt(h));h--);m&&_a.test(m)||(l=!0)}}else void 0===o?(v=i+1,o=e.slice(0,i).trim()):t();if(void 0===o?o=e.slice(0,i).trim():0!==v&&t(),a)for(i=0;i=Bo}function gn(e){return 34===e||39===e}function yn(e){var t=1;for(zo=Vo;!mn();)if(e=hn(),gn(e))_n(e);else if(91===e&&t++,93===e&&t--,0===t){Jo=Vo;break}}function _n(e){for(var t=e;!mn()&&(e=hn())!==t;);}function bn(e,t,n){Ko=n;var r=t.value,i=t.modifiers,o=e.tag,a=e.attrsMap.type;if("select"===o)wn(e,r,i);else if("input"===o&&"checkbox"===a)$n(e,r,i);else if("input"===o&&"radio"===a)xn(e,r,i);else if("input"===o||"textarea"===o)Cn(e,r,i);else if(!Pi.isReservedTag(o))return pn(e,r,i),!1;return!0}function $n(e,t,n){var r=n&&n.number,i=ln(e,"value")||"null",o=ln(e,"true-value")||"true",a=ln(e,"false-value")||"false";an(e,"checked","Array.isArray("+t+")?_i("+t+","+i+")>-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),un(e,$a,"var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(r?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$c){$$i<0&&("+t+"=$$a.concat($$v))}else{$$i>-1&&("+t+"=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{"+dn(t,"$$c")+"}",null,!0)}function xn(e,t,n){var r=n&&n.number,i=ln(e,"value")||"null";i=r?"_n("+i+")":i,an(e,"checked","_q("+t+","+i+")"),un(e,$a,dn(t,i),null,!0)}function wn(e,t,n){var r=n&&n.number,i='Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = "_value" in o ? o._value : o.value;return '+(r?"_n(val)":"val")+"})",o="var $$selectedVal = "+i+";";o=o+" "+dn(t,"$event.target.multiple ? $$selectedVal : $$selectedVal[0]"),un(e,"change",o,null,!0)}function Cn(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,u=o?"change":"range"===r?ba:"input",l="$event.target.value";s&&(l="$event.target.value.trim()"),a&&(l="_n("+l+")");var f=dn(t,l);c&&(f="if($event.target.composing)return;"+f),an(e,"value","("+t+")"),un(e,u,f,null,!0),(s||a||"number"===r)&&un(e,"blur","$forceUpdate()")}function kn(e){var n;t(e[ba])&&(n=zi?"change":"input",e[n]=[].concat(e[ba],e[n]||[]),delete e[ba]),t(e[$a])&&(n=Zi?"click":"change",e[n]=[].concat(e[$a],e[n]||[]),delete e[$a])}function An(e,t,n,r,i){if(n){var o=t,a=qo;t=function(n){null!==(1===arguments.length?o(n):o.apply(null,arguments))&&On(e,t,r,a)}}qo.addEventListener(e,t,Gi?{capture:r,passive:i}:r)}function On(e,t,n,r){(r||qo).removeEventListener(e,t,n)}function Sn(t,n){if(!e(t.data.on)||!e(n.data.on)){var r=n.data.on||{},i=t.data.on||{};qo=n.elm,kn(r),G(r,i,An,On,n.context)}}function Tn(n,r){if(!e(n.data.domProps)||!e(r.data.domProps)){var i,o,a=r.elm,s=n.data.domProps||{},c=r.data.domProps||{};t(c.__ob__)&&(c=r.data.domProps=h({},c));for(i in s)e(c[i])&&(a[i]="");for(i in c)if(o=c[i],"textContent"!==i&&"innerHTML"!==i||(r.children&&(r.children.length=0),o!==s[i]))if("value"===i){a._value=o;var u=null==o?"":String(o);En(a,r,u)&&(a.value=u)}else a[i]=o}}function En(e,t,n){return!e.composing&&("option"===t.tag||jn(e,n)||Nn(e,n))}function jn(e,t){return document.activeElement!==e&&e.value!==t}function Nn(e,n){var r=e.value,i=e._vModifiers;return t(i)&&i.number||"number"===e.type?c(r)!==c(n):t(i)&&i.trim?r.trim()!==n.trim():r!==n}function Ln(e){var t=In(e.style);return e.staticStyle?h(e.staticStyle,t):t}function In(e){return Array.isArray(e)?m(e):"string"==typeof e?Ca(e):e}function Dn(e,t){var n,r={};if(t)for(var i=e;i.componentInstance;)i=i.componentInstance._vnode,i.data&&(n=Ln(i.data))&&h(r,n);(n=Ln(e.data))&&h(r,n);for(var o=e;o=o.parent;)o.data&&(n=Ln(o.data))&&h(r,n);return r}function Mn(n,r){var i=r.data,o=n.data;if(!(e(i.staticStyle)&&e(i.style)&&e(o.staticStyle)&&e(o.style))){var a,s,c=r.elm,u=o.staticStyle,l=o.normalizedStyle||o.style||{},f=u||l,p=In(r.data.style)||{};r.data.normalizedStyle=t(p.__ob__)?h({},p):p;var d=Dn(r,!0);for(s in f)e(d[s])&&Oa(c,s,"");for(s in d)(a=d[s])!==f[s]&&Oa(c,s,null==a?"":a)}}function Pn(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(/\s+/).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function Rn(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(/\s+/).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t);else{for(var n=" "+(e.getAttribute("class")||"")+" ",r=" "+t+" ";n.indexOf(r)>=0;)n=n.replace(r," ");e.setAttribute("class",n.trim())}}function Fn(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&h(t,ja(e.name||"v")),h(t,e),t}return"string"==typeof e?ja(e):void 0}}function Bn(e){Fa(function(){Fa(e)})}function Hn(e,t){(e._transitionClasses||(e._transitionClasses=[])).push(t),Pn(e,t)}function Un(e,t){e._transitionClasses&&l(e._transitionClasses,t),Rn(e,t)}function Vn(e,t,n){var r=zn(e,t),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===La?Ma:Ra,c=0,u=function(){e.removeEventListener(s,l),n()},l=function(t){t.target===e&&++c>=a&&u()};setTimeout(function(){c0&&(n=La,l=a,f=o.length):t===Ia?u>0&&(n=Ia,l=u,f=c.length):(l=Math.max(a,u),n=l>0?a>u?La:Ia:null,f=n?n===La?o.length:c.length:0),{type:n,timeout:l,propCount:f,hasTransform:n===La&&Ba.test(r[Da+"Property"])}}function Jn(e,t){for(;e.length1}function Yn(e,t){!0!==t.data.show&&qn(t)}function Qn(e,t,n){var r=t.value,i=e.multiple;if(!i||Array.isArray(r)){for(var o,a,s=0,c=e.options.length;s-1,a.selected!==o&&(a.selected=o);else if(y(er(a),r))return void(e.selectedIndex!==s&&(e.selectedIndex=s));i||(e.selectedIndex=-1)}}function Xn(e,t){for(var n=0,r=t.length;n=0&&a[i].lowerCasedTag!==s;i--);else i=0;if(i>=0){for(var c=a.length-1;c>=i;c--)t.end&&t.end(a[c].tag,n,r);a.length=i,o=i&&a[i-1].tag}else"br"===s?t.start&&t.start(e,[],!0,n,r):"p"===s&&(t.start&&t.start(e,[],!1,n,r),t.end&&t.end(e,n,r))}for(var i,o,a=[],s=t.expectHTML,c=t.isUnaryTag||Ni,u=t.canBeLeftOpenTag||Ni,l=0;e;){if(i=e,o&&Ns(o)){var f=o.toLowerCase(),p=Ls[f]||(Ls[f]=new RegExp("([\\s\\S]*?)("+f+"[^>]*>)","i")),d=0,v=e.replace(p,function(e,n,r){return d=r.length,Ns(f)||"noscript"===f||(n=n.replace(//g,"$1").replace(//g,"$1")),t.chars&&t.chars(n),""});l+=e.length-v.length,e=v,r(f,l-d,l)}else{var h=e.indexOf("<");if(0===h){if(fs.test(e)){var m=e.indexOf("--\x3e");if(m>=0){n(m+3);continue}}if(ps.test(e)){var g=e.indexOf("]>");if(g>=0){n(g+2);continue}}var y=e.match(ls);if(y){n(y[0].length);continue}var _=e.match(us);if(_){var b=l;n(_[0].length),r(_[1],b,l);continue}var $=function(){var t=e.match(ss);if(t){var r={tagName:t[1],attrs:[],start:l};n(t[0].length);for(var i,o;!(i=e.match(cs))&&(o=e.match(os));)n(o[0].length),r.attrs.push(o);if(i)return r.unarySlash=i[1],n(i[0].length),r.end=l,r}}();if($){!function(e){var n=e.tagName,i=e.unarySlash;s&&("p"===o&&rs(n)&&r(o),u(n)&&o===n&&r(n));for(var l=c(n)||"html"===n&&"head"===o||!!i,f=e.attrs.length,p=new Array(f),d=0;d=0){for(w=e.slice(h);!(us.test(w)||ss.test(w)||fs.test(w)||ps.test(w)||(C=w.indexOf("<",1))<0);)h+=C,w=e.slice(h);x=e.substring(0,h),n(h)}h<0&&(x=e,e=""),t.chars&&x&&t.chars(x)}if(e===i){t.chars&&t.chars(e);break}}r()}function mr(e,t){var n=t?Rs(t):Ps;if(n.test(e)){for(var r,i,o=[],a=n.lastIndex=0;r=n.exec(e);){i=r.index,i>a&&o.push(JSON.stringify(e.slice(a,i)));var s=tn(r[1].trim());o.push("_s("+s+")"),a=i+r[0].length}return a0,Ki=Vi&&Vi.indexOf("edge/")>0,qi=Vi&&Vi.indexOf("android")>0,Wi=Vi&&/iphone|ipad|ipod|ios/.test(Vi),Zi=Vi&&/chrome\/\d+/.test(Vi)&&!Ki,Gi=!1;if(Ui)try{var Yi={};Object.defineProperty(Yi,"passive",{get:function(){Gi=!0}}),window.addEventListener("test-passive",null,Yi)}catch(e){}var Qi,Xi,eo=function(){return void 0===Qi&&(Qi=!Ui&&"undefined"!=typeof global&&"server"===global.process.env.VUE_ENV),Qi},to=Ui&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__,no="undefined"!=typeof Symbol&&k(Symbol)&&"undefined"!=typeof Reflect&&k(Reflect.ownKeys),ro=function(){function e(){r=!1;var e=n.slice(0);n.length=0;for(var t=0;t1?v(n):n;for(var r=v(arguments,1),i=0,o=n.length;i1&&(t[n[0].trim()]=n[1].trim())}}),t}),ka=/^--/,Aa=/\s*!important$/,Oa=function(e,t,n){if(ka.test(t))e.style.setProperty(t,n);else if(Aa.test(n))e.style.setProperty(t,n.replace(Aa,""),"important");else{var r=Ta(t);if(Array.isArray(n))for(var i=0,o=n.length;iv?(f=e(i[g+1])?null:i[g+1].elm,y(n,f,i,d,g,o)):d>g&&b(n,r,p,v)}function w(r,i,o,a){if(r!==i){if(n(i.isStatic)&&n(r.isStatic)&&i.key===r.key&&(n(i.isCloned)||n(i.isOnce)))return i.elm=r.elm,void(i.componentInstance=r.componentInstance);var s,c=i.data;t(c)&&t(s=c.hook)&&t(s=s.prepatch)&&s(r,i);var u=i.elm=r.elm,l=r.children,f=i.children;if(t(c)&&h(i)){for(s=0;s',n.innerHTML.indexOf(t)>0}("\n","
"),ts=u("area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),ns=u("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),rs=u("address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track"),is=[/"([^"]*)"+/.source,/'([^']*)'+/.source,/([^\s"'=<>`]+)/.source],os=new RegExp("^\\s*"+/([^\s"'<>\/=]+)/.source+"(?:\\s*("+/(?:=)/.source+")\\s*(?:"+is.join("|")+"))?"),as="[a-zA-Z_][\\w\\-\\.]*",ss=new RegExp("^<((?:"+as+"\\:)?"+as+")"),cs=/^\s*(\/?)>/,us=new RegExp("^<\\/((?:"+as+"\\:)?"+as+")[^>]*>"),ls=/^]+>/i,fs=/^
+ :value="value"
+ @input="e => $emit('input', e)"
+
+
+ :modelValue="modelValue"
+ @update:modelValue="e => $emit('update:modelValue', e)"
+
+ :type="type"
+ :fixed="fixed"
+ :disabled="disabled"
+ :disabledColor="disabledColor"
+ :clearable="clearable"
+ :password="password"
+ :maxlength="maxlength"
+ :placeholder="placeholder"
+ :placeholderClass="placeholderClass"
+ :placeholderStyle="placeholderStyle"
+ :showWordLimit="showWordLimit"
+ :confirmType="confirmType"
+ :confirmHold="confirmHold"
+ :holdKeyboard="holdKeyboard"
+ :focus="focus"
+ :autoBlur="autoBlur"
+ :disableDefaultPadding="disableDefaultPadding"
+ :cursor="cursor"
+ :cursorSpacing="cursorSpacing"
+ :selectionStart="selectionStart"
+ :selectionEnd="selectionEnd"
+ :adjustPosition="adjustPosition"
+ :inputAlign="inputAlign"
+ :fontSize="fontSize"
+ :color="color"
+ :prefixIcon="prefixIcon"
+ :suffixIcon="suffixIcon"
+ :suffixIconStyle="suffixIconStyle"
+ :prefixIconStyle="prefixIconStyle"
+ :border="border"
+ :readonly="readonly"
+ :shape="shape"
+ :customStyle="customStyle"
+ :formatter="formatter"
+ :ignoreCompositionEvent="ignoreCompositionEvent"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/uni_modules/uview-plus/components/u--text/u--text.vue b/src/uni_modules/uview-plus/components/u--text/u--text.vue
new file mode 100644
index 00000000..9cdcf7b6
--- /dev/null
+++ b/src/uni_modules/uview-plus/components/u--text/u--text.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
diff --git a/src/uni_modules/uview-plus/components/u--textarea/u--textarea.vue b/src/uni_modules/uview-plus/components/u--textarea/u--textarea.vue
new file mode 100644
index 00000000..becd23db
--- /dev/null
+++ b/src/uni_modules/uview-plus/components/u--textarea/u--textarea.vue
@@ -0,0 +1,47 @@
+
+ $emit('input', e)"
+ @update:modelValue="e => $emit('update:modelValue', e)"
+ >
+
+
+
diff --git a/src/uni_modules/uview-plus/components/u-action-sheet/props.js b/src/uni_modules/uview-plus/components/u-action-sheet/props.js
new file mode 100644
index 00000000..2f365f0a
--- /dev/null
+++ b/src/uni_modules/uview-plus/components/u-action-sheet/props.js
@@ -0,0 +1,57 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+ props: {
+ // 操作菜单是否展示 (默认false)
+ show: {
+ type: Boolean,
+ default: () => defProps.actionSheet.show
+ },
+ // 标题
+ title: {
+ type: String,
+ default: () => defProps.actionSheet.title
+ },
+ // 选项上方的描述信息
+ description: {
+ type: String,
+ default: () => defProps.actionSheet.description
+ },
+ // 数据
+ actions: {
+ type: Array,
+ default: () => defProps.actionSheet.actions
+ },
+ // 取消按钮的文字,不为空时显示按钮
+ cancelText: {
+ type: String,
+ default: () => defProps.actionSheet.cancelText
+ },
+ // 点击某个菜单项时是否关闭弹窗
+ closeOnClickAction: {
+ type: Boolean,
+ default: () => defProps.actionSheet.closeOnClickAction
+ },
+ // 处理底部安全区(默认true)
+ safeAreaInsetBottom: {
+ type: Boolean,
+ default: () => defProps.actionSheet.safeAreaInsetBottom
+ },
+ // 小程序的打开方式
+ openType: {
+ type: String,
+ default: () => defProps.actionSheet.openType
+ },
+ // 点击遮罩是否允许关闭 (默认true)
+ closeOnClickOverlay: {
+ type: Boolean,
+ default: () => defProps.actionSheet.closeOnClickOverlay
+ },
+ // 圆角值
+ round: {
+ type: [Boolean, String, Number],
+ default: () => defProps.actionSheet.round
+ }
+ }
+})
diff --git a/src/uni_modules/uview-plus/components/u-action-sheet/u-action-sheet.vue b/src/uni_modules/uview-plus/components/u-action-sheet/u-action-sheet.vue
new file mode 100644
index 00000000..3c283400
--- /dev/null
+++ b/src/uni_modules/uview-plus/components/u-action-sheet/u-action-sheet.vue
@@ -0,0 +1,281 @@
+
+
+
+
+
+ {{description}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{cancelText}}
+
+
+
+
+
+
+
+
diff --git a/src/uni_modules/uview-plus/components/u-album/props.js b/src/uni_modules/uview-plus/components/u-album/props.js
new file mode 100644
index 00000000..eb0f1818
--- /dev/null
+++ b/src/uni_modules/uview-plus/components/u-album/props.js
@@ -0,0 +1,61 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+ props: {
+ // 图片地址,Array|Array